aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/modem_if')
-rw-r--r--drivers/misc/modem_if/Kconfig29
-rw-r--r--drivers/misc/modem_if/Makefile10
-rw-r--r--drivers/misc/modem_if/modem_debug.c429
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram.c2169
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram.h390
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram_ext_op.c1175
-rw-r--r--[-rwxr-xr-x]drivers/misc/modem_if/modem_link_device_hsic.c122
-rw-r--r--[-rwxr-xr-x]drivers/misc/modem_if/modem_link_device_hsic.h7
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld.c1981
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld.h532
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld_ext_op.c556
-rw-r--r--drivers/misc/modem_if/modem_link_device_spi.c1795
-rw-r--r--drivers/misc/modem_if/modem_link_device_spi.h257
-rw-r--r--drivers/misc/modem_if/modem_link_device_usb.c95
-rw-r--r--drivers/misc/modem_if/modem_link_device_usb.h2
-rw-r--r--drivers/misc/modem_if/modem_link_pm_usb.c183
-rw-r--r--drivers/misc/modem_if/modem_link_pm_usb.h15
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cbp72.c15
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cmc221.c84
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_esc6270.c339
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_mdm6600.c307
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_sprd8803.c230
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_xmm6262.c37
-rw-r--r--drivers/misc/modem_if/modem_prj.h289
-rw-r--r--drivers/misc/modem_if/modem_sim_slot_switch.c92
-rw-r--r--drivers/misc/modem_if/modem_utils.c600
-rw-r--r--drivers/misc/modem_if/modem_utils.h142
-rw-r--r--drivers/misc/modem_if/modem_variation.h21
-rw-r--r--drivers/misc/modem_if/sipc4_io_device.c252
-rw-r--r--drivers/misc/modem_if/sipc4_modem.c103
-rw-r--r--drivers/misc/modem_if/sipc5_io_device.c878
-rw-r--r--drivers/misc/modem_if/sipc5_modem.c103
32 files changed, 10642 insertions, 2597 deletions
diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig
index 94cb48c..6a6eeab 100644
--- a/drivers/misc/modem_if/Kconfig
+++ b/drivers/misc/modem_if/Kconfig
@@ -34,6 +34,16 @@ config CDMA_MODEM_MDM6600
depends on SEC_MODEM
default n
+config TDSCDMA_MODEM_SPRD8803
+ bool "modem chip : SPRD SC8803"
+ depends on SEC_MODEM
+ default n
+
+config GSM_MODEM_ESC6270
+ bool "modem chip : QC ESC6270"
+ depends on SEC_MODEM
+ default n
+
config LINK_DEVICE_MIPI
bool "modem driver link device MIPI-HSI"
depends on SEC_MODEM
@@ -44,6 +54,10 @@ config LINK_DEVICE_DPRAM
depends on SEC_MODEM
default n
+config LINK_DEVICE_PLD
+ bool "modem driver link device PLD"
+ depends on SEC_MODEM
+ default n
config LINK_DEVICE_USB
bool "modem driver link device USB"
depends on SEC_MODEM
@@ -59,6 +73,16 @@ config LINK_DEVICE_C2C
depends on SEC_MODEM
default n
+config LINK_DEVICE_SPI
+ bool "modem driver link device SPI"
+ depends on SEC_MODEM
+ default n
+
+config WORKQUEUE_FRONT
+ bool "IPC: SPI workqueue front"
+ depends on SEC_MODEM
+ default n
+
config IPC_CMC22x_OLD_RFS
bool "IPC: CMC22x ancient RFS"
depends on SEC_MODEM
@@ -73,3 +97,8 @@ config SIM_DETECT
bool "SIM_DETECT pin"
depends on SEC_MODEM
default n
+
+config SIM_SLOT_SWITCH
+ bool "SIM_SLOT_SWITCH"
+ depends on SEC_MODEM
+ default n
diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile
index dafc8c0..5bd62ea 100644
--- a/drivers/misc/modem_if/Makefile
+++ b/drivers/misc/modem_if/Makefile
@@ -4,7 +4,7 @@ EXTRA_CFLAGS += -Idrivers/misc/modem_if
obj-y += sipc5_modem.o sipc5_io_device.o
obj-y += sipc4_modem.o sipc4_io_device.o
-obj-y += modem_net_flowcontrol_device.o modem_utils.o modem_debug.o
+obj-y += modem_net_flowcontrol_device.o modem_utils.o
obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o
obj-$(CONFIG_UMTS_MODEM_XMM6262) += modem_modemctl_device_xmm6262.o
@@ -12,9 +12,15 @@ obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o
obj-$(CONFIG_CDMA_MODEM_CBP72) += modem_modemctl_device_cbp72.o
obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o
obj-$(CONFIG_CDMA_MODEM_MDM6600) += modem_modemctl_device_mdm6600.o
+obj-$(CONFIG_GSM_MODEM_ESC6270) += modem_modemctl_device_esc6270.o
+obj-$(CONFIG_TDSCDMA_MODEM_SPRD8803) += modem_modemctl_device_sprd8803.o
obj-$(CONFIG_LINK_DEVICE_MIPI) += modem_link_device_mipi.o
-obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o
+obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o
+obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o
obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o
obj-$(CONFIG_LINK_DEVICE_HSIC) += modem_link_device_hsic.o
obj-$(CONFIG_LINK_DEVICE_C2C) += modem_link_device_c2c.o
+obj-$(CONFIG_LINK_DEVICE_SPI) += modem_link_device_spi.o
+
+obj-$(CONFIG_SIM_SLOT_SWITCH) += modem_sim_slot_switch.o
diff --git a/drivers/misc/modem_if/modem_debug.c b/drivers/misc/modem_if/modem_debug.c
deleted file mode 100644
index 1ad3073..0000000
--- a/drivers/misc/modem_if/modem_debug.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/* linux/drivers/misc/modem_if/modem_debug.c
- *
- * 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/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/miscdevice.h>
-#include <linux/if_arp.h>
-
-#include <linux/uaccess.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/irq.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/wakelock.h>
-
-#include <linux/platform_data/modem.h>
-#include "modem_prj.h"
-#include "modem_variation.h"
-#include "modem_utils.h"
-
-static const char *hex = "0123456789abcdef";
-
-static inline void mif_irq2str(struct mif_event_buff *evtb, char *buff)
-{
- int i;
- char tb[32];
-
- if (evtb->link_type == LINKDEV_DPRAM) {
- struct dpram_irq_buff *irqb = &evtb->dpram_irqb;
-
- sprintf(tb, "{0x%04X %d 0x%04X}",
- irqb->magic, irqb->access, irqb->int2ap);
- strcat(buff, tb);
-
- for (i = 0; i < IPC_RFS; i++) {
- snprintf(tb, 32, " {%d: %u %u %u %u}", i,
- irqb->qsp[i].txq.in, irqb->qsp[i].txq.out,
- irqb->qsp[i].rxq.in, irqb->qsp[i].rxq.out);
- strcat(buff, tb);
- }
- } else {
- sprintf(tb, "link unspeicified");
- strcat(buff, tb);
- }
-}
-
-static inline void mif_dump2hex(const char *data, size_t len, char *buff)
-{
- char *src = (char *)data;
- int i;
- char tb[4];
-
- tb[3] = 0;
- for (i = 0; i < len; i++) {
- tb[0] = hex[(*src >> 4) & 0xf];
- tb[1] = hex[*src & 0xf];
- tb[2] = ' ';
- strcat(buff, tb);
- src++;
- }
-}
-
-static inline void mif_fin_str(char *buff)
-{
- char tb[4];
- sprintf(tb, "\n");
- strcat(buff, tb);
-}
-
-static void mif_log2str(struct mif_event_buff *evtb, char *buff)
-{
- struct timeval *tv;
- struct tm date;
-
- tv = &evtb->tv;
-
- time_to_tm((tv->tv_sec - sys_tz.tz_minuteswest * 60), 0, &date);
- sprintf(evtb->time, "%02d:%02d:%02d.%03ld",
- date.tm_hour, date.tm_min, date.tm_sec, (tv->tv_usec / 1000));
-
- if (evtb->evt == MIF_IRQ_EVT) {
- sprintf(buff, "%s IRQ <%s> ", evtb->time, evtb->ld);
- mif_irq2str(evtb, buff);
- mif_fin_str(buff);
- } else {
- size_t len = evtb->len < (MAX_MIF_LOG_LEN >> 3) ?
- evtb->len : (MAX_MIF_LOG_LEN >> 3);
- sprintf(buff, "%s [%d] <%s:%s> ",
- evtb->time, evtb->evt, evtb->iod, evtb->ld);
- mif_dump2hex(evtb->data, len, buff);
- mif_fin_str(buff);
- }
-}
-
-static void mif_print_logs(struct modem_ctl *mc)
-{
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- u8 *buff;
-
- buff = kmalloc(2048, GFP_ATOMIC);
- if (!buff)
- return;
-
- while (1) {
- skb = skb_dequeue(&mc->evtq);
- if (!skb)
- break;
-
- evtb = (struct mif_event_buff *)skb->data;
- memset(buff, 0, 2048);
-
- mif_log2str(evtb, buff);
- pr_info("mif: %s", buff);
-
- dev_kfree_skb_any(skb);
- }
-
- kfree(buff);
-}
-
-static void mif_save_logs(struct modem_ctl *mc)
-{
- struct file *fp = mc->log_fp;
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- int qlen = mc->evtq.qlen;
- int i;
- int ret;
- mm_segment_t old_fs;
-
- old_fs = get_fs();
- set_fs(get_ds());
-
- for (i = 0; i < qlen; i++) {
- skb = skb_dequeue(&mc->evtq);
-
-#if 0
- if (evtb->evt < mc->log_level) {
- ret = fp->f_op->write(fp, skb->data,
- MAX_MIF_EVT_BUFF_SIZE, &fp->f_pos);
- if (ret < 0) {
- mif_log2str((struct mif_event_buff *)skb->data,
- mc->buff);
- printk(KERN_ERR "%s", mc->buff);
- }
- }
-#else
- evtb = (struct mif_event_buff *)skb->data;
- if (evtb->evt < mc->log_level) {
- mif_log2str(evtb, mc->buff);
- ret = fp->f_op->write(fp, mc->buff, strlen(mc->buff),
- &fp->f_pos);
- if (ret < 0)
- printk(KERN_ERR "%s", mc->buff);
- }
-#endif
-
- dev_kfree_skb_any(skb);
- }
-
- set_fs(old_fs);
-}
-
-static void mif_evt_work(struct work_struct *work)
-{
- struct modem_ctl *mc = container_of(work, struct modem_ctl, evt_work);
- struct file *fp = mc->log_fp;
- loff_t size;
- mm_segment_t old_fs;
-
- /* use_mif_log */
-
- if (!mc->log_level || mc->fs_failed) {
- mif_print_logs(mc);
- return;
- }
-
- /* use_mif_log && log_level && !fs_failed */
-
- if (!mc->fs_ready) {
- if (mc->evtq.qlen > 1000)
- mif_print_logs(mc);
- return;
- }
-
- /* use_mif_log && log_level && !fs_failed && fs_ready */
-
- if (fp) {
- mif_save_logs(mc);
-
- old_fs = get_fs();
- set_fs(get_ds());
- size = fp->f_pos;
- set_fs(old_fs);
- if (size > MAX_MIF_LOG_FILE_SIZE) {
- mif_err("%s size %lld > %d\n", mc->log_path, size,
- MAX_MIF_LOG_FILE_SIZE);
- mif_close_log_file(mc);
- mif_open_log_file(mc);
- }
- }
-}
-
-void mif_irq_log(struct modem_ctl *mc, struct sk_buff *skb)
-{
- if (!mc || !mc->use_mif_log)
- return;
- skb_queue_tail(&mc->evtq, skb);
-}
-
-void mif_ipc_log(struct modem_ctl *mc, enum mif_event_id evt,
- struct io_device *iod, struct link_device *ld,
- u8 *data, unsigned size)
-{
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- unsigned len;
-
- if (!mc || !mc->use_mif_log)
- return;
-
- skb = alloc_skb(MAX_MIF_EVT_BUFF_SIZE, GFP_ATOMIC);
- if (!skb)
- return;
-
- evtb = (struct mif_event_buff *)skb_put(skb, MAX_MIF_EVT_BUFF_SIZE);
- memset(evtb, 0, MAX_MIF_EVT_BUFF_SIZE);
-
- do_gettimeofday(&evtb->tv);
- evtb->evt = evt;
-
- strncpy(evtb->mc, mc->name, MAX_MIF_NAME_LEN);
-
- if (iod)
- strncpy(evtb->iod, iod->name, MAX_MIF_NAME_LEN);
-
- if (ld) {
- strncpy(evtb->ld, ld->name, MAX_MIF_NAME_LEN);
- evtb->link_type = ld->link_type;
- }
-
- len = min_t(unsigned, MAX_MIF_LOG_LEN, size);
- memcpy(evtb->data, data, len);
-
- evtb->rcvd = size;
- evtb->len = len;
-
- skb_queue_tail(&mc->evtq, skb);
-}
-
-void mif_flush_logs(struct modem_ctl *mc)
-{
- if (!mc || !mc->use_mif_log)
- return;
-
- if (atomic_read(&mc->log_open))
- queue_work(mc->evt_wq, &mc->evt_work);
-}
-
-int mif_init_log(struct modem_ctl *mc)
-{
- char wq_name[32];
- char wq_suffix[32];
-
- mc->log_level = 0;
-
- atomic_set(&mc->log_open, 0);
-
- memset(wq_name, 0, sizeof(wq_name));
- memset(wq_suffix, 0, sizeof(wq_suffix));
- strncpy(wq_name, mc->name, sizeof(wq_name));
- snprintf(wq_suffix, sizeof(wq_suffix), "%s", "_evt_wq");
- strncat(wq_name, wq_suffix, sizeof(wq_suffix));
- mc->evt_wq = create_singlethread_workqueue(wq_name);
- if (!mc->evt_wq) {
- printk(KERN_ERR "<%s:%s> fail to create %s\n",
- __func__, mc->name, wq_name);
- return -EFAULT;
- }
- printk(KERN_ERR "<%s:%s> %s created\n",
- __func__, mc->name, wq_name);
-
- INIT_WORK(&mc->evt_work, mif_evt_work);
-
- skb_queue_head_init(&mc->evtq);
-
- mc->buff = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!mc->buff) {
- printk(KERN_ERR "<%s> kzalloc fail\n", __func__);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void mif_set_log_level(struct modem_ctl *mc)
-{
- struct file *fp;
- int ret;
- mm_segment_t old_fs;
-
- mc->log_level = 0;
-
- old_fs = get_fs();
- set_fs(get_ds());
- fp = filp_open(MIF_LOG_LV_FILE, O_RDONLY, 0);
- if (IS_ERR(fp)) {
- printk(KERN_ERR "<%s:%s> %s open fail\n",
- __func__, mc->name, MIF_LOG_LV_FILE);
- } else {
- char tb;
-
- ret = fp->f_op->read(fp, &tb, 1, &fp->f_pos);
- if (ret > 0) {
- mc->log_level = tb & 0xF;
- } else {
- printk(KERN_ERR "<%s:%s> read fail (err %d)\n",
- __func__, mc->name, ret);
- }
- }
- set_fs(old_fs);
-
- if (mc->use_mif_log && !mc->log_level)
- atomic_set(&mc->log_open, 1);
-
- printk(KERN_ERR "<%s:%s> log level = %d\n",
- __func__, mc->name, mc->log_level);
-}
-
-int mif_open_log_file(struct modem_ctl *mc)
-{
- struct timeval now;
- struct tm date;
- mm_segment_t old_fs;
-
- if (!mc || !mc->use_mif_log)
- return -EINVAL;
-
- if (!mc->log_level) {
- printk(KERN_ERR "<%s:%s> IPC logger is disabled.\n",
- __func__, mc->name);
- return -EINVAL;
- }
-
- if (!mc->fs_ready) {
- printk(KERN_ERR "<%s:%s> File system is not ready.\n",
- __func__, mc->name);
- return -EINVAL;
- }
-
- if (mc->fs_failed) {
- printk(KERN_ERR "<%s:%s> Log file cannot be created.\n",
- __func__, mc->name);
- return -EINVAL;
- }
-
- do_gettimeofday(&now);
- time_to_tm((now.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date);
-
- snprintf(mc->log_path, MAX_MIF_LOG_PATH_LEN,
- "%s/%s_mif_log.%ld%02d%02d.%02d%02d%02d.txt",
- MIF_LOG_DIR, mc->name,
- (1900 + date.tm_year), (1 + date.tm_mon), date.tm_mday,
- date.tm_hour, date.tm_min, date.tm_sec);
-
- old_fs = get_fs();
- set_fs(get_ds());
- mc->log_fp = filp_open(mc->log_path, O_RDWR|O_CREAT, 0666);
- set_fs(old_fs);
- if (IS_ERR(mc->log_fp)) {
- printk(KERN_ERR "<%s:%s> %s open fail\n",
- __func__, mc->name, mc->log_path);
- mc->log_fp = NULL;
- mc->fs_failed = true;
- return -ENOENT;
- }
-
- atomic_set(&mc->log_open, 1);
-
- mif_err("open %s\n", mc->log_path);
-
- return 0;
-}
-
-void mif_close_log_file(struct modem_ctl *mc)
-{
- mm_segment_t old_fs;
-
- if (!mc || !mc->use_mif_log || !mc->log_level || mc->fs_failed ||
- !mc->fs_ready || !mc->log_fp)
- return;
-
- atomic_set(&mc->log_open, 0);
-
- flush_work_sync(&mc->evt_work);
-
- mif_err("close %s\n", mc->log_path);
-
- mif_save_logs(mc);
-
- old_fs = get_fs();
- set_fs(get_ds());
- filp_close(mc->log_fp, NULL);
- set_fs(old_fs);
-
- mc->log_fp = NULL;
-}
-
diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c
index 862de30..a650ed9 100644
--- a/drivers/misc/modem_if/modem_link_device_dpram.c
+++ b/drivers/misc/modem_if/modem_link_device_dpram.c
@@ -31,184 +31,79 @@
#include "modem_link_device_dpram.h"
#include "modem_utils.h"
-const inline char *get_dev_name(int dev)
-{
- if (dev == IPC_FMT)
- return "FMT";
- else if (dev == IPC_RAW)
- return "RAW";
- else if (dev == IPC_RFS)
- return "RFS";
- else
- return "NONE";
-}
-
-static void log_dpram_irq(struct dpram_link_device *dpld, u16 int2ap)
-{
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- struct dpram_irq_buff *irqb;
- struct link_device *ld = &dpld->ld;
-
- skb = alloc_skb(MAX_MIF_EVT_BUFF_SIZE, GFP_ATOMIC);
- if (!skb)
- return;
-
- evtb = (struct mif_event_buff *)skb_put(skb, MAX_MIF_EVT_BUFF_SIZE);
- memset(evtb, 0, MAX_MIF_EVT_BUFF_SIZE);
-
- do_gettimeofday(&evtb->tv);
- evtb->evt = MIF_IRQ_EVT;
-
- strncpy(evtb->mc, ld->mc->name, MAX_MIF_NAME_LEN);
- strncpy(evtb->ld, ld->name, MAX_MIF_NAME_LEN);
- evtb->link_type = ld->link_type;
-
- irqb = &evtb->dpram_irqb;
-
- irqb->magic = dpld->dpctl->get_magic();
- irqb->access = dpld->dpctl->get_access();
-
- irqb->qsp[IPC_FMT].txq.in = dpld->dpctl->get_tx_head(IPC_FMT);
- irqb->qsp[IPC_FMT].txq.out = dpld->dpctl->get_tx_tail(IPC_FMT);
- irqb->qsp[IPC_FMT].rxq.in = dpld->dpctl->get_rx_head(IPC_FMT);
- irqb->qsp[IPC_FMT].rxq.out = dpld->dpctl->get_rx_tail(IPC_FMT);
-
- irqb->qsp[IPC_RAW].txq.in = dpld->dpctl->get_tx_head(IPC_RAW);
- irqb->qsp[IPC_RAW].txq.out = dpld->dpctl->get_tx_tail(IPC_RAW);
- irqb->qsp[IPC_RAW].rxq.in = dpld->dpctl->get_rx_head(IPC_RAW);
- irqb->qsp[IPC_RAW].rxq.out = dpld->dpctl->get_rx_tail(IPC_RAW);
-
- irqb->int2ap = int2ap;
-
- evtb->rcvd = sizeof(struct dpram_irq_buff);
- evtb->len = sizeof(struct dpram_irq_buff);
-
- mif_irq_log(ld->mc, skb);
- mif_flush_logs(ld->mc);
-}
+/*
+** Function prototypes for basic DPRAM operations
+*/
+static inline void clear_intr(struct dpram_link_device *dpld);
+static inline u16 recv_intr(struct dpram_link_device *dpld);
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask);
+
+static inline u16 get_magic(struct dpram_link_device *dpld);
+static inline void set_magic(struct dpram_link_device *dpld, u16 val);
+static inline u16 get_access(struct dpram_link_device *dpld);
+static inline void set_access(struct dpram_link_device *dpld, u16 val);
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id);
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out);
+
+static void handle_cp_crash(struct dpram_link_device *dpld);
+static int trigger_force_cp_crash(struct dpram_link_device *dpld);
+static void dpram_dump_memory(struct link_device *ld, char *buff);
-static int memcmp16_to_io(const void __iomem *to, void *from, int size)
+/*
+** Functions for debugging
+*/
+static inline void log_dpram_status(struct dpram_link_device *dpld)
{
- u16 *d = (u16 *)to;
- u16 *s = (u16 *)from;
- int count = size >> 1;
- int diff = 0;
- int i;
- u16 d1;
- u16 s1;
-
- for (i = 0; i < count; i++) {
- d1 = ioread16(d);
- s1 = *s;
- if (d1 != s1) {
- diff++;
- mif_info("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1);
- }
- d++;
- s++;
- }
-
- return diff;
+ pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} "
+ "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n",
+ dpld->ld.mc->name,
+ get_magic(dpld), get_access(dpld),
+ get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT),
+ get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT),
+ get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW),
+ get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW),
+ recv_intr(dpld));
}
-static int test_dpram(char *dp_name, u8 __iomem *start, u32 size)
+static void set_dpram_map(struct dpram_link_device *dpld,
+ struct mif_irq_map *map)
{
- u8 __iomem *dst;
- int i;
- u16 val;
-
- mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size);
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16((i & 0xFFFF), dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != (i & 0xFFFF)) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n",
- dp_name, i, val, (i & 0xFFFF));
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0x00FF, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0x00FF) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0x0FF0, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0x0FF0) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0xFF00, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0xFF00) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- mif_info("%s: PASS!!!\n", dp_name);
- return 0;
+ map->magic = get_magic(dpld);
+ map->access = get_access(dpld);
+
+ map->fmt_tx_in = get_tx_head(dpld, IPC_FMT);
+ map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT);
+ map->fmt_rx_in = get_rx_head(dpld, IPC_FMT);
+ map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT);
+ map->raw_tx_in = get_tx_head(dpld, IPC_RAW);
+ map->raw_tx_out = get_tx_tail(dpld, IPC_RAW);
+ map->raw_rx_in = get_rx_head(dpld, IPC_RAW);
+ map->raw_rx_out = get_rx_tail(dpld, IPC_RAW);
+
+ map->cp2ap = recv_intr(dpld);
}
+/*
+** RXB (DPRAM RX buffer) functions
+*/
static struct dpram_rxb *rxbq_create_pool(unsigned size, int count)
{
struct dpram_rxb *rxb;
@@ -304,12 +199,16 @@ static inline void rxb_clear(struct dpram_rxb *rxb)
rxb->len = 0;
}
+/*
+** DPRAM operations
+*/
static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*),
- unsigned long flag, const char *name, struct link_device *ld)
+ unsigned long flag, const char *name,
+ struct dpram_link_device *dpld)
{
- int ret = 0;
+ int ret;
- ret = request_irq(irq, isr, flag, name, ld);
+ ret = request_irq(irq, isr, flag, name, dpld);
if (ret) {
mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret);
return ret;
@@ -319,11 +218,311 @@ static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*),
if (ret)
mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret);
- mif_info("%s: IRQ#%d handler registered\n", name, irq);
+ mif_info("%s (#%d) handler registered\n", name, irq);
return 0;
}
+static inline void clear_intr(struct dpram_link_device *dpld)
+{
+ if (likely(dpld->dpctl->clear_intr))
+ dpld->dpctl->clear_intr();
+}
+
+static inline u16 recv_intr(struct dpram_link_device *dpld)
+{
+ if (likely(dpld->dpctl->recv_intr))
+ return dpld->dpctl->recv_intr();
+ else
+ return ioread16(dpld->mbx2ap);
+}
+
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask)
+{
+ if (likely(dpld->dpctl->send_intr))
+ dpld->dpctl->send_intr(mask);
+ else
+ iowrite16(mask, dpld->mbx2cp);
+}
+
+static inline u16 get_magic(struct dpram_link_device *dpld)
+{
+ return ioread16(dpld->magic);
+}
+
+static inline void set_magic(struct dpram_link_device *dpld, u16 val)
+{
+ iowrite16(val, dpld->magic);
+}
+
+static inline u16 get_access(struct dpram_link_device *dpld)
+{
+ return ioread16(dpld->access);
+}
+
+static inline void set_access(struct dpram_link_device *dpld, u16 val)
+{
+ iowrite16(val, dpld->access);
+}
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->txq.head);
+}
+
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->txq.tail);
+}
+
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)in, dpld->dev[id]->txq.head);
+
+ do {
+ /* Check head value written */
+ val = ioread16(dpld->dev[id]->txq.head);
+ if (likely(val == in))
+ return;
+
+ mif_err("ERR: %s txq.head(%d) != in(%d)\n",
+ get_dev_name(id), val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16((u16)in, dpld->dev[id]->txq.head);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)out, dpld->dev[id]->txq.tail);
+
+ do {
+ /* Check tail value written */
+ val = ioread16(dpld->dev[id]->txq.tail);
+ if (likely(val == out))
+ return;
+
+ mif_err("ERR: %s txq.tail(%d) != out(%d)\n",
+ get_dev_name(id), val, out);
+ udelay(100);
+
+ /* Write tail value again */
+ iowrite16((u16)out, dpld->dev[id]->txq.tail);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.buff;
+}
+
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.size;
+}
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->rxq.head);
+}
+
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->rxq.tail);
+}
+
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)in, dpld->dev[id]->rxq.head);
+
+ do {
+ /* Check head value written */
+ val = ioread16(dpld->dev[id]->rxq.head);
+ if (val == in)
+ return;
+
+ mif_err("ERR: %s rxq.head(%d) != in(%d)\n",
+ get_dev_name(id), val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16((u16)in, dpld->dev[id]->rxq.head);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)out, dpld->dev[id]->rxq.tail);
+
+ do {
+ /* Check tail value written */
+ val = ioread16(dpld->dev[id]->rxq.tail);
+ if (val == out)
+ return;
+
+ mif_err("ERR: %s rxq.tail(%d) != out(%d)\n",
+ get_dev_name(id), val, out);
+ udelay(100);
+
+ /* Write tail value again */
+ iowrite16((u16)out, dpld->dev[id]->rxq.tail);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.buff;
+}
+
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.size;
+}
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_req_ack;
+}
+
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_res_ack;
+}
+
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_send;
+}
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out)
+{
+ if (in >= size)
+ return false;
+
+ if (out >= size)
+ return false;
+
+ return true;
+}
+
+/* Get free space in the TXQ as well as in & out pointers */
+static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ int space;
+
+ do {
+ head = get_tx_head(dpld, dev);
+ tail = get_tx_tail(dpld, dev);
+
+ space = (head < tail) ? (tail - head - 1) :
+ (qsize + tail - head - 1);
+ mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, space);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return space;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_TXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_tx_head(dpld, dev, 0);
+ set_tx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
+/* Get data size in the RXQ as well as in & out pointers */
+static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ u32 rcvd;
+
+ do {
+ head = get_rx_head(dpld, dev);
+ tail = get_rx_tail(dpld, dev);
+ if (head == tail) {
+ *in = head;
+ *out = tail;
+ return 0;
+ }
+
+ rcvd = (head > tail) ? (head - tail) : (qsize - tail + head);
+ mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, rcvd);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return rcvd;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_RXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_rx_head(dpld, dev, 0);
+ set_rx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
/*
** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success
*/
@@ -335,10 +534,21 @@ static int dpram_wake_up(struct dpram_link_device *dpld)
return 0;
if (dpld->dpctl->wakeup() < 0) {
- mif_info("%s: ERR! <%pF> DPRAM wakeup fail\n",
+ mif_err("%s: ERR! <%pf> DPRAM wakeup fail once\n",
ld->name, __builtin_return_address(0));
- return -EACCES;
+
+ if (dpld->dpctl->sleep)
+ dpld->dpctl->sleep();
+
+ udelay(10);
+
+ if (dpld->dpctl->wakeup() < 0) {
+ mif_err("%s: ERR! <%pf> DPRAM wakeup fail twice\n",
+ ld->name, __builtin_return_address(0));
+ return -EACCES;
+ }
}
+
atomic_inc(&dpld->accessing);
return 0;
}
@@ -361,19 +571,19 @@ static int dpram_check_access(struct dpram_link_device *dpld)
{
struct link_device *ld = &dpld->ld;
int i;
- u16 magic = dpld->dpctl->get_magic();
- u16 access = dpld->dpctl->get_access();
+ u16 magic = get_magic(dpld);
+ u16 access = get_access(dpld);
if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
return 0;
- for (i = 1; i <= 10; i++) {
+ for (i = 1; i <= 100; i++) {
mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n",
ld->name, magic, access, i);
- mdelay(1);
+ udelay(100);
- magic = dpld->dpctl->get_magic();
- access = dpld->dpctl->get_access();
+ magic = get_magic(dpld);
+ access = get_access(dpld);
if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
return 0;
}
@@ -388,13 +598,13 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld)
/* Check DPRAM mode */
if (ld->mode != LINK_MODE_IPC) {
- mif_info("%s: ERR! <%pF> ld->mode != LINK_MODE_IPC\n",
+ mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n",
ld->name, __builtin_return_address(0));
return false;
}
if (dpram_check_access(dpld) < 0) {
- mif_info("%s: ERR! <%pF> dpram_check_access fail\n",
+ mif_info("%s: ERR! <%pf> dpram_check_access fail\n",
ld->name, __builtin_return_address(0));
return false;
}
@@ -402,224 +612,108 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld)
return true;
}
-static inline bool dpram_circ_valid(u32 size, u32 in, u32 out)
-{
- if (in >= size)
- return false;
-
- if (out >= size)
- return false;
-
- return true;
-}
-
-/* get the size of the TXQ */
-static inline int dpram_get_txq_size(struct dpram_link_device *dpld, int dev)
-{
- return dpld->dpctl->get_tx_buff_size(dev);
-}
-
-/* get in & out pointers of the TXQ */
-static inline void dpram_get_txq_ptrs(struct dpram_link_device *dpld, int dev,
- u32 *in, u32 *out)
-{
- *in = dpld->dpctl->get_tx_head(dev);
- *out = dpld->dpctl->get_tx_tail(dev);
-}
-
-/* get free space in the TXQ as well as in & out pointers */
-static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev,
- u32 qsize, u32 *in, u32 *out)
-{
- struct link_device *ld = &dpld->ld;
-
- *in = dpld->dpctl->get_tx_head(dev);
- *out = dpld->dpctl->get_tx_tail(dev);
-
- if (!dpram_circ_valid(qsize, *in, *out)) {
- mif_info("%s: ERR! <%pF> "
- "%s_TXQ invalid (size:%d in:%d out:%d)\n",
- ld->name, __builtin_return_address(0),
- get_dev_name(dev), qsize, *in, *out);
- dpld->dpctl->set_tx_head(dev, 0);
- dpld->dpctl->set_tx_tail(dev, 0);
- *in = 0;
- *out = 0;
- return -EINVAL;
- }
-
- return (*in < *out) ? (*out - *in - 1) : (qsize + *out - *in - 1);
-}
-
static void dpram_ipc_write(struct dpram_link_device *dpld, int dev,
u32 qsize, u32 in, u32 out, struct sk_buff *skb)
{
struct link_device *ld = &dpld->ld;
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
- struct io_device *iod = skbpriv(skb)->iod;
- u8 __iomem *dst = dpctl->get_tx_buff(dev);
+ u8 __iomem *buff = get_tx_buff(dpld, dev);
u8 *src = skb->data;
u32 len = skb->len;
-
- /* check queue status */
- mif_debug("%s: {FMT %u %u %u %u} {RAW %u %u %u %u} ...\n", ld->name,
- dpctl->get_tx_head(IPC_FMT), dpctl->get_tx_tail(IPC_FMT),
- dpctl->get_rx_head(IPC_FMT), dpctl->get_rx_tail(IPC_FMT),
- dpctl->get_tx_head(IPC_RAW), dpctl->get_tx_tail(IPC_RAW),
- dpctl->get_rx_head(IPC_RAW), dpctl->get_rx_tail(IPC_RAW));
-
- if (dev == IPC_FMT) {
- mif_ipc_log(ld->mc, MIF_LNK_TX_EVT, iod, ld, src, len);
- mif_flush_logs(ld->mc);
- }
+ u32 inp;
+ struct mif_irq_map map;
if (in < out) {
/* +++++++++ in ---------- out ++++++++++ */
- memcpy((dst + in), src, len);
+ memcpy((buff + in), src, len);
} else {
/* ------ out +++++++++++ in ------------ */
u32 space = qsize - in;
/* 1) in -> buffer end */
- memcpy((dst + in), src, ((len > space) ? space : len));
+ memcpy((buff + in), src, ((len > space) ? space : len));
/* 2) buffer start -> out */
if (len > space)
- memcpy(dst, (src + space), (len - space));
+ memcpy(buff, (src + space), (len - space));
}
/* update new in pointer */
- in += len;
- if (in >= qsize)
- in -= qsize;
- dpctl->set_tx_head(dev, in);
+ inp = in + len;
+ if (inp >= qsize)
+ inp -= qsize;
+ set_tx_head(dpld, dev, inp);
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write"));
+ mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len);
+ }
+
+ if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) {
+ mif_err("%s: memcmp16_to_io fail\n", ld->name);
+ trigger_force_cp_crash(dpld);
+ }
}
static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev)
{
struct link_device *ld = &dpld->ld;
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
struct sk_buff_head *txq = ld->skb_txq[dev];
struct sk_buff *skb;
- u32 qsize = dpram_get_txq_size(dpld, dev);
+ unsigned long int flags;
+ u32 qsize = get_tx_buff_size(dpld, dev);
u32 in;
u32 out;
int space;
int copied = 0;
- u16 mask = 0;
- unsigned long int flags;
- while (1) {
- skb = skb_dequeue(txq);
- if (unlikely(!skb))
- break;
+ spin_lock_irqsave(&dpld->tx_lock[dev], flags);
+ while (1) {
space = dpram_get_txq_space(dpld, dev, qsize, &in, &out);
if (unlikely(space < 0)) {
- skb_queue_head(txq, skb);
- return -ENOSPC;
+ spin_unlock_irqrestore(&dpld->tx_lock[dev], flags);
+ dpram_reset_tx_circ(dpld, dev);
+ return space;
}
+ skb = skb_dequeue(txq);
+ if (unlikely(!skb))
+ break;
+
if (unlikely(space < skb->len)) {
atomic_set(&dpld->res_required[dev], 1);
skb_queue_head(txq, skb);
- mask = dpctl->get_mask_req_ack(dev);
+ spin_unlock_irqrestore(&dpld->tx_lock[dev], flags);
mif_info("%s: %s "
"qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n",
ld->name, get_dev_name(dev),
qsize, in, out, space, skb->len);
- break;
+ return -ENOSPC;
}
- /* TX if there is enough room in the queue
- */
- mif_debug("%s: %s "
- "qsize[%u] in[%u] out[%u] free[%u] >= len[%u]\n",
- ld->name, get_dev_name(dev),
- qsize, in, out, space, skb->len);
-
- spin_lock_irqsave(&dpld->tx_lock, flags);
+ /* TX if there is enough room in the queue */
dpram_ipc_write(dpld, dev, qsize, in, out, skb);
- spin_unlock_irqrestore(&dpld->tx_lock, flags);
-
copied += skb->len;
-
dev_kfree_skb_any(skb);
}
- if (mask)
- return -ENOSPC;
- else
- return copied;
-}
-
-static void dpram_trigger_crash(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- struct io_device *iod;
- int i;
-
- for (i = 0; i < dpld->max_ipc_dev; i++) {
- mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i));
- skb_queue_purge(ld->skb_txq[i]);
- }
-
- ld->mode = LINK_MODE_ULOAD;
-
- iod = link_get_iod_with_format(ld, IPC_FMT);
- iod->modem_state_changed(iod, STATE_CRASH_EXIT);
-
- iod = link_get_iod_with_format(ld, IPC_BOOT);
- iod->modem_state_changed(iod, STATE_CRASH_EXIT);
-
- iod = link_get_iod_with_channel(ld, PS_DATA_CH_0);
- if (iod)
- iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0);
-}
-
-static int dpram_trigger_force_cp_crash(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- int ret;
- int cnt = 5000;
-
- mif_info("%s\n", ld->name);
-
- dpld->dpctl->send_intr(INT_CMD(INT_CMD_CRASH_EXIT));
-
- while (cnt--) {
- ret = try_wait_for_completion(&dpld->crash_start_complete);
- if (ret)
- break;
- udelay(1000);
- }
-
- if (!ret) {
- mif_info("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->name);
- dpram_trigger_crash(dpld);
- }
+ spin_unlock_irqrestore(&dpld->tx_lock[dev], flags);
- return 0;
+ return copied;
}
static void dpram_ipc_rx_task(unsigned long data)
{
- struct link_device *ld;
- struct dpram_link_device *dpld;
- struct dpram_rxb *rxb;
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = &dpld->ld;
struct io_device *iod;
- u32 qlen;
+ struct dpram_rxb *rxb;
+ unsigned qlen;
int i;
- dpld = (struct dpram_link_device *)data;
- ld = &dpld->ld;
-
for (i = 0; i < dpld->max_ipc_dev; i++) {
- if (i == IPC_RAW)
- iod = link_get_iod_with_format(ld, IPC_MULTI_RAW);
- else
- iod = link_get_iod_with_format(ld, i);
-
+ iod = dpld->iod[i];
qlen = rxbq_size(&dpld->rxbq[i]);
while (qlen > 0) {
rxb = rxbq_get_data_rxb(&dpld->rxbq[i]);
@@ -656,37 +750,24 @@ static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst,
ret == 0 : no data
ret > 0 : valid data
*/
-static int dpram_ipc_recv_data(struct dpram_link_device *dpld, int dev,
- u16 non_cmd)
+static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev)
{
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
struct link_device *ld = &dpld->ld;
struct dpram_rxb *rxb;
- u8 __iomem *src = dpctl->get_rx_buff(dev);
- u32 in = dpctl->get_rx_head(dev);
- u32 out = dpctl->get_rx_tail(dev);
- u32 qsize = dpctl->get_rx_buff_size(dev);
- u32 rcvd = 0;
-
- if (in == out)
- return 0;
-
- if (dev == IPC_FMT)
- log_dpram_irq(dpld, non_cmd);
-
- /* Get data length in DPRAM*/
- rcvd = (in > out) ? (in - out) : (qsize - out + in);
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ struct mif_irq_map map;
- mif_debug("%s: %s qsize[%u] in[%u] out[%u] rcvd[%u]\n",
- ld->name, get_dev_name(dev), qsize, in, out, rcvd);
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0)
+ return rcvd;
- /* Check each queue */
- if (!dpram_circ_valid(qsize, in, out)) {
- mif_info("%s: ERR! %s_RXQ invalid (size:%d in:%d out:%d)\n",
- ld->name, get_dev_name(dev), qsize, in, out);
- dpctl->set_rx_head(dev, 0);
- dpctl->set_rx_tail(dev, 0);
- return -EINVAL;
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
}
/* Allocate an rxb */
@@ -704,30 +785,95 @@ static int dpram_ipc_recv_data(struct dpram_link_device *dpld, int dev,
out += rcvd;
if (out >= qsize)
out -= qsize;
- dpctl->set_rx_tail(dev, out);
+ set_rx_tail(dpld, dev, out);
return rcvd;
}
-static void dpram_purge_rx_circ(struct dpram_link_device *dpld, int dev)
+/*
+ ret < 0 : error
+ ret == 0 : no data
+ ret > 0 : valid data
+*/
+static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev)
{
- u32 in = dpld->dpctl->get_rx_head(dev);
- dpld->dpctl->set_rx_tail(dev, in);
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = dpld->iod[dev];
+ struct sk_buff *skb;
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ int rest;
+ u8 *frm;
+ u8 *dst;
+ unsigned int len;
+ unsigned int pad;
+ unsigned int tot;
+ struct mif_irq_map map;
+
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0)
+ return rcvd;
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
+ }
+
+ rest = rcvd;
+ while (rest > 0) {
+ frm = src + out;
+ if (unlikely(!sipc5_start_valid(frm[0]))) {
+ mif_err("%s: ERR! %s invalid start 0x%02X\n",
+ ld->name, get_dev_name(dev), frm[0]);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ len = sipc5_get_frame_sz16(frm);
+ if (unlikely(len > rest)) {
+ mif_err("%s: ERR! %s len %d > rest %d\n",
+ ld->name, get_dev_name(dev), len, rest);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ pad = sipc5_calc_padding_size(len);
+ tot = len + pad;
+
+ /* Allocate an skb */
+ skb = dev_alloc_skb(tot);
+ if (!skb) {
+ mif_err("%s: ERR! %s dev_alloc_skb fail\n",
+ ld->name, get_dev_name(dev));
+ return -ENOMEM;
+ }
+
+ /* Read data from each DPRAM buffer */
+ dst = skb_put(skb, tot);
+ dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize);
+ skb_trim(skb, len);
+ iod->recv_skb(iod, ld, skb);
+
+ /* Calculate and set new out */
+ rest -= tot;
+ out += tot;
+ if (out >= qsize)
+ out -= qsize;
+ }
+
+ set_rx_tail(dpld, dev, out);
+
+ return rcvd;
}
-static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd)
+static void non_command_handler(struct dpram_link_device *dpld, u16 intr)
{
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
struct link_device *ld = &dpld->ld;
- struct sk_buff_head *txq;
- struct sk_buff *skb;
- int i;
+ int i = 0;
int ret = 0;
- int copied = 0;
- u32 in;
- u32 out;
- u16 mask = 0;
- u16 req_mask = 0;
u16 tx_mask = 0;
if (!dpram_ipc_active(dpld))
@@ -735,85 +881,154 @@ static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd)
/* Read data from DPRAM */
for (i = 0; i < dpld->max_ipc_dev; i++) {
- ret = dpram_ipc_recv_data(dpld, i, non_cmd);
+ if (dpld->use_skb)
+ ret = dpram_ipc_recv_data_with_skb(dpld, i);
+ else
+ ret = dpram_ipc_recv_data_with_rxb(dpld, i);
if (ret < 0)
- dpram_purge_rx_circ(dpld, i);
+ dpram_reset_rx_circ(dpld, i);
/* Check and process REQ_ACK (at this time, in == out) */
- if (non_cmd & dpctl->get_mask_req_ack(i)) {
+ if (intr & get_mask_req_ack(dpld, i)) {
mif_debug("%s: send %s_RES_ACK\n",
ld->name, get_dev_name(i));
- mask = dpctl->get_mask_res_ack(i);
- dpctl->send_intr(INT_NON_CMD(mask));
+ tx_mask |= get_mask_res_ack(dpld, i);
}
}
- /* Schedule soft IRQ for RX */
- tasklet_hi_schedule(&dpld->rx_tsk);
+ if (!dpld->use_skb) {
+ /* Schedule soft IRQ for RX */
+ tasklet_hi_schedule(&dpld->rx_tsk);
+ }
/* Try TX via DPRAM */
for (i = 0; i < dpld->max_ipc_dev; i++) {
if (atomic_read(&dpld->res_required[i]) > 0) {
- dpram_get_txq_ptrs(dpld, i, &in, &out);
- if (likely(in == out)) {
- ret = dpram_try_ipc_tx(dpld, i);
- if (ret > 0) {
- atomic_set(&dpld->res_required[i], 0);
- tx_mask |= dpctl->get_mask_send(i);
- } else {
- req_mask |= dpctl->get_mask_req_ack(i);
- }
- } else {
- req_mask |= dpctl->get_mask_req_ack(i);
+ ret = dpram_try_ipc_tx(dpld, i);
+ if (ret > 0) {
+ atomic_set(&dpld->res_required[i], 0);
+ tx_mask |= get_mask_send(dpld, i);
+ } else if (ret == -ENOSPC) {
+ tx_mask |= get_mask_req_ack(dpld, i);
}
}
}
- if (req_mask || tx_mask) {
- tx_mask |= req_mask;
- dpctl->send_intr(INT_NON_CMD(tx_mask));
+ if (tx_mask) {
+ send_intr(dpld, INT_NON_CMD(tx_mask));
mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask);
}
}
+static void handle_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+ int i;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i));
+ skb_queue_purge(ld->skb_txq[i]);
+ }
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_format(ld, IPC_BOOT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_channel(ld, PS_DATA_CH_0);
+ if (iod)
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
+}
+
+static void handle_no_crash_ack(unsigned long arg)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
+ struct link_device *ld = &dpld->ld;
+
+ mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name);
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ handle_cp_crash(dpld);
+}
+
+static int trigger_force_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (ld->mode == LINK_MODE_ULOAD) {
+ mif_err("%s: CP crash is already in progress\n", ld->mc->name);
+ return 0;
+ }
+
+ ld->mode = LINK_MODE_ULOAD;
+ mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0));
+
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_wake_up(dpld);
+
+ send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT));
+
+ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT,
+ handle_no_crash_ack, (unsigned long)dpld);
+
+ return 0;
+}
+
static int dpram_init_ipc(struct dpram_link_device *dpld)
{
struct link_device *ld = &dpld->ld;
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
int i;
if (ld->mode == LINK_MODE_IPC &&
- dpctl->get_magic() == DPRAM_MAGIC_CODE &&
- dpctl->get_access() == 1)
+ get_magic(dpld) == DPRAM_MAGIC_CODE &&
+ get_access(dpld) == 1)
mif_info("%s: IPC already initialized\n", ld->name);
/* Clear pointers in every circular queue */
for (i = 0; i < dpld->max_ipc_dev; i++) {
- dpctl->set_tx_head(i, 0);
- dpctl->set_tx_tail(i, 0);
- dpctl->set_rx_head(i, 0);
- dpctl->set_rx_tail(i, 0);
+ set_tx_head(dpld, i, 0);
+ set_tx_tail(dpld, i, 0);
+ set_rx_head(dpld, i, 0);
+ set_rx_tail(dpld, i, 0);
}
- /* Enable IPC */
- dpctl->set_magic(DPRAM_MAGIC_CODE);
- dpctl->set_access(1);
- if (dpctl->get_magic() != DPRAM_MAGIC_CODE || dpctl->get_access() != 1)
- return -EACCES;
+ /* Initialize variables for efficient TX/RX processing */
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->iod[i] = link_get_iod_with_format(ld, i);
+ dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW);
- ld->mode = LINK_MODE_IPC;
+ if (dpld->iod[IPC_RAW]->recv_skb)
+ dpld->use_skb = true;
- for (i = 0; i < dpld->max_ipc_dev; i++)
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ spin_lock_init(&dpld->tx_lock[i]);
atomic_set(&dpld->res_required[i], 0);
+ skb_queue_purge(&dpld->skb_rxq[i]);
+ }
+ /* Enable IPC */
atomic_set(&dpld->accessing, 0);
+ set_magic(dpld, DPRAM_MAGIC_CODE);
+ set_access(dpld, 1);
+ if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1)
+ return -EACCES;
+
+ ld->mode = LINK_MODE_IPC;
+
+ if (wake_lock_active(&dpld->wlock))
+ wake_unlock(&dpld->wlock);
+
return 0;
}
static void cmd_req_active_handler(struct dpram_link_device *dpld)
{
- dpld->dpctl->send_intr(INT_CMD(INT_CMD_RES_ACTIVE));
+ send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
}
static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
@@ -821,10 +1036,13 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
struct link_device *ld = &dpld->ld;
struct io_device *iod = NULL;
- mif_info("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name);
-
ld->mode = LINK_MODE_ULOAD;
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name);
+
iod = link_get_iod_with_format(ld, IPC_FMT);
iod->modem_state_changed(iod, STATE_CRASH_RESET);
@@ -835,19 +1053,36 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
static void cmd_crash_exit_handler(struct dpram_link_device *dpld)
{
struct link_device *ld = &dpld->ld;
-
- mif_info("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name);
+ u32 size = dpld->dpctl->dp_size;
+ char *dpram_buff = NULL;
ld->mode = LINK_MODE_ULOAD;
- complete_all(&dpld->crash_start_complete);
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
- if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpld->dpctl->log_disp)
- dpld->dpctl->log_disp(dpld->dpctl);
+ mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name);
+
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_wake_up(dpld);
+
+ dpram_buff = kzalloc(size + (MAX_MIF_SEPA_SIZE * 2), GFP_ATOMIC);
+ if (!dpram_buff) {
+ mif_err("DPRAM dump failed!!\n");
+ } else {
+ memset(dpram_buff, 0, size + (MAX_MIF_SEPA_SIZE * 2));
+ memcpy(dpram_buff, MIF_SEPARATOR_DPRAM, MAX_MIF_SEPA_SIZE);
+ memcpy(dpram_buff + MAX_MIF_SEPA_SIZE, &size, sizeof(u32));
+ dpram_buff += (MAX_MIF_SEPA_SIZE * 2);
+ dpram_dump_memory(ld, dpram_buff);
}
- dpram_trigger_crash(dpld);
+ del_timer(&dpld->crash_ack_timer);
+
+ if (dpld->ext_op && dpld->ext_op->crash_log)
+ dpld->ext_op->crash_log(dpld);
+
+ handle_cp_crash(dpld);
}
static void cmd_phone_start_handler(struct dpram_link_device *dpld)
@@ -865,19 +1100,17 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld)
return;
}
- if (ld->mdm_data->modem_type == SEC_CMC221) {
- if (ld->mc->phone_state != STATE_ONLINE) {
- mif_info("%s: phone_state: %d -> ONLINE\n",
- ld->name, ld->mc->phone_state);
- iod->modem_state_changed(iod, STATE_ONLINE);
- }
- } else if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpld->dpctl->phone_boot_start_handler)
- dpld->dpctl->phone_boot_start_handler(dpld->dpctl);
+ if (dpld->ext_op && dpld->ext_op->cp_start_handler)
+ dpld->ext_op->cp_start_handler(dpld);
+
+ if (ld->mc->phone_state != STATE_ONLINE) {
+ mif_info("%s: phone_state: %d -> ONLINE\n",
+ ld->name, ld->mc->phone_state);
+ iod->modem_state_changed(iod, STATE_ONLINE);
}
mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name);
- dpld->dpctl->send_intr(INT_CMD(INT_CMD_INIT_END));
+ send_intr(dpld, INT_CMD(INT_CMD_INIT_END));
}
static void command_handler(struct dpram_link_device *dpld, u16 cmd)
@@ -942,7 +1175,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
if (dpld->dpctl->setup_speed) {
dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW);
- dpld->dpctl->send_intr(resp);
+ send_intr(dpld, resp);
}
break;
@@ -950,7 +1183,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
if (dpld->dpctl->setup_speed) {
dpld->dpctl->setup_speed(DPRAM_SPEED_MID);
resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID);
- dpld->dpctl->send_intr(resp);
+ send_intr(dpld, resp);
}
break;
@@ -958,7 +1191,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
if (dpld->dpctl->setup_speed) {
dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH);
- dpld->dpctl->send_intr(resp);
+ send_intr(dpld, resp);
}
break;
@@ -968,494 +1201,141 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
}
}
-static void cmc22x_idpram_enable_ipc(struct dpram_link_device *dpld)
-{
- dpram_init_ipc(dpld);
-}
-
-static int cmc22x_idpram_wait_response(struct dpram_link_device *dpld, u32 resp)
+static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd)
{
struct link_device *ld = &dpld->ld;
- int count = 50000;
- u32 rcvd = 0;
-
- if (resp == CMC22x_CP_REQ_NV_DATA) {
- while (1) {
- rcvd = ioread32(dpld->bt_map.resp);
- if (rcvd == resp)
- break;
-
- rcvd = dpld->dpctl->recv_msg();
- if (rcvd == 0x9999) {
- mif_info("%s: Invalid resp 0x%04X\n",
- ld->name, rcvd);
- panic("CP Crash ... BAD CRC in CP");
- }
-
- if (count-- < 0) {
- mif_info("%s: Invalid resp 0x%08X\n",
- ld->name, rcvd);
- return -EAGAIN;
- }
-
- udelay(100);
- }
- } else {
- while (1) {
- rcvd = dpld->dpctl->recv_msg();
-
- if (rcvd == resp)
- break;
-
- if (resp == CMC22x_CP_RECV_NV_END &&
- rcvd == CMC22x_CP_CAL_BAD) {
- mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name);
- break;
- }
-
- if (count-- < 0) {
- mif_info("%s: Invalid resp 0x%04X\n",
- ld->name, rcvd);
- return -EAGAIN;
- }
-
- udelay(100);
- }
- }
-
- return rcvd;
-}
-
-static int cmc22x_idpram_send_boot(struct link_device *ld, unsigned long arg)
-{
- int err = 0;
- int cnt = 0;
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- u8 __iomem *bt_buff = dpld->bt_map.buff;
- struct dpram_boot_img cp_img;
- u8 *img_buff = NULL;
-
- ld->mode = LINK_MODE_BOOT;
-
- dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
-
- /* Test memory... After testing, memory is cleared. */
- if (test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) {
- mif_info("%s: ERR! test_dpram fail!\n", ld->name);
- ld->mode = LINK_MODE_INVALID;
- return -EIO;
- }
-
- /* Get information about the boot image */
- err = copy_from_user((struct dpram_boot_img *)&cp_img, (void *)arg,
- sizeof(struct dpram_boot_img));
- mif_info("%s: CP image addr = 0x%08X, size = %d\n",
- ld->name, (int)cp_img.addr, cp_img.size);
-
- /* Alloc a buffer for the boot image */
- img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL);
- if (!img_buff) {
- mif_info("%s: ERR! kzalloc fail\n", ld->name);
- ld->mode = LINK_MODE_INVALID;
- return -ENOMEM;
- }
- /* Copy boot image from the user space to the image buffer */
- err = copy_from_user(img_buff, cp_img.addr, cp_img.size);
-
- /* Copy boot image to DPRAM and verify it */
- memcpy(bt_buff, img_buff, cp_img.size);
- if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) {
- mif_info("%s: ERR! Boot may be broken!!!\n", ld->name);
- goto err;
+ if (cmd & UDL_RESULT_FAIL) {
+ mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd);
+ return;
}
- dpld->dpctl->reset();
- udelay(1000);
-
- if (cp_img.mode == CMC22x_BOOT_MODE_NORMAL) {
- mif_info("%s: CMC22x_BOOT_MODE_NORMAL\n", ld->name);
- mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req);
- iowrite32(cp_img.req, dpld->bt_map.req);
-
- /* Wait for cp_img.resp for 1 second */
- mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp);
- while (ioread32(dpld->bt_map.resp) != cp_img.resp) {
- cnt++;
- msleep_interruptible(10);
- if (cnt > 100) {
- mif_info("%s: ERR! Invalid resp 0x%08X\n",
- ld->name, ioread32(dpld->bt_map.resp));
- goto err;
- }
- }
- } else {
- mif_info("%s: CMC22x_BOOT_MODE_DUMP\n", ld->name);
+ switch (UDL_CMD_MASK(cmd)) {
+ case UDL_CMD_RECV_READY:
+ mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name);
+ send_intr(dpld, CMD_IMG_START_REQ);
+ break;
+ default:
+ complete_all(&dpld->udl_cmd_complete);
}
-
- kfree(img_buff);
-
- mif_info("%s: Send BOOT done\n", ld->name);
-
- if (dpld->dpctl->setup_speed)
- dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
-
- return 0;
-
-err:
- ld->mode = LINK_MODE_INVALID;
- kfree(img_buff);
-
- mif_info("%s: ERR! Boot send fail!!!\n", ld->name);
- return -EIO;
}
-static int cmc22x_idpram_send_main(struct link_device *ld, struct sk_buff *skb)
+static inline void dpram_ipc_rx(struct dpram_link_device *dpld, u16 intr)
{
- int err = 0;
- int ret = 0;
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
- u8 __iomem *buff = (dpld->bt_map.buff + bf->offset);
-
- if ((bf->offset + bf->len) > dpld->bt_map.size) {
- mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
- err = -EINVAL;
- goto exit;
- }
-
- if (bf->len)
- memcpy(buff, bf->data, bf->len);
-
- if (bf->request)
- dpld->dpctl->send_msg((u16)bf->request);
-
- if (bf->response) {
- err = cmc22x_idpram_wait_response(dpld, bf->response);
- if (err < 0)
- mif_info("%s: ERR! wait_response fail (err %d)\n",
- ld->name, err);
- }
-
- if (bf->request == CMC22x_CAL_NV_DOWN_END) {
- mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name);
- cmc22x_idpram_enable_ipc(dpld);
- }
-
-exit:
- if (err < 0)
- ret = err;
+ if (unlikely(INT_CMD_VALID(intr)))
+ command_handler(dpld, intr);
else
- ret = skb->len;
-
- dev_kfree_skb_any(skb);
-
- return ret;
+ non_command_handler(dpld, intr);
}
-static void cmc22x_idpram_wait_dump(unsigned long arg)
+static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr)
{
- struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
- u16 msg;
-
- if (!dpld) {
- mif_info("ERR! dpld == NULL\n");
- return;
- }
+ char *name = dpld->ld.name;
- msg = dpld->dpctl->recv_msg();
-
- if (msg == CMC22x_CP_DUMP_END) {
- complete_all(&dpld->dump_recv_done);
- return;
- }
-
- if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) {
- complete_all(&dpld->dump_recv_done);
+ if (unlikely(intr == INT_POWERSAFE_FAIL)) {
+ mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name);
return;
}
- if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) {
- complete_all(&dpld->dump_recv_done);
+ if (unlikely(EXT_UDL_CMD(intr))) {
+ if (likely(EXT_INT_VALID(intr))) {
+ if (UDL_CMD_VALID(intr))
+ udl_command_handler(dpld, intr);
+ else if (EXT_CMD_VALID(intr))
+ ext_command_handler(dpld, intr);
+ else
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ name, intr);
+ } else {
+ mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr);
+ }
return;
}
- mif_add_timer(&dpld->dump_timer, CMC22x_DUMP_WAIT_TIMEOVER,
- cmc22x_idpram_wait_dump, (unsigned long)dpld);
-}
-
-static int cmc22x_idpram_upload(struct dpram_link_device *dpld,
- struct dpram_dump_arg *dumparg)
-{
- struct link_device *ld = &dpld->ld;
- int ret;
- u8 __iomem *src;
- int buff_size = CMC22x_DUMP_BUFF_SIZE;
-
- if ((dpld->dump_rcvd & 0x1) == 0)
- dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY);
- else
- dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY);
-
- init_completion(&dpld->dump_recv_done);
-
- mif_add_timer(&dpld->dump_timer, CMC22x_DUMP_WAIT_TIMEOVER,
- cmc22x_idpram_wait_dump, (unsigned long)dpld);
-
- ret = wait_for_completion_interruptible_timeout(
- &dpld->dump_recv_done, DUMP_TIMEOUT);
- if (!ret) {
- mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name);
- goto err_out;
- }
-
- if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) {
- mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name);
- wake_unlock(&dpld->dpram_wake_lock);
- return 0;
- }
-
- if ((dpld->dump_rcvd & 0x1) == 0)
- src = dpld->ul_map.buff;
+ if (likely(INT_VALID(intr)))
+ dpram_ipc_rx(dpld, intr);
else
- src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE;
-
- memcpy(dpld->buff, src, buff_size);
-
- ret = copy_to_user(dumparg->buff, dpld->buff, buff_size);
- if (ret < 0) {
- mif_info("%s: ERR! copy_to_user fail\n", ld->name);
- goto err_out;
- }
-
- dpld->dump_rcvd++;
- return buff_size;
-
-err_out:
- wake_unlock(&dpld->dpram_wake_lock);
- return -EIO;
+ mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr);
}
-static int cbp72_edpram_wait_response(struct dpram_link_device *dpld, u32 resp)
+static irqreturn_t ap_idpram_irq_handler(int irq, void *data)
{
- struct link_device *ld = &dpld->ld;
- int ret;
- int int2cp;
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = recv_intr(dpld);
- ret = wait_for_completion_interruptible_timeout(
- &dpld->udl_cmd_complete, UDL_TIMEOUT);
- if (!ret) {
- mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name);
- return -ENXIO;
- }
-
- int2cp = dpld->dpctl->recv_intr();
- mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp);
- if (resp == int2cp || int2cp == 0xA700)
- return int2cp;
- else
- return -EINVAL;
-}
-
-static int cbp72_edpram_send_bin(struct link_device *ld, struct sk_buff *skb)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
- u8 __iomem *buff = dpld->bt_map.buff;
- int err = 0;
-
- if (bf->len > dpld->bt_map.size) {
- mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
- err = -EINVAL;
- goto exit;
- }
-
- if (bf->len)
- memcpy(buff, bf->data, bf->len);
-
- init_completion(&dpld->udl_cmd_complete);
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
- if (bf->request)
- dpld->dpctl->send_intr((u16)bf->request);
-
- if (bf->response) {
- err = cbp72_edpram_wait_response(dpld, bf->response);
- if (err < 0) {
- mif_info("%s: ERR! wait_response fail (%d)\n",
- ld->name, err);
- goto exit;
- } else if (err == bf->response) {
- err = skb->len;
- }
- }
+ dpram_intr_handler(dpld, int2ap);
-exit:
- dev_kfree_skb_any(skb);
- return err;
+ return IRQ_HANDLED;
}
-static int dpram_upload(struct dpram_link_device *dpld,
- struct dpram_dump_arg *dump, unsigned char __user *target)
+static irqreturn_t cp_idpram_irq_handler(int irq, void *data)
{
- struct link_device *ld = &dpld->ld;
- struct ul_header header;
- u8 *dest;
- u8 *buff = vmalloc(DP_DEFAULT_DUMP_LEN);
- u16 plen = 0;
- int err = 0;
- int ret = 0;
- int buff_size = 0;
-
- mif_info("\n");
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap;
- wake_lock(&dpld->dpram_wake_lock);
- init_completion(&dpld->udl_cmd_complete);
-
- mif_info("%s: req = %x, resp =%x", ld->name, dump->req, dump->resp);
-
- if (dump->req)
- dpld->dpctl->send_intr((u16)dump->req);
-
- if (dump->resp) {
- err = cbp72_edpram_wait_response(dpld, dump->resp);
- if (err < 0) {
- mif_info("%s: ERR! wait_response fail (%d)\n",
- ld->name, err);
- goto exit;
- }
- }
-
- if (dump->cmd)
- return err;
-
- dest = (u8 *)dpld->ul_map.buff;
-
- header.bop = *(u8 *)(dest);
- header.total_frame = *(u16 *)(dest + 1);
- header.curr_frame = *(u16 *)(dest + 3);
- header.len = *(u16 *)(dest + 5);
-
- mif_info("%s: total frame:%d, current frame:%d, data len:%d\n",
- ld->name, header.total_frame, header.curr_frame, header.len);
-
- plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN);
-
- memcpy(buff, dest + sizeof(struct ul_header), plen);
- ret = copy_to_user(dump->buff, buff, plen);
- if (ret < 0) {
- mif_info("%s: copy_to_user fail\n", ld->name);
- goto exit;
- }
- buff_size = plen;
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
- ret = copy_to_user(target + 4, &buff_size, sizeof(int));
- if (ret < 0) {
- mif_info("%s: copy_to_user fail\n", ld->name);
- goto exit;
+ if (dpram_wake_up(dpld) < 0) {
+ log_dpram_status(dpld);
+ trigger_force_cp_crash(dpld);
+ return IRQ_HANDLED;
}
- wake_unlock(&dpld->dpram_wake_lock);
+ int2ap = recv_intr(dpld);
- return err;
+ dpram_intr_handler(dpld, int2ap);
-exit:
- vfree(buff);
- iowrite32(0, dpld->ul_map.magic);
- wake_unlock(&dpld->dpram_wake_lock);
- return -EIO;
-}
+ clear_intr(dpld);
-static void udl_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
-{
- struct link_device *ld = &dpld->ld;
-
- if (cmd & UDL_RESULT_FAIL) {
- mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd);
- return;
- }
+ dpram_allow_sleep(dpld);
- switch (UDL_CMD_MASK(cmd)) {
- case UDL_CMD_RECEIVE_READY:
- mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name);
- dpld->dpctl->send_intr(CMD_IMG_START_REQ);
- break;
- default:
- complete_all(&dpld->udl_cmd_complete);
- }
+ return IRQ_HANDLED;
}
-static irqreturn_t dpram_irq_handler(int irq, void *data)
+static irqreturn_t ext_dpram_irq_handler(int irq, void *data)
{
- struct link_device *ld = (struct link_device *)data;
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- u16 int2ap = 0;
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = recv_intr(dpld);
- if (!ld->mc || ld->mc->phone_state == STATE_OFFLINE)
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
return IRQ_HANDLED;
- if (dpram_wake_up(dpld) < 0)
- return IRQ_HANDLED;
-
- int2ap = dpld->dpctl->recv_intr();
-
- if (dpld->dpctl->clear_intr)
- dpld->dpctl->clear_intr();
-
- if (int2ap == INT_POWERSAFE_FAIL) {
- mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name);
- goto exit_isr;
- }
-
- if (ld->mdm_data->modem_type == QC_MDM6600) {
- if ((int2ap == 0x1234)|(int2ap == 0xDBAB)|(int2ap == 0xABCD)) {
- if (dpld->dpctl->dload_cmd_hdlr)
- dpld->dpctl->dload_cmd_hdlr(dpld->dpctl,
- int2ap);
- goto exit_isr;
- }
- }
-
- if (UDL_CMD_VALID(int2ap))
- udl_cmd_handler(dpld, int2ap);
- else if (EXT_INT_VALID(int2ap) && EXT_CMD_VALID(int2ap))
- ext_command_handler(dpld, int2ap);
- else if (INT_CMD_VALID(int2ap))
- command_handler(dpld, int2ap);
- else if (INT_VALID(int2ap))
- non_command_handler(dpld, int2ap);
- else
- mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap);
+ dpram_intr_handler(dpld, int2ap);
-exit_isr:
- dpram_allow_sleep(dpld);
return IRQ_HANDLED;
}
-static void dpram_send_ipc(struct link_device *ld, int dev, struct sk_buff *skb)
+static void dpram_send_ipc(struct link_device *ld, int dev,
+ struct io_device *iod, struct sk_buff *skb)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct sk_buff_head *txq;
+ struct sk_buff_head *txq = ld->skb_txq[dev];
int ret;
u16 mask;
- if (unlikely(dev >= dpld->max_ipc_dev)) {
- mif_info("%s: ERR! dev %d >= max_ipc_dev(%s)\n",
- ld->name, dev, get_dev_name(dpld->max_ipc_dev));
- return;
+ skb_queue_tail(txq, skb);
+ if (txq->qlen > 1024) {
+ mif_debug("%s: %s txq->qlen %d > 1024\n",
+ ld->name, get_dev_name(dev), txq->qlen);
}
- if (dpram_wake_up(dpld) < 0)
- return;
+ if (dpld->dp_type == CP_IDPRAM) {
+ if (dpram_wake_up(dpld) < 0) {
+ trigger_force_cp_crash(dpld);
+ return;
+ }
+ }
if (!dpram_ipc_active(dpld))
goto exit;
- txq = ld->skb_txq[dev];
- if (txq->qlen > 1024)
- mif_info("%s: txq->qlen %d > 1024\n", ld->name, txq->qlen);
-
- skb_queue_tail(txq, skb);
-
if (atomic_read(&dpld->res_required[dev]) > 0) {
mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev));
goto exit;
@@ -1463,46 +1343,51 @@ static void dpram_send_ipc(struct link_device *ld, int dev, struct sk_buff *skb)
ret = dpram_try_ipc_tx(dpld, dev);
if (ret > 0) {
- mask = dpld->dpctl->get_mask_send(dev);
- dpld->dpctl->send_intr(INT_NON_CMD(mask));
- } else {
- mask = dpld->dpctl->get_mask_req_ack(dev);
- dpld->dpctl->send_intr(INT_NON_CMD(mask));
+ mask = get_mask_send(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
+ } else if (ret == -ENOSPC) {
+ mask = get_mask_req_ack(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask);
+ } else {
+ mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret);
}
exit:
- dpram_allow_sleep(dpld);
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_allow_sleep(dpld);
}
-static int dpram_send_binary(struct link_device *ld, struct sk_buff *skb)
+static int dpram_send_cp_binary(struct link_device *ld, struct sk_buff *skb)
{
- int err = 0;
-
- if (ld->mdm_data->modem_type == SEC_CMC221)
- err = cmc22x_idpram_send_main(ld, skb);
- else if (ld->mdm_data->modem_type == VIA_CBP72)
- err = cbp72_edpram_send_bin(ld, skb);
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
- return err;
+ if (dpld->ext_op && dpld->ext_op->download_binary)
+ return dpld->ext_op->download_binary(dpld, skb);
+ else
+ return -ENODEV;
}
static int dpram_send(struct link_device *ld, struct io_device *iod,
struct sk_buff *skb)
{
- enum dev_format fmt = iod->format;
+ enum dev_format dev = iod->format;
int len = skb->len;
- switch (fmt) {
+ switch (dev) {
case IPC_FMT:
case IPC_RAW:
case IPC_RFS:
- if (likely(ld->mc->phone_state == STATE_ONLINE))
- dpram_send_ipc(ld, fmt, skb);
+ if (likely(ld->mode == LINK_MODE_IPC)) {
+ dpram_send_ipc(ld, dev, iod, skb);
+ } else {
+ mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name);
+ dev_kfree_skb_any(skb);
+ }
return len;
case IPC_BOOT:
- return dpram_send_binary(ld, skb);
+ return dpram_send_cp_binary(ld, skb);
default:
mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name);
@@ -1511,291 +1396,252 @@ static int dpram_send(struct link_device *ld, struct io_device *iod,
}
}
-static int dpram_set_dl_magic(struct link_device *ld, struct io_device *iod)
+static int dpram_force_dump(struct link_device *ld, struct io_device *iod)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
-
- ld->mode = LINK_MODE_DLOAD;
-
- iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic);
-
+ trigger_force_cp_crash(dpld);
return 0;
}
-static int dpram_force_dump(struct link_device *ld, struct io_device *iod)
+static void dpram_dump_memory(struct link_device *ld, char *buff)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ u8 __iomem *base = dpld->dpctl->dp_base;
+ u32 size = dpld->dpctl->dp_size;
- mif_info("%s\n", ld->name);
-
- if (dpram_wake_up(dpld) < 0)
- mif_info("%s: WARNING! dpram_wake_up fail\n", ld->name);
-
- dpram_trigger_force_cp_crash(dpld);
-
- dpram_allow_sleep(dpld);
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_wake_up(dpld);
- return 0;
+ memcpy(buff, base, size);
}
-static int dpram_set_ul_magic(struct link_device *ld, struct io_device *iod)
+static int dpram_dump_start(struct link_device *ld, struct io_device *iod)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- u8 *dest = dpld->ul_map.buff;
-
- ld->mode = LINK_MODE_ULOAD;
-
- if (ld->mdm_data->modem_type == SEC_CMC221) {
- wake_lock(&dpld->dpram_wake_lock);
- dpld->dump_rcvd = 0;
- iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic);
- } else {
- iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic);
- iowrite8((u8)START_INDEX, dest + 0);
- iowrite8((u8)0x1, dest + 1);
- iowrite8((u8)0x1, dest + 2);
- iowrite8((u8)0x0, dest + 3);
- iowrite8((u8)END_INDEX, dest + 4);
- }
-
- init_completion(&dpld->dump_start_complete);
-
- return 0;
+ if (dpld->ext_op && dpld->ext_op->dump_start)
+ return dpld->ext_op->dump_start(dpld);
+ else
+ return -ENODEV;
}
static int dpram_dump_update(struct link_device *ld, struct io_device *iod,
unsigned long arg)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct dpram_dump_arg dump;
- int ret;
-
- ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
- if (ret < 0) {
- mif_info("%s: ERR! copy_from_user fail\n", ld->name);
- return ret;
- }
- if (ld->mdm_data->modem_type == SEC_CMC221)
- return cmc22x_idpram_upload(dpld, &dump);
+ if (dpld->ext_op && dpld->ext_op->dump_update)
+ return dpld->ext_op->dump_update(dpld, (void *)arg);
else
- return dpram_upload(dpld, &dump, (unsigned char __user *)arg);
+ return -ENODEV;
}
-static int dpram_link_ioctl(struct link_device *ld, struct io_device *iod,
+static int dpram_ioctl(struct link_device *ld, struct io_device *iod,
unsigned int cmd, unsigned long arg)
{
- int err = -EFAULT;
struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ int err = 0;
mif_info("%s: cmd 0x%08X\n", ld->name, cmd);
switch (cmd) {
- case IOCTL_DPRAM_SEND_BOOT:
- err = cmc22x_idpram_send_boot(ld, arg);
- if (err < 0) {
- mif_info("%s: ERR! dpram_send_boot fail\n", ld->name);
- goto exit;
- }
- break;
+ case IOCTL_DPRAM_INIT_STATUS:
+ mif_debug("%s: get dpram init status\n", ld->name);
+ return dpld->dpram_init_status;
- case IOCTL_DPRAM_PHONE_POWON:
- if (dpld->dpctl->cpimage_load_prepare) {
- err = dpld->dpctl->cpimage_load_prepare(dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! cpimage_load_prepare fail\n",
- ld->name);
- goto exit;
- }
+ default:
+ if (dpld->ext_ioctl) {
+ err = dpld->ext_ioctl(dpld, iod, cmd, arg);
+ } else {
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
}
- break;
- case IOCTL_DPRAM_PHONEIMG_LOAD:
- if (dpld->dpctl->cpimage_load) {
- err = dpld->dpctl->cpimage_load(
- (void *)arg, dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! cpimage_load fail\n",
- ld->name);
- goto exit;
- }
- }
break;
+ }
- case IOCTL_DPRAM_NVDATA_LOAD:
- if (dpld->dpctl->nvdata_load) {
- err = dpld->dpctl->nvdata_load(
- (void *)arg, dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! nvdata_load fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+ return err;
+}
- case IOCTL_DPRAM_PHONE_BOOTSTART:
- if (dpld->dpctl->phone_boot_start) {
- err = dpld->dpctl->phone_boot_start(dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! phone_boot_start fail\n",
- ld->name);
- goto exit;
- }
- }
- if (dpld->dpctl->phone_boot_start_post_process) {
- err = dpld->dpctl->phone_boot_start_post_process();
- if (err < 0) {
- mif_info("%s: ERR! "
- "phone_boot_start_post_process fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+static void dpram_remap_std_16k_region(struct dpram_link_device *dpld)
+{
+ struct dpram_ipc_16k_map *dpram_map;
+ struct dpram_ipc_device *dev;
- case IOCTL_DPRAM_PHONE_UPLOAD_STEP1:
- disable_irq_nosync(dpld->irq);
-
- if (dpld->dpctl->cpupload_step1) {
- err = dpld->dpctl->cpupload_step1(dpld->dpctl);
- if (err < 0) {
- dpld->dpctl->clear_intr();
- enable_irq(dpld->irq);
- mif_info("%s: ERR! cpupload_step1 fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+ dpram_map = (struct dpram_ipc_16k_map *)dpld->dp_base;
- case IOCTL_DPRAM_PHONE_UPLOAD_STEP2:
- if (dpld->dpctl->cpupload_step2) {
- err = dpld->dpctl->cpupload_step2(
- (void *)arg, dpld->dpctl);
- if (err < 0) {
- dpld->dpctl->clear_intr();
- enable_irq(dpld->irq);
- mif_info("%s: ERR! cpupload_step2 fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+ /* magic code and access enable fields */
+ dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic;
+ dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access;
- case IOCTL_DPRAM_INIT_STATUS:
- mif_debug("%s: get dpram init status\n", ld->name);
- return dpld->dpram_init_status;
+ /* FMT */
+ dev = &dpld->ipc_map.dev[IPC_FMT];
- case IOCTL_MODEM_DL_START:
- err = dpram_set_dl_magic(ld, iod);
- if (err < 0) {
- mif_info("%s: ERR! dpram_set_dl_magic fail\n",
- ld->name);
- goto exit;
- }
+ strcpy(dev->name, "FMT");
+ dev->id = IPC_FMT;
- default:
- break;
- }
+ dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head;
+ dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail;
+ dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0];
+ dev->txq.size = DP_16K_FMT_TX_BUFF_SZ;
- return 0;
+ dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head;
+ dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail;
+ dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0];
+ dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ;
-exit:
- return err;
+ dev->mask_req_ack = INT_MASK_REQ_ACK_F;
+ dev->mask_res_ack = INT_MASK_RES_ACK_F;
+ dev->mask_send = INT_MASK_SEND_F;
+
+ /* RAW */
+ dev = &dpld->ipc_map.dev[IPC_RAW];
+
+ strcpy(dev->name, "RAW");
+ dev->id = IPC_RAW;
+
+ dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head;
+ dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail;
+ dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0];
+ dev->txq.size = DP_16K_RAW_TX_BUFF_SZ;
+
+ dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head;
+ dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail;
+ dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0];
+ dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ;
+
+ dev->mask_req_ack = INT_MASK_REQ_ACK_R;
+ dev->mask_res_ack = INT_MASK_RES_ACK_R;
+ dev->mask_send = INT_MASK_SEND_R;
+
+ /* interrupt ports */
+ dpld->ipc_map.mbx_cp2ap = (u16 __iomem *)&dpram_map->mbx_cp2ap;
+ dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp;
}
-static void dpram_table_init(struct dpram_link_device *dpld)
+static int dpram_table_init(struct dpram_link_device *dpld)
{
- struct link_device *ld;
+ struct link_device *ld = &dpld->ld;
u8 __iomem *dp_base;
-
- if (!dpld) {
- mif_info("ERR! dpld == NULL\n");
- return;
- }
- ld = &dpld->ld;
+ int i;
if (!dpld->dp_base) {
mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name);
- return;
+ return -EINVAL;
}
-
dp_base = dpld->dp_base;
+ /* Map for IPC */
+ if (dpld->dpctl->ipc_map) {
+ memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map,
+ sizeof(struct dpram_ipc_map));
+ } else {
+ if (dpld->dp_size == DPRAM_SIZE_16KB)
+ dpram_remap_std_16k_region(dpld);
+ else
+ return -EINVAL;
+ }
+
+ dpld->magic = dpld->ipc_map.magic;
+ dpld->access = dpld->ipc_map.access;
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->dev[i] = &dpld->ipc_map.dev[i];
+ dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap;
+ dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp;
+
/* Map for booting */
- if (ld->mdm_data->modem_type == SEC_CMC221) {
- dpld->bt_map.buff = (u8 *)(dp_base);
- dpld->bt_map.req = (u32 *)(dp_base + DP_BOOT_REQ_OFFSET);
- dpld->bt_map.resp = (u32 *)(dp_base + DP_BOOT_RESP_OFFSET);
- dpld->bt_map.size = dpld->dp_size;
- } else if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpld->dpctl->bt_map_init)
- dpld->dpctl->bt_map_init(dpld->dpctl);
- } else if (ld->mdm_data->modem_type == VIA_CBP72) {
+ if (dpld->ext_op && dpld->ext_op->init_boot_map) {
+ dpld->ext_op->init_boot_map(dpld);
+ } else {
dpld->bt_map.magic = (u32 *)(dp_base);
dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET);
- dpld->bt_map.size = dpld->dp_size - 4;
- } else {
- dpld->bt_map.buff = (u8 *)(dp_base);
- dpld->bt_map.req = (u32 *)(dp_base + DP_BOOT_REQ_OFFSET);
- dpld->bt_map.resp = (u32 *)(dp_base + DP_BOOT_RESP_OFFSET);
- dpld->bt_map.size = dpld->dp_size - 4;
+ dpld->bt_map.size = dpld->dp_size - 8;
}
/* Map for download (FOTA, UDL, etc.) */
- if (ld->mdm_data->modem_type == SEC_CMC221 ||
- ld->mdm_data->modem_type == VIA_CBP72) {
+ if (dpld->ext_op && dpld->ext_op->init_dl_map) {
+ dpld->ext_op->init_dl_map(dpld);
+ } else {
dpld->dl_map.magic = (u32 *)(dp_base);
- dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET);
+ dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET);
}
/* Map for upload mode */
- dpld->ul_map.magic = (u32 *)(dp_base);
- if (ld->mdm_data->modem_type == SEC_CMC221)
- dpld->ul_map.buff = (u8 *)(dp_base);
- else
+ if (dpld->ext_op && dpld->ext_op->init_ul_map) {
+ dpld->ext_op->init_ul_map(dpld);
+ } else {
+ dpld->ul_map.magic = (u32 *)(dp_base);
dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET);
+ }
+
+ return 0;
}
-#if defined(CONFIG_MACH_M0_CTC)
-static void dpram_link_terminate(struct link_device *ld, struct io_device *iod)
+static void dpram_setup_common_op(struct dpram_link_device *dpld)
{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ dpld->clear_intr = clear_intr;
+ dpld->recv_intr = recv_intr;
+ dpld->send_intr = send_intr;
+ dpld->get_magic = get_magic;
+ dpld->set_magic = set_magic;
+ dpld->get_access = get_access;
+ dpld->set_access = set_access;
+ dpld->get_tx_head = get_tx_head;
+ dpld->get_tx_tail = get_tx_tail;
+ dpld->set_tx_head = set_tx_head;
+ dpld->set_tx_tail = set_tx_tail;
+ dpld->get_tx_buff = get_tx_buff;
+ dpld->get_tx_buff_size = get_tx_buff_size;
+ dpld->get_rx_head = get_rx_head;
+ dpld->get_rx_tail = get_rx_tail;
+ dpld->set_rx_head = set_rx_head;
+ dpld->set_rx_tail = set_rx_tail;
+ dpld->get_rx_buff = get_rx_buff;
+ dpld->get_rx_buff_size = get_rx_buff_size;
+ dpld->get_mask_req_ack = get_mask_req_ack;
+ dpld->get_mask_res_ack = get_mask_res_ack;
+ dpld->get_mask_send = get_mask_send;
+ dpld->ipc_rx_handler = dpram_ipc_rx;
+}
- mif_info("dpram_link_terminate\n");
+static int dpram_link_init(struct link_device *ld, struct io_device *iod)
+{
+ return 0;
+}
- if (dpld->dpctl->terminate_link)
- dpld->dpctl->terminate_link(dpld->dpctl);
+static void dpram_link_terminate(struct link_device *ld, struct io_device *iod)
+{
+ return;
}
-#endif
struct link_device *dpram_create_link_device(struct platform_device *pdev)
{
+ struct modem_data *mdm_data = NULL;
struct dpram_link_device *dpld = NULL;
struct link_device *ld = NULL;
- struct modem_data *pdata = NULL;
+ struct resource *res = NULL;
+ resource_size_t res_size;
struct modemlink_dpram_control *dpctl = NULL;
+ unsigned long task_data;
int ret = 0;
int i = 0;
int bsize;
int qsize;
- char wq_name[32];
- char wq_suffix[32];
/* Get the platform data */
- pdata = (struct modem_data *)pdev->dev.platform_data;
- if (!pdata) {
- mif_info("ERR! pdata == NULL\n");
+ mdm_data = (struct modem_data *)pdev->dev.platform_data;
+ if (!mdm_data) {
+ mif_info("ERR! mdm_data == NULL\n");
goto err;
}
- if (!pdata->dpram_ctl) {
- mif_info("ERR! pdata->dpram_ctl == NULL\n");
+ mif_info("modem = %s\n", mdm_data->name);
+ mif_info("link device = %s\n", mdm_data->link_name);
+
+ if (!mdm_data->dpram_ctl) {
+ mif_info("ERR! mdm_data->dpram_ctl == NULL\n");
goto err;
}
- mif_info("link device = %s\n", pdata->link_name);
- mif_info("modem = %s\n", pdata->name);
+ dpctl = mdm_data->dpram_ctl;
/* Alloc DPRAM link device structure */
dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL);
@@ -1805,25 +1651,37 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev)
}
ld = &dpld->ld;
- /* Extract modem data and DPRAM control data from the platform data */
- ld->mdm_data = pdata;
- ld->name = pdata->link_name;
- ld->ipc_version = pdata->ipc_version;
+ /* Retrieve modem data and DPRAM control data from the modem data */
+ ld->mdm_data = mdm_data;
+ ld->name = mdm_data->link_name;
+ ld->ipc_version = mdm_data->ipc_version;
- /* Set attributes as a link device */
- ld->aligned = pdata->dpram_ctl->aligned;
- if (ld->aligned)
- mif_info("%s: ld->aligned == TRUE\n", ld->name);
+ /* Retrieve the most basic data for IPC from the modem data */
+ dpld->dpctl = dpctl;
+ dpld->dp_type = dpctl->dp_type;
+ if (mdm_data->ipc_version < SIPC_VER_50) {
+ if (!dpctl->max_ipc_dev) {
+ mif_info("ERR! no max_ipc_dev\n");
+ goto err;
+ }
+
+ ld->aligned = dpctl->aligned;
+ dpld->max_ipc_dev = dpctl->max_ipc_dev;
+ } else {
+ ld->aligned = 1;
+ dpld->max_ipc_dev = MAX_SIPC5_DEV;
+ }
+
+ /* Set attributes as a link device */
+ ld->init_comm = dpram_link_init;
+ ld->terminate_comm = dpram_link_terminate;
ld->send = dpram_send;
ld->force_dump = dpram_force_dump;
- ld->dump_start = dpram_set_ul_magic;
+ ld->dump_start = dpram_dump_start;
ld->dump_update = dpram_dump_update;
- ld->ioctl = dpram_link_ioctl;
+ ld->ioctl = dpram_ioctl;
-#if defined(CONFIG_MACH_M0_CTC)
- ld->terminate_comm = dpram_link_terminate;
-#endif
INIT_LIST_HEAD(&ld->list);
skb_queue_head_init(&ld->sk_fmt_tx_q);
@@ -1833,54 +1691,68 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev)
ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q;
ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q;
- /* Set attributes as a dpram link device */
- dpctl = pdata->dpram_ctl;
- dpld->dpctl = dpctl;
+ /* Set up function pointers */
+ dpram_setup_common_op(dpld);
+ dpld->dpram_dump = dpram_dump_memory;
+ dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type);
+ if (dpld->ext_op && dpld->ext_op->ioctl)
+ dpld->ext_ioctl = dpld->ext_op->ioctl;
+
+ /* Retrieve DPRAM resource */
+ if (!dpctl->dp_base) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ mif_info("%s: ERR! platform_get_resource fail\n",
+ ld->name);
+ goto err;
+ }
+ res_size = resource_size(res);
+ dpctl->dp_base = ioremap_nocache(res->start, res_size);
+ dpctl->dp_size = res_size;
+ }
dpld->dp_base = dpctl->dp_base;
dpld->dp_size = dpctl->dp_size;
- dpld->dp_type = dpctl->dp_type;
- dpld->max_ipc_dev = dpctl->max_ipc_dev;
+ mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n",
+ ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base,
+ dpld->dp_size);
- dpld->irq = dpctl->dpram_irq;
- if (dpld->irq < 0) {
- mif_info("%s: ERR! failed to get IRQ#\n", ld->name);
+ /* Initialize DPRAM map (physical map -> logical map) */
+ ret = dpram_table_init(dpld);
+ if (ret < 0) {
+ mif_info("%s: ERR! dpram_table_init fail (err %d)\n",
+ ld->name, ret);
goto err;
}
- mif_info("%s: DPRAM IRQ# = %d\n", ld->name, dpld->irq);
- wake_lock_init(&dpld->dpram_wake_lock, WAKE_LOCK_SUSPEND,
- dpctl->dpram_wlock_name);
+ /* Disable IPC */
+ set_magic(dpld, 0);
+ set_access(dpld, 0);
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+
+ /* Initialize locks, completions, and bottom halves */
+ snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name);
+ wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name);
init_completion(&dpld->dpram_init_cmd);
init_completion(&dpld->modem_pif_init_done);
init_completion(&dpld->udl_start_complete);
init_completion(&dpld->udl_cmd_complete);
- init_completion(&dpld->crash_start_complete);
init_completion(&dpld->dump_start_complete);
init_completion(&dpld->dump_recv_done);
- spin_lock_init(&dpld->tx_lock);
-
- tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, (unsigned long)dpld);
-
- /* Initialize DPRAM map (physical map -> logical map) */
- dpram_table_init(dpld);
+ task_data = (unsigned long)dpld;
+ tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data);
-#if 0
- dpld->magic = dpctl->ipc_map->magic;
- dpld->access = dpctl->ipc_map->access;
+ /* Prepare SKB queue head for RX processing */
for (i = 0; i < dpld->max_ipc_dev; i++)
- dpld->dev[i] = &dpctl->ipc_map->dev[i];
- dpld->mbx2ap = dpctl->ipc_map->mbx_cp2ap;
- dpld->mbx2cp = dpctl->ipc_map->mbx_ap2cp;
-#endif
+ skb_queue_head_init(&dpld->skb_rxq[i]);
- /* Prepare rxb queue */
+ /* Prepare RXB queue */
qsize = DPRAM_MAX_RXBQ_SIZE;
for (i = 0; i < dpld->max_ipc_dev; i++) {
- bsize = rxbq_get_page_size(dpctl->get_rx_buff_size(i));
+ bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i));
dpld->rxbq[i].size = qsize;
dpld->rxbq[i].in = 0;
dpld->rxbq[i].out = 0;
@@ -1894,30 +1766,49 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev)
ld->name, get_dev_name(i), bsize, qsize);
}
- /* Prepare a clean buffer */
+ /* Prepare a multi-purpose miscellaneous buffer */
dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL);
if (!dpld->buff) {
mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name);
goto err;
}
- if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpctl->load_init)
- dpctl->load_init(dpctl);
+ /* Retrieve DPRAM IRQ GPIO# */
+ dpld->gpio_dpram_int = mdm_data->gpio_dpram_int;
+
+ /* Retrieve DPRAM IRQ# */
+ if (!dpctl->dpram_irq) {
+ dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq");
+ if (dpctl->dpram_irq < 0) {
+ mif_info("%s: ERR! platform_get_irq_byname fail\n",
+ ld->name);
+ goto err;
+ }
}
+ dpld->irq = dpctl->dpram_irq;
- /* Disable IPC */
- dpctl->set_magic(0);
- dpctl->set_access(0);
- dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+ /* Retrieve DPRAM IRQ flags */
+ if (!dpctl->dpram_irq_flags)
+ dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW);
+ dpld->irq_flags = dpctl->dpram_irq_flags;
/* Register DPRAM interrupt handler */
- ret = dpram_register_isr(dpld->irq, dpram_irq_handler,
- dpctl->dpram_irq_flags, dpctl->dpram_irq_name, ld);
+ snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name);
+ if (dpld->ext_op && dpld->ext_op->irq_handler)
+ dpld->irq_handler = dpld->ext_op->irq_handler;
+ else if (dpld->dp_type == CP_IDPRAM)
+ dpld->irq_handler = cp_idpram_irq_handler;
+ else if (dpld->dp_type == AP_IDPRAM)
+ dpld->irq_handler = ap_idpram_irq_handler;
+ else
+ dpld->irq_handler = ext_dpram_irq_handler;
+
+ ret = dpram_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags,
+ dpld->irq_name, dpld);
if (ret)
goto err;
-
- return ld;
+ else
+ return ld;
err:
if (dpld) {
diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h
index d61da83..c651e51 100644
--- a/drivers/misc/modem_if/modem_link_device_dpram.h
+++ b/drivers/misc/modem_if/modem_link_device_dpram.h
@@ -23,44 +23,61 @@
#include "modem_prj.h"
-/* for DPRAM hostboot */
-#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876
-#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5
-#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A
-#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD
-
-#define CMC22x_HOST_DOWN_START 0x1234
-#define CMC22x_HOST_DOWN_END 0x4321
-#define CMC22x_REG_NV_DOWN_END 0xABCD
-#define CMC22x_CAL_NV_DOWN_END 0xDCBA
-
-#define CMC22x_1ST_BUFF_READY 0xAAAA
-#define CMC22x_2ND_BUFF_READY 0xBBBB
-#define CMC22x_1ST_BUFF_FULL 0x1111
-#define CMC22x_2ND_BUFF_FULL 0x2222
-
-#define CMC22x_CP_RECV_NV_END 0x8888
-#define CMC22x_CP_CAL_OK 0x4F4B
-#define CMC22x_CP_CAL_BAD 0x4552
-#define CMC22x_CP_DUMP_END 0xFADE
-
-#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */
-#define CMC22x_DUMP_WAIT_TIMEOVER 1 /* 1 ms */
+#define DPRAM_MAGIC_CODE 0xAA
/* interrupt masks.*/
-#define INT_MASK_VALID 0x0080
-#define INT_MASK_CMD 0x0040
-#define INT_VALID(x) ((x) & INT_MASK_VALID)
+#define INT_MASK_VALID 0x0080
+#define INT_MASK_CMD 0x0040
+#define INT_VALID(x) ((x) & INT_MASK_VALID)
#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD)
-#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
-#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
+#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+#define EXT_UDL_MASK 0xF000
+#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK)
#define EXT_INT_VALID_MASK 0x8000
#define EXT_CMD_VALID_MASK 0x4000
+#define UDL_CMD_VALID_MASK 0x2000
#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK)
#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK)
+#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK)
#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x))
+#define EXT_CMD_MASK(x) ((x) & 0x0FFF)
+#define EXT_CMD_SET_SPEED_LOW 0x0011
+#define EXT_CMD_SET_SPEED_MID 0x0012
+#define EXT_CMD_SET_SPEED_HIGH 0x0013
+
+#define UDL_RESULT_SUCCESS 0x1
+#define UDL_RESULT_FAIL 0x2
+
+#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF)
+#define UDL_CMD_RECV_READY 0x1
+#define UDL_CMD_DL_START_REQ 0x2
+#define UDL_CMD_DL_START_RESP 0x3
+#define UDL_CMD_IMAGE_SEND_REQ 0x4
+#define UDL_CMD_SEND_DONE_RESP 0x5
+#define UDL_CMD_SEND_DONE_REQ 0x6
+#define UDL_CMD_UPDATE_DONE 0x7
+#define UDL_CMD_STATUS_UPDATE 0x8
+#define UDL_CMD_IMAGE_SEND_RESP 0x9
+#define UDL_CMD_EFS_CLEAR_RESP 0xB
+#define UDL_CMD_ALARM_BOOT_OK 0xC
+#define UDL_CMD_ALARM_BOOT_FAIL 0xD
+
+#define CMD_IMG_START_REQ 0x9200
+#define CMD_IMG_SEND_REQ 0x9400
+#define CMD_DL_SEND_DONE_REQ 0x9600
+#define CMD_UL_RECV_RESP 0x9601
+#define CMD_UL_RECV_DONE_RESP 0x9801
+
+/* special interrupt cmd indicating modem boot failure. */
+#define INT_POWERSAFE_FAIL 0xDEAD
+
+#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
+#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
+#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
+
#define INT_MASK_REQ_ACK_F 0x0020
#define INT_MASK_REQ_ACK_R 0x0010
#define INT_MASK_RES_ACK_F 0x0008
@@ -68,10 +85,6 @@
#define INT_MASK_SEND_F 0x0002
#define INT_MASK_SEND_R 0x0001
-#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
-#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
-#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
-
#define INT_MASK_RES_ACK_SET \
(INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS)
@@ -95,59 +108,26 @@
#define INT_CMD_SILENT_NV_REBUILDING 0xE
#define INT_CMD_NORMAL_PWR_OFF 0xF
-#define EXT_CMD_MASK(x) ((x) & 0x3FFF)
-#define EXT_CMD_SET_SPEED_LOW 0x0011
-#define EXT_CMD_SET_SPEED_MID 0x0012
-#define EXT_CMD_SET_SPEED_HIGH 0x0013
-
-/* special interrupt cmd indicating modem boot failure. */
-#define INT_POWERSAFE_FAIL 0xDEAD
-
-#define UDL_CMD_VALID(x) (((x) & 0xA000) == 0xA000)
-#define UDL_RESULT_FAIL 0x2
-#define UDL_RESULT_SUCCESS 0x1
-#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF)
-#define UDL_CMD_RECEIVE_READY 0x1
-#define UDL_CMD_DOWNLOAD_START_REQ 0x2
-#define UDL_CMD_DOWNLOAD_START_RESP 0x3
-#define UDL_CMD_IMAGE_SEND_REQ 0x4
-/* change dpram download flow */
-#define UDL_CMD_IMAGE_SEND_RESP 0x9
-
-#define UDL_CMD_SEND_DONE_RESP 0x5
-#define UDL_CMD_SEND_DONE_REQ 0x6
-#define UDL_CMD_UPDATE_DONE 0x7
-
-#define UDL_CMD_STATUS_UPDATE 0x8
-#define UDL_CMD_EFS_CLEAR_RESP 0xB
-#define UDL_CMD_ALARM_BOOT_OK 0xC
-#define UDL_CMD_ALARM_BOOT_FAIL 0xD
-
-#define CMD_IMG_START_REQ 0x9200
-#define CMD_IMG_SEND_REQ 0x9400
-#define CMD_DL_SEND_DONE_REQ 0x9600
-#define CMD_UL_RECEIVE_RESP 0x9601
-#define CMD_UL_RECEIVE_DONE_RESP 0x9801
-
-#define START_INDEX 0x7F
-#define END_INDEX 0x7E
-
-#define DP_MAGIC_DMDL 0x4445444C
-#define DP_MAGIC_UMDL 0x4445444D
-#define DP_DPRAM_SIZE 0x4000
-#define DP_DEFAULT_WRITE_LEN 8168
-#define DP_DEFAULT_DUMP_LEN 16128
-#define DP_DUMP_HEADER_SIZE 7
-
-#define UDL_TIMEOUT (50 * HZ)
-#define UDL_SEND_TIMEOUT (200 * HZ)
-#define FORCE_CRASH_TIMEOUT (3 * HZ)
-#define DUMP_TIMEOUT (30 * HZ)
-#define DUMP_START_TIMEOUT (100 * HZ)
-
-enum cmc22x_boot_mode {
- CMC22x_BOOT_MODE_NORMAL,
- CMC22x_BOOT_MODE_DUMP,
+#define START_FLAG 0x7F
+#define END_FLAG 0x7E
+
+#define DP_MAGIC_DMDL 0x4445444C
+#define DP_MAGIC_UMDL 0x4445444D
+#define DP_DPRAM_SIZE 0x4000
+#define DP_DEFAULT_WRITE_LEN 8168
+#define DP_DEFAULT_DUMP_LEN 16128
+#define DP_DUMP_HEADER_SIZE 7
+
+#define UDL_TIMEOUT (50 * HZ)
+#define UDL_SEND_TIMEOUT (200 * HZ)
+#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ)
+#define DUMP_TIMEOUT (30 * HZ)
+#define DUMP_START_TIMEOUT (100 * HZ)
+#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */
+
+enum host_boot_mode {
+ HOST_BOOT_MODE_NORMAL,
+ HOST_BOOT_MODE_DUMP,
};
enum dpram_init_status {
@@ -158,40 +138,27 @@ enum dpram_init_status {
struct dpram_boot_img {
char *addr;
int size;
- enum cmc22x_boot_mode mode;
+ enum host_boot_mode mode;
unsigned req;
unsigned resp;
};
#define MAX_PAYLOAD_SIZE 0x2000
struct dpram_boot_frame {
- unsigned request; /* AP to CP Message */
- unsigned response; /* CP to AP Response */
- ssize_t len; /* request size*/
- unsigned offset; /* offset to write */
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* response expected by AP */
+ ssize_t len; /* data size in the buffer */
+ unsigned offset; /* offset to write into DPRAM */
char data[MAX_PAYLOAD_SIZE];
};
/* buffer type for modem image */
struct dpram_dump_arg {
- char *buff;
- int buff_size;/* AP->CP: Buffer size */
- unsigned req; /* AP->CP request */
- unsigned resp; /* CP->AP response */
- bool cmd; /* AP->CP command */
-};
-
-struct dpram_firmware {
- char *firmware;
- int size;
- int is_delta;
-};
-enum dpram_link_mode {
- DPRAM_LINK_MODE_INVALID = 0,
- DPRAM_LINK_MODE_IPC,
- DPRAM_LINK_MODE_BOOT,
- DPRAM_LINK_MODE_DLOAD,
- DPRAM_LINK_MODE_ULOAD,
+ char *buff; /* pointer to the buffer */
+ int buff_size; /* buffer size */
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* CP->AP response */
+ bool cmd; /* AP->CP command */
};
struct dpram_boot_map {
@@ -202,6 +169,13 @@ struct dpram_boot_map {
u32 size;
};
+struct qc_dpram_boot_map {
+ u8 __iomem *buff;
+ u16 __iomem *frame_size;
+ u16 __iomem *tag;
+ u16 __iomem *count;
+};
+
struct dpram_dload_map {
u32 __iomem *magic;
u8 __iomem *buff;
@@ -212,20 +186,54 @@ struct dpram_uload_map {
u8 __iomem *buff;
};
-struct dpram_ota_header {
- u8 start_index;
- u16 nframes;
- u16 curframe;
- u16 len;
-
-} __packed;
-
struct ul_header {
u8 bop;
u16 total_frame;
u16 curr_frame;
u16 len;
} __packed;
+
+struct dpram_udl_param {
+ unsigned char *addr;
+ unsigned int size;
+ unsigned int count;
+ unsigned int tag;
+};
+
+struct dpram_udl_check {
+ unsigned int total_size;
+ unsigned int rest_size;
+ unsigned int send_size;
+ unsigned int copy_start;
+ unsigned int copy_complete;
+ unsigned int boot_complete;
+};
+
+#define DP_BOOT_BUFF_OFFSET 4
+#define DP_DLOAD_BUFF_OFFSET 4
+#define DP_ULOAD_BUFF_OFFSET 4
+#define DP_BOOT_REQ_OFFSET 0
+#define DP_BOOT_RESP_OFFSET 8
+
+#define MAX_WQ_NAME_LENGTH 64
+
+#define DPRAM_MAX_RXBQ_SIZE 256
+
+struct dpram_rxb {
+ u8 *buff;
+ unsigned size;
+
+ u8 *data;
+ unsigned len;
+};
+
+struct dpram_rxb_queue {
+ int size;
+ int in;
+ int out;
+ struct dpram_rxb *rxb;
+};
+
/*
magic_code +
access_enable +
@@ -245,89 +253,72 @@ struct ul_header {
2
= 16384
*/
+#define DP_16K_FMT_TX_BUFF_SZ 1336
+#define DP_16K_RAW_TX_BUFF_SZ 4564
+#define DP_16K_FMT_RX_BUFF_SZ 1336
+#define DP_16K_RAW_RX_BUFF_SZ 9124
-#define SIPC5_DP_FMT_TX_BUFF_SZ 1336
-#define SIPC5_DP_RAW_TX_BUFF_SZ 4564
-#define SIPC5_DP_FMT_RX_BUFF_SZ 1336
-#define SIPC5_DP_RAW_RX_BUFF_SZ 9124
-
-struct sipc5_dpram_ipc_cfg {
+struct dpram_ipc_16k_map {
u16 magic;
u16 access;
u16 fmt_tx_head;
u16 fmt_tx_tail;
- u8 fmt_tx_buff[SIPC5_DP_FMT_TX_BUFF_SZ];
+ u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ];
u16 raw_tx_head;
u16 raw_tx_tail;
- u8 raw_tx_buff[SIPC5_DP_RAW_TX_BUFF_SZ];
+ u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ];
u16 fmt_rx_head;
u16 fmt_rx_tail;
- u8 fmt_rx_buff[SIPC5_DP_FMT_RX_BUFF_SZ];
+ u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ];
u16 raw_rx_head;
u16 raw_rx_tail;
- u8 raw_rx_buff[SIPC5_DP_RAW_RX_BUFF_SZ];
+ u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ];
u16 mbx_cp2ap;
u16 mbx_ap2cp;
};
-#define DPRAM_MAX_SKB_SIZE 3072 /* 3 KB */
+#define DP_MAX_NAME_LEN 32
-#define DP_BOOT_REQ_OFFSET 0
-#define DP_BOOT_BUFF_OFFSET 4
-#define DP_BOOT_RESP_OFFSET 8
-#define DP_DLOAD_BUFF_OFFSET 4
-#define DP_ULOAD_BUFF_OFFSET 4
-
-#define MAX_WQ_NAME_LENGTH 64
-
-#define DPRAM_MAX_RXBQ_SIZE 256
-
-struct dpram_rxb {
- u8 *buff;
- unsigned size;
-
- u8 *data;
- unsigned len;
-};
-
-struct dpram_rxb_queue {
- int size;
- int in;
- int out;
- struct dpram_rxb *rxb;
-};
+struct dpram_ext_op;
struct dpram_link_device {
struct link_device ld;
- /* The mode of this DPRAM link device */
- enum dpram_link_mode mode;
-
/* DPRAM address and size */
- u32 dp_size; /* DPRAM size */
u8 __iomem *dp_base; /* DPRAM base virtual address */
+ u32 dp_size; /* DPRAM size */
enum dpram_type dp_type; /* DPRAM type */
+ /* DPRAM IRQ GPIO# */
+ unsigned gpio_dpram_int;
+
/* DPRAM IRQ from CP */
int irq;
+ unsigned long irq_flags;
+ char irq_name[DP_MAX_NAME_LEN];
/* Link to DPRAM control functions dependent on each platform */
int max_ipc_dev;
struct modemlink_dpram_control *dpctl;
/* Physical configuration -> logical configuration */
- struct dpram_boot_map bt_map;
+ union {
+ struct dpram_boot_map bt_map;
+ struct qc_dpram_boot_map qc_bt_map;
+ };
+
struct dpram_dload_map dl_map;
struct dpram_uload_map ul_map;
/* IPC device map */
- struct dpram_ipc_map *ipc_map;
+ struct dpram_ipc_map ipc_map;
+ /* Pointers (aliases) to IPC device map */
u16 __iomem *magic;
u16 __iomem *access;
struct dpram_ipc_device *dev[MAX_IPC_DEV];
@@ -335,45 +326,116 @@ struct dpram_link_device {
u16 __iomem *mbx2cp;
/* Wakelock for DPRAM device */
- struct wake_lock dpram_wake_lock;
+ struct wake_lock wlock;
+ char wlock_name[DP_MAX_NAME_LEN];
/* For booting */
+ unsigned boot_start_complete;
struct completion dpram_init_cmd;
struct completion modem_pif_init_done;
/* For UDL */
+ struct tasklet_struct ul_tsk;
+ struct tasklet_struct dl_tsk;
struct completion udl_start_complete;
struct completion udl_cmd_complete;
+ struct dpram_udl_check udl_check;
+ struct dpram_udl_param udl_param;
/* For CP RAM dump */
- struct completion crash_start_complete;
+ struct timer_list crash_ack_timer;
struct completion dump_start_complete;
struct completion dump_recv_done;
struct timer_list dump_timer;
int dump_rcvd; /* Count of dump packets received */
- /* For locking Tx process */
- spinlock_t tx_lock;
+ /* For locking TX process */
+ spinlock_t tx_lock[MAX_IPC_DEV];
/* For efficient RX process */
struct tasklet_struct rx_tsk;
struct dpram_rxb_queue rxbq[MAX_IPC_DEV];
-
- /* For wake-up/sleep control */
- atomic_t accessing;
+ struct io_device *iod[MAX_IPC_DEV];
+ bool use_skb;
+ struct sk_buff_head skb_rxq[MAX_IPC_DEV];
/* For retransmission after buffer full state */
atomic_t res_required[MAX_IPC_DEV];
+ /* For wake-up/sleep control */
+ atomic_t accessing;
+
/* Multi-purpose miscellaneous buffer */
u8 *buff;
/* DPRAM IPC initialization status */
int dpram_init_status;
+
+ /* Alias to device-specific IOCTL function */
+ int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+
+ /* Alias to DPRAM IRQ handler */
+ irqreturn_t (*irq_handler)(int irq, void *data);
+
+ /* For DPRAM dump */
+ void (*dpram_dump)(struct link_device *ld, char *buff);
+
+ /* Common operations for each DPRAM */
+ void (*clear_intr)(struct dpram_link_device *dpld);
+ u16 (*recv_intr)(struct dpram_link_device *dpld);
+ void (*send_intr)(struct dpram_link_device *dpld, u16 mask);
+ u16 (*get_magic)(struct dpram_link_device *dpld);
+ void (*set_magic)(struct dpram_link_device *dpld, u16 value);
+ u16 (*get_access)(struct dpram_link_device *dpld);
+ void (*set_access)(struct dpram_link_device *dpld, u16 value);
+ u32 (*get_tx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_send)(struct dpram_link_device *dpld, int id);
+ void (*ipc_rx_handler)(struct dpram_link_device *dpld, u16 int2ap);
+
+ /* Extended operations for various modems */
+ struct dpram_ext_op *ext_op;
};
/* converts from struct link_device* to struct xxx_link_device* */
#define to_dpram_link_device(linkdev) \
container_of(linkdev, struct dpram_link_device, ld)
+struct dpram_ext_op {
+ int exist;
+
+ void (*init_boot_map)(struct dpram_link_device *dpld);
+ void (*init_dl_map)(struct dpram_link_device *dpld);
+ void (*init_ul_map)(struct dpram_link_device *dpld);
+
+ int (*download_binary)(struct dpram_link_device *dpld,
+ struct sk_buff *skb);
+
+ void (*cp_start_handler)(struct dpram_link_device *dpld);
+
+ void (*crash_log)(struct dpram_link_device *dpld);
+ int (*dump_start)(struct dpram_link_device *dpld);
+ int (*dump_update)(struct dpram_link_device *dpld, void *arg);
+
+ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+
+ irqreturn_t (*irq_handler)(int irq, void *data);
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem);
+
#endif
diff --git a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
new file mode 100644
index 0000000..1475a46
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
@@ -0,0 +1,1175 @@
+/*
+ * 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/kallsyms.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+#include "modem_link_device_dpram.h"
+#include "modem_utils.h"
+
+#if defined(CONFIG_LTE_MODEM_CMC221)
+/*
+** For host (flashless) booting via DPRAM
+*/
+#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876
+#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5
+#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A
+#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD
+
+#define CMC22x_HOST_DOWN_START 0x1234
+#define CMC22x_HOST_DOWN_END 0x4321
+#define CMC22x_REG_NV_DOWN_END 0xABCD
+#define CMC22x_CAL_NV_DOWN_END 0xDCBA
+
+#define CMC22x_1ST_BUFF_READY 0xAAAA
+#define CMC22x_2ND_BUFF_READY 0xBBBB
+#define CMC22x_1ST_BUFF_FULL 0x1111
+#define CMC22x_2ND_BUFF_FULL 0x2222
+
+#define CMC22x_CP_RECV_NV_END 0x8888
+#define CMC22x_CP_CAL_OK 0x4F4B
+#define CMC22x_CP_CAL_BAD 0x4552
+#define CMC22x_CP_DUMP_END 0xFADE
+
+#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */
+#endif
+
+#if defined(CONFIG_CDMA_MODEM_CBP72)
+static void cbp72_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct dpram_boot_map *bt_map = &dpld->bt_map;
+
+ bt_map->magic = (u32 *)dpld->dp_base;
+ bt_map->buff = (u8 *)(dpld->dp_base + DP_BOOT_BUFF_OFFSET);
+ bt_map->size = dpld->dp_size - 4;
+}
+
+static void cbp72_init_dl_map(struct dpram_link_device *dpld)
+{
+ dpld->dl_map.magic = (u32 *)dpld->dp_base;
+ dpld->dl_map.buff = (u8 *)(dpld->dp_base + DP_DLOAD_BUFF_OFFSET);
+}
+
+static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
+{
+ struct link_device *ld = &dpld->ld;
+ int ret;
+ int int2cp;
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->udl_cmd_complete, UDL_TIMEOUT);
+ if (!ret) {
+ mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name);
+ return -ENXIO;
+ }
+
+ int2cp = dpld->recv_intr(dpld);
+ mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp);
+ if (resp == int2cp || int2cp == 0xA700)
+ return int2cp;
+ else
+ return -EINVAL;
+}
+
+static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
+ u8 __iomem *buff = dpld->bt_map.buff;
+ int err = 0;
+
+ if (bf->len > dpld->bt_map.size) {
+ mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (bf->len)
+ memcpy(buff, bf->data, bf->len);
+
+ init_completion(&dpld->udl_cmd_complete);
+
+ if (bf->req)
+ dpld->send_intr(dpld, (u16)bf->req);
+
+ if (bf->resp) {
+ err = _cbp72_edpram_wait_resp(dpld, bf->resp);
+ if (err < 0) {
+ mif_info("%s: ERR! wait_response fail (%d)\n",
+ ld->name, err);
+ goto exit;
+ } else if (err == bf->resp) {
+ err = skb->len;
+ }
+ }
+
+exit:
+ dev_kfree_skb_any(skb);
+ return err;
+}
+
+static int cbp72_download_binary(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ if (dpld->dp_type == EXT_DPRAM)
+ return _cbp72_edpram_download_bin(dpld, skb);
+ else
+ return -ENODEV;
+}
+
+static int cbp72_dump_start(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 *dest = dpld->ul_map.buff;
+ int ret;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ ret = del_timer(&dpld->dump_timer);
+ wake_lock(&dpld->wlock);
+
+ iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic);
+
+ iowrite8((u8)START_FLAG, dest + 0);
+ iowrite8((u8)0x1, dest + 1);
+ iowrite8((u8)0x1, dest + 2);
+ iowrite8((u8)0x0, dest + 3);
+ iowrite8((u8)END_FLAG, dest + 4);
+
+ init_completion(&dpld->dump_start_complete);
+
+ return 0;
+}
+
+static int _cbp72_edpram_upload(struct dpram_link_device *dpld,
+ struct dpram_dump_arg *dump, unsigned char __user *target)
+{
+ struct link_device *ld = &dpld->ld;
+ struct ul_header header;
+ u8 *dest = NULL;
+ u8 *buff = NULL;
+ u16 plen = 0;
+ int err = 0;
+ int ret = 0;
+ int buff_size = 0;
+
+ mif_debug("\n");
+
+ init_completion(&dpld->udl_cmd_complete);
+
+ mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp);
+
+ if (dump->req)
+ dpld->send_intr(dpld, (u16)dump->req);
+
+ if (dump->resp) {
+ err = _cbp72_edpram_wait_resp(dpld, dump->resp);
+ if (err < 0) {
+ mif_info("%s: ERR! wait_response fail (%d)\n",
+ ld->name, err);
+ goto exit;
+ }
+ }
+
+ if (dump->cmd)
+ return err;
+
+ dest = (u8 *)dpld->ul_map.buff;
+
+ header.bop = *(u8 *)(dest);
+ header.total_frame = *(u16 *)(dest + 1);
+ header.curr_frame = *(u16 *)(dest + 3);
+ header.len = *(u16 *)(dest + 5);
+
+ mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n",
+ ld->name, header.total_frame, header.curr_frame, header.len);
+
+ plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN);
+
+ buff = vmalloc(DP_DEFAULT_DUMP_LEN);
+ if (!buff) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(buff, dest + sizeof(struct ul_header), plen);
+ ret = copy_to_user(dump->buff, buff, plen);
+ if (ret < 0) {
+ mif_info("%s: ERR! dump copy_to_user fail\n", ld->name);
+ err = -EIO;
+ goto exit;
+ }
+ buff_size = plen;
+
+ ret = copy_to_user(target + 4, &buff_size, sizeof(int));
+ if (ret < 0) {
+ mif_info("%s: ERR! size copy_to_user fail\n", ld->name);
+ err = -EIO;
+ goto exit;
+ }
+
+ vfree(buff);
+ return err;
+
+exit:
+ if (buff)
+ vfree(buff);
+ iowrite32(0, dpld->ul_map.magic);
+ wake_unlock(&dpld->wlock);
+ return err;
+}
+
+static int cbp72_dump_update(struct dpram_link_device *dpld, void *arg)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_dump_arg dump;
+ int ret;
+
+ ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
+ if (ret < 0) {
+ mif_info("%s: ERR! copy_from_user fail\n", ld->name);
+ return ret;
+ }
+
+ return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg);
+}
+
+static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ ld->mode = LINK_MODE_DLOAD;
+
+ iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic);
+
+ return 0;
+}
+
+static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_MODEM_DL_START:
+ err = cbp72_set_dl_magic(ld, iod);
+ if (err < 0)
+ mif_err("%s: ERR! set_dl_magic fail\n", ld->name);
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif
+
+#if defined(CONFIG_LTE_MODEM_CMC221)
+static void cmc221_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct dpram_boot_map *bt_map = &dpld->bt_map;
+
+ bt_map->buff = dpld->dp_base;
+ bt_map->size = dpld->dp_size;
+ bt_map->req = (u32 *)(dpld->dp_base + DP_BOOT_REQ_OFFSET);
+ bt_map->resp = (u32 *)(dpld->dp_base + DP_BOOT_RESP_OFFSET);
+}
+
+static void cmc221_init_dl_map(struct dpram_link_device *dpld)
+{
+ dpld->dl_map.magic = (u32 *)dpld->dp_base;
+ dpld->dl_map.buff = (u8 *)dpld->dp_base;
+}
+
+static void cmc221_init_ul_map(struct dpram_link_device *dpld)
+{
+ dpld->ul_map.magic = (u32 *)dpld->dp_base;
+ dpld->ul_map.buff = (u8 *)dpld->dp_base;
+}
+
+static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
+{
+ struct link_device *ld = &dpld->ld;
+ int count = 50000;
+ u32 rcvd = 0;
+
+ if (resp == CMC22x_CP_REQ_NV_DATA) {
+ while (1) {
+ rcvd = ioread32(dpld->bt_map.resp);
+ if (rcvd == resp)
+ break;
+
+ rcvd = dpld->dpctl->recv_msg();
+ if (rcvd == 0x9999) {
+ mif_info("%s: Invalid resp 0x%04X\n",
+ ld->name, rcvd);
+ panic("CP Crash ... BAD CRC in CP");
+ }
+
+ if (count-- < 0) {
+ mif_info("%s: Invalid resp 0x%08X\n",
+ ld->name, rcvd);
+ return -EAGAIN;
+ }
+
+ udelay(100);
+ }
+ } else {
+ while (1) {
+ rcvd = dpld->dpctl->recv_msg();
+
+ if (rcvd == resp)
+ break;
+
+ if (resp == CMC22x_CP_RECV_NV_END &&
+ rcvd == CMC22x_CP_CAL_BAD) {
+ mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name);
+ break;
+ }
+
+ if (count-- < 0) {
+ mif_info("%s: Invalid resp 0x%04X\n",
+ ld->name, rcvd);
+ return -EAGAIN;
+ }
+
+ udelay(100);
+ }
+ }
+
+ return rcvd;
+}
+
+static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 __iomem *bt_buff = dpld->bt_map.buff;
+ struct dpram_boot_img cp_img;
+ u8 *img_buff = NULL;
+ int err = 0;
+ int cnt = 0;
+
+ ld->mode = LINK_MODE_BOOT;
+
+ dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
+
+ /* Test memory... After testing, memory is cleared. */
+ if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) {
+ mif_info("%s: ERR! mif_test_dpram fail!\n", ld->name);
+ ld->mode = LINK_MODE_OFFLINE;
+ return -EIO;
+ }
+
+ memset(&cp_img, 0, sizeof(struct dpram_boot_img));
+
+ /* Get information about the boot image */
+ err = copy_from_user(&cp_img, arg, sizeof(cp_img));
+ mif_info("%s: CP image addr = 0x%08X, size = %d\n",
+ ld->name, (int)cp_img.addr, cp_img.size);
+
+ /* Alloc a buffer for the boot image */
+ img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL);
+ if (!img_buff) {
+ mif_info("%s: ERR! kzalloc fail\n", ld->name);
+ ld->mode = LINK_MODE_OFFLINE;
+ return -ENOMEM;
+ }
+
+ /* Copy boot image from the user space to the image buffer */
+ err = copy_from_user(img_buff, cp_img.addr, cp_img.size);
+
+ /* Copy boot image to DPRAM and verify it */
+ memcpy(bt_buff, img_buff, cp_img.size);
+ if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) {
+ mif_info("%s: ERR! Boot may be broken!!!\n", ld->name);
+ goto err;
+ }
+
+ dpld->dpctl->reset();
+ usleep_range(1000, 2000);
+
+ if (cp_img.mode == HOST_BOOT_MODE_NORMAL) {
+ mif_info("%s: HOST_BOOT_MODE_NORMAL\n", ld->name);
+ mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req);
+ iowrite32(cp_img.req, dpld->bt_map.req);
+
+ /* Wait for cp_img.resp for up to 2 seconds */
+ mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp);
+ while (ioread32(dpld->bt_map.resp) != cp_img.resp) {
+ cnt++;
+ usleep_range(1000, 2000);
+
+ if (cnt > 1000) {
+ mif_info("%s: ERR! Invalid resp 0x%08X\n",
+ ld->name, ioread32(dpld->bt_map.resp));
+ goto err;
+ }
+ }
+ } else {
+ mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name);
+ }
+
+ kfree(img_buff);
+
+ mif_info("%s: Send BOOT done\n", ld->name);
+
+ dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
+
+ return 0;
+
+err:
+ ld->mode = LINK_MODE_OFFLINE;
+ kfree(img_buff);
+
+ mif_info("%s: ERR! Boot send fail!!!\n", ld->name);
+ return -EIO;
+}
+
+static int cmc221_download_boot(struct dpram_link_device *dpld, void *arg)
+{
+ if (dpld->dp_type == CP_IDPRAM)
+ return _cmc221_idpram_send_boot(dpld, arg);
+ else
+ return -ENODEV;
+}
+
+static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ int err = 0;
+ int ret = 0;
+ struct link_device *ld = &dpld->ld;
+ struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
+ u8 __iomem *buff = (dpld->bt_map.buff + bf->offset);
+
+ if ((bf->offset + bf->len) > dpld->bt_map.size) {
+ mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (bf->len)
+ memcpy(buff, bf->data, bf->len);
+
+ if (bf->req)
+ dpld->dpctl->send_msg((u16)bf->req);
+
+ if (bf->resp) {
+ err = _cmc221_idpram_wait_resp(dpld, bf->resp);
+ if (err < 0)
+ mif_info("%s: ERR! wait_response fail (err %d)\n",
+ ld->name, err);
+ }
+
+ if (bf->req == CMC22x_CAL_NV_DOWN_END)
+ mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name);
+
+exit:
+ if (err < 0)
+ ret = err;
+ else
+ ret = skb->len;
+
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+static int cmc221_download_binary(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ if (dpld->dp_type == CP_IDPRAM)
+ return _cmc221_idpram_download_bin(dpld, skb);
+ else
+ return -ENODEV;
+}
+
+static int cmc221_dump_start(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ int ret;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ ret = del_timer(&dpld->dump_timer);
+ wake_lock(&dpld->wlock);
+
+ dpld->dump_rcvd = 0;
+ iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic);
+
+ init_completion(&dpld->dump_start_complete);
+
+ return 0;
+}
+
+static void _cmc221_idpram_wait_dump(unsigned long arg)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
+ u16 msg;
+
+ msg = dpld->dpctl->recv_msg();
+ if (msg == CMC22x_CP_DUMP_END) {
+ complete_all(&dpld->dump_recv_done);
+ return;
+ }
+
+ if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) {
+ complete_all(&dpld->dump_recv_done);
+ return;
+ }
+
+ if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) {
+ complete_all(&dpld->dump_recv_done);
+ return;
+ }
+
+ mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT,
+ _cmc221_idpram_wait_dump, (unsigned long)dpld);
+}
+
+static int _cmc221_idpram_upload(struct dpram_link_device *dpld,
+ struct dpram_dump_arg *dumparg)
+{
+ struct link_device *ld = &dpld->ld;
+ int ret;
+ u8 __iomem *src;
+ int buff_size = CMC22x_DUMP_BUFF_SIZE;
+
+ if ((dpld->dump_rcvd & 0x1) == 0)
+ dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY);
+ else
+ dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY);
+
+ init_completion(&dpld->dump_recv_done);
+
+ mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT,
+ _cmc221_idpram_wait_dump, (unsigned long)dpld);
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->dump_recv_done, DUMP_TIMEOUT);
+ if (!ret) {
+ mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name);
+ goto err_out;
+ }
+
+ if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) {
+ mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name);
+ return 0;
+ }
+
+ if ((dpld->dump_rcvd & 0x1) == 0)
+ src = dpld->ul_map.buff;
+ else
+ src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE;
+
+ memcpy(dpld->buff, src, buff_size);
+
+ ret = copy_to_user(dumparg->buff, dpld->buff, buff_size);
+ if (ret < 0) {
+ mif_info("%s: ERR! copy_to_user fail\n", ld->name);
+ goto err_out;
+ }
+
+ dpld->dump_rcvd++;
+ return buff_size;
+
+err_out:
+ return -EIO;
+}
+
+static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_dump_arg dump;
+ int ret;
+
+ ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
+ if (ret < 0) {
+ mif_info("%s: ERR! copy_from_user fail\n", ld->name);
+ return ret;
+ }
+
+ return _cmc221_idpram_upload(dpld, &dump);
+}
+
+static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_DPRAM_SEND_BOOT:
+ err = cmc221_download_boot(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_boot fail\n", ld->name);
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif
+
+#if defined(CONFIG_CDMA_MODEM_MDM6600) || defined(CONFIG_GSM_MODEM_ESC6270)
+enum qc_dload_tag {
+ QC_DLOAD_TAG_NONE = 0,
+ QC_DLOAD_TAG_BIN,
+ QC_DLOAD_TAG_NV,
+ QC_DLOAD_TAG_MAX
+};
+
+static void qc_dload_task(unsigned long data);
+
+static void qc_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct modemlink_dpram_control *dpctl = dpld->dpctl;
+
+ bt_map->buff = dpld->dp_base;
+ bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset);
+ bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset);
+ bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset);
+
+ tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld);
+}
+
+static int qc_prepare_download(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+
+ while (1) {
+ if (dpld->udl_check.copy_start) {
+ dpld->udl_check.copy_start = 0;
+ break;
+ }
+
+ msleep_interruptible(10);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return retval;
+}
+
+static void _qc_do_download(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+
+ if (param->size <= dpld->dpctl->max_boot_frame_size) {
+ memcpy(bt_map->buff, param->addr, param->size);
+ iowrite16(param->size, bt_map->frame_size);
+ iowrite16(param->tag, bt_map->tag);
+ iowrite16(param->count, bt_map->count);
+ dpld->send_intr(dpld, 0xDB12);
+ } else {
+ mif_info("param->size %d\n", param->size);
+ }
+}
+
+static int _qc_download(struct dpram_link_device *dpld, void *arg,
+ enum qc_dload_tag tag)
+{
+ int retval = 0;
+ int count = 0;
+ int cnt_limit;
+ unsigned char *img;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail\n");
+ return -1;
+ }
+
+ img = vmalloc(param.size);
+ if (!img) {
+ mif_err("ERR! vmalloc fail\n");
+ return -1;
+ }
+ memset(img, 0, param.size);
+ memcpy(img, param.addr, param.size);
+
+ dpld->udl_check.total_size = param.size;
+ dpld->udl_check.rest_size = param.size;
+ dpld->udl_check.send_size = 0;
+ dpld->udl_check.copy_complete = 0;
+
+ dpld->udl_param.addr = img;
+ dpld->udl_param.size = dpld->dpctl->max_boot_frame_size;
+ if (tag == QC_DLOAD_TAG_NV)
+ dpld->udl_param.count = 1;
+ else
+ dpld->udl_param.count = param.count;
+ dpld->udl_param.tag = tag;
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ /* Download image (binary or NV) */
+ _qc_do_download(dpld, &dpld->udl_param);
+
+ /* Wait for completion
+ */
+ if (tag == QC_DLOAD_TAG_NV)
+ cnt_limit = 200;
+ else
+ cnt_limit = 1000;
+
+ while (1) {
+ if (dpld->udl_check.copy_complete) {
+ dpld->udl_check.copy_complete = 0;
+ retval = 0;
+ break;
+ }
+
+ msleep(10);
+
+ count++;
+ if (count > cnt_limit) {
+ dpld->udl_check.total_size = 0;
+ dpld->udl_check.rest_size = 0;
+ mif_err("ERR! count %d\n", count);
+ retval = -1;
+ break;
+ }
+ }
+
+ vfree(img);
+
+ return retval;
+}
+
+static int qc_download_binary(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN);
+}
+
+static int qc_download_nv(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_NV);
+}
+
+static void qc_dload_task(unsigned long data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+
+ dpld->udl_check.send_size += dpld->udl_param.size;
+ dpld->udl_check.rest_size -= dpld->udl_param.size;
+
+ dpld->udl_param.addr += dpld->udl_param.size;
+
+ if (dpld->udl_check.send_size >= dpld->udl_check.total_size) {
+ dpld->udl_check.copy_complete = 1;
+ dpld->udl_param.tag = 0;
+ return;
+ }
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ dpld->udl_param.count += 1;
+
+ _qc_do_download(dpld, &dpld->udl_param);
+}
+
+static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ switch (cmd) {
+ case 0x1234:
+ dpld->udl_check.copy_start = 1;
+ break;
+
+ case 0xDBAB:
+ if (dpld->udl_check.total_size)
+ tasklet_schedule(&dpld->dl_tsk);
+ break;
+
+ case 0xABCD:
+ dpld->udl_check.boot_complete = 1;
+ break;
+
+ default:
+ mif_err("ERR! unknown command 0x%04X\n", cmd);
+ }
+}
+
+static int qc_boot_start(struct dpram_link_device *dpld)
+{
+ u16 mask = 0;
+ int count = 0;
+
+ /* Send interrupt -> '0x4567' */
+ mask = 0x4567;
+ dpld->send_intr(dpld, mask);
+
+ while (1) {
+ if (dpld->udl_check.boot_complete) {
+ dpld->udl_check.boot_complete = 0;
+ break;
+ }
+
+ msleep_interruptible(10);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int qc_boot_post_process(struct dpram_link_device *dpld)
+{
+ int count = 0;
+
+ while (1) {
+ if (dpld->boot_start_complete) {
+ dpld->boot_start_complete = 0;
+ break;
+ }
+
+ msleep_interruptible(10);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void qc_start_handler(struct dpram_link_device *dpld)
+{
+ /*
+ * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT |
+ * INT_MASK_CP_AP_ANDROID | INT_MASK_CMD_INIT_END
+ */
+ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002);
+
+ dpld->boot_start_complete = 1;
+
+ /* Send INIT_END code to CP */
+ mif_info("send 0x%04X (INIT_END)\n", mask);
+
+ dpld->send_intr(dpld, mask);
+}
+
+static void qc_crash_log(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ static unsigned char buf[151];
+ u8 __iomem *data = NULL;
+
+ data = dpld->get_rx_buff(dpld, IPC_FMT);
+ memcpy(buf, data, (sizeof(buf) - 1));
+
+ mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name);
+ mif_info("PHONE ERR MSG\t| %s\n", buf);
+}
+
+static int _qc_data_upload(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ int retval = 0;
+ u16 intval = 0;
+ int count = 0;
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ if (intval == 0xDBAB) {
+ break;
+ } else {
+ mif_err("intr 0x%08x\n", intval);
+ return -1;
+ }
+ }
+
+ msleep_interruptible(1);
+
+ count++;
+ if (count > 200) {
+ mif_err("<%s:%d>\n", __func__, __LINE__);
+ return -1;
+ }
+ }
+
+ param->size = ioread16(bt_map->frame_size);
+ memcpy(param->addr, bt_map->buff, param->size);
+ param->tag = ioread16(bt_map->tag);
+ param->count = ioread16(bt_map->count);
+
+ dpld->send_intr(dpld, 0xDB12);
+
+ return retval;
+}
+
+static int qc_uload_step1(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+ u16 intval = 0;
+ u16 mask = 0;
+
+ mif_info("+---------------------------------------------+\n");
+ mif_info("| UPLOAD PHONE SDRAM |\n");
+ mif_info("+---------------------------------------------+\n");
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("intr 0x%04x\n", intval);
+ if (intval == 0x1234) {
+ break;
+ } else {
+ mif_info("ERR! invalid intr\n");
+ return -1;
+ }
+ }
+
+ msleep_interruptible(1);
+
+ count++;
+ if (count > 200) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("count %d, intr 0x%04x\n", count, intval);
+ if (intval == 0x1234)
+ break;
+ return -1;
+ }
+ }
+
+ mask = 0xDEAD;
+ dpld->send_intr(dpld, mask);
+
+ return retval;
+}
+
+static int qc_uload_step2(struct dpram_link_device *dpld, void *arg)
+{
+ int retval = 0;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ retval = _qc_data_upload(dpld, &param);
+ if (retval < 0) {
+ mif_err("ERR! _qc_data_upload fail (err %d)\n", retval);
+ return -1;
+ }
+
+ if (!(param.count % 500))
+ mif_info("param->count = %d\n", param.count);
+
+ if (param.tag == 4) {
+ enable_irq(dpld->irq);
+ mif_info("param->tag = %d\n", param.tag);
+ }
+
+ retval = copy_to_user((unsigned long *)arg, &param, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_to_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ return retval;
+}
+
+static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_DPRAM_PHONE_POWON:
+ err = qc_prepare_download(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! prepare_download fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONEIMG_LOAD:
+ err = qc_download_binary(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_binary fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_NVDATA_LOAD:
+ err = qc_download_nv(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_nv fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONE_BOOTSTART:
+ err = qc_boot_start(dpld);
+ if (err < 0) {
+ mif_info("%s: ERR! boot_start fail\n", ld->name);
+ break;
+ }
+
+ err = qc_boot_post_process(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! boot_post_process fail\n", ld->name);
+
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP1:
+ disable_irq_nosync(dpld->irq);
+ err = qc_uload_step1(dpld);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step1 fail\n", ld->name);
+ }
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP2:
+ err = qc_uload_step2(dpld, (void *)arg);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step2 fail\n", ld->name);
+ }
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static irqreturn_t qc_dpram_irq_handler(int irq, void *data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = 0;
+
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
+
+ int2ap = dpld->recv_intr(dpld);
+
+ if (int2ap == INT_POWERSAFE_FAIL) {
+ mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name);
+ goto exit;
+ }
+
+ if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) {
+ qc_dload_cmd_handler(dpld, int2ap);
+ goto exit;
+ }
+
+ if (likely(INT_VALID(int2ap)))
+ dpld->ipc_rx_handler(dpld, int2ap);
+ else
+ mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap);
+
+exit:
+ return IRQ_HANDLED;
+}
+#endif
+
+static struct dpram_ext_op ext_op_set[] = {
+#ifdef CONFIG_CDMA_MODEM_CBP72
+ [VIA_CBP72] = {
+ .exist = 1,
+ .init_boot_map = cbp72_init_boot_map,
+ .init_dl_map = cbp72_init_dl_map,
+ .download_binary = cbp72_download_binary,
+ .dump_start = cbp72_dump_start,
+ .dump_update = cbp72_dump_update,
+ .ioctl = cbp72_ioctl,
+ },
+#endif
+#ifdef CONFIG_LTE_MODEM_CMC221
+ [SEC_CMC221] = {
+ .exist = 1,
+ .init_boot_map = cmc221_init_boot_map,
+ .init_dl_map = cmc221_init_dl_map,
+ .init_ul_map = cmc221_init_ul_map,
+ .download_binary = cmc221_download_binary,
+ .dump_start = cmc221_dump_start,
+ .dump_update = cmc221_dump_update,
+ .ioctl = cmc221_ioctl,
+ },
+#endif
+#if defined(CONFIG_CDMA_MODEM_MDM6600)
+ [QC_MDM6600] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ .irq_handler = qc_dpram_irq_handler,
+ },
+#endif
+#if defined(CONFIG_GSM_MODEM_ESC6270)
+ [QC_ESC6270] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ .irq_handler = qc_dpram_irq_handler,
+ },
+#endif
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem)
+{
+ if (ext_op_set[modem].exist)
+ return &ext_op_set[modem];
+ else
+ return NULL;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_hsic.c b/drivers/misc/modem_if/modem_link_device_hsic.c
index af058f4..955c620 100755..100644
--- a/drivers/misc/modem_if/modem_link_device_hsic.c
+++ b/drivers/misc/modem_if/modem_link_device_hsic.c
@@ -47,18 +47,6 @@ static int usb_tx_urb_with_skb(struct usb_device *usbdev, struct sk_buff *skb,
#endif
static void usb_rx_complete(struct urb *urb);
-#if 1
-static void usb_set_autosuspend_delay(struct usb_device *usbdev, int delay)
-{
- pm_runtime_set_autosuspend_delay(&usbdev->dev, delay);
-}
-#else
-static void usb_set_autosuspend_delay(struct usb_device *usbdev, int delay)
-{
- usbdev->autosuspend_delay = msecs_to_jiffies(delay);
-}
-#endif
-
static int start_ipc(struct link_device *ld, struct io_device *iod)
{
struct sk_buff *skb;
@@ -177,7 +165,7 @@ static int usb_rx_submit(struct usb_link_device *usb_ld,
pipe_data->rx_buf_size, usb_rx_complete,
(void *)pipe_data);
- if (!usb_ld->if_usb_connected || !usb_ld->usbdev)
+ if (pipe_data->disconnected)
return -ENOENT;
usb_mark_last_busy(usb_ld->usbdev);
@@ -406,6 +394,8 @@ static void usb_tx_complete(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct io_device *iod = skbpriv(skb)->iod;
+ struct link_device *ld = skbpriv(skb)->ld;
+ struct usb_link_device *usb_ld = to_usb_link_device(ld);
switch (urb->status) {
case 0:
@@ -419,7 +409,7 @@ static void usb_tx_complete(struct urb *urb)
}
dev_kfree_skb_any(skb);
- if (urb->dev)
+ if (urb->dev && usb_ld->if_usb_connected)
usb_mark_last_busy(urb->dev);
usb_free_urb(urb);
}
@@ -439,14 +429,7 @@ static int usb_tx_urb_with_skb(struct usb_device *usbdev, struct sk_buff *skb,
mif_err("alloc urb error\n");
return -ENOMEM;
}
-#if 0
- int i;
- for (i = 0; i < skb->len; i++) {
- if (i > 16)
- break;
- mif_err("[0x%02x]", *(skb->data + i));
- }
-#endif
+
urb->transfer_flags = URB_ZERO_PACKET;
usb_fill_bulk_urb(urb, pipe_data->usbdev, pipe_data->tx_pipe, skb->data,
skb->len, usb_tx_complete, (void *)skb);
@@ -537,7 +520,7 @@ static void usb_tx_work(struct work_struct *work)
* probing, _usb_tx_work return to -ENOENT then runtime usage
* count allways positive and never enter to L2
*/
- if (!usb_ld->if_usb_connected_last) {
+ if (!usb_ld->if_usb_connected) {
mif_info("link is available, but if was not readey\n");
goto retry_tx_work;
}
@@ -649,7 +632,7 @@ static void link_pm_runtime_start(struct work_struct *work)
struct link_pm_data *pm_data =
container_of(work, struct link_pm_data, link_pm_start.work);
struct usb_device *usbdev = pm_data->usb_ld->usbdev;
- struct device *dev, *ppdev;
+ struct device *dev, *hdev;
struct link_device *ld = &pm_data->usb_ld->ld;
if (!pm_data->usb_ld->if_usb_connected
@@ -671,13 +654,15 @@ static void link_pm_runtime_start(struct work_struct *work)
if (pm_data->usb_ld->usbdev && dev->parent) {
mif_info("rpm_status: %d\n",
dev->power.runtime_status);
- usb_set_autosuspend_delay(usbdev, 200);
- ppdev = dev->parent->parent;
+ pm_runtime_set_autosuspend_delay(dev, 200);
+ hdev = usbdev->bus->root_hub->dev.parent;
+ mif_info("EHCI runtime %s, %s\n", dev_driver_string(hdev),
+ dev_name(hdev));
pm_runtime_allow(dev);
- pm_runtime_allow(ppdev);/*ehci*/
+ pm_runtime_allow(hdev);/*ehci*/
pm_data->link_pm_active = true;
pm_data->resume_requested = false;
- pm_data->link_reconnect_cnt = 2;
+ pm_data->link_reconnect_cnt = 5;
pm_data->resume_retry_cnt = 0;
/* retry prvious link tx q */
@@ -768,12 +753,6 @@ static inline int link_pm_slave_wake(struct link_pm_data *pm_data)
HOSTWAKE_TRIGLEVEL)
mdelay(5);
}
- /* runtime pm goes to active */
- if (!gpio_get_value(pm_data->gpio_link_active)) {
- mif_debug("gpio [H ACTV : %d] set 1\n",
- gpio_get_value(pm_data->gpio_link_active));
- gpio_set_value(pm_data->gpio_link_active, 1);
- }
return spin;
}
@@ -833,6 +812,11 @@ static void link_pm_runtime_work(struct work_struct *work)
}
wake_unlock(&pm_data->rpm_wake);
break;
+ case RPM_SUSPENDING:
+ /* Checking the usb_runtime_suspend running time.*/
+ mif_info("rpm_states=%d", dev->power.runtime_status);
+ msleep(20);
+ break;
default:
break;
}
@@ -919,6 +903,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
{
int value;
struct link_pm_data *pm_data = file->private_data;
+ struct modem_ctl *mc = if_usb_get_modemctl(pm_data);
mif_info("%x\n", cmd);
@@ -955,6 +940,8 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
irq_set_irq_type(pm_data->irq_link_hostwake,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING);
}
+ case IOCTL_LINK_GET_PHONEACTIVE:
+ return gpio_get_value(mc->gpio_phone_active);
default:
break;
}
@@ -1001,7 +988,10 @@ static int link_pm_notifier_event(struct notifier_block *this,
pm_data->dpm_suspending = true;
#ifdef CONFIG_UMTS_MODEM_XMM6262
/* set PDA Active High if previous state was LPA */
- gpio_set_value(mc->gpio_pda_active, 1);
+ if (!gpio_get_value(pm_data->gpio_link_active)) {
+ mif_info("PDA active High to LPA suspend spot\n");
+ gpio_set_value(mc->gpio_pda_active, 1);
+ }
#endif
mif_debug("dpm suspending set to true\n");
return NOTIFY_OK;
@@ -1017,6 +1007,14 @@ static int link_pm_notifier_event(struct notifier_block *this,
0);
mif_info("post resume\n");
}
+#ifdef CONFIG_UMTS_MODEM_XMM6262
+ /* LPA to Kernel suspend and User Freezing task fail resume,
+ restore to LPA GPIO states. */
+ if (!gpio_get_value(pm_data->gpio_link_active)) {
+ mif_info("PDA active low to LPA GPIO state\n");
+ gpio_set_value(mc->gpio_pda_active, 0);
+ }
+#endif
mif_debug("dpm suspending set to false\n");
return NOTIFY_OK;
}
@@ -1122,7 +1120,7 @@ static void if_usb_disconnect(struct usb_interface *intf)
{
struct if_usb_devdata *devdata = usb_get_intfdata(intf);
struct link_pm_data *pm_data = devdata->usb_ld->link_pm_data;
- struct device *dev, *ppdev;
+ struct device *dev, *hdev;
struct link_device *ld = &devdata->usb_ld->ld;
mif_info("\n");
@@ -1130,13 +1128,14 @@ static void if_usb_disconnect(struct usb_interface *intf)
if (devdata->disconnected)
return;
+ devdata->usb_ld->if_usb_connected = 0;
+
usb_driver_release_interface(to_usb_driver(intf->dev.driver), intf);
usb_kill_urb(devdata->urb);
- dev = &devdata->usb_ld->usbdev->dev;
- ppdev = dev->parent->parent;
- pm_runtime_forbid(ppdev); /*ehci*/
+ hdev = devdata->usbdev->bus->root_hub->dev.parent;
+ pm_runtime_forbid(hdev); /*ehci*/
mif_info("put dev 0x%p\n", devdata->usbdev);
usb_put_dev(devdata->usbdev);
@@ -1148,8 +1147,6 @@ static void if_usb_disconnect(struct usb_interface *intf)
devdata->state = STATE_SUSPENDED;
pm_data->ipc_debug_cnt = 0;
- devdata->usb_ld->if_usb_connected = 0;
- devdata->usb_ld->if_usb_connected_last = 0;
devdata->usb_ld->suspended = 0;
wake_lock(&pm_data->boot_wake);
@@ -1170,9 +1167,12 @@ static void if_usb_disconnect(struct usb_interface *intf)
if (devdata->usb_ld->ld.com_state != COM_ONLINE) {
cancel_delayed_work(&pm_data->link_reconnect_work);
return;
- } else
+ } else {
+ if (pm_data->ehci_reg_dump)
+ pm_data->ehci_reg_dump(hdev);
schedule_delayed_work(&pm_data->link_reconnect_work,
msecs_to_jiffies(500));
+ }
return;
}
@@ -1206,9 +1206,6 @@ static int if_usb_set_pipe(struct usb_link_device *usb_ld,
return 0;
}
-
-static struct usb_id_info hsic_channel_info;
-
static int __devinit if_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1226,12 +1223,25 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
mif_info("usbdev = 0x%p\n", usbdev);
+ pr_debug("%s: Class=%d, SubClass=%d, Protocol=%d\n", __func__,
+ intf->altsetting->desc.bInterfaceClass,
+ intf->altsetting->desc.bInterfaceSubClass,
+ intf->altsetting->desc.bInterfaceProtocol);
+
+ /* if usb disconnected, AP try to reconnect 5 times.
+ * but because if_sub_connected is configured
+ * at the end of if_usb_probe, there was a chance
+ * that swk will be called again during enumeration.
+ * so.. cancel reconnect work_queue in this case. */
+ if (usb_ld->ld.com_state == COM_ONLINE)
+ cancel_delayed_work(&usb_ld->link_pm_data->link_reconnect_work);
+
usb_ld->usbdev = usbdev;
pm_runtime_forbid(&usbdev->dev);
usb_ld->link_pm_data->link_pm_active = false;
usb_ld->link_pm_data->dpm_suspending = false;
usb_ld->link_pm_data->ipc_debug_cnt = 0;
- usb_ld->if_usb_is_main = (info == &hsic_channel_info);
+ usb_ld->if_usb_is_main = (info->intf_id != BOOT_DOWN);
union_hdr = NULL;
/* for WMC-ACM compatibility, WMC-ACM use an end-point for control msg*/
@@ -1314,7 +1324,6 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
usb_ld->devdata[pipe].disconnected = 0;
usb_ld->devdata[pipe].state = STATE_RESUMED;
- usb_ld->if_usb_connected = 1;
usb_ld->suspended = 0;
err = usb_driver_claim_interface(usbdrv, data_intf,
@@ -1345,7 +1354,7 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
link_pm_change_modem_state(usb_ld->link_pm_data, STATE_ONLINE);
if (pipe == IF_USB_CMD_EP || info->intf_id == BOOT_DOWN)
- usb_ld->if_usb_connected_last = 1;
+ usb_ld->if_usb_connected = 1;
mif_info("successfully done\n");
@@ -1369,9 +1378,11 @@ static struct usb_id_info hsic_channel_info = {
};
static struct usb_device_id if_usb_ids[] = {
- {USB_DEVICE(IMC_BOOT_VID, IMC_BOOT_PID),
+ {USB_DEVICE_AND_INTERFACE_INFO(IMC_BOOT_VID, IMC_BOOT_PID,
+ USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&hsic_boot_down_info,},
- {USB_DEVICE(IMC_MAIN_VID, IMC_MAIN_PID),
+ {USB_DEVICE_AND_INTERFACE_INFO(IMC_MAIN_VID, IMC_MAIN_PID,
+ USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 1),
.driver_info = (unsigned long)&hsic_channel_info,},
{USB_DEVICE(STE_BOOT_VID, STE_BOOT_PID),
.driver_info = (unsigned long)&hsic_boot_down_info,},
@@ -1446,13 +1457,21 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data)
struct platform_device *pdev = (struct platform_device *)data;
struct modem_data *pdata =
(struct modem_data *)pdev->dev.platform_data;
- struct modemlink_pm_data *pm_pdata = pdata->link_pm_data;
+ struct modemlink_pm_data *pm_pdata;
struct link_pm_data *pm_data =
kzalloc(sizeof(struct link_pm_data), GFP_KERNEL);
+
+ if (!pdata || !pdata->link_pm_data) {
+ mif_err("platform data is NULL\n");
+ return -EINVAL;
+ }
+ pm_pdata = pdata->link_pm_data;
+
if (!pm_data) {
mif_err("link_pm_data is NULL\n");
return -ENOMEM;
}
+
/* get link pm data from modemcontrol's platform data */
pm_data->gpio_link_active = pm_pdata->gpio_link_active;
pm_data->gpio_link_enable = pm_pdata->gpio_link_enable;
@@ -1461,6 +1480,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data)
pm_data->irq_link_hostwake = gpio_to_irq(pm_data->gpio_link_hostwake);
pm_data->link_ldo_enable = pm_pdata->link_ldo_enable;
pm_data->link_reconnect = pm_pdata->link_reconnect;
+ pm_data->ehci_reg_dump = pm_pdata->ehci_reg_dump;
pm_data->usb_ld = usb_ld;
pm_data->link_pm_active = false;
diff --git a/drivers/misc/modem_if/modem_link_device_hsic.h b/drivers/misc/modem_if/modem_link_device_hsic.h
index 8aec412..604b067 100755..100644
--- a/drivers/misc/modem_if/modem_link_device_hsic.h
+++ b/drivers/misc/modem_if/modem_link_device_hsic.h
@@ -36,6 +36,7 @@ enum {
#define IOCTL_LINK_GET_HOSTWAKE _IO('o', 0x32)
#define IOCTL_LINK_CONNECTED _IO('o', 0x33)
#define IOCTL_LINK_SET_BIAS_CLEAR _IO('o', 0x34)
+#define IOCTL_LINK_GET_PHONEACTIVE _IO('o', 0x35)
/* VID,PID for IMC - XMM6260, XMM6262*/
#define IMC_BOOT_VID 0x058b
@@ -102,6 +103,8 @@ struct link_pm_data {
unsigned ipc_debug_cnt;
unsigned long tx_cnt;
unsigned long rx_cnt;
+
+ void (*ehci_reg_dump)(struct device *);
};
struct if_usb_devdata {
@@ -130,10 +133,6 @@ struct usb_link_device {
unsigned int suspended;
int if_usb_connected;
- /*It is same with if_usb_connected, but we need to check the side-effect
- * from timming changed, it will merge with if_usb_connect variable.*/
- int if_usb_connected_last;
-
bool if_usb_is_main; /* boot,down(false) or main(true) */
/* LINK PM DEVICE DATA */
diff --git a/drivers/misc/modem_if/modem_link_device_pld.c b/drivers/misc/modem_if/modem_link_device_pld.c
new file mode 100644
index 0000000..b68040e
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_pld.c
@@ -0,0 +1,1981 @@
+/*
+ * 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/kallsyms.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+#include "modem_link_device_pld.h"
+#include "modem_utils.h"
+
+
+/*
+** Function prototypes for basic DPRAM operations
+*/
+static inline void clear_intr(struct dpram_link_device *dpld);
+static inline u16 recv_intr(struct dpram_link_device *dpld);
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask);
+
+static inline u16 get_magic(struct dpram_link_device *dpld);
+static inline void set_magic(struct dpram_link_device *dpld, u16 val);
+static inline u16 get_access(struct dpram_link_device *dpld);
+static inline void set_access(struct dpram_link_device *dpld, u16 val);
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id);
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out);
+
+static void handle_cp_crash(struct dpram_link_device *dpld);
+static int trigger_force_cp_crash(struct dpram_link_device *dpld);
+
+/*
+** Functions for debugging
+*/
+static inline void log_dpram_status(struct dpram_link_device *dpld)
+{
+ pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} "
+ "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n",
+ dpld->ld.mc->name,
+ get_magic(dpld), get_access(dpld),
+ get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT),
+ get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT),
+ get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW),
+ get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW),
+ recv_intr(dpld));
+}
+
+static void set_dpram_map(struct dpram_link_device *dpld,
+ struct mif_irq_map *map)
+{
+ map->magic = get_magic(dpld);
+ map->access = get_access(dpld);
+
+ map->fmt_tx_in = get_tx_head(dpld, IPC_FMT);
+ map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT);
+ map->fmt_rx_in = get_rx_head(dpld, IPC_FMT);
+ map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT);
+ map->raw_tx_in = get_tx_head(dpld, IPC_RAW);
+ map->raw_tx_out = get_tx_tail(dpld, IPC_RAW);
+ map->raw_rx_in = get_rx_head(dpld, IPC_RAW);
+ map->raw_rx_out = get_rx_tail(dpld, IPC_RAW);
+
+ map->cp2ap = recv_intr(dpld);
+}
+
+/*
+** RXB (DPRAM RX buffer) functions
+*/
+static struct dpram_rxb *rxbq_create_pool(unsigned size, int count)
+{
+ struct dpram_rxb *rxb;
+ u8 *buff;
+ int i;
+
+ rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL);
+ if (!rxb) {
+ mif_info("ERR! kzalloc rxb fail\n");
+ return NULL;
+ }
+
+ buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA);
+ if (!buff) {
+ mif_info("ERR! kzalloc buff fail\n");
+ kfree(rxb);
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++) {
+ rxb[i].buff = buff;
+ rxb[i].size = size;
+ buff += size;
+ }
+
+ return rxb;
+}
+
+static inline unsigned rxbq_get_page_size(unsigned len)
+{
+ return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT;
+}
+
+static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq)
+{
+ return (rxbq->in == rxbq->out) ? true : false;
+}
+
+static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq)
+{
+ int in = rxbq->in;
+ int out = rxbq->out;
+ int qsize = rxbq->size;
+ return (in < out) ? (out - in - 1) : (qsize + out - in - 1);
+}
+
+static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq)
+{
+ struct dpram_rxb *rxb = NULL;
+
+ if (likely(rxbq_free_size(rxbq) > 0)) {
+ rxb = &rxbq->rxb[rxbq->in];
+ rxbq->in++;
+ if (rxbq->in >= rxbq->size)
+ rxbq->in -= rxbq->size;
+ rxb->data = rxb->buff;
+ }
+
+ return rxb;
+}
+
+static inline int rxbq_size(struct dpram_rxb_queue *rxbq)
+{
+ int in = rxbq->in;
+ int out = rxbq->out;
+ int qsize = rxbq->size;
+ return (in >= out) ? (in - out) : (qsize - out + in);
+}
+
+static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq)
+{
+ struct dpram_rxb *rxb = NULL;
+
+ if (likely(!rxbq_empty(rxbq))) {
+ rxb = &rxbq->rxb[rxbq->out];
+ rxbq->out++;
+ if (rxbq->out >= rxbq->size)
+ rxbq->out -= rxbq->size;
+ }
+
+ return rxb;
+}
+
+static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len)
+{
+ rxb->len = len;
+ return rxb->data;
+}
+
+static inline void rxb_clear(struct dpram_rxb *rxb)
+{
+ rxb->data = NULL;
+ rxb->len = 0;
+}
+
+/*
+** DPRAM operations
+*/
+static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*),
+ unsigned long flag, const char *name,
+ struct dpram_link_device *dpld)
+{
+ int ret = 0;
+
+ ret = request_irq(irq, isr, flag, name, dpld);
+ if (ret) {
+ mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret)
+ mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret);
+
+ mif_info("%s (#%d) handler registered\n", name, irq);
+
+ return 0;
+}
+
+static inline void clear_intr(struct dpram_link_device *dpld)
+{
+ if (dpld->dpctl->clear_intr)
+ dpld->dpctl->clear_intr();
+}
+
+static inline u16 recv_intr(struct dpram_link_device *dpld)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: intr1(%d) != intr1(%d)\n", val1, val2);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+
+ return val1;
+}
+
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)mask, dpld->dp_base);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == mask)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: intr1(%d) != intr2(%d)\n", val, mask);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)mask, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+
+ return;
+}
+
+static inline u16 get_magic(struct dpram_link_device *dpld)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: txq.head(%d) != in(%d)\n", val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+
+}
+
+static inline void set_magic(struct dpram_link_device *dpld, u16 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == in)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: magic1(%d) != magic2(%d)\n", val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline u16 get_access(struct dpram_link_device *dpld)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: access1(%d) != access2(%d)\n", val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+
+}
+
+static inline void set_access(struct dpram_link_device *dpld, u16 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == in)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: access(%d) != access(%d)\n", val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s txq.head(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s txq.tail(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == in)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: %s txq.head(%d) != in(%d)\n",
+ get_dev_name(id), val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ return;
+}
+
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.buff;
+}
+
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.size;
+}
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s rxq.head(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s rxq.tail(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ return;
+}
+
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)out, dpld->dp_base);
+
+ do {
+ /* Check tail value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (val == out) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: %s rxq.tail(%d) != out(%d)\n",
+ get_dev_name(id), val, out);
+ udelay(100);
+
+ /* Write tail value again */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)out, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.buff;
+}
+
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.size;
+}
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_req_ack;
+}
+
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_res_ack;
+}
+
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_send;
+}
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out)
+{
+ if (in >= size)
+ return false;
+
+ if (out >= size)
+ return false;
+
+ return true;
+}
+
+/* Get free space in the TXQ as well as in & out pointers */
+static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ int space;
+
+ do {
+ head = get_tx_head(dpld, dev);
+ tail = get_tx_tail(dpld, dev);
+
+ space = (head < tail) ? (tail - head - 1) :
+ (qsize + tail - head - 1);
+ mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, space);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return space;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_TXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_tx_head(dpld, dev, 0);
+ set_tx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
+/* Get data size in the RXQ as well as in & out pointers */
+static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ u32 rcvd;
+
+ do {
+ head = get_rx_head(dpld, dev);
+ tail = get_rx_tail(dpld, dev);
+ if (head == tail) {
+ *in = head;
+ *out = tail;
+ return 0;
+ }
+
+ rcvd = (head > tail) ? (head - tail) : (qsize - tail + head);
+ mif_info("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, rcvd);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return rcvd;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_RXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_rx_head(dpld, dev, 0);
+ set_rx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
+/*
+** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success
+*/
+static int dpram_wake_up(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (!dpld->dpctl->wakeup)
+ return 0;
+
+ if (dpld->dpctl->wakeup() < 0) {
+ mif_err("%s: ERR! <%pf> DPRAM wakeup fail\n",
+ ld->name, __builtin_return_address(0));
+ return -EACCES;
+ }
+
+ atomic_inc(&dpld->accessing);
+ return 0;
+}
+
+static void dpram_allow_sleep(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (!dpld->dpctl->sleep)
+ return;
+
+ if (atomic_dec_return(&dpld->accessing) <= 0) {
+ dpld->dpctl->sleep();
+ atomic_set(&dpld->accessing, 0);
+ mif_debug("%s: DPRAM sleep possible\n", ld->name);
+ }
+}
+
+static int dpram_check_access(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ int i;
+ u16 magic = get_magic(dpld);
+ u16 access = get_access(dpld);
+
+ if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
+ return 0;
+
+ for (i = 1; i <= 100; i++) {
+ mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n",
+ ld->name, magic, access, i);
+ udelay(100);
+
+ magic = get_magic(dpld);
+ access = get_access(dpld);
+ if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
+ return 0;
+ }
+
+ mif_info("%s: !CRISIS! magic:%X access:%X\n", ld->name, magic, access);
+ return -EACCES;
+}
+
+static bool dpram_ipc_active(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ /* Check DPRAM mode */
+ if (ld->mode != LINK_MODE_IPC) {
+ mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n",
+ ld->name, __builtin_return_address(0));
+ return false;
+ }
+
+ if (dpram_check_access(dpld) < 0) {
+ mif_info("%s: ERR! <%pf> dpram_check_access fail\n",
+ ld->name, __builtin_return_address(0));
+ return false;
+ }
+
+ return true;
+}
+
+static void dpram_ipc_write(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 in, u32 out, struct sk_buff *skb)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 __iomem *buff = get_tx_buff(dpld, dev);
+ u8 *src = skb->data;
+ u32 len = skb->len;
+ u32 inp;
+ struct mif_irq_map map;
+
+ if (in < out) {
+ /* +++++++++ in ---------- out ++++++++++ */
+ iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer);
+ memcpy(dpld->dp_base, src, len);
+ } else {
+ /* ------ out +++++++++++ in ------------ */
+ u32 space = qsize - in;
+
+ /* 1) in -> buffer end */
+ iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer);
+ memcpy(dpld->dp_base, src, ((len > space) ? space : len));
+
+ if (len > space) {
+ iowrite16(PLD_ADDR_MASK(&buff[0]),
+ dpld->address_buffer);
+ memcpy(dpld->dp_base, (src+space), (len-space));
+ }
+ }
+
+ /* update new in pointer */
+ inp = in + len;
+ if (inp >= qsize)
+ inp -= qsize;
+ set_tx_head(dpld, dev, inp);
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write"));
+ mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len);
+ }
+}
+
+static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev)
+{
+ struct link_device *ld = &dpld->ld;
+ struct sk_buff_head *txq = ld->skb_txq[dev];
+ struct sk_buff *skb;
+ unsigned long int flags;
+ u32 qsize = get_tx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ int space;
+ int copied = 0;
+
+ spin_lock_irqsave(&dpld->tx_rx_lock, flags);
+
+ while (1) {
+ space = dpram_get_txq_space(dpld, dev, qsize, &in, &out);
+ if (unlikely(space < 0)) {
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ dpram_reset_tx_circ(dpld, dev);
+ return space;
+ }
+
+ skb = skb_dequeue(txq);
+ if (unlikely(!skb))
+ break;
+
+ if (unlikely(space < skb->len)) {
+ atomic_set(&dpld->res_required[dev], 1);
+ skb_queue_head(txq, skb);
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ mif_info("%s: %s "
+ "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n",
+ ld->name, get_dev_name(dev),
+ qsize, in, out, space, skb->len);
+ return -ENOSPC;
+ }
+
+ /* TX if there is enough room in the queue */
+ dpram_ipc_write(dpld, dev, qsize, in, out, skb);
+ copied += skb->len;
+ dev_kfree_skb_any(skb);
+ }
+
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+
+ return copied;
+}
+
+static void dpram_ipc_rx_task(unsigned long data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+ struct dpram_rxb *rxb;
+ unsigned qlen;
+ int i;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ iod = dpld->iod[i];
+ qlen = rxbq_size(&dpld->rxbq[i]);
+ while (qlen > 0) {
+ rxb = rxbq_get_data_rxb(&dpld->rxbq[i]);
+ iod->recv(iod, ld, rxb->data, rxb->len);
+ rxb_clear(rxb);
+ qlen--;
+ }
+ }
+}
+
+static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst,
+ u8 __iomem *src, u32 out, u32 len, u32 qsize)
+{
+ u8 *ori_det = dst;
+ unsigned long flags;
+
+ if ((out + len) <= qsize) {
+ /* ----- (out) (in) ----- */
+ /* ----- 7f 00 00 7e ----- */
+ iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer);
+ memcpy(dst, dpld->dp_base, len);
+ } else {
+ /* (in) ----------- (out) */
+ /* 00 7e ----------- 7f 00 */
+ unsigned len1 = qsize - out;
+
+ /* 1) out -> buffer end */
+ iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer);
+ memcpy(dst, dpld->dp_base, len1);
+
+ /* 2) buffer start -> in */
+ dst += len1;
+ iowrite16(PLD_ADDR_MASK(&src[0]), dpld->address_buffer);
+ memcpy(dst, dpld->dp_base, (len - len1));
+ }
+
+ if (dpld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) {
+ mif_info("ipc read error!! in[%d], out[%d]\n",
+ get_rx_head(dpld, dev),
+ get_rx_tail(dpld, dev));
+ }
+
+}
+
+/*
+ ret < 0 : error
+ ret == 0 : no data
+ ret > 0 : valid data
+*/
+static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_rxb *rxb;
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ struct mif_irq_map map;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->tx_rx_lock, flags);
+
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0) {
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ return rcvd;
+ }
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
+ }
+
+ /* Allocate an rxb */
+ rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]);
+ if (!rxb) {
+ mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n",
+ ld->name, get_dev_name(dev));
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ return -ENOMEM;
+ }
+
+ /* Read data from each DPRAM buffer */
+ dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize);
+
+ /* Calculate and set new out */
+ out += rcvd;
+ if (out >= qsize)
+ out -= qsize;
+ set_rx_tail(dpld, dev, out);
+
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ return rcvd;
+}
+
+/*
+ ret < 0 : error
+ ret == 0 : no data
+ ret > 0 : valid data
+*/
+static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = dpld->iod[dev];
+ struct sk_buff *skb;
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ int rest;
+ u8 *frm;
+ u8 *dst;
+ unsigned int len;
+ unsigned int pad;
+ unsigned int tot;
+ struct mif_irq_map map;
+
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0)
+ return rcvd;
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
+ }
+
+ rest = rcvd;
+ while (rest > 0) {
+ frm = src + out;
+ if (unlikely(!sipc5_start_valid(frm[0]))) {
+ mif_err("%s: ERR! %s invalid start 0x%02X\n",
+ ld->name, get_dev_name(dev), frm[0]);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ len = sipc5_get_frame_sz16(frm);
+ if (unlikely(len > rest)) {
+ mif_err("%s: ERR! %s len %d > rest %d\n",
+ ld->name, get_dev_name(dev), len, rest);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ pad = sipc5_calc_padding_size(len);
+ tot = len + pad;
+
+ /* Allocate an skb */
+ skb = dev_alloc_skb(tot);
+ if (!skb) {
+ mif_err("%s: ERR! %s dev_alloc_skb fail\n",
+ ld->name, get_dev_name(dev));
+ return -ENOMEM;
+ }
+
+ /* Read data from each DPRAM buffer */
+ dst = skb_put(skb, tot);
+ dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize);
+ skb_trim(skb, len);
+ iod->recv_skb(iod, ld, skb);
+
+ /* Calculate and set new out */
+ rest -= tot;
+ out += tot;
+ if (out >= qsize)
+ out -= qsize;
+ }
+
+ set_rx_tail(dpld, dev, out);
+
+ return rcvd;
+}
+
+static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd)
+{
+ struct link_device *ld = &dpld->ld;
+ int i = 0;
+ int ret = 0;
+ u16 tx_mask = 0;
+
+ if (!dpram_ipc_active(dpld))
+ return;
+
+ /* Read data from DPRAM */
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ if (dpld->use_skb)
+ ret = dpram_ipc_recv_data_with_skb(dpld, i);
+ else
+ ret = dpram_ipc_recv_data_with_rxb(dpld, i);
+ if (ret < 0)
+ dpram_reset_rx_circ(dpld, i);
+
+ /* Check and process REQ_ACK (at this time, in == out) */
+ if (non_cmd & get_mask_req_ack(dpld, i)) {
+ mif_debug("%s: send %s_RES_ACK\n",
+ ld->name, get_dev_name(i));
+ tx_mask |= get_mask_res_ack(dpld, i);
+ }
+ }
+
+ if (!dpld->use_skb) {
+ /* Schedule soft IRQ for RX */
+ tasklet_hi_schedule(&dpld->rx_tsk);
+ }
+
+ /* Try TX via DPRAM */
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ if (atomic_read(&dpld->res_required[i]) > 0) {
+ ret = dpram_try_ipc_tx(dpld, i);
+ if (ret > 0) {
+ atomic_set(&dpld->res_required[i], 0);
+ tx_mask |= get_mask_send(dpld, i);
+ } else if (ret == -ENOSPC) {
+ tx_mask |= get_mask_req_ack(dpld, i);
+ }
+ }
+ }
+
+ if (tx_mask) {
+ send_intr(dpld, INT_NON_CMD(tx_mask));
+ mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask);
+ }
+}
+
+static void handle_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+ int i;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i));
+ skb_queue_purge(ld->skb_txq[i]);
+ }
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_format(ld, IPC_BOOT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_channel(ld, PS_DATA_CH_0);
+ if (iod)
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
+}
+
+static void handle_no_crash_ack(unsigned long arg)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
+ struct link_device *ld = &dpld->ld;
+
+ mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name);
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ handle_cp_crash(dpld);
+}
+
+static int trigger_force_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (ld->mode == LINK_MODE_ULOAD) {
+ mif_err("%s: CP crash is already in progress\n", ld->mc->name);
+ return 0;
+ }
+
+ ld->mode = LINK_MODE_ULOAD;
+ mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0));
+
+ dpram_wake_up(dpld);
+
+ send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT));
+
+ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT,
+ handle_no_crash_ack, (unsigned long)dpld);
+
+ return 0;
+}
+
+static int dpram_init_ipc(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ int i;
+
+ if (ld->mode == LINK_MODE_IPC &&
+ get_magic(dpld) == DPRAM_MAGIC_CODE &&
+ get_access(dpld) == 1)
+ mif_info("%s: IPC already initialized\n", ld->name);
+
+ /* Clear pointers in every circular queue */
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ set_tx_head(dpld, i, 0);
+ set_tx_tail(dpld, i, 0);
+ set_rx_head(dpld, i, 0);
+ set_rx_tail(dpld, i, 0);
+ }
+
+ /* Initialize variables for efficient TX/RX processing */
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->iod[i] = link_get_iod_with_format(ld, i);
+ dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW);
+
+ if (dpld->iod[IPC_RAW]->recv_skb)
+ dpld->use_skb = true;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ atomic_set(&dpld->res_required[i], 0);
+ skb_queue_purge(&dpld->skb_rxq[i]);
+ }
+
+ spin_lock_init(&dpld->tx_rx_lock);
+
+ /* Enable IPC */
+ atomic_set(&dpld->accessing, 0);
+
+ set_magic(dpld, DPRAM_MAGIC_CODE);
+ set_access(dpld, 1);
+ if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1)
+ return -EACCES;
+
+ ld->mode = LINK_MODE_IPC;
+
+ if (wake_lock_active(&dpld->wlock))
+ wake_unlock(&dpld->wlock);
+
+ return 0;
+}
+
+static void cmd_req_active_handler(struct dpram_link_device *dpld)
+{
+ send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
+}
+
+static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = NULL;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name);
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ iod->modem_state_changed(iod, STATE_CRASH_RESET);
+
+ iod = link_get_iod_with_format(ld, IPC_BOOT);
+ iod->modem_state_changed(iod, STATE_CRASH_RESET);
+}
+
+static void cmd_crash_exit_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name);
+
+ dpram_wake_up(dpld);
+
+ del_timer(&dpld->crash_ack_timer);
+
+ if (dpld->ext_op && dpld->ext_op->crash_log)
+ dpld->ext_op->crash_log(dpld);
+
+ handle_cp_crash(dpld);
+}
+
+static void cmd_phone_start_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = NULL;
+
+ mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name);
+
+ dpram_init_ipc(dpld);
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ if (!iod) {
+ mif_info("%s: ERR! no iod\n", ld->name);
+ return;
+ }
+
+ if (dpld->ext_op && dpld->ext_op->cp_start_handler)
+ dpld->ext_op->cp_start_handler(dpld);
+
+ if (ld->mc->phone_state != STATE_ONLINE) {
+ mif_info("%s: phone_state: %d -> ONLINE\n",
+ ld->name, ld->mc->phone_state);
+ iod->modem_state_changed(iod, STATE_ONLINE);
+ }
+
+ mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name);
+ send_intr(dpld, INT_CMD(INT_CMD_INIT_END));
+}
+
+static void command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ struct link_device *ld = &dpld->ld;
+
+ switch (INT_CMD_MASK(cmd)) {
+ case INT_CMD_REQ_ACTIVE:
+ cmd_req_active_handler(dpld);
+ break;
+
+ case INT_CMD_CRASH_RESET:
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+ cmd_crash_reset_handler(dpld);
+ break;
+
+ case INT_CMD_CRASH_EXIT:
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+ cmd_crash_exit_handler(dpld);
+ break;
+
+ case INT_CMD_PHONE_START:
+ dpld->dpram_init_status = DPRAM_INIT_STATE_READY;
+ cmd_phone_start_handler(dpld);
+ complete_all(&dpld->dpram_init_cmd);
+ break;
+
+ case INT_CMD_NV_REBUILDING:
+ mif_info("%s: NV_REBUILDING\n", ld->name);
+ break;
+
+ case INT_CMD_PIF_INIT_DONE:
+ complete_all(&dpld->modem_pif_init_done);
+ break;
+
+ case INT_CMD_SILENT_NV_REBUILDING:
+ mif_info("%s: SILENT_NV_REBUILDING\n", ld->name);
+ break;
+
+ case INT_CMD_NORMAL_PWR_OFF:
+ /*ToDo:*/
+ /*kernel_sec_set_cp_ack()*/;
+ break;
+
+ case INT_CMD_REQ_TIME_SYNC:
+ case INT_CMD_CP_DEEP_SLEEP:
+ case INT_CMD_EMER_DOWN:
+ break;
+
+ default:
+ mif_info("%s: unknown command 0x%04X\n", ld->name, cmd);
+ }
+}
+
+static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ struct link_device *ld = &dpld->ld;
+ u16 resp;
+
+ switch (EXT_CMD_MASK(cmd)) {
+ case EXT_CMD_SET_SPEED_LOW:
+ if (dpld->dpctl->setup_speed) {
+ dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
+ resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW);
+ send_intr(dpld, resp);
+ }
+ break;
+
+ case EXT_CMD_SET_SPEED_MID:
+ if (dpld->dpctl->setup_speed) {
+ dpld->dpctl->setup_speed(DPRAM_SPEED_MID);
+ resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID);
+ send_intr(dpld, resp);
+ }
+ break;
+
+ case EXT_CMD_SET_SPEED_HIGH:
+ if (dpld->dpctl->setup_speed) {
+ dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
+ resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH);
+ send_intr(dpld, resp);
+ }
+ break;
+
+ default:
+ mif_info("%s: unknown command 0x%04X\n", ld->name, cmd);
+ break;
+ }
+}
+
+static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (cmd & UDL_RESULT_FAIL) {
+ mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd);
+ return;
+ }
+
+ switch (UDL_CMD_MASK(cmd)) {
+ case UDL_CMD_RECV_READY:
+ mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name);
+ send_intr(dpld, CMD_IMG_START_REQ);
+ break;
+ default:
+ complete_all(&dpld->udl_cmd_complete);
+ }
+}
+
+static irqreturn_t dpram_irq_handler(int irq, void *data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = 0;
+
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
+
+ if (dpram_wake_up(dpld) < 0) {
+ log_dpram_status(dpld);
+ trigger_force_cp_crash(dpld);
+ return IRQ_HANDLED;
+ }
+
+ int2ap = recv_intr(dpld);
+
+ if (unlikely(int2ap == INT_POWERSAFE_FAIL)) {
+ mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name);
+ goto exit;
+ } else if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) {
+ if (dpld->ext_op && dpld->ext_op->dload_cmd_handler) {
+ dpld->ext_op->dload_cmd_handler(dpld, int2ap);
+ goto exit;
+ }
+ }
+
+ if (unlikely(EXT_UDL_CMD(int2ap))) {
+ if (likely(EXT_INT_VALID(int2ap))) {
+ if (UDL_CMD_VALID(int2ap))
+ udl_command_handler(dpld, int2ap);
+ else if (EXT_CMD_VALID(int2ap))
+ ext_command_handler(dpld, int2ap);
+ else
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ ld->name, int2ap);
+ } else {
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ ld->name, int2ap);
+ }
+ } else {
+ if (likely(INT_VALID(int2ap))) {
+ if (unlikely(INT_CMD_VALID(int2ap)))
+ command_handler(dpld, int2ap);
+ else
+ non_command_handler(dpld, int2ap);
+ } else {
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ ld->name, int2ap);
+ }
+ }
+
+exit:
+ clear_intr(dpld);
+ dpram_allow_sleep(dpld);
+ return IRQ_HANDLED;
+}
+
+static void dpram_send_ipc(struct link_device *ld, int dev,
+ struct io_device *iod, struct sk_buff *skb)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct sk_buff_head *txq = ld->skb_txq[dev];
+ int ret;
+ u16 mask;
+
+ skb_queue_tail(txq, skb);
+ if (txq->qlen > 1024) {
+ mif_debug("%s: %s txq->qlen %d > 1024\n",
+ ld->name, get_dev_name(dev), txq->qlen);
+ }
+
+ if (dpram_wake_up(dpld) < 0) {
+ trigger_force_cp_crash(dpld);
+ return;
+ }
+
+ if (!dpram_ipc_active(dpld))
+ goto exit;
+
+ if (atomic_read(&dpld->res_required[dev]) > 0) {
+ mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev));
+ goto exit;
+ }
+
+ ret = dpram_try_ipc_tx(dpld, dev);
+ if (ret > 0) {
+ mask = get_mask_send(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
+ } else if (ret == -ENOSPC) {
+ mask = get_mask_req_ack(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
+ mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask);
+ } else {
+ mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret);
+ }
+
+exit:
+ dpram_allow_sleep(dpld);
+}
+
+static int dpram_download_bin(struct link_device *ld, struct sk_buff *skb)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ if (dpld->ext_op && dpld->ext_op->dload_bin)
+ return dpld->ext_op->dload_bin(dpld, skb);
+ else
+ return -ENODEV;
+}
+
+static int dpram_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ enum dev_format dev = iod->format;
+ int len = skb->len;
+
+ switch (dev) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ if (likely(ld->mode == LINK_MODE_IPC)) {
+ dpram_send_ipc(ld, dev, iod, skb);
+ } else {
+ mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name);
+ dev_kfree_skb_any(skb);
+ }
+ return len;
+
+ case IPC_BOOT:
+ return dpram_download_bin(ld, skb);
+
+ default:
+ mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name);
+ dev_kfree_skb_any(skb);
+ return -ENODEV;
+ }
+}
+
+static int dpram_force_dump(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ trigger_force_cp_crash(dpld);
+ return 0;
+}
+
+static void dpram_dump_memory(struct link_device *ld, char *buff)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ u8 __iomem *base = dpld->dpctl->dp_base;
+ u32 size = dpld->dpctl->dp_size;
+
+ dpram_wake_up(dpld);
+ memcpy(buff, base, size);
+}
+
+static int dpram_dump_start(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ if (dpld->ext_op && dpld->ext_op->dump_start)
+ return dpld->ext_op->dump_start(dpld);
+ else
+ return -ENODEV;
+}
+
+static int dpram_dump_update(struct link_device *ld, struct io_device *iod,
+ unsigned long arg)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ if (dpld->ext_op && dpld->ext_op->dump_update)
+ return dpld->ext_op->dump_update(dpld, (void *)arg);
+ else
+ return -ENODEV;
+}
+
+static int dpram_ioctl(struct link_device *ld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ int err = 0;
+
+/*
+ mif_info("%s: cmd 0x%08X\n", ld->name, cmd);
+*/
+
+ switch (cmd) {
+ case IOCTL_DPRAM_INIT_STATUS:
+ mif_debug("%s: get dpram init status\n", ld->name);
+ return dpld->dpram_init_status;
+
+ default:
+ if (dpld->ext_ioctl) {
+ err = dpld->ext_ioctl(dpld, iod, cmd, arg);
+ } else {
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ }
+
+ break;
+ }
+
+ return err;
+}
+
+static int dpram_table_init(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 __iomem *dp_base;
+ int i;
+
+ if (!dpld->dp_base) {
+ mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name);
+ return -EINVAL;
+ }
+ dp_base = dpld->dp_base;
+
+ /* Map for IPC */
+ if (dpld->dpctl->ipc_map) {
+ memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map,
+ sizeof(struct dpram_ipc_map));
+ }
+
+ dpld->magic_ap2cp = dpld->ipc_map.magic_ap2cp;
+ dpld->access_ap2cp = dpld->ipc_map.access_ap2cp;
+
+ dpld->magic_cp2ap = dpld->ipc_map.magic_cp2ap;
+ dpld->access_cp2ap = dpld->ipc_map.access_cp2ap;
+
+ dpld->address_buffer = dpld->ipc_map.address_buffer;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->dev[i] = &dpld->ipc_map.dev[i];
+ dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap;
+ dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp;
+
+ /* Map for booting */
+ if (dpld->ext_op && dpld->ext_op->init_boot_map) {
+ dpld->ext_op->init_boot_map(dpld);
+ } else {
+ dpld->bt_map.magic = (u32 *)(dp_base);
+ dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET);
+ dpld->bt_map.size = dpld->dp_size - 8;
+ }
+
+ /* Map for download (FOTA, UDL, etc.) */
+ if (dpld->ext_op && dpld->ext_op->init_dl_map) {
+ dpld->ext_op->init_dl_map(dpld);
+ } else {
+ dpld->dl_map.magic = (u32 *)(dp_base);
+ dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET);
+ }
+
+ /* Map for upload mode */
+ if (dpld->ext_op && dpld->ext_op->init_ul_map) {
+ dpld->ext_op->init_ul_map(dpld);
+ } else {
+ dpld->ul_map.magic = (u32 *)(dp_base);
+ dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET);
+ }
+
+ return 0;
+}
+
+static void dpram_setup_common_op(struct dpram_link_device *dpld)
+{
+ dpld->clear_intr = clear_intr;
+ dpld->recv_intr = recv_intr;
+ dpld->send_intr = send_intr;
+ dpld->get_magic = get_magic;
+ dpld->set_magic = set_magic;
+ dpld->get_access = get_access;
+ dpld->set_access = set_access;
+ dpld->get_tx_head = get_tx_head;
+ dpld->get_tx_tail = get_tx_tail;
+ dpld->set_tx_head = set_tx_head;
+ dpld->set_tx_tail = set_tx_tail;
+ dpld->get_tx_buff = get_tx_buff;
+ dpld->get_tx_buff_size = get_tx_buff_size;
+ dpld->get_rx_head = get_rx_head;
+ dpld->get_rx_tail = get_rx_tail;
+ dpld->set_rx_head = set_rx_head;
+ dpld->set_rx_tail = set_rx_tail;
+ dpld->get_rx_buff = get_rx_buff;
+ dpld->get_rx_buff_size = get_rx_buff_size;
+ dpld->get_mask_req_ack = get_mask_req_ack;
+ dpld->get_mask_res_ack = get_mask_res_ack;
+ dpld->get_mask_send = get_mask_send;
+}
+
+static int dpram_link_init(struct link_device *ld, struct io_device *iod)
+{
+ return 0;
+}
+
+static void dpram_link_terminate(struct link_device *ld, struct io_device *iod)
+{
+ return;
+}
+
+struct link_device *pld_create_link_device(struct platform_device *pdev)
+{
+ struct modem_data *mdm_data = NULL;
+ struct dpram_link_device *dpld = NULL;
+ struct link_device *ld = NULL;
+ struct resource *res = NULL;
+ resource_size_t res_size;
+ struct modemlink_dpram_control *dpctl = NULL;
+ unsigned long task_data;
+ int ret = 0;
+ int i = 0;
+ int bsize;
+ int qsize;
+
+ /* Get the platform data */
+ mdm_data = (struct modem_data *)pdev->dev.platform_data;
+ if (!mdm_data) {
+ mif_info("ERR! mdm_data == NULL\n");
+ goto err;
+ }
+ mif_info("modem = %s\n", mdm_data->name);
+ mif_info("link device = %s\n", mdm_data->link_name);
+
+ if (!mdm_data->dpram_ctl) {
+ mif_info("ERR! mdm_data->dpram_ctl == NULL\n");
+ goto err;
+ }
+ dpctl = mdm_data->dpram_ctl;
+
+ /* Alloc DPRAM link device structure */
+ dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL);
+ if (!dpld) {
+ mif_info("ERR! kzalloc dpld fail\n");
+ goto err;
+ }
+ ld = &dpld->ld;
+
+ /* Retrieve modem data and DPRAM control data from the modem data */
+ ld->mdm_data = mdm_data;
+ ld->name = mdm_data->link_name;
+ ld->ipc_version = mdm_data->ipc_version;
+
+ /* Retrieve the most basic data for IPC from the modem data */
+ dpld->dpctl = dpctl;
+ dpld->dp_type = dpctl->dp_type;
+
+ if (mdm_data->ipc_version < SIPC_VER_50) {
+ if (!dpctl->max_ipc_dev) {
+ mif_info("ERR! no max_ipc_dev\n");
+ goto err;
+ }
+
+ ld->aligned = dpctl->aligned;
+ dpld->max_ipc_dev = dpctl->max_ipc_dev;
+ } else {
+ ld->aligned = 1;
+ dpld->max_ipc_dev = MAX_SIPC5_DEV;
+ }
+
+ /* Set attributes as a link device */
+ ld->init_comm = dpram_link_init;
+ ld->terminate_comm = dpram_link_terminate;
+ ld->send = dpram_send;
+ ld->force_dump = dpram_force_dump;
+ ld->dump_start = dpram_dump_start;
+ ld->dump_update = dpram_dump_update;
+ ld->ioctl = dpram_ioctl;
+
+ 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;
+
+ /* Set up function pointers */
+ dpram_setup_common_op(dpld);
+ dpld->dpram_dump = dpram_dump_memory;
+ dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type);
+ if (dpld->ext_op && dpld->ext_op->ioctl)
+ dpld->ext_ioctl = dpld->ext_op->ioctl;
+
+ /* Retrieve DPRAM resource */
+ if (!dpctl->dp_base) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ mif_info("%s: ERR! platform_get_resource fail\n",
+ ld->name);
+ goto err;
+ }
+ res_size = resource_size(res);
+
+ dpctl->dp_base = ioremap_nocache(res->start, res_size);
+ dpctl->dp_size = res_size;
+ }
+ dpld->dp_base = dpctl->dp_base;
+ dpld->dp_size = dpctl->dp_size;
+
+ mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n",
+ ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base,
+ dpld->dp_size);
+
+ /* Initialize DPRAM map (physical map -> logical map) */
+ ret = dpram_table_init(dpld);
+ if (ret < 0) {
+ mif_info("%s: ERR! dpram_table_init fail (err %d)\n",
+ ld->name, ret);
+ goto err;
+ }
+
+ spin_lock_init(&dpld->pld_lock);
+
+ /* Disable IPC */
+ set_magic(dpld, 0);
+ set_access(dpld, 0);
+
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+
+ /* Initialize locks, completions, and bottom halves */
+ snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name);
+ wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name);
+
+ init_completion(&dpld->dpram_init_cmd);
+ init_completion(&dpld->modem_pif_init_done);
+ init_completion(&dpld->udl_start_complete);
+ init_completion(&dpld->udl_cmd_complete);
+ init_completion(&dpld->dump_start_complete);
+ init_completion(&dpld->dump_recv_done);
+
+ task_data = (unsigned long)dpld;
+ tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data);
+
+ /* Prepare SKB queue head for RX processing */
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ skb_queue_head_init(&dpld->skb_rxq[i]);
+
+ /* Prepare RXB queue */
+ qsize = DPRAM_MAX_RXBQ_SIZE;
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i));
+ dpld->rxbq[i].size = qsize;
+ dpld->rxbq[i].in = 0;
+ dpld->rxbq[i].out = 0;
+ dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize);
+ if (!dpld->rxbq[i].rxb) {
+ mif_info("%s: ERR! %s rxbq_create_pool fail\n",
+ ld->name, get_dev_name(i));
+ goto err;
+ }
+ mif_info("%s: %s rxbq_pool created (bsize:%d, qsize:%d)\n",
+ ld->name, get_dev_name(i), bsize, qsize);
+ }
+
+ /* Prepare a multi-purpose miscellaneous buffer */
+ dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL);
+ if (!dpld->buff) {
+ mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name);
+ goto err;
+ }
+
+ /* Retrieve DPRAM IRQ GPIO# */
+ dpld->gpio_dpram_int = mdm_data->gpio_dpram_int;
+
+ /* Retrieve DPRAM IRQ# */
+ if (!dpctl->dpram_irq) {
+ dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq");
+ if (dpctl->dpram_irq < 0) {
+ mif_info("%s: ERR! platform_get_irq_byname fail\n",
+ ld->name);
+ goto err;
+ }
+ }
+ dpld->irq = dpctl->dpram_irq;
+
+ /* Retrieve DPRAM IRQ flags */
+ if (!dpctl->dpram_irq_flags)
+ dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW);
+ dpld->irq_flags = dpctl->dpram_irq_flags;
+
+ /* Register DPRAM interrupt handler */
+ snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name);
+ ret = dpram_register_isr(dpld->irq, dpram_irq_handler, dpld->irq_flags,
+ dpld->irq_name, dpld);
+ if (ret)
+ goto err;
+
+ return ld;
+
+err:
+ if (dpld) {
+ kfree(dpld->buff);
+ kfree(dpld);
+ }
+
+ return NULL;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_pld.h b/drivers/misc/modem_if/modem_link_device_pld.h
new file mode 100644
index 0000000..2656110
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_pld.h
@@ -0,0 +1,532 @@
+/*
+ * 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.
+ *
+ */
+#ifndef __MODEM_LINK_DEVICE_DPRAM_H__
+#define __MODEM_LINK_DEVICE_DPRAM_H__
+
+#include <linux/spinlock.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+
+#define DPRAM_MAGIC_CODE 0xAA
+
+/* interrupt masks.*/
+#define INT_MASK_VALID 0x0080
+#define INT_MASK_CMD 0x0040
+#define INT_VALID(x) ((x) & INT_MASK_VALID)
+#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD)
+#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
+#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+
+#define EXT_UDL_MASK 0xF000
+#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK)
+#define EXT_INT_VALID_MASK 0x8000
+#define EXT_CMD_VALID_MASK 0x4000
+#define UDL_CMD_VALID_MASK 0x2000
+#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK)
+#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK)
+#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK)
+#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x))
+
+#define EXT_CMD_MASK(x) ((x) & 0x0FFF)
+#define EXT_CMD_SET_SPEED_LOW 0x0011
+#define EXT_CMD_SET_SPEED_MID 0x0012
+#define EXT_CMD_SET_SPEED_HIGH 0x0013
+
+#define UDL_RESULT_SUCCESS 0x1
+#define UDL_RESULT_FAIL 0x2
+
+#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF)
+#define UDL_CMD_RECV_READY 0x1
+#define UDL_CMD_DL_START_REQ 0x2
+#define UDL_CMD_DL_START_RESP 0x3
+#define UDL_CMD_IMAGE_SEND_REQ 0x4
+#define UDL_CMD_SEND_DONE_RESP 0x5
+#define UDL_CMD_SEND_DONE_REQ 0x6
+#define UDL_CMD_UPDATE_DONE 0x7
+#define UDL_CMD_STATUS_UPDATE 0x8
+#define UDL_CMD_IMAGE_SEND_RESP 0x9
+#define UDL_CMD_EFS_CLEAR_RESP 0xB
+#define UDL_CMD_ALARM_BOOT_OK 0xC
+#define UDL_CMD_ALARM_BOOT_FAIL 0xD
+
+#define CMD_IMG_START_REQ 0x9200
+#define CMD_IMG_SEND_REQ 0x9400
+#define CMD_DL_SEND_DONE_REQ 0x9600
+#define CMD_UL_RECV_RESP 0x9601
+#define CMD_UL_RECV_DONE_RESP 0x9801
+
+/* special interrupt cmd indicating modem boot failure. */
+#define INT_POWERSAFE_FAIL 0xDEAD
+
+#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
+#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
+#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
+
+#define INT_MASK_REQ_ACK_F 0x0020
+#define INT_MASK_REQ_ACK_R 0x0010
+#define INT_MASK_RES_ACK_F 0x0008
+#define INT_MASK_RES_ACK_R 0x0004
+#define INT_MASK_SEND_F 0x0002
+#define INT_MASK_SEND_R 0x0001
+
+#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
+#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
+#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
+
+#define INT_MASK_RES_ACK_SET \
+ (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS)
+
+#define INT_MASK_SEND_SET \
+ (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS)
+
+#define INT_CMD_MASK(x) ((x) & 0xF)
+#define INT_CMD_INIT_START 0x1
+#define INT_CMD_INIT_END 0x2
+#define INT_CMD_REQ_ACTIVE 0x3
+#define INT_CMD_RES_ACTIVE 0x4
+#define INT_CMD_REQ_TIME_SYNC 0x5
+#define INT_CMD_CRASH_RESET 0x7
+#define INT_CMD_PHONE_START 0x8
+#define INT_CMD_ERR_DISPLAY 0x9
+#define INT_CMD_CRASH_EXIT 0x9
+#define INT_CMD_CP_DEEP_SLEEP 0xA
+#define INT_CMD_NV_REBUILDING 0xB
+#define INT_CMD_EMER_DOWN 0xC
+#define INT_CMD_PIF_INIT_DONE 0xD
+#define INT_CMD_SILENT_NV_REBUILDING 0xE
+#define INT_CMD_NORMAL_PWR_OFF 0xF
+
+#define START_FLAG 0x7F
+#define END_FLAG 0x7E
+
+#define DP_MAGIC_DMDL 0x4445444C
+#define DP_MAGIC_UMDL 0x4445444D
+#define DP_DPRAM_SIZE 0x4000
+#define DP_DEFAULT_WRITE_LEN 8168
+#define DP_DEFAULT_DUMP_LEN 16128
+#define DP_DUMP_HEADER_SIZE 7
+
+#define UDL_TIMEOUT (50 * HZ)
+#define UDL_SEND_TIMEOUT (200 * HZ)
+#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ)
+#define DUMP_TIMEOUT (30 * HZ)
+#define DUMP_START_TIMEOUT (100 * HZ)
+#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */
+
+#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x))
+
+enum host_boot_mode {
+ HOST_BOOT_MODE_NORMAL,
+ HOST_BOOT_MODE_DUMP,
+};
+
+enum dpram_init_status {
+ DPRAM_INIT_STATE_NONE,
+ DPRAM_INIT_STATE_READY,
+};
+
+struct dpram_boot_img {
+ char *addr;
+ int size;
+ enum host_boot_mode mode;
+ unsigned req;
+ unsigned resp;
+};
+
+#define MAX_PAYLOAD_SIZE 0x2000
+struct dpram_boot_frame {
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* response expected by AP */
+ ssize_t len; /* data size in the buffer */
+ unsigned offset; /* offset to write into DPRAM */
+ char data[MAX_PAYLOAD_SIZE];
+};
+
+/* buffer type for modem image */
+struct dpram_dump_arg {
+ char *buff; /* pointer to the buffer */
+ int buff_size; /* buffer size */
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* CP->AP response */
+ bool cmd; /* AP->CP command */
+};
+
+struct dpram_firmware {
+ char *firmware;
+ int size;
+ int is_delta;
+};
+enum dpram_link_mode {
+ DPRAM_LINK_MODE_INVALID = 0,
+ DPRAM_LINK_MODE_IPC,
+ DPRAM_LINK_MODE_BOOT,
+ DPRAM_LINK_MODE_DLOAD,
+ DPRAM_LINK_MODE_ULOAD,
+};
+
+struct dpram_boot_map {
+ u32 __iomem *magic;
+ u8 __iomem *buff;
+ u32 __iomem *req;
+ u32 __iomem *resp;
+ u32 size;
+};
+
+struct qc_dpram_boot_map {
+ u8 __iomem *buff;
+ u16 __iomem *frame_size;
+ u16 __iomem *tag;
+ u16 __iomem *count;
+};
+
+struct dpram_dload_map {
+ u32 __iomem *magic;
+ u8 __iomem *buff;
+};
+
+struct dpram_uload_map {
+ u32 __iomem *magic;
+ u8 __iomem *buff;
+};
+
+struct dpram_ota_header {
+ u8 start_index;
+ u16 nframes;
+ u16 curframe;
+ u16 len;
+
+} __packed;
+
+struct ul_header {
+ u8 bop;
+ u16 total_frame;
+ u16 curr_frame;
+ u16 len;
+} __packed;
+
+struct dpram_udl_param {
+ unsigned char *addr;
+ unsigned int size;
+ unsigned int count;
+ unsigned int tag;
+};
+
+struct dpram_udl_check {
+ unsigned int total_size;
+ unsigned int rest_size;
+ unsigned int send_size;
+ unsigned int copy_start;
+ unsigned int copy_complete;
+ unsigned int boot_complete;
+};
+
+#define DP_BOOT_BUFF_OFFSET 4
+#define DP_DLOAD_BUFF_OFFSET 4
+#define DP_ULOAD_BUFF_OFFSET 4
+#define DP_BOOT_REQ_OFFSET 0
+#define DP_BOOT_RESP_OFFSET 8
+
+#define MAX_WQ_NAME_LENGTH 64
+
+#define DPRAM_MAX_RXBQ_SIZE 256
+
+struct dpram_rxb {
+ u8 *buff;
+ unsigned size;
+
+ u8 *data;
+ unsigned len;
+};
+
+struct dpram_rxb_queue {
+ int size;
+ int in;
+ int out;
+ struct dpram_rxb *rxb;
+};
+
+/*
+ mbx_ap2cp + 0x0
+ magic_code +
+ access_enable +
+ padding +
+ mbx_cp2ap + 0x1000
+ magic_code +
+ access_enable +
+ padding +
+ fmt_tx_head + fmt_tx_tail + fmt_tx_buff + 0x2000
+ raw_tx_head + raw_tx_tail + raw_tx_buff +
+ fmt_rx_head + fmt_rx_tail + fmt_rx_buff + 0x3000
+ raw_rx_head + raw_rx_tail + raw_rx_buff +
+ = 2 +
+ 4094 +
+ 2 +
+ 4094 +
+ 2 +
+ 2 +
+ 2 + 2 + 1020 +
+ 2 + 2 + 3064 +
+ 2 + 2 + 1020 +
+ 2 + 2 + 3064
+ */
+
+#define DP_PLD_FMT_TX_BUFF_SZ 1024
+#define DP_PLD_RAW_TX_BUFF_SZ 3072
+#define DP_PLD_FMT_RX_BUFF_SZ 1024
+#define DP_PLD_RAW_RX_BUFF_SZ 3072
+
+#define MAX_MSM_EDPRAM_IPC_DEV 2 /* FMT, RAW */
+
+struct dpram_ipc_pld_map {
+ u16 mbx_ap2cp;
+ u16 magic_ap2cp;
+ u16 access_ap2cp;
+ u16 fmt_tx_head;
+ u16 raw_tx_head;
+ u16 fmt_rx_tail;
+ u16 raw_rx_tail;
+ u16 temp1;
+ u8 padding1[4080];
+
+ u16 mbx_cp2ap;
+ u16 magic_cp2ap;
+ u16 access_cp2ap;
+ u16 fmt_tx_tail;
+ u16 raw_tx_tail;
+ u16 fmt_rx_head;
+ u16 raw_rx_head;
+ u16 temp2;
+ u8 padding2[4080];
+
+ u8 fmt_tx_buff[DP_PLD_FMT_TX_BUFF_SZ];
+ u8 raw_tx_buff[DP_PLD_RAW_TX_BUFF_SZ];
+ u8 fmt_rx_buff[DP_PLD_RAW_TX_BUFF_SZ];
+ u8 raw_rx_buff[DP_PLD_RAW_RX_BUFF_SZ];
+
+ u8 padding3[16384];
+
+ u16 address_buffer;
+};
+
+/*
+ magic_code +
+ access_enable +
+ fmt_tx_head + fmt_tx_tail + fmt_tx_buff +
+ raw_tx_head + raw_tx_tail + raw_tx_buff +
+ fmt_rx_head + fmt_rx_tail + fmt_rx_buff +
+ raw_rx_head + raw_rx_tail + raw_rx_buff +
+ mbx_cp2ap +
+ mbx_ap2cp
+ = 2 +
+ 2 +
+ 2 + 2 + 1336 +
+ 2 + 2 + 4564 +
+ 2 + 2 + 1336 +
+ 2 + 2 + 9124 +
+ 2 +
+ 2
+ = 16384
+*/
+#define DP_16K_FMT_TX_BUFF_SZ 1336
+#define DP_16K_RAW_TX_BUFF_SZ 4564
+#define DP_16K_FMT_RX_BUFF_SZ 1336
+#define DP_16K_RAW_RX_BUFF_SZ 9124
+
+struct dpram_ipc_16k_map {
+ u16 magic;
+ u16 access;
+
+ u16 fmt_tx_head;
+ u16 fmt_tx_tail;
+ u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ];
+
+ u16 raw_tx_head;
+ u16 raw_tx_tail;
+ u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ];
+
+ u16 fmt_rx_head;
+ u16 fmt_rx_tail;
+ u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ];
+
+ u16 raw_rx_head;
+ u16 raw_rx_tail;
+ u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ];
+
+ u16 mbx_cp2ap;
+ u16 mbx_ap2cp;
+};
+
+#define DP_MAX_NAME_LEN 32
+
+struct dpram_ext_op;
+
+struct dpram_link_device {
+ struct link_device ld;
+
+ /* The mode of this DPRAM link device */
+ enum dpram_link_mode mode;
+
+ /* DPRAM address and size */
+ u8 __iomem *dp_base; /* DPRAM base virtual address */
+ u32 dp_size; /* DPRAM size */
+ enum dpram_type dp_type; /* DPRAM type */
+
+ /* DPRAM IRQ GPIO# */
+ unsigned gpio_dpram_int;
+
+ /* DPRAM IRQ from CP */
+ int irq;
+ unsigned long irq_flags;
+ char irq_name[DP_MAX_NAME_LEN];
+
+ /* Link to DPRAM control functions dependent on each platform */
+ int max_ipc_dev;
+ struct modemlink_dpram_control *dpctl;
+
+ /* Physical configuration -> logical configuration */
+ union {
+ struct dpram_boot_map bt_map;
+ struct qc_dpram_boot_map qc_bt_map;
+ };
+
+ struct dpram_dload_map dl_map;
+ struct dpram_uload_map ul_map;
+
+ /* IPC device map */
+ struct dpram_ipc_map ipc_map;
+
+ /* Pointers (aliases) to IPC device map */
+ u16 __iomem *magic_ap2cp;
+ u16 __iomem *access_ap2cp;
+ u16 __iomem *magic_cp2ap;
+ u16 __iomem *access_cp2ap;
+ u16 __iomem *address_buffer;
+
+ struct dpram_ipc_device *dev[MAX_IPC_DEV];
+ u16 __iomem *mbx2ap;
+ u16 __iomem *mbx2cp;
+
+ /* Wakelock for DPRAM device */
+ struct wake_lock wlock;
+ char wlock_name[DP_MAX_NAME_LEN];
+
+ /* For booting */
+ unsigned boot_start_complete;
+ struct completion dpram_init_cmd;
+ struct completion modem_pif_init_done;
+
+ /* For UDL */
+ struct tasklet_struct ul_tsk;
+ struct tasklet_struct dl_tsk;
+ struct completion udl_start_complete;
+ struct completion udl_cmd_complete;
+ struct dpram_udl_check udl_check;
+ struct dpram_udl_param udl_param;
+
+ /* For CP RAM dump */
+ struct timer_list crash_ack_timer;
+ struct completion dump_start_complete;
+ struct completion dump_recv_done;
+ struct timer_list dump_timer;
+ int dump_rcvd; /* Count of dump packets received */
+
+ /* For locking TX process */
+ spinlock_t tx_rx_lock;
+ spinlock_t pld_lock;
+
+ /* For efficient RX process */
+ struct tasklet_struct rx_tsk;
+ struct dpram_rxb_queue rxbq[MAX_IPC_DEV];
+ struct io_device *iod[MAX_IPC_DEV];
+ bool use_skb;
+ struct sk_buff_head skb_rxq[MAX_IPC_DEV];
+
+ /* For retransmission after buffer full state */
+ atomic_t res_required[MAX_IPC_DEV];
+
+ /* For wake-up/sleep control */
+ atomic_t accessing;
+
+ /* Multi-purpose miscellaneous buffer */
+ u8 *buff;
+
+ /* DPRAM IPC initialization status */
+ int dpram_init_status;
+
+ /* Alias to device-specific IOCTL function */
+ int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+
+ /* For DPRAM dump */
+ void (*dpram_dump)(struct link_device *ld, char *buff);
+
+ /* Common operations for each DPRAM */
+ void (*clear_intr)(struct dpram_link_device *dpld);
+ u16 (*recv_intr)(struct dpram_link_device *dpld);
+ void (*send_intr)(struct dpram_link_device *dpld, u16 mask);
+ u16 (*get_magic)(struct dpram_link_device *dpld);
+ void (*set_magic)(struct dpram_link_device *dpld, u16 value);
+ u16 (*get_access)(struct dpram_link_device *dpld);
+ void (*set_access)(struct dpram_link_device *dpld, u16 value);
+ u32 (*get_tx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_send)(struct dpram_link_device *dpld, int id);
+
+ /* Extended operations for various modems */
+ struct dpram_ext_op *ext_op;
+};
+
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_dpram_link_device(linkdev) \
+ container_of(linkdev, struct dpram_link_device, ld)
+
+struct dpram_ext_op {
+ int exist;
+
+ void (*init_boot_map)(struct dpram_link_device *dpld);
+ void (*init_dl_map)(struct dpram_link_device *dpld);
+ void (*init_ul_map)(struct dpram_link_device *dpld);
+
+ int (*dload_bin)(struct dpram_link_device *dpld, struct sk_buff *skb);
+ void (*dload_cmd_handler)(struct dpram_link_device *dpld, u16 cmd);
+
+ void (*cp_start_handler)(struct dpram_link_device *dpld);
+
+ void (*crash_log)(struct dpram_link_device *dpld);
+ int (*dump_start)(struct dpram_link_device *dpld);
+ int (*dump_update)(struct dpram_link_device *dpld, void *arg);
+
+ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem);
+#endif
diff --git a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c
new file mode 100644
index 0000000..ae6578c
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c
@@ -0,0 +1,556 @@
+/*
+ * 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/kallsyms.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+#include "modem_link_device_pld.h"
+#include "modem_utils.h"
+
+#if defined(CONFIG_CDMA_MODEM_MDM6600) || defined(CONFIG_GSM_MODEM_ESC6270)
+enum qc_dload_tag {
+ QC_DLOAD_TAG_NONE = 0,
+ QC_DLOAD_TAG_BIN,
+ QC_DLOAD_TAG_NV,
+ QC_DLOAD_TAG_MAX
+};
+
+static void qc_dload_task(unsigned long data);
+
+static void qc_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct modemlink_dpram_control *dpctl = dpld->dpctl;
+
+ bt_map->buff = dpld->dev[0]->txq.buff;
+ bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset);
+ bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset);
+ bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset);
+
+ tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld);
+}
+
+static void qc_dload_map(struct dpram_link_device *dpld, u8 is_upload)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct modemlink_dpram_control *dpctl = dpld->dpctl;
+ unsigned int upload_offset = 0;
+
+ if (is_upload == 1) {
+ upload_offset = 0x1000;
+ bt_map->buff = dpld->dev[0]->rxq.buff;
+ } else {
+ upload_offset = 0;
+ bt_map->buff = dpld->dev[0]->txq.buff;
+ }
+
+ bt_map->frame_size = (u16 *)(dpld->dp_base +
+ dpctl->boot_size_offset + upload_offset);
+ bt_map->tag = (u16 *)(dpld->dp_base +
+ dpctl->boot_tag_offset + upload_offset);
+ bt_map->count = (u16 *)(dpld->dp_base +
+ dpctl->boot_count_offset + upload_offset);
+
+}
+
+static int qc_prepare_download(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+
+ qc_dload_map(dpld, 0);
+
+ while (1) {
+ if (dpld->udl_check.copy_start) {
+ dpld->udl_check.copy_start = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 1000) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return retval;
+}
+
+static void _qc_do_download(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+
+ if (param->size <= dpld->dpctl->max_boot_frame_size) {
+ iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]),
+ dpld->address_buffer);
+ memcpy(dpld->dp_base, param->addr, param->size);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]),
+ dpld->address_buffer);
+ iowrite16(param->size, dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]),
+ dpld->address_buffer);
+ iowrite16(param->tag, dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->count[0]),
+ dpld->address_buffer);
+ iowrite16(param->count, dpld->dp_base);
+
+ dpld->send_intr(dpld, 0xDB12);
+ } else {
+ mif_info("param->size %d\n", param->size);
+ }
+}
+
+static int _qc_download(struct dpram_link_device *dpld, void *arg,
+ enum qc_dload_tag tag)
+{
+ int retval = 0;
+ int count = 0;
+ int cnt_limit;
+ unsigned char *img;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail\n");
+ return -1;
+ }
+
+ img = vmalloc(param.size);
+ if (!img) {
+ mif_err("ERR! vmalloc fail\n");
+ return -1;
+ }
+ memset(img, 0, param.size);
+ memcpy(img, param.addr, param.size);
+
+ dpld->udl_check.total_size = param.size;
+ dpld->udl_check.rest_size = param.size;
+ dpld->udl_check.send_size = 0;
+ dpld->udl_check.copy_complete = 0;
+
+ dpld->udl_param.addr = img;
+ dpld->udl_param.size = dpld->dpctl->max_boot_frame_size;
+ if (tag == QC_DLOAD_TAG_NV)
+ dpld->udl_param.count = 1;
+ else
+ dpld->udl_param.count = param.count;
+ dpld->udl_param.tag = tag;
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ /* Download image (binary or NV) */
+ _qc_do_download(dpld, &dpld->udl_param);
+
+ /* Wait for completion
+ */
+ if (tag == QC_DLOAD_TAG_NV)
+ cnt_limit = 200;
+ else
+ cnt_limit = 1000;
+
+ while (1) {
+ if (dpld->udl_check.copy_complete) {
+ dpld->udl_check.copy_complete = 0;
+ retval = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > cnt_limit) {
+ mif_err("ERR! count %d\n", count);
+ retval = -1;
+ break;
+ }
+ }
+
+ vfree(img);
+
+ return retval;
+}
+
+static int qc_download_bin(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN);
+}
+
+static int qc_download_nv(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_NV);
+}
+
+static void qc_dload_task(unsigned long data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+
+ dpld->udl_check.send_size += dpld->udl_param.size;
+ dpld->udl_check.rest_size -= dpld->udl_param.size;
+
+ dpld->udl_param.addr += dpld->udl_param.size;
+
+ if (dpld->udl_check.send_size >= dpld->udl_check.total_size) {
+ dpld->udl_check.copy_complete = 1;
+ dpld->udl_param.tag = 0;
+ return;
+ }
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ dpld->udl_param.count += 1;
+
+ _qc_do_download(dpld, &dpld->udl_param);
+}
+
+static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ switch (cmd) {
+ case 0x1234:
+ dpld->udl_check.copy_start = 1;
+ break;
+
+ case 0xDBAB:
+ tasklet_schedule(&dpld->dl_tsk);
+ break;
+
+ case 0xABCD:
+ mif_info("[%s] booting Start\n", dpld->ld.name);
+ dpld->udl_check.boot_complete = 1;
+ break;
+
+ default:
+ mif_err("ERR! unknown command 0x%04X\n", cmd);
+ }
+}
+
+static int qc_boot_start(struct dpram_link_device *dpld)
+{
+ u16 mask = 0;
+ int count = 0;
+
+ /* Send interrupt -> '0x4567' */
+ mask = 0x4567;
+ dpld->send_intr(dpld, mask);
+
+ while (1) {
+ if (dpld->udl_check.boot_complete) {
+ dpld->udl_check.boot_complete = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int qc_boot_post_process(struct dpram_link_device *dpld)
+{
+ int count = 0;
+
+ while (1) {
+ if (dpld->boot_start_complete) {
+ dpld->boot_start_complete = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void qc_start_handler(struct dpram_link_device *dpld)
+{
+ /*
+ * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT |
+ * INT_MASK_CP_AP_ANDROID | INT_MASK_CMD_INIT_END
+ */
+ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002);
+
+ dpld->boot_start_complete = 1;
+
+ /* Send INIT_END code to CP */
+ mif_info("send 0x%04X (INIT_END)\n", mask);
+
+ dpld->send_intr(dpld, mask);
+}
+
+static void qc_crash_log(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ static unsigned char buf[151];
+ u8 __iomem *data = NULL;
+
+ data = dpld->get_rx_buff(dpld, IPC_FMT);
+ memcpy(buf, data, (sizeof(buf) - 1));
+
+ mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name);
+ mif_info("PHONE ERR MSG\t| %s\n", buf);
+}
+
+static int _qc_data_upload(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ int retval = 0;
+ u16 intval = 0;
+ int count = 0;
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ if (intval == 0xDBAB) {
+ break;
+ } else {
+ mif_err("intr 0x%08x\n", intval);
+ return -1;
+ }
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ mif_err("<%s:%d>\n", __func__, __LINE__);
+ return -1;
+ }
+ }
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]),
+ dpld->address_buffer);
+ param->size = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]),
+ dpld->address_buffer);
+ param->tag = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->count[0]),
+ dpld->address_buffer);
+ param->count = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]),
+ dpld->address_buffer);
+ memcpy(param->addr, dpld->dp_base, param->size);
+
+ dpld->send_intr(dpld, 0xDB12);
+
+ return retval;
+}
+
+static int qc_uload_step1(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+ u16 intval = 0;
+ u16 mask = 0;
+
+ qc_dload_map(dpld, 1);
+
+ mif_info("+---------------------------------------------+\n");
+ mif_info("| UPLOAD PHONE SDRAM |\n");
+ mif_info("+---------------------------------------------+\n");
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("intr 0x%04x\n", intval);
+ if (intval == 0x1234) {
+ break;
+ } else {
+ mif_info("ERR! invalid intr\n");
+ return -1;
+ }
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("count %d, intr 0x%04x\n", count, intval);
+ if (intval == 0x1234)
+ break;
+ return -1;
+ }
+ }
+
+ mask = 0xDEAD;
+ dpld->send_intr(dpld, mask);
+
+ return retval;
+}
+
+static int qc_uload_step2(struct dpram_link_device *dpld, void *arg)
+{
+ int retval = 0;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ retval = _qc_data_upload(dpld, &param);
+ if (retval < 0) {
+ mif_err("ERR! _qc_data_upload fail (err %d)\n", retval);
+ return -1;
+ }
+
+ if (!(param.count % 500))
+ mif_info("param->count = %d\n", param.count);
+
+ if (param.tag == 4) {
+ enable_irq(dpld->irq);
+ mif_info("param->tag = %d\n", param.tag);
+ }
+
+ retval = copy_to_user((unsigned long *)arg, &param, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_to_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ return retval;
+}
+
+static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_DPRAM_PHONE_POWON:
+ err = qc_prepare_download(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! prepare_download fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONEIMG_LOAD:
+ err = qc_download_bin(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_bin fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_NVDATA_LOAD:
+ err = qc_download_nv(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_nv fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONE_BOOTSTART:
+ err = qc_boot_start(dpld);
+ if (err < 0) {
+ mif_info("%s: ERR! boot_start fail\n", ld->name);
+ break;
+ }
+
+ err = qc_boot_post_process(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! boot_post_process fail\n", ld->name);
+
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP1:
+ disable_irq_nosync(dpld->irq);
+ err = qc_uload_step1(dpld);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step1 fail\n", ld->name);
+ }
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP2:
+ err = qc_uload_step2(dpld, (void *)arg);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step2 fail\n", ld->name);
+ }
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif
+
+static struct dpram_ext_op ext_op_set[] = {
+#if defined(CONFIG_CDMA_MODEM_MDM6600)
+ [QC_MDM6600] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .dload_cmd_handler = qc_dload_cmd_handler,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ },
+#endif
+#if defined(CONFIG_GSM_MODEM_ESC6270)
+ [QC_ESC6270] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .dload_cmd_handler = qc_dload_cmd_handler,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ },
+#endif
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem)
+{
+ if (ext_op_set[modem].exist)
+ return &ext_op_set[modem];
+ else
+ return NULL;
+}
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;
+}
diff --git a/drivers/misc/modem_if/modem_link_device_spi.h b/drivers/misc/modem_if/modem_link_device_spi.h
new file mode 100644
index 0000000..2289a46
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_spi.h
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ *
+ */
+#ifndef __MODEM_LINK_DEVICE_SPI_H__
+#define __MODEM_LINK_DEVICE_SPI_H__
+
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/platform_data/modem.h>
+
+
+#define SPI_TIMER_TX_WAIT_TIME 60 /* ms */
+#define SPI_TIMER_RX_WAIT_TIME 500 /* ms */
+
+#define SPI_MAX_PACKET_SIZE (2048 * 6)
+#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_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_MAX_PACKET_SIZE- \
+ SPI_DATA_PACKET_HEADER_SIZE-SPI_DATA_HEADER_SIZE- \
+ SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE)
+
+#define SPI_DATA_DIVIDE_BUFFER_SIZE SPI_MAX_PACKET_SIZE
+
+struct spi_work_type {
+ struct work_struct work;
+ int signal_code;
+};
+
+enum spi_msg_t {
+ SPI_WORK_SEND,
+ SPI_WORK_RECEIVE
+};
+
+enum spi_state_t {
+ SPI_STATE_START, /* before init complete */
+ SPI_STATE_INIT, /* initialising */
+ SPI_STATE_IDLE, /* suspend. Waiting for event */
+ /* state to start tx. Become from idle */
+ SPI_STATE_TX_START,
+ SPI_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_STATE_TX_WAIT,
+ SPI_STATE_TX_SENDING, /* tx data sending */
+ SPI_STATE_TX_TERMINATE,
+ SPI_STATE_TX_MORE,
+
+ /* in case of slave, wait submrdy rising interrupt to */
+ SPI_STATE_RX_WAIT,
+
+ /* check sync complete then it starts to read buffer */
+ SPI_STATE_RX_MORE,
+ SPI_STATE_RX_TERMINATE,
+ SPI_STATE_END /* spi task is stopped */
+};
+
+enum spi_timer_state_t {
+ SPI_STATE_TIME_START,
+ SPI_STATE_TIME_OVER
+};
+
+enum spi_gpiolevel_t {
+ SPI_GPIOLEVEL_LOW = 0,
+ SPI_GPIOLEVEL_HIGH
+};
+
+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;
+};
+
+struct spi_link_device {
+ struct link_device ld;
+
+ /* Link to SPI control functions dependent on each platform */
+ int max_ipc_dev;
+
+ /* Wakelock for SPI device */
+ struct wake_lock spi_wake_lock;
+ /* Workqueue for modem bin transfers */
+ struct workqueue_struct *ipc_spi_wq;
+
+ /* SPI state */
+ int spi_state;
+ int spi_is_restart;
+
+ /* SPI Timer state */
+ int spi_timer_tx_state;
+ int spi_timer_rx_state;
+
+ /* SPI GPIO pins */
+ unsigned int gpio_ipc_mrdy;
+ unsigned int gpio_ipc_srdy;
+ unsigned int gpio_ipc_sub_mrdy;
+ unsigned int gpio_ipc_sub_srdy;
+
+ unsigned int gpio_modem_bin_srdy;
+
+ unsigned int gpio_ap_cp_int1;
+ unsigned int gpio_ap_cp_int2;
+
+ /* value for checking spi pin status */
+ unsigned long mrdy_low_time_save;
+ unsigned long mrdy_high_time_save;
+
+ /* SPI Wait Timer */
+ struct timer_list spi_tx_timer;
+ struct timer_list spi_rx_timer;
+
+ struct workqueue_struct *spi_wq;
+ struct spi_work_type spi_work;
+
+ /* For send modem bin */
+ struct work_struct send_modem_w;
+
+ struct io_device *iod[MAX_DEV_FORMAT];
+ struct sk_buff_head skb_rxq[MAX_DEV_FORMAT];
+
+ /* Multi-purpose miscellaneous buffer */
+ u8 *buff;
+ u8 *sync_buff;
+
+ struct completion ril_init;
+ struct semaphore srdy_sem;
+
+ int send_modem_spi;
+ int is_cp_reset;
+ int boot_done;
+ int ril_send_modem_img;
+ unsigned long ril_send_cnt;
+
+ void __iomem *p_virtual_buff;
+};
+
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_spi_link_device(linkdev) \
+ container_of(linkdev, struct spi_link_device, ld)
+
+extern unsigned int lpcharge;
+extern int get_console_suspended(void);
+static void spi_work(struct work_struct *work);
+
+/* Send SPRD main image through SPI */
+#define SPRD_BLOCK_SIZE 32768
+
+enum image_type {
+ MODEM_MAIN,
+ MODEM_DSP,
+ MODEM_NV,
+ MODEM_EFS,
+ MODEM_RUN,
+};
+
+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_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]; \
+ }
+
+#endif
diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c
index 615971e..5b7c98b 100644
--- a/drivers/misc/modem_if/modem_link_device_usb.c
+++ b/drivers/misc/modem_if/modem_link_device_usb.c
@@ -32,6 +32,8 @@
#include "modem_utils.h"
#include "modem_link_pm_usb.h"
+#include <mach/regs-gpio.h>
+
#define URB_COUNT 4
static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld,
@@ -61,8 +63,8 @@ static int start_ipc(struct link_device *ld, struct io_device *iod)
struct usb_link_device *usb_ld = to_usb_link_device(ld);
struct if_usb_devdata *pipe_data = &usb_ld->devdata[IF_USB_FMT_EP];
- if (usb_ld->link_pm_data->hub_handshake_done) {
- mif_err("Aleady send start ipc, skip start ipc\n");
+ if (has_hub(usb_ld) && usb_ld->link_pm_data->hub_handshake_done) {
+ mif_err("Already send start ipc, skip start ipc\n");
err = 0;
goto exit;
}
@@ -73,8 +75,9 @@ static int start_ipc(struct link_device *ld, struct io_device *iod)
goto exit;
}
- if (usb_ld->if_usb_initstates == INIT_IPC_START_DONE) {
- mif_debug("aleady IPC started\n");
+ if (has_hub(usb_ld) &&
+ usb_ld->if_usb_initstates == INIT_IPC_START_DONE) {
+ mif_debug("Already IPC started\n");
err = 0;
goto exit;
}
@@ -262,8 +265,8 @@ static void usb_tx_complete(struct urb *urb)
usb_mark_last_busy(urb->dev);
ret = pm_runtime_put_autosuspend(&urb->dev->dev);
- if (ret < 0)
- mif_debug("pm_runtime_put_autosuspend failed : ret(%d)\n", ret);
+ if (ret < 0 && ret != -EAGAIN)
+ mif_debug("pm_runtime_put_autosuspend failed: %d\n", ret);
usb_free_urb(urb);
dev_kfree_skb_any(skb);
}
@@ -274,9 +277,18 @@ static void if_usb_force_disconnect(struct work_struct *work)
container_of(work, struct usb_link_device, disconnect_work);
struct usb_device *udev = usb_ld->usbdev;
+ /* if already disconnected before run this workqueue */
+ if (!udev || !(&udev->dev) || !usb_ld->if_usb_connected)
+ return;
+
+ /* disconnect udev's parent if usb hub used */
+ if (has_hub(usb_ld))
+ udev = udev->parent;
+
pm_runtime_get_sync(&udev->dev);
if (udev->state != USB_STATE_NOTATTACHED) {
- mif_info("force disconnect by modem not responding!!\n");
+ usb_force_disconnect(udev);
+ mif_info("force disconnect\n");
}
pm_runtime_put_autosuspend(&udev->dev);
}
@@ -438,6 +450,7 @@ static void usb_tx_work(struct work_struct *work)
static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
int i;
if (atomic_inc_return(&usb_ld->suspend_count) == IF_USB_DEVNUM_MAX) {
@@ -446,8 +459,8 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
for (i = 0; i < IF_USB_DEVNUM_MAX; i++)
usb_kill_anchored_urbs(&usb_ld->devdata[i].reading);
- if (usb_ld->link_pm_data->cpufreq_unlock)
- usb_ld->link_pm_data->cpufreq_unlock();
+ if (pm_data->freq_unlock)
+ pm_data->freq_unlock(&usb_ld->usbdev->dev);
wake_unlock(&usb_ld->susplock);
}
@@ -472,10 +485,16 @@ static void post_resume_work(struct work_struct *work)
{
struct usb_link_device *usb_ld = container_of(work,
struct usb_link_device, post_resume_work.work);
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+ struct usb_device *udev = usb_ld->usbdev;
- /* lock cpu frequency when L2->L0 */
- if (usb_ld->link_pm_data->cpufreq_lock)
- usb_ld->link_pm_data->cpufreq_lock();
+ /* if already disconnected before run this workqueue */
+ if (!udev || !(&udev->dev) || !usb_ld->if_usb_connected)
+ return;
+
+ /* lock cpu/bus frequency when L2->L0 */
+ if (pm_data->freq_lock)
+ pm_data->freq_lock(&udev->dev);
}
static void wait_enumeration_work(struct work_struct *work)
@@ -532,10 +551,11 @@ static int if_usb_resume(struct usb_interface *intf)
skb = urb->context;
dev_kfree_skb_any(skb);
usb_free_urb(urb);
- if (pm_runtime_put_autosuspend(
- &usb_ld->usbdev->dev) < 0)
- mif_debug(
- "pm_runtime_put_autosuspend fail\n");
+ ret = pm_runtime_put_autosuspend(
+ &usb_ld->usbdev->dev);
+ if (ret < 0 && ret != -EAGAIN)
+ mif_debug("pm_runtime_put_autosuspend "
+ "failed: %d\n", ret);
}
}
SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
@@ -576,9 +596,11 @@ static void if_usb_disconnect(struct usb_interface *intf)
{
struct usb_link_device *usb_ld = usb_get_intfdata(intf);
struct usb_device *usbdev = usb_ld->usbdev;
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
int dev_id = intf->altsetting->desc.bInterfaceNumber;
struct if_usb_devdata *pipe_data = &usb_ld->devdata[dev_id];
+
usb_set_intfdata(intf, NULL);
pipe_data->disconnected = 1;
@@ -602,6 +624,7 @@ static void if_usb_disconnect(struct usb_interface *intf)
cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work);
usb_put_dev(usbdev);
usb_ld->usbdev = NULL;
+ pm_runtime_forbid(pm_data->root_hub);
}
}
@@ -642,7 +665,6 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
dev_id, id, usb_ld);
usb_ld->usbdev = usbdev;
-
usb_get_dev(usbdev);
for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
@@ -726,7 +748,8 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
usb_ld->host_wake_timeout_flag = 0;
if (gpio_get_value(usb_ld->pdata->gpio_phone_active)) {
- int delay = usb_ld->link_pm_data->autosuspend_delay_ms ?:
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+ int delay = pm_data->autosuspend_delay_ms ?:
DEFAULT_AUTOSUSPEND_DELAY_MS;
pm_runtime_set_autosuspend_delay(&usbdev->dev, delay);
dev = &usbdev->dev;
@@ -741,13 +764,13 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
dev_name(ehci_dev));
pm_runtime_allow(ehci_dev);
- if (has_hub(usb_ld)) {
- usb_ld->link_pm_data->hub_status =
- (usb_ld->link_pm_data->root_hub) ?
- HUB_STATE_PREACTIVE : HUB_STATE_ACTIVE;
- }
+ if (!pm_data->autosuspend)
+ pm_runtime_forbid(dev);
- usb_ld->link_pm_data->root_hub = root_hub;
+ if (has_hub(usb_ld))
+ link_pm_preactive(pm_data);
+
+ pm_data->root_hub = root_hub;
}
usb_ld->flow_suspend = 0;
@@ -764,6 +787,14 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
usb_change_modem_state(usb_ld, STATE_LOADER_DONE);
}
+ /* check dynamic switching gpio received
+ * before usb enumeration is completed
+ */
+ if (ld->mc->need_switch_to_usb) {
+ ld->mc->need_switch_to_usb = false;
+ rawdevs_set_tx_link(ld->msd, LINKDEV_USB);
+ }
+
return 0;
out2:
@@ -791,6 +822,13 @@ irqreturn_t usb_resume_irq(int irq, void *data)
wake_status = hwup;
irq_set_irq_type(irq, hwup ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ /*
+ * exynos BSP has problem when using level interrupt.
+ * If we change irq type from interrupt handler,
+ * we can get level interrupt twice.
+ * this is temporary solution until SYS.LSI resolve this problem.
+ */
+ __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq)));
wake_lock_timeout(&usb_ld->gpiolock, 100);
mif_err("< H-WUP %d\n", hwup);
@@ -945,6 +983,15 @@ static void __exit if_usb_exit(void)
usb_deregister(&if_usb_driver);
}
+bool usb_is_enumerated(struct modem_shared *msd)
+{
+ struct link_device *ld = find_linkdev(msd, LINKDEV_USB);
+ if (ld)
+ return to_usb_link_device(ld)->usbdev != NULL;
+ else
+ return false;
+}
+
/* lte specific functions */
diff --git a/drivers/misc/modem_if/modem_link_device_usb.h b/drivers/misc/modem_if/modem_link_device_usb.h
index 44f6b1b..8233fd1 100644
--- a/drivers/misc/modem_if/modem_link_device_usb.h
+++ b/drivers/misc/modem_if/modem_link_device_usb.h
@@ -52,7 +52,6 @@ enum IPC_INIT_STATUS {
enum hub_status {
HUB_STATE_OFF, /* usb3503 0ff*/
HUB_STATE_RESUMMING, /* usb3503 on, but enummerattion was not yet*/
- HUB_STATE_PREACTIVE,
HUB_STATE_ACTIVE, /* hub and CMC221 enumerate */
};
@@ -128,5 +127,6 @@ do { \
#define has_hub(usb_ld) ((usb_ld)->link_pm_data->has_usbhub)
irqreturn_t usb_resume_irq(int irq, void *data);
+bool usb_is_enumerated(struct modem_shared *msd);
#endif
diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c
index 244256f..1b2614e 100644
--- a/drivers/misc/modem_if/modem_link_pm_usb.c
+++ b/drivers/misc/modem_if/modem_link_pm_usb.c
@@ -27,15 +27,34 @@
#include "modem_link_pm_usb.h"
+static inline void start_hub_work(struct link_pm_data *pm_data, int delay)
+{
+ if (pm_data->hub_work_running == false) {
+ pm_data->hub_work_running = true;
+ wake_lock(&pm_data->hub_lock);
+ mif_debug("link_pm_hub_work is started\n");
+ }
+
+ schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay));
+}
+
+static inline void end_hub_work(struct link_pm_data *pm_data)
+{
+ wake_unlock(&pm_data->hub_lock);
+ pm_data->hub_work_running = false;
+ mif_debug("link_pm_hub_work is done\n");
+}
+
bool link_pm_is_connected(struct usb_link_device *usb_ld)
{
if (has_hub(usb_ld)) {
- if (usb_ld->link_pm_data->hub_init_lock)
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+ if (pm_data->hub_init_lock)
return false;
- if (usb_ld->link_pm_data->hub_status != HUB_STATE_ACTIVE) {
- schedule_delayed_work(
- &usb_ld->link_pm_data->link_pm_hub, 0);
+ if (pm_data->hub_status == HUB_STATE_OFF) {
+ if (pm_data->hub_work_running == false)
+ start_hub_work(pm_data, 0);
return false;
}
}
@@ -48,27 +67,40 @@ bool link_pm_is_connected(struct usb_link_device *usb_ld)
return true;
}
+void link_pm_preactive(struct link_pm_data *pm_data)
+{
+ if (pm_data->root_hub) {
+ mif_info("pre-active\n");
+ pm_data->hub_on_retry_cnt = 0;
+ complete(&pm_data->hub_active);
+ pm_runtime_put_sync(pm_data->root_hub);
+ }
+
+ pm_data->hub_status = HUB_STATE_ACTIVE;
+}
+
static void link_pm_hub_work(struct work_struct *work)
{
int err;
struct link_pm_data *pm_data =
container_of(work, struct link_pm_data, link_pm_hub.work);
- if (pm_data->hub_status == HUB_STATE_ACTIVE)
+ if (pm_data->hub_status == HUB_STATE_ACTIVE) {
+ end_hub_work(pm_data);
return;
+ }
if (!pm_data->port_enable) {
mif_err("mif: hub power func not assinged\n");
+ end_hub_work(pm_data);
return;
}
- wake_lock(&pm_data->hub_lock);
/* If kernel if suspend, wait the ehci resume */
if (pm_data->dpm_suspending) {
mif_info("dpm_suspending\n");
- schedule_delayed_work(&pm_data->link_pm_hub,
- msecs_to_jiffies(500));
- goto exit;
+ start_hub_work(pm_data, 500);
+ return;
}
switch (pm_data->hub_status) {
@@ -88,11 +120,11 @@ static void link_pm_hub_work(struct work_struct *work)
pm_data->hub_status = HUB_STATE_OFF;
if (pm_data->root_hub)
pm_runtime_put_sync(pm_data->root_hub);
- goto exit;
+ end_hub_work(pm_data);
+ } else {
+ /* resume root hub */
+ start_hub_work(pm_data, 100);
}
- /* resume root hub */
- schedule_delayed_work(&pm_data->link_pm_hub,
- msecs_to_jiffies(100));
break;
case HUB_STATE_RESUMMING:
if (pm_data->hub_on_retry_cnt++ > 50) {
@@ -100,34 +132,26 @@ static void link_pm_hub_work(struct work_struct *work)
pm_data->hub_status = HUB_STATE_OFF;
if (pm_data->root_hub)
pm_runtime_put_sync(pm_data->root_hub);
+ end_hub_work(pm_data);
+ } else {
+ mif_info("hub resumming: %d\n",
+ pm_data->hub_on_retry_cnt);
+ start_hub_work(pm_data, 200);
}
- mif_trace("hub resumming\n");
- schedule_delayed_work(&pm_data->link_pm_hub,
- msecs_to_jiffies(200));
- break;
- case HUB_STATE_PREACTIVE:
- pm_data->hub_status = HUB_STATE_ACTIVE;
- mif_trace("hub active\n");
- pm_data->hub_on_retry_cnt = 0;
- wake_unlock(&pm_data->hub_lock);
- complete(&pm_data->hub_active);
- if (pm_data->root_hub)
- pm_runtime_put_sync(pm_data->root_hub);
break;
}
exit:
return;
}
-static int link_pm_hub_standby(struct link_pm_data *pm_data)
+static int link_pm_hub_standby(void *args)
{
+ struct link_pm_data *pm_data = args;
struct usb_link_device *usb_ld = pm_data->usb_ld;
int err = 0;
- mif_info("wait hub standby\n");
-
if (!pm_data->port_enable) {
- mif_err("hub power func not assinged\n");
+ mif_err("port power func not assinged\n");
return -ENODEV;
}
@@ -136,6 +160,13 @@ static int link_pm_hub_standby(struct link_pm_data *pm_data)
mif_err("hub off fail err=%d\n", err);
pm_data->hub_status = HUB_STATE_OFF;
+
+ /* this function is atomic.
+ * make force disconnect in workqueue..
+ */
+ if (pm_data->usb_ld->if_usb_connected)
+ schedule_work(&usb_ld->disconnect_work);
+
return err;
}
@@ -169,6 +200,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
{
int value, err = 0;
struct link_pm_data *pm_data = file->private_data;
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
mif_info("cmd: 0x%08x\n", cmd);
@@ -182,15 +214,13 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
case IOCTL_LINK_GET_HOSTWAKE:
return !gpio_get_value(pm_data->gpio_link_hostwake);
case IOCTL_LINK_CONNECTED:
- return pm_data->usb_ld->if_usb_connected;
- case IOCTL_LINK_PORT_ON: /* hub only */
+ return usb_ld->if_usb_connected;
+ case IOCTL_LINK_PORT_ON:
/* ignore cp host wakeup irq, set the hub_init_lock when AP try
CP off and release hub_init_lock when CP boot done */
pm_data->hub_init_lock = 0;
- if (pm_data->root_hub) {
- pm_runtime_resume(pm_data->root_hub);
- pm_runtime_forbid(pm_data->root_hub->parent);
- }
+ if (pm_data->root_hub)
+ pm_runtime_get_sync(pm_data->root_hub);
if (pm_data->port_enable) {
err = pm_data->port_enable(2, 1);
if (err < 0) {
@@ -200,17 +230,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
pm_data->hub_status = HUB_STATE_RESUMMING;
}
break;
- case IOCTL_LINK_PORT_OFF: /* hub only */
- if (pm_data->usb_ld->if_usb_connected) {
- struct usb_device *udev =
- pm_data->usb_ld->usbdev->parent;
- pm_runtime_get_sync(&udev->dev);
- if (udev->state != USB_STATE_NOTATTACHED) {
- usb_force_disconnect(udev);
- pr_info("force disconnect maybe cp-reset!!\n");
- }
- pm_runtime_put_autosuspend(&udev->dev);
- }
+ case IOCTL_LINK_PORT_OFF:
err = link_pm_hub_standby(pm_data);
if (err < 0) {
mif_err("usb3503 active fail\n");
@@ -218,7 +238,6 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
}
pm_data->hub_init_lock = 1;
pm_data->hub_handshake_done = 0;
-
break;
default:
break;
@@ -227,6 +246,50 @@ exit:
return err;
}
+static ssize_t show_autosuspend(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct link_pm_data *pm_data = container_of(miscdev,
+ struct link_pm_data, miscdev);
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
+
+ p += sprintf(buf, "%s\n", pm_data->autosuspend ? "on" : "off");
+
+ return p - buf;
+}
+
+static ssize_t store_autosuspend(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct link_pm_data *pm_data = container_of(miscdev,
+ struct link_pm_data, miscdev);
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
+ struct task_struct *task = get_current();
+ char taskname[TASK_COMM_LEN];
+
+ mif_info("autosuspend: %s: %s(%d)'\n",
+ buf, get_task_comm(taskname, task), task->pid);
+
+ if (!strncmp(buf, "on", 2)) {
+ pm_data->autosuspend = true;
+ if (usb_ld->usbdev)
+ pm_runtime_allow(&usb_ld->usbdev->dev);
+ } else if (!strncmp(buf, "off", 3)) {
+ pm_data->autosuspend = false;
+ if (usb_ld->usbdev)
+ pm_runtime_forbid(&usb_ld->usbdev->dev);
+ }
+
+ return count;
+}
+
+static struct device_attribute attr_autosuspend =
+ __ATTR(autosuspend, S_IRUGO | S_IWUSR,
+ show_autosuspend, store_autosuspend);
+
static int link_pm_open(struct inode *inode, struct file *file)
{
struct link_pm_data *pm_data =
@@ -253,11 +316,13 @@ static int link_pm_notifier_event(struct notifier_block *this,
{
struct link_pm_data *pm_data =
container_of(this, struct link_pm_data, pm_notifier);
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
switch (event) {
case PM_SUSPEND_PREPARE:
pm_data->dpm_suspending = true;
- link_pm_hub_standby(pm_data);
+ if (has_hub(usb_ld))
+ link_pm_hub_standby(pm_data);
return NOTIFY_OK;
case PM_POST_SUSPEND:
pm_data->dpm_suspending = false;
@@ -286,12 +351,12 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
pm_data->gpio_link_slavewake = pm_pdata->gpio_link_slavewake;
pm_data->link_reconnect = pm_pdata->link_reconnect;
pm_data->port_enable = pm_pdata->port_enable;
- pm_data->cpufreq_lock = pm_pdata->cpufreq_lock;
- pm_data->cpufreq_unlock = pm_pdata->cpufreq_unlock;
+ pm_data->freq_lock = pm_pdata->freq_lock;
+ pm_data->freq_unlock = pm_pdata->freq_unlock;
pm_data->autosuspend_delay_ms = pm_pdata->autosuspend_delay_ms;
+ pm_data->autosuspend = true;
pm_data->usb_ld = usb_ld;
- pm_data->link_pm_active = false;
usb_ld->link_pm_data = pm_data;
pm_data->miscdev.minor = MISC_DYNAMIC_MINOR;
@@ -304,6 +369,13 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
goto err_misc_register;
}
+ err = device_create_file(pm_data->miscdev.this_device,
+ &attr_autosuspend);
+ if (err) {
+ mif_err("fail to create file: autosuspend: %d\n", err);
+ goto err_create_file;
+ }
+
pm_data->hub_init_lock = 1;
irq = gpio_to_irq(usb_ld->pdata->gpio_host_wakeup);
err = request_threaded_irq(irq, NULL, usb_resume_irq,
@@ -319,12 +391,16 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
if (has_hub(usb_ld)) {
init_completion(&pm_data->hub_active);
pm_data->hub_status = HUB_STATE_OFF;
- pm_pdata->p_hub_status = &pm_data->hub_status;
pm_data->hub_handshake_done = 0;
pm_data->root_hub = NULL;
+
+ pm_pdata->hub_standby = link_pm_hub_standby;
+ pm_pdata->hub_pm_data = pm_data;
+
wake_lock_init(&pm_data->hub_lock, WAKE_LOCK_SUSPEND,
"modem_hub_enum_lock");
INIT_DELAYED_WORK(&pm_data->link_pm_hub, link_pm_hub_work);
+ pm_data->hub_work_running = false;
}
pm_data->pm_notifier.notifier_call = link_pm_notifier_event;
@@ -333,10 +409,9 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
return 0;
err_request_irq:
+err_create_file:
misc_deregister(&pm_data->miscdev);
err_misc_register:
kfree(pm_data);
return err;
}
-
-
diff --git a/drivers/misc/modem_if/modem_link_pm_usb.h b/drivers/misc/modem_if/modem_link_pm_usb.h
index 103e4f4..d26af76 100644
--- a/drivers/misc/modem_if/modem_link_pm_usb.h
+++ b/drivers/misc/modem_if/modem_link_pm_usb.h
@@ -41,30 +41,25 @@ struct link_pm_data {
int hub_handshake_done;
struct wake_lock hub_lock;
struct delayed_work link_pm_hub;
+ bool hub_work_running;
int hub_on_retry_cnt;
struct device *root_hub;
- struct delayed_work link_pm_work;
- struct delayed_work link_pm_start;
- struct delayed_work link_reconnect_work;
- bool resume_requested;
- bool link_pm_active;
-
- struct wake_lock l2_wake;
- struct wake_lock boot_wake;
struct notifier_block pm_notifier;
bool dpm_suspending;
int (*port_enable)(int, int);
- int (*cpufreq_lock)(void);
- int (*cpufreq_unlock)(void);
+ int (*freq_lock)(struct device *dev);
+ int (*freq_unlock)(struct device *dev);
int autosuspend_delay_ms; /* if zero, the default value is used */
+ bool autosuspend;
};
bool link_pm_set_active(struct usb_link_device *usb_ld);
bool link_pm_is_connected(struct usb_link_device *usb_ld);
+void link_pm_preactive(struct link_pm_data *pm_data);
int link_pm_init(struct usb_link_device *usb_ld, void *data);
#endif
diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c
index b8d2711..2617be8 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c
@@ -43,8 +43,10 @@ static irqreturn_t phone_active_handler(int irq, void *arg)
phone_state, phone_reset, phone_active);
if (phone_reset && phone_active) {
- phone_state = STATE_ONLINE;
- mc->bootd->modem_state_changed(mc->bootd, phone_state);
+ if (mc->phone_state == STATE_BOOTING) {
+ phone_state = STATE_ONLINE;
+ mc->bootd->modem_state_changed(mc->bootd, phone_state);
+ }
} else if (phone_reset && !phone_active) {
if (mc->phone_state == STATE_ONLINE) {
phone_state = STATE_CRASH_EXIT;
@@ -70,6 +72,10 @@ static int cbp72_on(struct modem_ctl *mc)
{
mif_info("start!!!\n");
+ /* prevent sleep during bootloader downloading */
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+
gpio_set_value(mc->gpio_cp_on, 0);
if (mc->gpio_cp_off)
gpio_set_value(mc->gpio_cp_off, 1);
@@ -178,6 +184,9 @@ static int cbp72_boot_off(struct modem_ctl *mc)
return -ENXIO;
}
mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE);
+
+ wake_unlock(&mc->mc_wake_lock);
+
return 0;
}
@@ -253,6 +262,8 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
return ret;
}
+ wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cbp72_wake_lock");
+
ret = enable_irq_wake(irq);
if (ret)
mif_err("enable_irq_wake fail (%d)\n", ret);
diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
index 2d564e9..fe8ce69 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
@@ -30,9 +30,9 @@
#define PIF_TIMEOUT (180 * HZ)
#define DPRAM_INIT_TIMEOUT (30 * HZ)
-
static void mc_state_fsm(struct modem_ctl *mc)
{
+ struct link_device *ld = get_current_link(mc->iod);
int cp_on = gpio_get_value(mc->gpio_cp_on);
int cp_reset = gpio_get_value(mc->gpio_cp_reset);
int cp_active = gpio_get_value(mc->gpio_phone_active);
@@ -46,6 +46,7 @@ static void mc_state_fsm(struct modem_ctl *mc)
if (!cp_on) {
gpio_set_value(mc->gpio_cp_reset, 0);
new_state = STATE_OFFLINE;
+ ld->mode = LINK_MODE_OFFLINE;
mif_err("%s: new_state = PHONE_PWR_OFF\n", mc->name);
} else if (old_state == STATE_ONLINE) {
new_state = STATE_CRASH_EXIT;
@@ -55,7 +56,6 @@ static void mc_state_fsm(struct modem_ctl *mc)
}
}
-exit:
if (old_state != new_state) {
mc->bootd->modem_state_changed(mc->bootd, new_state);
mc->iod->modem_state_changed(mc->iod, new_state);
@@ -73,37 +73,53 @@ static irqreturn_t phone_active_handler(int irq, void *arg)
return IRQ_HANDLED;
}
+/* TX dynamic switching between DPRAM and USB in one modem */
static irqreturn_t dynamic_switching_handler(int irq, void *arg)
{
struct modem_ctl *mc = (struct modem_ctl *)arg;
int txpath = gpio_get_value(mc->gpio_dynamic_switching);
+ bool enumerated = usb_is_enumerated(mc->msd);
+
+ mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated);
+
+ /* do not switch to USB, when USB is not enumerated. */
+ if (!enumerated && txpath) {
+ mc->need_switch_to_usb = true;
+ return IRQ_HANDLED;
+ }
- rawdevs_set_tx_link(&mc->commons, txpath ? LINKDEV_USB : LINKDEV_DPRAM);
+ mc->need_switch_to_usb = false;
+ rawdevs_set_tx_link(mc->msd, txpath ? LINKDEV_USB : LINKDEV_DPRAM);
return IRQ_HANDLED;
}
static int cmc221_on(struct modem_ctl *mc)
{
- struct link_device *ld = get_current_link(mc->bootd);
+ struct link_device *ld = get_current_link(mc->iod);
+
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+ set_sromc_access(true);
+
+ mc->phone_state = STATE_OFFLINE;
+ ld->mode = LINK_MODE_OFFLINE;
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
disable_irq_nosync(mc->irq_phone_active);
- gpio_set_value(mc->gpio_cp_reset, 0);
gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 0);
msleep(500);
gpio_set_value(mc->gpio_cp_on, 1);
-
msleep(100);
gpio_set_value(mc->gpio_cp_reset, 1);
- mc->phone_state = STATE_OFFLINE;
-
return 0;
}
@@ -111,13 +127,14 @@ static int cmc221_off(struct modem_ctl *mc)
{
int cp_on = gpio_get_value(mc->gpio_cp_on);
+ mif_err("%s\n", mc->name);
+
if (mc->phone_state == STATE_OFFLINE || cp_on == 0)
return 0;
- mif_err("%s\n", mc->bootd->name);
-
- if (mc->log_fp)
- mif_close_log_file(mc);
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+ set_sromc_access(true);
gpio_set_value(mc->gpio_cp_on, 0);
@@ -128,7 +145,7 @@ static int cmc221_force_crash_exit(struct modem_ctl *mc)
{
struct link_device *ld = get_current_link(mc->bootd);
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
/* Make DUMP start */
ld->force_dump(ld, mc->bootd);
@@ -138,10 +155,11 @@ static int cmc221_force_crash_exit(struct modem_ctl *mc)
static int cmc221_dump_reset(struct modem_ctl *mc)
{
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
- if (mc->log_fp)
- mif_close_log_file(mc);
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+ set_sromc_access(true);
gpio_set_value(mc->gpio_host_active, 0);
gpio_set_value(mc->gpio_cp_reset, 0);
@@ -157,7 +175,7 @@ static int cmc221_dump_reset(struct modem_ctl *mc)
static int cmc221_reset(struct modem_ctl *mc)
{
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
if (cmc221_off(mc))
return -ENXIO;
@@ -172,13 +190,13 @@ static int cmc221_reset(struct modem_ctl *mc)
static int cmc221_boot_on(struct modem_ctl *mc)
{
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING);
mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
- mif_set_log_level(mc);
-
return 0;
}
@@ -188,7 +206,7 @@ static int cmc221_boot_off(struct modem_ctl *mc)
struct link_device *ld = get_current_link(mc->bootd);
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
ret = wait_for_completion_interruptible_timeout(&dpld->dpram_init_cmd,
DPRAM_INIT_TIMEOUT);
@@ -198,14 +216,18 @@ static int cmc221_boot_off(struct modem_ctl *mc)
return -ENXIO;
}
- if (!mc->fs_ready)
- mc->fs_ready = true;
+ enable_irq(mc->irq_phone_active);
- if (mc->use_mif_log && mc->log_level && !mc->fs_failed &&
- mc->fs_ready && !mc->log_fp)
- mif_open_log_file(mc);
+ return 0;
+}
- enable_irq(mc->irq_phone_active);
+static int cmc221_boot_done(struct modem_ctl *mc)
+{
+ mif_err("%s\n", mc->name);
+
+ set_sromc_access(false);
+ if (wake_lock_active(&mc->mc_wake_lock))
+ wake_unlock(&mc->mc_wake_lock);
return 0;
}
@@ -217,6 +239,7 @@ static void cmc221_get_ops(struct modem_ctl *mc)
mc->ops.modem_reset = cmc221_reset;
mc->ops.modem_boot_on = cmc221_boot_on;
mc->ops.modem_boot_off = cmc221_boot_off;
+ mc->ops.modem_boot_done = cmc221_boot_done;
mc->ops.modem_force_crash_exit = cmc221_force_crash_exit;
mc->ops.modem_dump_reset = cmc221_dump_reset;
}
@@ -231,8 +254,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
mc->gpio_cp_on = pdata->gpio_cp_on;
mc->gpio_cp_reset = pdata->gpio_cp_reset;
mc->gpio_phone_active = pdata->gpio_phone_active;
-#if 0 /*TODO: check the GPIO map*/
mc->gpio_pda_active = pdata->gpio_pda_active;
+#if 0 /*TODO: check the GPIO map*/
mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup;
@@ -240,6 +263,7 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
mc->gpio_host_wakeup = pdata->gpio_host_wakeup;
#endif
mc->gpio_dynamic_switching = pdata->gpio_dynamic_switching;
+ mc->need_switch_to_usb = false;
if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) {
mif_err("%s: ERR! no GPIO data\n", mc->name);
@@ -260,6 +284,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
}
mif_err("%s: PHONE_ACTIVE IRQ# = %d\n", mc->name, mc->irq_phone_active);
+ wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cmc_wake_lock");
+
flag = IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND;
irq = mc->irq_phone_active;
ret = request_irq(irq, phone_active_handler, flag, "cmc_active", mc);
diff --git a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c
new file mode 100644
index 0000000..df0ff80
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c
@@ -0,0 +1,339 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_esc6270.c
+ *
+ * Copyright (C) 2010 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/init.h>
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include <linux/regulator/consumer.h>
+
+#include <plat/gpio-cfg.h>
+
+#if defined(CONFIG_LINK_DEVICE_DPRAM)
+#include "modem_link_device_dpram.h"
+#elif defined(CONFIG_LINK_DEVICE_PLD)
+#include "modem_link_device_pld.h"
+#endif
+
+#if defined(CONFIG_LINK_DEVICE_DPRAM) || defined(CONFIG_LINK_DEVICE_PLD)
+#include <linux/mfd/max77693.h>
+
+#define PIF_TIMEOUT (180 * HZ)
+#define DPRAM_INIT_TIMEOUT (30 * HZ)
+
+static int esc6270_on(struct modem_ctl *mc)
+{
+ int ret;
+ struct link_device *ld = get_current_link(mc->iod);
+
+ pr_info("[MODEM_IF:ESC] <%s> start!!!\n", __func__);
+
+ if (!mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+
+ if (mc->gpio_reset_req_n)
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(30);
+
+ gpio_set_value(mc->gpio_cp_on, 1);
+ msleep(500);
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(500);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
+
+ return 0;
+}
+
+static int esc6270_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF:ESC] esc6270_off()\n");
+
+#if 1
+ if (!mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
+#endif
+
+ mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
+
+ return 0;
+}
+
+static int esc6270_reset(struct modem_ctl *mc)
+{
+ int ret = 0;
+
+ pr_debug("[MODEM_IF:ESC] esc6270_reset()\n");
+
+ ret = esc6270_off(mc);
+ if (ret)
+ return -ENXIO;
+
+ msleep(100);
+
+ ret = esc6270_on(mc);
+ if (ret)
+ return -ENXIO;
+
+ return 0;
+}
+
+int esc6270_boot_on(struct modem_ctl *mc)
+{
+ struct link_device *ld = get_current_link(mc->iod);
+
+ pr_info("[MODEM_IF:ESC] <%s>\n", __func__);
+
+ /* Need to init uart byt gpio_flm_uart_sel GPIO */
+ if (!mc->gpio_cp_reset || !mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+ gpio_set_value(mc->gpio_flm_uart_sel, 1);
+
+ pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n",
+ gpio_get_value(mc->gpio_cp_on),
+ gpio_get_value(mc->gpio_cp_reset));
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_direction_output(mc->gpio_cp_reset, 0);
+ msleep(100);
+
+ gpio_direction_output(mc->gpio_cp_on, 1);
+ msleep(44);
+
+ pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n",
+ gpio_get_value(mc->gpio_cp_on),
+ gpio_get_value(mc->gpio_cp_reset));
+
+ gpio_direction_input(mc->gpio_cp_reset);
+ msleep(600);
+ gpio_direction_output(mc->gpio_cp_on, 0);
+
+ msleep(20);
+ pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n",
+ gpio_get_value(mc->gpio_cp_on),
+ gpio_get_value(mc->gpio_cp_reset));
+
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ gpio_direction_output(mc->gpio_fpga_cs_n, 1);
+#endif
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
+
+ return 0;
+}
+
+static int esc6270_boot_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF:ESC] <%s>\n", __func__);
+
+ if (!mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_flm_uart_sel, 0);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
+
+ return 0;
+}
+
+static int esc6270_active_count;
+
+static irqreturn_t phone_active_irq_handler(int irq, void *arg)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)arg;
+ int phone_reset = 0;
+ int phone_active = 0;
+ int phone_state = 0;
+ int cp_dump_int = 0;
+
+ if (!mc->gpio_cp_reset ||
+ !mc->gpio_phone_active) { /* || !mc->gpio_cp_dump_int) { */
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return IRQ_HANDLED;
+ }
+
+ phone_reset = gpio_get_value(mc->gpio_cp_reset);
+ phone_active = gpio_get_value(mc->gpio_phone_active);
+ cp_dump_int = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_info("[MODEM_IF:ESC] <%s> phone_reset=%d, phone_active=%d, cp_dump_int=%d\n",
+ __func__, phone_reset, phone_active, cp_dump_int);
+
+ if (phone_reset && phone_active) {
+ phone_state = STATE_ONLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ } else if (phone_reset && !phone_active) {
+ if (mc->phone_state == STATE_ONLINE) {
+ if (cp_dump_int)
+ phone_state = STATE_CRASH_EXIT;
+ else
+ phone_state = STATE_CRASH_RESET;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod,
+ phone_state);
+ }
+ } else {
+ phone_state = STATE_OFFLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ }
+
+ if (phone_active)
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW);
+ else
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH);
+
+ pr_info("[MODEM_IF::ESC] <%s> phone_state = %d\n",
+ __func__, phone_state);
+
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_SIM_DETECT)
+static irqreturn_t sim_detect_irq_handler(int irq, void *_mc)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ pr_info("[MODEM_IF:ESC] <%s> gpio_sim_detect = %d\n",
+ __func__, gpio_get_value(mc->gpio_sim_detect));
+
+ if (mc->iod && mc->iod->sim_state_changed)
+ mc->iod->sim_state_changed(mc->iod,
+ !gpio_get_value(mc->gpio_sim_detect));
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static void esc6270_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = esc6270_on;
+ mc->ops.modem_off = esc6270_off;
+ mc->ops.modem_reset = esc6270_reset;
+ mc->ops.modem_boot_on = esc6270_boot_on;
+ mc->ops.modem_boot_off = esc6270_boot_off;
+}
+
+int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
+{
+ int ret = 0;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+ mc->gpio_sim_detect = pdata->gpio_sim_detect;
+
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ mc->gpio_fpga_cs_n = pdata->gpio_fpga1_cs_n;
+#endif
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq");
+ pr_info("[MODEM_IF:ESC] <%s> PHONE_ACTIVE IRQ# = %d\n",
+ __func__, mc->irq_phone_active);
+
+ esc6270_get_ops(mc);
+
+ if (mc->irq_phone_active) {
+ ret = request_irq(mc->irq_phone_active,
+ phone_active_irq_handler,
+ IRQF_TRIGGER_HIGH,
+ "esc_active",
+ mc);
+ if (ret) {
+ pr_err("[MODEM_IF:ESC] <%s> failed to request_irq IRQ# %d (err=%d)\n",
+ __func__, mc->irq_phone_active, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret) {
+ pr_err("[MODEM_IF:ESC] %s: failed to enable_irq_wake IRQ# %d (err=%d)\n",
+ __func__, mc->irq_phone_active, ret);
+ free_irq(mc->irq_phone_active, mc);
+ return ret;
+ }
+ }
+
+#if defined(CONFIG_SIM_DETECT)
+ mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq");
+ pr_info("[MODEM_IF:ESC] <%s> SIM_DECTCT IRQ# = %d\n",
+ __func__, mc->irq_sim_detect);
+
+ if (mc->irq_sim_detect) {
+ ret = request_irq(mc->irq_sim_detect, sim_detect_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "esc_sim_detect", mc);
+ if (ret) {
+ mif_err("failed to request_irq: %d\n", ret);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_sim_detect);
+ if (ret) {
+ mif_err("failed to enable_irq_wake: %d\n", ret);
+ free_irq(mc->irq_sim_detect, mc);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ /* initialize sim_state => insert: gpio=0, remove: gpio=1 */
+ mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect);
+ }
+#endif
+
+ return ret;
+}
+#endif
diff --git a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c
index 6f1807e..2706e5e 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c
@@ -258,14 +258,72 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
}
#endif /* CONFIG_MACH_U1_KOR_LGT */
-#if defined(CONFIG_MACH_C1CTC) || defined(CONFIG_MACH_M0_CTC)
+#if defined(CONFIG_MACH_M0_CTC) || defined(CONFIG_MACH_T0_CHN_CTC)
+
+#if defined(CONFIG_LINK_DEVICE_DPRAM)
#include "modem_link_device_dpram.h"
+#elif defined(CONFIG_LINK_DEVICE_PLD)
+#include "modem_link_device_pld.h"
+#endif
#define PIF_TIMEOUT (180 * HZ)
#define DPRAM_INIT_TIMEOUT (30 * HZ)
+#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_M0_GRANDECTC) || \
+ defined(CONFIG_MACH_T0_CHN_CTC)
+static void mdm6600_vbus_on(void)
+{
+ struct regulator *regulator;
+
+ pr_info("[MSM] <%s>\n", __func__);
+
+#if defined(CONFIG_MACH_T0_CHN_CTC)
+ if (system_rev == 4)
+ regulator = regulator_get(NULL, "vcc_1.8v_lcd");
+ else
+ regulator = regulator_get(NULL, "vcc_1.8v_usb");
+#else
+ regulator = regulator_get(NULL, "vusbhub_osc_1.8v");
+#endif
+ if (IS_ERR(regulator)) {
+ pr_err("[MSM] error getting regulator_get <%s>\n", __func__);
+ return ;
+ }
+ regulator_enable(regulator);
+ regulator_put(regulator);
+
+ pr_info("[MSM] <%s> enable\n", __func__);
+}
+
+static void mdm6600_vbus_off(void)
+{
+ struct regulator *regulator;
+
+ pr_info("[MSM] <%s>\n", __func__);
+
+#if defined(CONFIG_MACH_T0_CHN_CTC)
+ if (system_rev == 4)
+ regulator = regulator_get(NULL, "vcc_1.8v_lcd");
+ else
+ regulator = regulator_get(NULL, "vcc_1.8v_usb");
+#else
+ regulator = regulator_get(NULL, "vusbhub_osc_1.8v");
+#endif
+ if (IS_ERR(regulator)) {
+ pr_err("[MSM] error getting regulator_get <%s>\n", __func__);
+ return ;
+ }
+ regulator_disable(regulator);
+ regulator_put(regulator);
+
+ pr_info("[MSM] <%s> disable\n", __func__);
+}
+#endif
+
static int mdm6600_on(struct modem_ctl *mc)
{
+ struct link_device *ld = get_current_link(mc->iod);
+
pr_info("[MSM] <%s>\n", __func__);
if (!mc->gpio_reset_req_n || !mc->gpio_cp_reset
@@ -274,20 +332,28 @@ static int mdm6600_on(struct modem_ctl *mc)
return -ENXIO;
}
- gpio_set_value(mc->gpio_reset_req_n, 1);
-
- gpio_set_value(mc->gpio_cp_reset, 1);
- msleep(30);
+ gpio_set_value(mc->gpio_pda_active, 0);
gpio_set_value(mc->gpio_cp_on, 1);
msleep(500);
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+ msleep(50);
+
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(50);
+
gpio_set_value(mc->gpio_cp_on, 0);
msleep(500);
gpio_set_value(mc->gpio_pda_active, 1);
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ gpio_set_value(mc->gpio_fpga_cs_n, 1);
+#endif
+
mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
return 0;
}
@@ -302,8 +368,11 @@ static int mdm6600_off(struct modem_ctl *mc)
}
gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_reset_req_n, 0);
gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(200);
+
mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
return 0;
@@ -312,26 +381,46 @@ static int mdm6600_off(struct modem_ctl *mc)
static int mdm6600_reset(struct modem_ctl *mc)
{
int ret = 0;
+ struct link_device *ld = get_current_link(mc->iod);
pr_info("[MSM] <%s>\n", __func__);
- ret = mdm6600_off(mc);
- if (ret)
+ if (!mc->gpio_reset_req_n || !mc->gpio_cp_reset
+ || !mc->gpio_cp_on || !mc->gpio_pda_active) {
+ pr_err("[MSM] no gpio data\n");
return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_reset_req_n, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
-#if 0
msleep(100);
-#endif
- ret = mdm6600_on(mc);
- if (ret)
- return -ENXIO;
+ gpio_set_value(mc->gpio_cp_on, 1);
+ msleep(300);
+
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+ msleep(50);
+
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(50);
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(100);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
return 0;
}
static int mdm6600_boot_on(struct modem_ctl *mc)
{
+ struct regulator *regulator;
+
pr_info("[MSM] <%s>\n", __func__);
if (!mc->gpio_flm_uart_sel) {
@@ -339,62 +428,106 @@ static int mdm6600_boot_on(struct modem_ctl *mc)
return -ENXIO;
}
- pr_info("[MSM] <%s> %s\n", __func__, "USB_BOOT_EN initializing");
+#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_T0_CHN_CTC)
+ mdm6600_vbus_on();
+#elif defined(CONFIG_MACH_M0_GRANDECTC)
+ if (system_rev >= 14)
+ mdm6600_vbus_on();
+#endif
+ pr_info("[MSM] <%s> %s\n", __func__, "USB_BOOT_EN initializing");
if (system_rev < 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN, 0);
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+
+ msleep(100);
+
gpio_direction_output(GPIO_USB_BOOT_EN, 1);
gpio_set_value(GPIO_USB_BOOT_EN, 1);
pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
gpio_get_value(GPIO_USB_BOOT_EN));
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 1);
+
+ pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__,
+ gpio_get_value(GPIO_BOOT_SW_SEL));
} else if (system_rev == 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN, 0);
+ gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
+ s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
+
+ msleep(100);
+
gpio_direction_output(GPIO_USB_BOOT_EN, 1);
gpio_set_value(GPIO_USB_BOOT_EN, 1);
pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
gpio_get_value(GPIO_USB_BOOT_EN));
- gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
- s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
- gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
-
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 1);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1);
- pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
- gpio_get_value(GPIO_USB_BOOT_EN_REV06));
+ pr_info("[MSM(%d)] <%s> USB_BOOT_EN:[%d]\n", system_rev,
+ __func__, gpio_get_value(GPIO_USB_BOOT_EN_REV06));
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
+
+ msleep(100);
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 1);
+
+ pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__,
+ gpio_get_value(GPIO_BOOT_SW_SEL));
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 1);
+
+ pr_info("[MSM(%d)] <%s> BOOT_SW_SEL : [%d]\n", system_rev,
+ __func__, gpio_get_value(GPIO_BOOT_SW_SEL_REV06));
} else { /* system_rev>11 */
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
+
+ msleep(100);
+
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 1);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1);
pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
gpio_get_value(GPIO_USB_BOOT_EN_REV06));
- }
-
- gpio_direction_output(mc->gpio_flm_uart_sel, 0);
- s3c_gpio_setpull(mc->gpio_flm_uart_sel, S3C_GPIO_PULL_NONE);
- gpio_set_value(mc->gpio_flm_uart_sel, 0);
-
- gpio_direction_output(mc->gpio_flm_uart_sel, 1);
- gpio_set_value(mc->gpio_flm_uart_sel, 1);
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 1);
pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__,
- gpio_get_value(mc->gpio_flm_uart_sel));
+ gpio_get_value(GPIO_BOOT_SW_SEL_REV06));
+
+ }
mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
@@ -405,11 +538,22 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
{
pr_info("[MSM] <%s>\n", __func__);
- if (!mc->gpio_flm_uart_sel || !mc->gpio_flm_uart_sel_rev06) {
+ if (!mc->gpio_flm_uart_sel
+#if defined(CONFIG_MACH_M0_CTC)
+ || !mc->gpio_flm_uart_sel_rev06
+#endif
+ ) {
pr_err("[MSM] no gpio data\n");
return -ENXIO;
}
+#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_T0_CHN_CTC)
+ mdm6600_vbus_off();
+#elif defined(CONFIG_MACH_M0_GRANDECTC)
+ if (system_rev >= 14)
+ mdm6600_vbus_off();
+#endif
+
if (system_rev < 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
@@ -422,19 +566,23 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN, 0);
- gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
- s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
- gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
gpio_direction_output(GPIO_BOOT_SW_SEL, 0);
s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+#if defined(CONFIG_MACH_M0_CTC)
+ gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
+ s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
+
gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
+#endif
} else { /* system_rev>11 */
+#if defined(CONFIG_MACH_M0_CTC)
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
@@ -442,9 +590,10 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
-
+#endif
}
+#if defined(CONFIG_MACH_M0_CTC)
if (max7693_muic_cp_usb_state()) {
msleep(30);
gpio_direction_output(GPIO_USB_BOOT_EN, 1);
@@ -454,8 +603,29 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1);
}
+#endif
+
+ gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+
+ return 0;
+}
+
+
+static int mdm6600_force_crash_exit(struct modem_ctl *mc)
+{
+ pr_info("[MSM] <%s>\n", __func__);
+
+ if (!mc->gpio_cp_reset || !mc->gpio_cp_on) {
+ pr_err("[MSM] no gpio data\n");
+ return -ENXIO;
+ }
+
+ s3c_gpio_cfgpin(mc->gpio_cp_dump_int, S3C_GPIO_OUTPUT);
+ gpio_direction_output(mc->gpio_cp_dump_int, 1);
- gpio_set_value(mc->gpio_flm_uart_sel, 0);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_cp_reset, 1);
return 0;
}
@@ -466,16 +636,20 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg)
int phone_reset = 0;
int phone_active = 0;
int phone_state = 0;
+ int cp_dump_int = 0;
- if (!mc->gpio_cp_reset || !mc->gpio_phone_active) {
+ if (!mc->gpio_cp_reset ||
+ !mc->gpio_phone_active || !mc->gpio_cp_dump_int) {
pr_err("[MSM] no gpio data\n");
return IRQ_HANDLED;
}
phone_reset = gpio_get_value(mc->gpio_cp_reset);
phone_active = gpio_get_value(mc->gpio_phone_active);
- pr_info("[MSM] <%s> phone_reset = %d, phone_active = %d\n",
- __func__, phone_reset, phone_active);
+ cp_dump_int = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_info("[MSM] <%s> phone_reset=%d, phone_active=%d, cp_dump_int=%d\n",
+ __func__, phone_reset, phone_active, cp_dump_int);
if (phone_reset && phone_active) {
phone_state = STATE_ONLINE;
@@ -483,7 +657,10 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg)
mc->iod->modem_state_changed(mc->iod, phone_state);
} else if (phone_reset && !phone_active) {
if (mc->phone_state == STATE_ONLINE) {
- phone_state = STATE_CRASH_EXIT;
+ if (cp_dump_int)
+ phone_state = STATE_CRASH_EXIT;
+ else
+ phone_state = STATE_CRASH_RESET;
if (mc->iod && mc->iod->modem_state_changed)
mc->iod->modem_state_changed(mc->iod,
phone_state);
@@ -504,6 +681,22 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
+#if defined(CONFIG_SIM_DETECT)
+static irqreturn_t sim_detect_irq_handler(int irq, void *_mc)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ pr_info("[MSM] <%s> gpio_sim_detect = %d\n",
+ __func__, gpio_get_value(mc->gpio_sim_detect));
+
+ if (mc->iod && mc->iod->sim_state_changed)
+ mc->iod->sim_state_changed(mc->iod,
+ !gpio_get_value(mc->gpio_sim_detect));
+
+ return IRQ_HANDLED;
+}
+#endif
+
static void mdm6600_get_ops(struct modem_ctl *mc)
{
mc->ops.modem_on = mdm6600_on;
@@ -511,6 +704,7 @@ static void mdm6600_get_ops(struct modem_ctl *mc)
mc->ops.modem_reset = mdm6600_reset;
mc->ops.modem_boot_on = mdm6600_boot_on;
mc->ops.modem_boot_off = mdm6600_boot_off;
+ mc->ops.modem_force_crash_exit = mdm6600_force_crash_exit;
}
int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
@@ -525,10 +719,15 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
mc->gpio_phone_active = pdata->gpio_phone_active;
mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
-#if 1
+#if defined(CONFIG_MACH_M0_CTC)
mc->gpio_flm_uart_sel_rev06 = pdata->gpio_flm_uart_sel_rev06;
#endif
mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+ mc->gpio_sim_detect = pdata->gpio_sim_detect;
+
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ mc->gpio_fpga_cs_n = pdata->gpio_fpga2_cs_n;
+#endif
gpio_set_value(mc->gpio_cp_reset, 0);
gpio_set_value(mc->gpio_cp_on, 0);
@@ -557,6 +756,36 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
return ret;
}
+#if defined(CONFIG_SIM_DETECT)
+ mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq");
+ pr_info("[MSM] <%s> SIM_DECTCT IRQ# = %d\n",
+ __func__, mc->irq_sim_detect);
+
+ if (mc->irq_sim_detect) {
+ ret = request_irq(mc->irq_sim_detect, sim_detect_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "msm_sim_detect", mc);
+ if (ret) {
+ mif_err("[MSM] failed to request_irq: %d\n", ret);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_sim_detect);
+ if (ret) {
+ mif_err("[MSM] failed to enable_irq_wake: %d\n", ret);
+ free_irq(mc->irq_sim_detect, mc);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ /* initialize sim_state => insert: gpio=0, remove: gpio=1 */
+ mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect);
+ }
+#endif
+
return ret;
}
#endif
diff --git a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c
new file mode 100644
index 0000000..9db864de
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c
@@ -0,0 +1,230 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c
+ *
+ * Copyright (C) 2010 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/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/cma.h>
+#include <plat/devs.h>
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include <plat/gpio-cfg.h>
+
+int sprd_boot_done;
+extern int spi_thread_restart(void);
+
+static int sprd8803_on(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ if (!mc->gpio_cp_on || !mc->gpio_pda_active) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ s3c_gpio_cfgpin(EXYNOS4_GPA1(4), S3C_GPIO_SFN(2));
+ s3c_gpio_cfgpin(EXYNOS4_GPA1(5), S3C_GPIO_SFN(2));
+
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ gpio_set_value(mc->gpio_sim_io_sel, 1);
+ gpio_set_value(mc->gpio_cp_ctrl1, 0);
+ gpio_set_value(mc->gpio_cp_ctrl2, 1);
+#endif
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_pda_active, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_on, 1);
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int sprd8803_off(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ if (!mc->gpio_cp_on) {
+ mif_err("no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+
+ mc->phone_state = STATE_OFFLINE;
+
+ return 0;
+}
+
+static int sprd8803_reset(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ spi_thread_restart();
+
+ return 0;
+}
+
+static int sprd8803_boot_on(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s %d\n", __func__, sprd_boot_done);
+ return sprd_boot_done;
+}
+
+static int sprd8803_boot_off(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+ spi_sema_init();
+ return 0;
+}
+
+static int sprd8803_dump_reset(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ if (!mc->gpio_ap_cp_int2)
+ return -ENXIO;
+
+ gpio_set_value(mc->gpio_ap_cp_int2, 0);
+ mc->phone_state = STATE_OFFLINE;
+ msleep(100);
+ gpio_set_value(mc->gpio_ap_cp_int2, 1);
+
+ return 0;
+}
+
+static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
+{
+ int phone_reset = 0;
+ int phone_active_value = 0;
+ int cp_dump_value = 0;
+ int phone_state = 0;
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ disable_irq_nosync(mc->irq_phone_active);
+ disable_irq_nosync(gpio_to_irq(mc->gpio_cp_dump_int));
+
+ if (!mc->gpio_phone_active ||
+ !mc->gpio_cp_dump_int) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ goto exit;
+ }
+
+ if (!sprd_boot_done)
+ goto exit;
+
+ phone_active_value = gpio_get_value(mc->gpio_phone_active);
+ cp_dump_value = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_err("PA EVENT : pa=%d, cp_dump=%d\n",
+ phone_active_value, cp_dump_value);
+
+ if (phone_active_value)
+ phone_state = STATE_ONLINE;
+ else
+ phone_state = STATE_OFFLINE;
+
+ if (phone_active_value && cp_dump_value)
+ phone_state = STATE_CRASH_EXIT;
+
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+
+ if (mc->bootd && mc->bootd->modem_state_changed)
+ mc->bootd->modem_state_changed(mc->bootd, phone_state);
+
+exit:
+ enable_irq(mc->irq_phone_active);
+ enable_irq(gpio_to_irq(mc->gpio_cp_dump_int));
+
+ return IRQ_HANDLED;
+}
+
+static void sprd8803_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = sprd8803_on;
+ mc->ops.modem_off = sprd8803_off;
+ mc->ops.modem_reset = sprd8803_reset;
+ mc->ops.modem_boot_on = sprd8803_boot_on;
+ mc->ops.modem_boot_off = sprd8803_boot_off;
+ mc->ops.modem_dump_reset = sprd8803_dump_reset;
+ mc->ops.modem_force_crash_exit = sprd8803_dump_reset;
+}
+
+int sprd8803_init_modemctl_device(struct modem_ctl *mc,
+ struct modem_data *pdata)
+{
+ int ret = 0;
+ int irq_cp_dump_int;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_ap_cp_int1 = pdata->gpio_ap_cp_int1;
+ mc->gpio_ap_cp_int2 = pdata->gpio_ap_cp_int2;
+
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ mc->gpio_sim_io_sel = pdata->gpio_sim_io_sel;
+ mc->gpio_cp_ctrl1 = pdata->gpio_cp_ctrl1;
+ mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2;
+#endif
+
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active);
+ irq_cp_dump_int = gpio_to_irq(mc->gpio_cp_dump_int);
+
+ sprd8803_get_ops(mc);
+
+ ret = request_irq(mc->irq_phone_active, phone_active_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "phone_active", mc);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to request_irq:%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n",
+ __func__, ret);
+ free_irq(mc->irq_phone_active, mc);
+ }
+
+ ret = request_irq(irq_cp_dump_int, phone_active_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "cp_dump_int", mc);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to request_irq:%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq_cp_dump_int);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n",
+ __func__, ret);
+ free_irq(irq_cp_dump_int, mc);
+ }
+ return ret;
+}
diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c
index 10b0811..4d0b69c 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c
@@ -39,6 +39,12 @@ static int xmm6262_on(struct modem_ctl *mc)
if (mc->gpio_revers_bias_clear)
mc->gpio_revers_bias_clear();
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ gpio_set_value(mc->gpio_sim_io_sel, 0);
+ gpio_set_value(mc->gpio_cp_ctrl1, 1);
+ gpio_set_value(mc->gpio_cp_ctrl2, 0);
+#endif
+
/* TODO */
gpio_set_value(mc->gpio_reset_req_n, 0);
gpio_set_value(mc->gpio_cp_on, 0);
@@ -137,15 +143,19 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
mif_info("PA EVENT : reset =%d, pa=%d, cp_dump=%d\n",
phone_reset, phone_active_value, cp_dump_value);
- if (phone_reset && phone_active_value)
+ if (phone_reset && phone_active_value) {
phone_state = STATE_BOOTING;
- else if (phone_reset && !phone_active_value) {
- if (cp_dump_value)
- phone_state = STATE_CRASH_EXIT;
- else
- phone_state = STATE_CRASH_RESET;
- } else
+ } else if (mc->dev->power.is_suspended && !phone_active_value) {
+ /*fixing dpm timeout by port2 resume retry*/
+ mif_err("CP reset while dpm resume\n");
+ xmm6262_off(mc);
+ phone_state = STATE_CRASH_RESET;
+ } else if (phone_reset && !phone_active_value) {
+ phone_state =
+ (cp_dump_value) ? STATE_CRASH_EXIT : STATE_CRASH_RESET;
+ } else {
phone_state = STATE_OFFLINE;
+ }
if (mc->iod && mc->iod->modem_state_changed)
mc->iod->modem_state_changed(mc->iod, phone_state);
@@ -168,7 +178,8 @@ static irqreturn_t sim_detect_irq_handler(int irq, void *_mc)
if (mc->iod && mc->iod->sim_state_changed)
mc->iod->sim_state_changed(mc->iod,
- !gpio_get_value(mc->gpio_sim_detect));
+ gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity
+ );
return IRQ_HANDLED;
}
@@ -196,10 +207,17 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc,
mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
mc->gpio_sim_detect = pdata->gpio_sim_detect;
+ mc->sim_polarity = pdata->sim_polarity;
mc->gpio_revers_bias_clear = pdata->gpio_revers_bias_clear;
mc->gpio_revers_bias_restore = pdata->gpio_revers_bias_restore;
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ mc->gpio_sim_io_sel = pdata->gpio_sim_io_sel;
+ mc->gpio_cp_ctrl1 = pdata->gpio_cp_ctrl1;
+ mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2;
+#endif
+
pdev = to_platform_device(mc->dev);
mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active);
@@ -241,7 +259,8 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc,
}
/* initialize sim_state => insert: gpio=0, remove: gpio=1 */
- mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect);
+ mc->sim_state.online =
+ gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity;
}
return ret;
diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h
index 464370f..f85596f 100644
--- a/drivers/misc/modem_if/modem_prj.h
+++ b/drivers/misc/modem_if/modem_prj.h
@@ -23,6 +23,8 @@
#include <linux/wakelock.h>
#include <linux/rbtree.h>
#include <linux/spinlock.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
#define MAX_CPINFO_SIZE 512
@@ -38,7 +40,7 @@
#define IOCTL_MODEM_RESET _IO('o', 0x21)
#define IOCTL_MODEM_BOOT_ON _IO('o', 0x22)
#define IOCTL_MODEM_BOOT_OFF _IO('o', 0x23)
-#define IOCTL_MODEM_START _IO('o', 0x24)
+#define IOCTL_MODEM_BOOT_DONE _IO('o', 0x24)
#define IOCTL_MODEM_PROTOCOL_SUSPEND _IO('o', 0x25)
#define IOCTL_MODEM_PROTOCOL_RESUME _IO('o', 0x26)
@@ -56,6 +58,10 @@
#define IOCTL_MODEM_CP_UPLOAD _IO('o', 0x35)
#define IOCTL_MODEM_DUMP_RESET _IO('o', 0x36)
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+#define IOCTL_MODEM_SWITCH_MODEM _IO('o', 0x37)
+#endif
+
#define IOCTL_DPRAM_SEND_BOOT _IO('o', 0x40)
#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43)
@@ -68,6 +74,10 @@
#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde)
#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf)
+/* ioctl command for IPC Logger */
+#define IOCTL_MIF_LOG_DUMP _IO('o', 0x51)
+#define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52)
+
/* modem status */
#define MODEM_OFF 0
#define MODEM_CRASHED 1
@@ -90,6 +100,13 @@
#define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
+/* loopback: CP -> AP -> CP */
+#define CP2AP_LOOPBACK_CHANNEL 30
+
+/* ip loopback */
+#define RMNET0_CH_ID 10
+#define DATA_LOOPBACK_CHANNEL 31
+
/* Debugging features */
#define MAX_MIF_LOG_PATH_LEN 128
#define MAX_MIF_LOG_FILE_SIZE 0x800000 /* 8 MB */
@@ -129,6 +146,7 @@ struct dpram_irq_buff {
unsigned int2cp;
};
+/* Not use */
struct mif_event_buff {
char time[MAX_MIF_TIME_LEN];
@@ -164,6 +182,9 @@ enum modem_state {
STATE_LOADER_DONE,
STATE_SIM_ATTACH,
STATE_SIM_DETACH,
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+ STATE_MODEM_SWITCH,
+#endif
};
enum com_state {
@@ -175,9 +196,9 @@ enum com_state {
};
enum link_mode {
- LINK_MODE_INVALID = 0,
- LINK_MODE_IPC,
+ LINK_MODE_OFFLINE = 0,
LINK_MODE_BOOT,
+ LINK_MODE_IPC,
LINK_MODE_DLOAD,
LINK_MODE_ULOAD,
};
@@ -247,6 +268,8 @@ struct sipc_fmt_hdr {
#define SIPC5_CTL_OFFSET 4
#define SIPC5_CH_ID_RAW_0 0
+#define SIPC5_CH_ID_PDP_0 10
+#define SIPC5_CH_ID_PDP_LAST 24
#define SIPC5_CH_ID_FMT_0 235
#define SIPC5_CH_ID_RFS_0 245
#define SIPC5_CH_ID_MAX 255
@@ -265,7 +288,10 @@ struct sipc5_link_hdr {
u8 cfg;
u8 ch;
u16 len;
- u8 ctl;
+ union {
+ u8 ctl;
+ u16 ext_len;
+ };
} __packed;
struct sipc5_frame_data {
@@ -280,7 +306,6 @@ struct sipc5_frame_data {
/* Frame configuration set by header analysis */
bool padding;
- bool ext_fld;
bool ctl_fld;
bool ext_len;
@@ -303,24 +328,6 @@ struct sipc5_frame_data {
u8 hdr[SIPC5_MAX_HEADER_SIZE];
};
-static inline unsigned sipc5_get_hdr_size(u8 cfg)
-{
- if (cfg & SIPC5_EXT_FIELD_EXIST) {
- if (cfg & SIPC5_CTL_FIELD_EXIST)
- return SIPC5_HEADER_SIZE_WITH_CTL_FLD;
- else
- return SIPC5_HEADER_SIZE_WITH_EXT_LEN;
- } else {
- return SIPC5_MIN_HEADER_SIZE;
- }
-}
-
-static inline unsigned sipc5_calc_padding_size(unsigned len)
-{
- unsigned residue = len & 0x3;
- return residue ? (4 - residue) : 0;
-}
-
struct vnet {
struct io_device *iod;
};
@@ -342,7 +349,9 @@ struct skbuff_private {
struct io_device *iod;
struct link_device *ld;
struct io_device *real_iod; /* for rx multipdp */
-};
+ u8 ch_id;
+ u8 control;
+} __packed;
static inline struct skbuff_private *skbpriv(struct sk_buff *skb)
{
@@ -350,25 +359,6 @@ static inline struct skbuff_private *skbpriv(struct sk_buff *skb)
return (struct skbuff_private *)&skb->cb;
}
-/** rx_alloc_skb - allocate an skbuff and set skb's iod, ld
- * @length: length to allocate
- * @gfp_mask: get_free_pages mask, passed to alloc_skb
- * @iod: struct io_device *
- * @ld: struct link_device *
- *
- * %NULL is returned if there is no free memory.
- */
-static inline struct sk_buff *rx_alloc_skb(unsigned int length,
- gfp_t gfp_mask, struct io_device *iod, struct link_device *ld)
-{
- struct sk_buff *skb = alloc_skb(length, gfp_mask);
- if (likely(skb)) {
- skbpriv(skb)->iod = iod;
- skbpriv(skb)->ld = ld;
- }
- return skb;
-}
-
struct io_device {
/* rb_tree node for an io device */
struct rb_node node_chan;
@@ -398,9 +388,6 @@ struct io_device {
/* SIPC version */
enum sipc_ver ipc_version;
- /* Tx header buffer */
- struct sipc5_frame_data meta_frame;
-
/* Rx queue of sk_buff */
struct sk_buff_head sk_rx_q;
@@ -418,6 +405,8 @@ struct io_device {
/* called from linkdevice when a packet arrives for this iodevice */
int (*recv)(struct io_device *iod, struct link_device *ld,
const char *data, unsigned int len);
+ int (*recv_skb)(struct io_device *iod, struct link_device *ld,
+ struct sk_buff *skb);
/* inform the IO device that the modem is now online or offline or
* crashing or whatever...
@@ -428,6 +417,7 @@ struct io_device {
void (*sim_state_changed)(struct io_device *iod, bool sim_online);
struct modem_ctl *mc;
+ struct modem_shared *msd;
struct wake_lock wakelock;
long waketime;
@@ -463,6 +453,9 @@ struct link_device {
/* Modem control */
struct modem_ctl *mc;
+ /* Modem shared data */
+ struct modem_shared *msd;
+
/* Operation mode of the link device */
enum link_mode mode;
@@ -518,26 +511,68 @@ struct link_device {
unsigned cmd, unsigned long _arg);
};
+/** rx_alloc_skb - allocate an skbuff and set skb's iod, ld
+ * @length: length to allocate
+ * @iod: struct io_device *
+ * @ld: struct link_device *
+ *
+ * %NULL is returned if there is no free memory.
+ */
+static inline struct sk_buff *rx_alloc_skb(unsigned int length,
+ struct io_device *iod, struct link_device *ld)
+{
+ struct sk_buff *skb;
+
+ if (iod->format == IPC_MULTI_RAW || iod->format == IPC_RAW)
+ skb = dev_alloc_skb(length);
+ else
+ skb = alloc_skb(length, GFP_ATOMIC);
+
+ if (likely(skb)) {
+ skbpriv(skb)->iod = iod;
+ skbpriv(skb)->ld = ld;
+ }
+ return skb;
+}
+
struct modemctl_ops {
int (*modem_on) (struct modem_ctl *);
int (*modem_off) (struct modem_ctl *);
int (*modem_reset) (struct modem_ctl *);
int (*modem_boot_on) (struct modem_ctl *);
int (*modem_boot_off) (struct modem_ctl *);
+ int (*modem_boot_done) (struct modem_ctl *);
int (*modem_force_crash_exit) (struct modem_ctl *);
int (*modem_dump_reset) (struct modem_ctl *);
};
-/* mif_common - common data for all io devices and link devices and a modem ctl
- * commons : mc : iod : ld = 1 : 1 : M : N
+/* for IPC Logger */
+struct mif_storage {
+ char *addr;
+ unsigned int cnt;
+};
+
+/* modem_shared - shared data for all io/link devices and a modem ctl
+ * msd : mc : iod : ld = 1 : 1 : M : N
*/
-struct mif_common {
+struct modem_shared {
/* list of link devices */
struct list_head link_dev_list;
/* rb_tree root of io devices. */
struct rb_root iodevs_tree_chan; /* group by channel */
struct rb_root iodevs_tree_fmt; /* group by dev_format */
+
+ /* for IPC Logger */
+ struct mif_storage storage;
+ spinlock_t lock;
+
+ /* loopbacked IP address
+ * default is 0.0.0.0 (disabled)
+ * after you setted this, you can use IP packet loopback using this IP.
+ * exam: echo 1.2.3.4 > /sys/devices/virtual/misc/umts_multipdp/loopback
+ */
+ __be32 loopback_ipaddr;
};
struct modem_ctl {
@@ -545,7 +580,7 @@ struct modem_ctl {
char *name;
struct modem_data *mdm_data;
- struct mif_common commons;
+ struct modem_shared *msd;
enum modem_state phone_state;
struct sim_state sim_state;
@@ -589,46 +624,146 @@ struct modem_ctl {
bool usb_boot;
#endif
+#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803
+ unsigned gpio_ap_cp_int1;
+ unsigned gpio_ap_cp_int2;
+#endif
+
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ unsigned gpio_sim_io_sel;
+ unsigned gpio_cp_ctrl1;
+ unsigned gpio_cp_ctrl2;
+#endif
+
+#ifdef CONFIG_LINK_DEVICE_PLD
+ unsigned gpio_fpga_cs_n;
+#endif
+
struct modemctl_ops ops;
struct io_device *iod;
struct io_device *bootd;
+ /* Wakelock for modem_ctl */
+ struct wake_lock mc_wake_lock;
+
void (*gpio_revers_bias_clear)(void);
void (*gpio_revers_bias_restore)(void);
- /* TODO this will be move to struct mif_common */
- /* For debugging log */
- bool use_mif_log;
- enum mif_event_id log_level;
- atomic_t log_open;
+ bool need_switch_to_usb;
+ bool sim_polarity;
+};
- struct workqueue_struct *evt_wq;
- struct work_struct evt_work;
- struct sk_buff_head evtq;
+int sipc4_init_io_device(struct io_device *iod);
+int sipc5_init_io_device(struct io_device *iod);
- char log_path[MAX_MIF_LOG_PATH_LEN];
- struct file *log_fp;
+/**
+ * sipc5_start_valid
+ * @cfg: configuration field of an SIPC5 link frame
+ *
+ * Returns TRUE if the start (configuration field) of an SIPC5 link frame
+ * is valid or returns FALSE if it is not valid.
+ *
+ */
+static inline int sipc5_start_valid(u8 cfg)
+{
+ return (cfg & SIPC5_START_MASK) == SIPC5_START_MASK;
+}
- bool fs_ready;
- bool fs_failed;
+/**
+ * sipc5_get_hdr_len
+ * @cfg: configuration field of an SIPC5 link frame
+ *
+ * Returns the length of SIPC5 link layer header in an SIPC5 link frame
+ *
+ */
+static inline unsigned sipc5_get_hdr_len(u8 cfg)
+{
+ if (cfg & SIPC5_EXT_FIELD_EXIST) {
+ if (cfg & SIPC5_CTL_FIELD_EXIST)
+ return SIPC5_HEADER_SIZE_WITH_CTL_FLD;
+ else
+ return SIPC5_HEADER_SIZE_WITH_EXT_LEN;
+ } else {
+ return SIPC5_MIN_HEADER_SIZE;
+ }
+}
- char *buff;
-};
-#define to_modem_ctl(mif_common) \
- container_of(mif_common, struct modem_ctl, commons)
+/**
+ * sipc5_get_ch_id
+ * @frm: pointer to an SIPC5 frame
+ *
+ * Returns the channel ID in an SIPC5 link frame
+ *
+ */
+static inline u8 sipc5_get_ch_id(u8 *frm)
+{
+ return *(frm + SIPC5_CH_ID_OFFSET);
+}
-int sipc4_init_io_device(struct io_device *iod);
-int sipc5_init_io_device(struct io_device *iod);
+/**
+ * sipc5_get_frame_sz16
+ * @frm: pointer to an SIPC5 link frame
+ *
+ * Returns the length of an SIPC5 link frame without the extended length field
+ *
+ */
+static inline unsigned sipc5_get_frame_sz16(u8 *frm)
+{
+ return *((u16 *)(frm + SIPC5_LEN_OFFSET));
+}
+
+/**
+ * sipc5_get_frame_sz32
+ * @frm: pointer to an SIPC5 frame
+ *
+ * Returns the length of an SIPC5 link frame with the extended length field
+ *
+ */
+static inline unsigned sipc5_get_frame_sz32(u8 *frm)
+{
+ return *((u32 *)(frm + SIPC5_LEN_OFFSET));
+}
+
+/**
+ * sipc5_calc_padding_size
+ * @len: length of an SIPC5 link frame
+ *
+ * Returns the padding size for an SIPC5 link frame
+ *
+ */
+static inline unsigned sipc5_calc_padding_size(unsigned len)
+{
+ unsigned residue = len & 0x3;
+ return residue ? (4 - residue) : 0;
+}
-int mif_init_log(struct modem_ctl *mc);
-void mif_set_log_level(struct modem_ctl *mc);
-int mif_open_log_file(struct modem_ctl *mc);
-void mif_close_log_file(struct modem_ctl *mc);
+extern void set_sromc_access(bool access);
-void mif_irq_log(struct modem_ctl *mc, struct sk_buff *skb);
-void mif_ipc_log(struct modem_ctl *mc, enum mif_event_id evt,
- struct io_device *iod, struct link_device *ld,
- u8 *data, unsigned size);
-void mif_flush_logs(struct modem_ctl *mc);
+#if defined(CONFIG_TDSCDMA_MODEM_SPRD8803) && defined(CONFIG_LINK_DEVICE_SPI)
+extern int spi_sema_init(void);
+extern int sprd_boot_done;
+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;
+};
+#endif
#endif
diff --git a/drivers/misc/modem_if/modem_sim_slot_switch.c b/drivers/misc/modem_if/modem_sim_slot_switch.c
new file mode 100644
index 0000000..1dd4c67
--- /dev/null
+++ b/drivers/misc/modem_if/modem_sim_slot_switch.c
@@ -0,0 +1,92 @@
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <plat/gpio-cfg.h>
+
+#include <mach/gpio.h>
+
+extern struct class *sec_class;
+struct device *slot_switch_dev;
+
+static ssize_t get_slot_switch(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int value;
+
+ //return '0' slot path is '||', return '1' slot path is 'X'
+ value = gpio_get_value(GPIO_UIM_SIM_SEL);
+ printk("Current Slot is %x\n", value);
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ int value;
+
+ sscanf(buf, "%d", &value);
+
+ switch(value) {
+ case 0:
+ gpio_set_value(GPIO_UIM_SIM_SEL, 0);
+ printk("set slot switch to %x\n", gpio_get_value(GPIO_UIM_SIM_SEL));
+ break;
+ case 1:
+ gpio_set_value(GPIO_UIM_SIM_SEL, 1);
+ printk("set slot switch to %x\n", gpio_get_value(GPIO_UIM_SIM_SEL));
+ break;
+ default:
+ printk("Enter 0 or 1!!\n");
+ }
+
+ return size;
+}
+
+static DEVICE_ATTR(slot_sel, S_IRUGO |S_IWUGO | S_IRUSR | S_IWUSR, get_slot_switch, set_slot_switch);
+
+static int __init slot_switch_manager_init(void)
+{
+ int ret = 0;
+ int err = 0;
+
+ printk("slot_switch_manager_init\n");
+
+ //initailize uim_sim_switch gpio
+ err = gpio_request(GPIO_UIM_SIM_SEL, "PDA_ACTIVE");
+ if (err) {
+ pr_err("fail to request gpio %s, gpio %d, errno %d\n",
+ "PDA_ACTIVE", GPIO_UIM_SIM_SEL, err);
+ } else {
+ gpio_direction_output(GPIO_UIM_SIM_SEL, 1);
+ s3c_gpio_setpull(GPIO_UIM_SIM_SEL, S3C_GPIO_PULL_NONE);
+#if defined(CONFIG_MACH_T0_CHN_CTC)
+ gpio_set_value(GPIO_UIM_SIM_SEL, 1);
+#else
+ gpio_set_value(GPIO_UIM_SIM_SEL, 0);
+#endif
+ }
+
+ //initailize slot switch device
+ slot_switch_dev = device_create(sec_class,
+ NULL, 0, NULL, "slot_switch");
+ if (IS_ERR(slot_switch_dev))
+ pr_err("Failed to create device(switch)!\n");
+
+ if (device_create_file(slot_switch_dev, &dev_attr_slot_sel) < 0)
+ pr_err("Failed to create device file(%s)!\n",
+ dev_attr_slot_sel.attr.name);
+
+ return ret;
+}
+
+static void __exit slot_switch_manager_exit(void)
+{
+}
+
+module_init(slot_switch_manager_init);
+module_exit(slot_switch_manager_exit);
+
+MODULE_AUTHOR("SAMSUNG ELECTRONICS CO., LTD");
+MODULE_DESCRIPTION("Slot Switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/modem_if/modem_utils.c b/drivers/misc/modem_if/modem_utils.c
index 24a9d19..d62aaa6 100644
--- a/drivers/misc/modem_if/modem_utils.c
+++ b/drivers/misc/modem_if/modem_utils.c
@@ -19,6 +19,8 @@
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
+#include <linux/rtc.h>
+#include <linux/time.h>
#include <net/ip.h>
#include "modem_prj.h"
@@ -27,6 +29,183 @@
#define CMD_SUSPEND ((unsigned short)(0x00CA))
#define CMD_RESUME ((unsigned short)(0x00CB))
+#define TX_SEPARATOR "mif: >>>>>>>>>> Outgoing packet "
+#define RX_SEPARATOR "mif: Incoming packet <<<<<<<<<<"
+#define LINE_SEPARATOR \
+ "mif: ------------------------------------------------------------"
+#define LINE_BUFF_SIZE 80
+
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+#include "modem_link_device_dpram.h"
+int mif_dump_dpram(struct io_device *iod)
+{
+ struct link_device *ld = get_current_link(iod);
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ u32 size = dpld->dp_size;
+ unsigned long read_len = 0;
+ struct sk_buff *skb;
+ char *buff;
+
+ buff = kzalloc(size, GFP_ATOMIC);
+ if (!buff) {
+ pr_err("[MIF] <%s> alloc dpram buff failed\n", __func__);
+ return -ENOMEM;
+ } else {
+ dpld->dpram_dump(ld, buff);
+ }
+
+ while (read_len < size) {
+ skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MIF] <%s> alloc skb failed\n", __func__);
+ kfree(buff);
+ return -ENOMEM;
+ }
+ memcpy(skb_put(skb, MAX_IPC_SKB_SIZE),
+ buff + read_len, MAX_IPC_SKB_SIZE);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ read_len += MAX_IPC_SKB_SIZE;
+ wake_up(&iod->wq);
+ }
+ kfree(buff);
+ return 0;
+}
+#endif
+
+int mif_dump_log(struct modem_shared *msd, struct io_device *iod)
+{
+ struct sk_buff *skb;
+ unsigned long read_len = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+ while (read_len < MAX_MIF_BUFF_SIZE) {
+ skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MIF] <%s> alloc skb failed\n", __func__);
+ spin_unlock_irqrestore(&msd->lock, flags);
+ return -ENOMEM;
+ }
+ memcpy(skb_put(skb, MAX_IPC_SKB_SIZE),
+ msd->storage.addr + read_len, MAX_IPC_SKB_SIZE);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ read_len += MAX_IPC_SKB_SIZE;
+ wake_up(&iod->wq);
+ }
+ spin_unlock_irqrestore(&msd->lock, flags);
+ return 0;
+}
+
+static unsigned long long get_kernel_time(void)
+{
+ int this_cpu;
+ unsigned long flags;
+ unsigned long long time;
+
+ preempt_disable();
+ raw_local_irq_save(flags);
+
+ this_cpu = smp_processor_id();
+ time = cpu_clock(this_cpu);
+
+ preempt_enable();
+ raw_local_irq_restore(flags);
+
+ return time;
+}
+
+void mif_ipc_log(enum mif_log_id id,
+ struct modem_shared *msd, const char *data, size_t len)
+{
+ struct mif_ipc_block *block;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_ipc_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+ block->len = (len > MAX_IPC_LOG_SIZE) ? MAX_IPC_LOG_SIZE : len;
+ memcpy(block->buff, data, block->len);
+}
+
+void _mif_irq_log(enum mif_log_id id, struct modem_shared *msd,
+ struct mif_irq_map map, const char *data, size_t len)
+{
+ struct mif_irq_block *block;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_irq_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+ memcpy(&(block->map), &map, sizeof(struct mif_irq_map));
+ if (data)
+ memcpy(block->buff, data,
+ (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len);
+}
+
+void _mif_com_log(enum mif_log_id id,
+ struct modem_shared *msd, const char *format, ...)
+{
+ struct mif_common_block *block;
+ unsigned long int flags;
+ va_list args;
+ int ret;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_common_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+
+ va_start(args, format);
+ ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args);
+ va_end(args);
+}
+
+void _mif_time_log(enum mif_log_id id, struct modem_shared *msd,
+ struct timespec epoch, const char *data, size_t len)
+{
+ struct mif_time_block *block;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_time_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+ memcpy(&block->epoch, &epoch, sizeof(struct timespec));
+
+ if (data)
+ memcpy(block->buff, data,
+ (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len);
+}
/* dump2hex
* dump data to hex as fast as possible.
@@ -52,6 +231,23 @@ static inline int dump2hex(char *buf, const char *data, size_t len)
return dest - buf;
}
+int pr_ipc(const char *str, const char *data, size_t len)
+{
+ struct timeval tv;
+ struct tm date;
+ unsigned char hexstr[128];
+
+ do_gettimeofday(&tv);
+ time_to_tm((tv.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date);
+
+ dump2hex(hexstr, data, (len > 40 ? 40 : len));
+
+ return pr_info("mif: %s: [%02d-%02d %02d:%02d:%02d.%03ld] %s\n",
+ str, date.tm_mon + 1, date.tm_mday,
+ date.tm_hour, date.tm_min, date.tm_sec,
+ (tv.tv_usec > 0 ? tv.tv_usec / 1000 : 0), hexstr);
+}
+
/* print buffer as hex string */
int pr_buffer(const char *tag, const char *data, size_t data_len,
size_t max_len)
@@ -61,14 +257,14 @@ int pr_buffer(const char *tag, const char *data, size_t data_len,
dump2hex(hexstr, data, len);
/* don't change this printk to mif_debug for print this as level7 */
- return printk(KERN_DEBUG "%s(%u): %s%s\n", tag, data_len, hexstr,
+ return printk(KERN_INFO "mif: %s(%u): %s%s\n", tag, data_len, hexstr,
len == data_len ? "" : " ...");
}
-/* flow control CMfrom CP, it use in serial devices */
+/* flow control CM from CP, it use in serial devices */
int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len)
{
- struct mif_common *commons = &ld->mc->commons;
+ struct modem_shared *msd = ld->msd;
unsigned short *cmd, *end = (unsigned short *)(data + len);
mif_debug("flow control cmd: size=%d\n", len);
@@ -76,13 +272,13 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len)
for (cmd = (unsigned short *)data; cmd < end; cmd++) {
switch (*cmd) {
case CMD_SUSPEND:
- iodevs_for_each(commons, iodev_netif_stop, 0);
+ iodevs_for_each(msd, iodev_netif_stop, 0);
ld->raw_tx_suspended = true;
mif_info("flowctl CMD_SUSPEND(%04X)\n", *cmd);
break;
case CMD_RESUME:
- iodevs_for_each(commons, iodev_netif_wake, 0);
+ iodevs_for_each(msd, iodev_netif_wake, 0);
ld->raw_tx_suspended = false;
complete_all(&ld->raw_tx_resumed_by_cp);
mif_info("flowctl CMD_RESUME(%04X)\n", *cmd);
@@ -97,10 +293,10 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len)
return 0;
}
-struct io_device *get_iod_with_channel(struct mif_common *commons,
+struct io_device *get_iod_with_channel(struct modem_shared *msd,
unsigned channel)
{
- struct rb_node *n = commons->iodevs_tree_chan.rb_node;
+ struct rb_node *n = msd->iodevs_tree_chan.rb_node;
struct io_device *iodev;
while (n) {
iodev = rb_entry(n, struct io_device, node_chan);
@@ -114,10 +310,10 @@ struct io_device *get_iod_with_channel(struct mif_common *commons,
return NULL;
}
-struct io_device *get_iod_with_format(struct mif_common *commons,
+struct io_device *get_iod_with_format(struct modem_shared *msd,
enum dev_format format)
{
- struct rb_node *n = commons->iodevs_tree_fmt.rb_node;
+ struct rb_node *n = msd->iodevs_tree_fmt.rb_node;
struct io_device *iodev;
while (n) {
iodev = rb_entry(n, struct io_device, node_fmt);
@@ -131,10 +327,10 @@ struct io_device *get_iod_with_format(struct mif_common *commons,
return NULL;
}
-struct io_device *insert_iod_with_channel(struct mif_common *commons,
+struct io_device *insert_iod_with_channel(struct modem_shared *msd,
unsigned channel, struct io_device *iod)
{
- struct rb_node **p = &commons->iodevs_tree_chan.rb_node;
+ struct rb_node **p = &msd->iodevs_tree_chan.rb_node;
struct rb_node *parent = NULL;
struct io_device *iodev;
while (*p) {
@@ -148,14 +344,14 @@ struct io_device *insert_iod_with_channel(struct mif_common *commons,
return iodev;
}
rb_link_node(&iod->node_chan, parent, p);
- rb_insert_color(&iod->node_chan, &commons->iodevs_tree_chan);
+ rb_insert_color(&iod->node_chan, &msd->iodevs_tree_chan);
return NULL;
}
-struct io_device *insert_iod_with_format(struct mif_common *commons,
+struct io_device *insert_iod_with_format(struct modem_shared *msd,
enum dev_format format, struct io_device *iod)
{
- struct rb_node **p = &commons->iodevs_tree_fmt.rb_node;
+ struct rb_node **p = &msd->iodevs_tree_fmt.rb_node;
struct rb_node *parent = NULL;
struct io_device *iodev;
while (*p) {
@@ -169,14 +365,14 @@ struct io_device *insert_iod_with_format(struct mif_common *commons,
return iodev;
}
rb_link_node(&iod->node_fmt, parent, p);
- rb_insert_color(&iod->node_fmt, &commons->iodevs_tree_fmt);
+ rb_insert_color(&iod->node_fmt, &msd->iodevs_tree_fmt);
return NULL;
}
-void iodevs_for_each(struct mif_common *commons, action_fn action, void *args)
+void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args)
{
struct io_device *iod;
- struct rb_node *node = rb_first(&commons->iodevs_tree_chan);
+ struct rb_node *node = rb_first(&msd->iodevs_tree_chan);
for (; node; node = rb_next(node)) {
iod = rb_entry(node, struct io_device, node_chan);
action(iod, args);
@@ -202,22 +398,48 @@ void iodev_netif_stop(struct io_device *iod, void *args)
static void iodev_set_tx_link(struct io_device *iod, void *args)
{
struct link_device *ld = (struct link_device *)args;
- if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld)) {
+ if (iod->format == IPC_RAW && IS_CONNECTED(iod, ld)) {
set_current_link(iod, ld);
mif_err("%s -> %s\n", iod->name, ld->name);
}
}
-void rawdevs_set_tx_link(struct mif_common *commons, enum modem_link link_type)
+void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type)
{
- struct link_device *ld = find_linkdev(commons, link_type);
+ struct link_device *ld = find_linkdev(msd, link_type);
if (ld)
- iodevs_for_each(commons, iodev_set_tx_link, ld);
+ iodevs_for_each(msd, iodev_set_tx_link, ld);
+}
+
+/**
+ * ipv4str_to_be32 - ipv4 string to be32 (big endian 32bits integer)
+ * @return: return zero when errors occurred
+ */
+__be32 ipv4str_to_be32(const char *ipv4str, size_t count)
+{
+ unsigned char ip[4];
+ char ipstr[16]; /* == strlen("xxx.xxx.xxx.xxx") + 1 */
+ char *next = ipstr;
+ char *p;
+ int i;
+
+ strncpy(ipstr, ipv4str, ARRAY_SIZE(ipstr));
+
+ for (i = 0; i < 4; i++) {
+ p = strsep(&next, ".");
+ if (kstrtou8(p, 10, &ip[i]) < 0)
+ return 0; /* == 0.0.0.0 */
+ }
+
+ return *((__be32 *)ip);
}
void mif_add_timer(struct timer_list *timer, unsigned long expire,
void (*function)(unsigned long), unsigned long data)
{
+ if (timer_pending(timer))
+ return;
+
init_timer(timer);
timer->expires = get_jiffies_64() + expire;
timer->function = function;
@@ -375,13 +597,12 @@ void print_sipc5_link_fmt_frame(const u8 *psrc)
mif_err("-----------------------------------------------------------\n");
}
-static void print_tcp_header(u8 *pkt)
+static void strcat_tcp_header(char *buff, u8 *pkt)
{
- int i;
- char tcp_flags[32];
struct tcphdr *tcph = (struct tcphdr *)pkt;
- u8 *opt = pkt + TCP_HDR_SIZE;
- unsigned opt_len = (tcph->doff << 2) - TCP_HDR_SIZE;
+ int eol;
+ char line[LINE_BUFF_SIZE];
+ char flag_str[32];
/*-------------------------------------------------------------------------
@@ -409,44 +630,54 @@ static void print_tcp_header(u8 *pkt)
-------------------------------------------------------------------------*/
- memset(tcp_flags, 0, sizeof(tcp_flags));
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: TCP:: Src.Port %u, Dst.Port %u\n",
+ ntohs(tcph->source), ntohs(tcph->dest));
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n",
+ ntohs(tcph->seq), ntohs(tcph->seq),
+ ntohs(tcph->ack_seq), ntohs(tcph->ack_seq));
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ memset(flag_str, 0, sizeof(flag_str));
if (tcph->cwr)
- strcat(tcp_flags, "CWR ");
+ strcat(flag_str, "CWR ");
if (tcph->ece)
- strcat(tcp_flags, "EC");
+ strcat(flag_str, "ECE");
if (tcph->urg)
- strcat(tcp_flags, "URG ");
+ strcat(flag_str, "URG ");
if (tcph->ack)
- strcat(tcp_flags, "ACK ");
+ strcat(flag_str, "ACK ");
if (tcph->psh)
- strcat(tcp_flags, "PSH ");
+ strcat(flag_str, "PSH ");
if (tcph->rst)
- strcat(tcp_flags, "RST ");
+ strcat(flag_str, "RST ");
if (tcph->syn)
- strcat(tcp_flags, "SYN ");
+ strcat(flag_str, "SYN ");
if (tcph->fin)
- strcat(tcp_flags, "FIN ");
-
- mif_err("TCP:: Src.Port %u, Dst.Port %u\n",
- ntohs(tcph->source), ntohs(tcph->dest));
- mif_err("TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n",
- ntohs(tcph->seq), ntohs(tcph->seq),
- ntohs(tcph->ack_seq), ntohs(tcph->ack_seq));
- mif_err("TCP:: Flags {%s}\n", tcp_flags);
- mif_err("TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n",
+ strcat(flag_str, "FIN ");
+ eol = strlen(flag_str) - 1;
+ if (eol > 0)
+ flag_str[eol] = 0;
+ snprintf(line, LINE_BUFF_SIZE, "mif: TCP:: Flags {%s}\n", flag_str);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n",
ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr));
-
- if (opt_len > 0) {
- mif_err("TCP:: Options {");
- for (i = 0; i < opt_len; i++)
- mif_err("%02X ", opt[i]);
- mif_err("}\n");
- }
+ strcat(buff, line);
}
-static void print_udp_header(u8 *pkt)
+static void strcat_udp_header(char *buff, u8 *pkt)
{
struct udphdr *udph = (struct udphdr *)pkt;
+ char line[LINE_BUFF_SIZE];
/*-------------------------------------------------------------------------
@@ -462,30 +693,43 @@ static void print_udp_header(u8 *pkt)
-------------------------------------------------------------------------*/
- mif_err("UDP:: Src.Port %u, Dst.Port %u\n",
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: UDP:: Src.Port %u, Dst.Port %u\n",
ntohs(udph->source), ntohs(udph->dest));
- mif_err("UDP:: Length %u, Checksum 0x%04X\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: UDP:: Length %u, Checksum 0x%04X\n",
ntohs(udph->len), ntohs(udph->check));
+ strcat(buff, line);
- if (ntohs(udph->dest) == 53)
- mif_err("UDP:: DNS query!!!\n");
+ if (ntohs(udph->dest) == 53) {
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS query!!!\n");
+ strcat(buff, line);
+ }
- if (ntohs(udph->source) == 53)
- mif_err("UDP:: DNS response!!!\n");
+ if (ntohs(udph->source) == 53) {
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS response!!!\n");
+ strcat(buff, line);
+ }
}
-void print_ip4_packet(u8 *ip_pkt)
+void print_ip4_packet(u8 *ip_pkt, bool tx)
{
- char ip_flags[16];
+ char *buff;
struct iphdr *iph = (struct iphdr *)ip_pkt;
u8 *pkt = ip_pkt + (iph->ihl << 2);
u16 flags = (ntohs(iph->frag_off) & 0xE000);
u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF);
-
- mif_err("-----------------------------------------------------------\n");
+ int eol;
+ char line[LINE_BUFF_SIZE];
+ char flag_str[16];
/*---------------------------------------------------------------------------
-
IPv4 Header Format
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -513,40 +757,234 @@ void print_ip4_packet(u8 *ip_pkt)
---------------------------------------------------------------------------*/
- memset(ip_flags, 0, sizeof(ip_flags));
- if (flags & IP_CE)
- strcat(ip_flags, "C");
- if (flags & IP_DF)
- strcat(ip_flags, "D");
- if (flags & IP_MF)
- strcat(ip_flags, "M");
+ if (iph->version != 4)
+ return;
- mif_err("IP4:: Version %u, Header Length %u, TOS %u, Length %u\n",
+ buff = kzalloc(4096, GFP_ATOMIC);
+ if (!buff)
+ return;
+
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ if (tx)
+ snprintf(line, LINE_BUFF_SIZE, "\n%s\n", TX_SEPARATOR);
+ else
+ snprintf(line, LINE_BUFF_SIZE, "\n%s\n", RX_SEPARATOR);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n",
iph->version, (iph->ihl << 2), iph->tos, ntohs(iph->tot_len));
- mif_err("IP4:: I%u, Fragment Offset %u\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: ID %u, Fragment Offset %u\n",
ntohs(iph->id), frag_off);
- mif_err("IP4:: Flags {%s}\n", ip_flags);
- mif_err("IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ memset(flag_str, 0, sizeof(flag_str));
+ if (flags & IP_CE)
+ strcat(flag_str, "CE ");
+ if (flags & IP_DF)
+ strcat(flag_str, "DF ");
+ if (flags & IP_MF)
+ strcat(flag_str, "MF ");
+ eol = strlen(flag_str) - 1;
+ if (eol > 0)
+ flag_str[eol] = 0;
+ snprintf(line, LINE_BUFF_SIZE, "mif: IP4:: Flags {%s}\n", flag_str);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n",
iph->ttl, iph->protocol, ntohs(iph->check));
- mif_err("IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n",
ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15],
ip_pkt[16], ip_pkt[17], ip_pkt[18], ip_pkt[19]);
+ strcat(buff, line);
switch (iph->protocol) {
- case 6:
- /* TCP */
- print_tcp_header(pkt);
+ case 6: /* TCP */
+ strcat_tcp_header(buff, pkt);
break;
- case 17:
- /* UDP */
- print_udp_header(pkt);
+ case 17: /* UDP */
+ strcat_udp_header(buff, pkt);
break;
default:
break;
}
- mif_err("-----------------------------------------------------------\n");
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR);
+ strcat(buff, line);
+
+ pr_info("%s", buff);
+
+ kfree(buff);
+}
+
+bool is_dns_packet(u8 *ip_pkt)
+{
+ struct iphdr *iph = (struct iphdr *)ip_pkt;
+ struct udphdr *udph = (struct udphdr *)(ip_pkt + (iph->ihl << 2));
+
+ /* If this packet is not a UDP packet, return here. */
+ if (iph->protocol != 17)
+ return false;
+
+ if (ntohs(udph->dest) == 53 || ntohs(udph->source) == 53)
+ return true;
+ else
+ return false;
+}
+
+bool is_syn_packet(u8 *ip_pkt)
+{
+ struct iphdr *iph = (struct iphdr *)ip_pkt;
+ struct tcphdr *tcph = (struct tcphdr *)(ip_pkt + (iph->ihl << 2));
+
+ /* If this packet is not a TCP packet, return here. */
+ if (iph->protocol != 6)
+ return false;
+
+ if (tcph->syn || tcph->fin)
+ return true;
+ else
+ return false;
+}
+
+int memcmp16_to_io(const void __iomem *to, void *from, int size)
+{
+ u16 *d = (u16 *)to;
+ u16 *s = (u16 *)from;
+ int count = size >> 1;
+ int diff = 0;
+ int i;
+ u16 d1;
+ u16 s1;
+
+ for (i = 0; i < count; i++) {
+ d1 = ioread16(d);
+ s1 = *s;
+ if (d1 != s1) {
+ diff++;
+ mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1);
+ }
+ d++;
+ s++;
+ }
+
+ return diff;
+}
+
+int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size)
+{
+ u8 __iomem *dst;
+ int i;
+ u16 val;
+
+ mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size);
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16((i & 0xFFFF), dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != (i & 0xFFFF)) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n",
+ dp_name, i, val, (i & 0xFFFF));
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0x00FF, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0x00FF) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0x0FF0, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0x0FF0) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0xFF00, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0xFF00) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ mif_info("%s: PASS!!!\n", dp_name);
+ return 0;
}
diff --git a/drivers/misc/modem_if/modem_utils.h b/drivers/misc/modem_if/modem_utils.h
index 60e4820..7225ec2 100644
--- a/drivers/misc/modem_if/modem_utils.h
+++ b/drivers/misc/modem_if/modem_utils.h
@@ -19,14 +19,110 @@
#define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type))
+#define MAX_MIF_BUFF_SIZE 0x80000 /* 512kb */
+#define MAX_MIF_SEPA_SIZE 32
+#define MIF_SEPARATOR "IPC_LOGGER(VER1.1)"
+#define MIF_SEPARATOR_DPRAM "DPRAM_LOGGER(VER1.1)"
+#define MAX_IPC_SKB_SIZE 4096
+#define MAX_LOG_SIZE 64
+
+#define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE)
+#define MIF_ID_SIZE sizeof(enum mif_log_id)
+
+#define MAX_IPC_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long) - sizeof(size_t))
+#define MAX_IRQ_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long) - sizeof(struct mif_irq_map))
+#define MAX_COM_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long))
+#define MAX_TIM_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long) - sizeof(struct timespec))
+
+enum mif_log_id {
+ MIF_IPC_RL2AP = 1,
+ MIF_IPC_AP2CP,
+ MIF_IPC_CP2AP,
+ MIF_IPC_AP2RL,
+ MIF_IRQ,
+ MIF_COM,
+ MIF_TIME
+};
+
+struct mif_irq_map {
+ u16 magic;
+ u16 access;
+
+ u16 fmt_tx_in;
+ u16 fmt_tx_out;
+ u16 fmt_rx_in;
+ u16 fmt_rx_out;
+
+ u16 raw_tx_in;
+ u16 raw_tx_out;
+ u16 raw_rx_in;
+ u16 raw_rx_out;
+
+ u16 cp2ap;
+};
+
+struct mif_ipc_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ size_t len;
+ char buff[MAX_IPC_LOG_SIZE];
+};
+
+struct mif_irq_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ struct mif_irq_map map;
+ char buff[MAX_IRQ_LOG_SIZE];
+};
+
+struct mif_common_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ char buff[MAX_COM_LOG_SIZE];
+};
+
+struct mif_time_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ struct timespec epoch;
+ char buff[MAX_TIM_LOG_SIZE];
+};
+
+int mif_dump_dpram(struct io_device *);
+int mif_dump_log(struct modem_shared *, struct io_device *);
+
+#define mif_irq_log(msd, map, data, len) \
+ _mif_irq_log(MIF_IRQ, msd, map, data, len)
+#define mif_com_log(msd, format, ...) \
+ _mif_com_log(MIF_COM, msd, pr_fmt(format), ##__VA_ARGS__)
+#define mif_time_log(msd, epoch, data, len) \
+ _mif_time_log(MIF_TIME, msd, epoch, data, len)
+
+void mif_ipc_log(enum mif_log_id,
+ struct modem_shared *, const char *, size_t);
+void _mif_irq_log(enum mif_log_id,
+ struct modem_shared *, struct mif_irq_map, const char *, size_t);
+void _mif_com_log(enum mif_log_id,
+ struct modem_shared *, const char *, ...);
+void _mif_time_log(enum mif_log_id,
+ struct modem_shared *, struct timespec, const char *, size_t);
+
/** find_linkdev - find a link device
- * @commons: struct mif_common
+ * @msd: struct modem_shared *
*/
-static inline struct link_device *find_linkdev(struct mif_common *commons,
+static inline struct link_device *find_linkdev(struct modem_shared *msd,
enum modem_link link_type)
{
struct link_device *ld;
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (ld->link_type == link_type)
return ld;
}
@@ -44,6 +140,9 @@ static inline unsigned int countbits(unsigned int n)
return i;
}
+/* print IPC message as hex string with UTC time */
+int pr_ipc(const char *str, const char *data, size_t len);
+
/* print buffer as hex string */
int pr_buffer(const char *tag, const char *data, size_t data_len,
size_t max_len);
@@ -63,41 +162,43 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len);
/* get iod from tree functions */
-struct io_device *get_iod_with_format(struct mif_common *commons,
+struct io_device *get_iod_with_format(struct modem_shared *msd,
enum dev_format format);
-struct io_device *get_iod_with_channel(struct mif_common *commons,
+struct io_device *get_iod_with_channel(struct modem_shared *msd,
unsigned channel);
static inline struct io_device *link_get_iod_with_format(
struct link_device *ld, enum dev_format format)
{
- struct io_device *iod = get_iod_with_format(&ld->mc->commons, format);
+ struct io_device *iod = get_iod_with_format(ld->msd, format);
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
}
static inline struct io_device *link_get_iod_with_channel(
struct link_device *ld, unsigned channel)
{
- struct io_device *iod = get_iod_with_channel(&ld->mc->commons, channel);
+ struct io_device *iod = get_iod_with_channel(ld->msd, channel);
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
}
/* insert iod to tree functions */
-struct io_device *insert_iod_with_format(struct mif_common *commons,
+struct io_device *insert_iod_with_format(struct modem_shared *msd,
enum dev_format format, struct io_device *iod);
-struct io_device *insert_iod_with_channel(struct mif_common *commons,
+struct io_device *insert_iod_with_channel(struct modem_shared *msd,
unsigned channel, struct io_device *iod);
/* iodev for each */
typedef void (*action_fn)(struct io_device *iod, void *args);
-void iodevs_for_each(struct mif_common *commons, action_fn action, void *args);
+void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args);
/* netif wake/stop queue of iod */
void iodev_netif_wake(struct io_device *iod, void *args);
void iodev_netif_stop(struct io_device *iod, void *args);
/* change tx_link of raw devices */
-void rawdevs_set_tx_link(struct mif_common *commons, enum modem_link link_type);
+void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type);
+
+__be32 ipv4str_to_be32(const char *ipv4str, size_t count);
void mif_add_timer(struct timer_list *timer, unsigned long expire,
void (*function)(unsigned long), unsigned long data);
@@ -181,6 +282,23 @@ void print_sipc5_link_fmt_frame(const u8 *psrc);
-------------------------------------------------------------------------*/
#define UDP_HDR_SIZE 8
-void print_ip4_packet(u8 *ip_pkt);
+void print_ip4_packet(u8 *ip_pkt, bool tx);
+bool is_dns_packet(u8 *ip_pkt);
+bool is_syn_packet(u8 *ip_pkt);
+
+int memcmp16_to_io(const void __iomem *to, void *from, int size);
+int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size);
+
+static const inline char *get_dev_name(int dev)
+{
+ if (dev == IPC_FMT)
+ return "FMT";
+ else if (dev == IPC_RAW)
+ return "RAW";
+ else if (dev == IPC_RFS)
+ return "RFS";
+ else
+ return "NONE";
+}
#endif/*__MODEM_UTILS_H__*/
diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h
index 596abd1..b5ec61b 100644
--- a/drivers/misc/modem_if/modem_variation.h
+++ b/drivers/misc/modem_if/modem_variation.h
@@ -73,6 +73,18 @@ DECLARE_MODEM_INIT(mdm6600);
DECLARE_MODEM_INIT_DUMMY(mdm6600)
#endif
+#ifdef CONFIG_GSM_MODEM_ESC6270
+DECLARE_MODEM_INIT(esc6270);
+#else
+DECLARE_MODEM_INIT_DUMMY(esc6270)
+#endif
+
+#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803
+DECLARE_MODEM_INIT(sprd8803);
+#else
+DECLARE_MODEM_INIT_DUMMY(sprd8803)
+#endif
+
/* link device support */
DECLARE_LINK_INIT_DUMMY(undefined)
@@ -88,6 +100,12 @@ DECLARE_LINK_INIT(dpram);
DECLARE_LINK_INIT_DUMMY(dpram)
#endif
+#ifdef CONFIG_LINK_DEVICE_PLD
+DECLARE_LINK_INIT(pld);
+#else
+DECLARE_LINK_INIT_DUMMY(pld)
+#endif
+
#ifdef CONFIG_LINK_DEVICE_SPI
DECLARE_LINK_INIT(spi);
#else
@@ -120,6 +138,8 @@ static modem_init_call modem_init_func[] = {
MODEM_INIT_CALL(cbp72),
MODEM_INIT_CALL(cmc221),
MODEM_INIT_CALL(mdm6600),
+ MODEM_INIT_CALL(esc6270),
+ MODEM_INIT_CALL(sprd8803),
MODEM_INIT_CALL(dummy),
};
@@ -132,6 +152,7 @@ static link_init_call link_init_func[] = {
LINK_INIT_CALL(usb),
LINK_INIT_CALL(hsic),
LINK_INIT_CALL(c2c),
+ LINK_INIT_CALL(pld),
};
static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata)
diff --git a/drivers/misc/modem_if/sipc4_io_device.c b/drivers/misc/modem_if/sipc4_io_device.c
index 94cd85b..28f95f7 100644
--- a/drivers/misc/modem_if/sipc4_io_device.c
+++ b/drivers/misc/modem_if/sipc4_io_device.c
@@ -85,6 +85,35 @@ static ssize_t store_waketime(struct device *dev,
static struct device_attribute attr_waketime =
__ATTR(waketime, S_IRUGO | S_IWUSR, show_waketime, store_waketime);
+static ssize_t show_loopback(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr;
+ char *p = buf;
+
+ p += sprintf(buf, "%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
+
+ return p - buf;
+}
+
+static ssize_t store_loopback(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+
+ msd->loopback_ipaddr = ipv4str_to_be32(buf, count);
+
+ return count;
+}
+
+static struct device_attribute attr_loopback =
+ __ATTR(loopback, S_IRUGO | S_IWUSR, show_loopback, store_loopback);
+
static int get_header_size(struct io_device *iod)
{
switch (iod->format) {
@@ -300,7 +329,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld,
* make skb for header info first
*/
if (iod->format == IPC_RFS && !hdr->frag_len) {
- skb = rx_alloc_skb(head_size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(head_size, iod, ld);
if (unlikely(!skb))
return -ENOMEM;
memcpy(skb_put(skb, head_size), hdr->hdr, head_size);
@@ -311,17 +340,11 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld,
* MAX_RXDATA_SIZE, this packet will split to
* multiple packets
*/
- if (iod->use_handover)
- alloc_size += sizeof(struct ethhdr);
-
- skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(alloc_size, iod, ld);
if (unlikely(!skb)) {
fragdata(iod, ld)->realloc_offset = continue_len;
return -ENOMEM;
}
-
- if (iod->use_handover)
- skb_reserve(skb, sizeof(struct ethhdr));
fragdata(iod, ld)->skb_recv = skb;
}
@@ -357,7 +380,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld,
alloc_size = min(rest_len, MAX_RXDATA_SIZE);
- skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(alloc_size, iod, ld);
if (unlikely(!skb)) {
fragdata(iod, ld)->realloc_offset = done_len;
return -ENOMEM;
@@ -399,8 +422,7 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb)
return 0;
} else {
struct fmt_hdr *fh = NULL;
- skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC,
- iod, ld);
+ skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, iod, ld);
if (!skb) {
mif_err("<%d> alloc_skb fail\n",
__LINE__);
@@ -482,8 +504,7 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb)
return 0;
} else {
struct fmt_hdr *fh = NULL;
- skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC,
- real_iod, ld);
+ skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, real_iod, ld);
if (!skb) {
mif_err("alloc_skb fail\n");
return -ENOMEM;
@@ -631,6 +652,9 @@ static int rx_multipdp(struct sk_buff *skb)
struct io_device *real_iod = NULL;
ch = raw_header->channel;
+ if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr)
+ ch = RMNET0_CH_ID;
+
real_iod = link_get_iod_with_channel(ld, 0x20 | ch);
if (!real_iod) {
mif_err("wrong channel %d\n", ch);
@@ -828,7 +852,7 @@ static int rx_rfs_packet(struct io_device *iod, struct link_device *ld,
}
}
- skb = rx_alloc_skb(size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(size, iod, ld);
if (unlikely(!skb)) {
mif_err("alloc_skb fail\n");
return -ENOMEM;
@@ -850,6 +874,9 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
{
struct sk_buff *skb;
int err;
+ unsigned int alloc_size, rest_len;
+ char *cur;
+
/* check the iod(except IODEV_DUMMY) is open?
* if the iod is MULTIPDP, check this data on rx_iodev_skb_raw()
@@ -887,18 +914,39 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
case IPC_BOOT:
case IPC_RAMDUMP:
/* save packet to sk_buff */
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
- if (!skb) {
- mif_err("fail alloc skb (%d)\n", __LINE__);
- return -ENOMEM;
- }
+ skb = rx_alloc_skb(len, iod, ld);
+ if (skb) {
+ mif_debug("boot len : %d\n", len);
- mif_debug("boot len : %d\n", len);
+ memcpy(skb_put(skb, len), data, len);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ mif_debug("skb len : %d\n", skb->len);
- memcpy(skb_put(skb, len), data, len);
- skb_queue_tail(&iod->sk_rx_q, skb);
- mif_debug("skb len : %d\n", skb->len);
+ wake_up(&iod->wq);
+ return len;
+ }
+ /* 32KB page alloc fail case, alloc 3.5K a page.. */
+ mif_info("(%d)page fail, alloc fragment pages\n", len);
+
+ rest_len = len;
+ cur = (char *)data;
+ while (rest_len) {
+ alloc_size = min_t(unsigned int, MAX_RXDATA_SIZE,
+ rest_len);
+ skb = rx_alloc_skb(alloc_size, iod, ld);
+ if (!skb) {
+ mif_err("fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+ mif_debug("boot len : %d\n", alloc_size);
+ memcpy(skb_put(skb, alloc_size), cur, alloc_size);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ mif_debug("skb len : %d\n", skb->len);
+
+ rest_len -= alloc_size;
+ cur += alloc_size;
+ }
wake_up(&iod->wq);
return len;
@@ -950,7 +998,7 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online)
static int misc_open(struct inode *inode, struct file *filp)
{
struct io_device *iod = to_io_device(filp->private_data);
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
int ret;
filp->private_data = (void *)iod;
@@ -958,7 +1006,7 @@ static int misc_open(struct inode *inode, struct file *filp)
mif_err("iod = %s\n", iod->name);
atomic_inc(&iod->opened);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->init_comm) {
ret = ld->init_comm(ld, iod);
if (ret < 0) {
@@ -975,14 +1023,14 @@ static int misc_open(struct inode *inode, struct file *filp)
static int misc_release(struct inode *inode, struct file *filp)
{
struct io_device *iod = (struct io_device *)filp->private_data;
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
mif_err("iod = %s\n", iod->name);
atomic_dec(&iod->opened);
skb_queue_purge(&iod->sk_rx_q);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->terminate_comm)
ld->terminate_comm(ld, iod);
}
@@ -1002,6 +1050,9 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait)
} else if ((iod->mc->phone_state == STATE_CRASH_RESET) ||
(iod->mc->phone_state == STATE_CRASH_EXIT) ||
(iod->mc->phone_state == STATE_NV_REBUILDING) ||
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+ (iod->mc->phone_state == STATE_MODEM_SWITCH) ||
+#endif
(iod->mc->sim_state.changed)) {
if (iod->format == IPC_RAW) {
msleep(20);
@@ -1019,6 +1070,9 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
char cpinfo_buf[530] = "CP Crash ";
+ unsigned long size;
+ int ret;
+ char str[TASK_COMM_LEN];
mif_debug("cmd = 0x%x\n", cmd);
@@ -1044,8 +1098,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return iod->mc->ops.modem_boot_off(iod->mc);
/* TODO - will remove this command after ril updated */
- case IOCTL_MODEM_START:
- mif_debug("misc_ioctl : IOCTL_MODEM_START\n");
+ case IOCTL_MODEM_BOOT_DONE:
+ mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n");
return 0;
case IOCTL_MODEM_STATUS:
@@ -1056,10 +1110,13 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
(p_state == STATE_CRASH_EXIT)) {
mif_err("<%s> send err state : %d\n",
iod->name, p_state);
- } else if (iod->mc->sim_state.changed) {
+ } else if (iod->mc->sim_state.changed &&
+ !strcmp(get_task_comm(str, get_current()), "rild")) {
int s_state = iod->mc->sim_state.online ?
STATE_SIM_ATTACH : STATE_SIM_DETACH;
iod->mc->sim_state.changed = false;
+
+ mif_info("SIM states (%d) to %s\n", s_state, str);
return s_state;
} else if (p_state == STATE_NV_REBUILDING) {
mif_info("send nv rebuild state : %d\n",
@@ -1074,7 +1131,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0);
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
return 0;
case IOCTL_MODEM_PROTOCOL_RESUME:
@@ -1083,7 +1140,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_wake, 0);
+ iodevs_for_each(iod->msd, iodev_netif_wake, 0);
return 0;
case IOCTL_MODEM_DUMP_START:
@@ -1113,6 +1170,38 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n");
return iod->mc->ops.modem_dump_reset(iod->mc);
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+ case IOCTL_MODEM_SWITCH_MODEM:
+ mif_err("misc_ioctl : IOCTL_MODEM_SWITCH_MODEM\n");
+ iod->mc->phone_state = STATE_MODEM_SWITCH;
+ wake_up(&iod->wq);
+ return 0;
+#endif
+
+ case IOCTL_MIF_LOG_DUMP:
+ size = MAX_MIF_BUFF_SIZE;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+
+ mif_dump_log(iod->mc->msd, iod);
+ return 0;
+
+ case IOCTL_MIF_DPRAM_DUMP:
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+ if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) {
+ size = iod->mc->mdm_data->dpram_ctl->dp_size;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+ mif_dump_dpram(iod);
+ return 0;
+ }
+#endif
+ return -EINVAL;
+
default:
/* If you need to handle the ioctl for specific link device,
* then assign the link ioctl handler to ld->ioctl
@@ -1246,6 +1335,8 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
struct io_device *iod = (struct io_device *)filp->private_data;
struct sk_buff *skb = NULL;
int pktsize = 0;
+ unsigned int rest_len, copy_len;
+ char *cur = buf;
skb = skb_dequeue(&iod->sk_rx_q);
if (!skb) {
@@ -1254,43 +1345,67 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
}
mif_debug("<%s> skb->len : %d\n", iod->name, skb->len);
- if (skb->len > count) {
- /* BOOT device receviced rx data as serial stream, return data
- by User requested size */
- if (iod->format == IPC_BOOT) {
- mif_err("skb->len %d > count %d\n", skb->len,
- count);
- pr_skb("BOOT-wRX", skb);
- if (copy_to_user(buf, skb->data, count) != 0) {
+ if (iod->format == IPC_BOOT) {
+ pktsize = rest_len = count;
+ while (rest_len) {
+ if (skb->len > rest_len) {
+ /* BOOT device receviced rx data as serial
+ stream, return data by User requested size */
+ mif_err("skb->len %d > count %d\n", skb->len,
+ rest_len);
+ pr_skb("BOOT-wRX", skb);
+ if (copy_to_user(cur, skb->data, rest_len)
+ != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ cur += rest_len;
+ skb_pull(skb, rest_len);
+ if (skb->len) {
+ mif_info("queue-head, skb->len = %d\n",
+ skb->len);
+ skb_queue_head(&iod->sk_rx_q, skb);
+ }
+ mif_debug("return %u\n", rest_len);
+ return rest_len;
+ }
+
+ copy_len = min(rest_len, skb->len);
+ if (copy_to_user(cur, skb->data, copy_len) != 0) {
dev_kfree_skb_any(skb);
return -EFAULT;
}
- skb_pull(skb, count);
- if (skb->len) {
- mif_info("queue-head, skb->len = %d\n",
- skb->len);
- skb_queue_head(&iod->sk_rx_q, skb);
- }
+ cur += skb->len;
+ dev_kfree_skb_any(skb);
+ rest_len -= copy_len;
- return count;
- } else {
- mif_err("<%s> skb->len %d > count %d\n",
- iod->name, skb->len, count);
+ if (!rest_len)
+ break;
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ if (!skb) {
+ mif_err("<%s> %d / %d sk_rx_q\n", iod->name,
+ (count - rest_len), count);
+ return count - rest_len;
+ }
+ }
+ } else {
+ if (skb->len > count) {
+ mif_err("<%s> skb->len %d > count %d\n", iod->name,
+ skb->len, count);
dev_kfree_skb_any(skb);
return -EFAULT;
}
- }
+ pktsize = skb->len;
+ if (copy_to_user(buf, skb->data, pktsize) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ if (iod->format == IPC_FMT)
+ mif_debug("copied %d bytes to user\n", pktsize);
- pktsize = skb->len;
- if (copy_to_user(buf, skb->data, pktsize) != 0) {
dev_kfree_skb_any(skb);
- return -EFAULT;
}
- if (iod->format == IPC_FMT)
- mif_debug("copied %d bytes to user\n", pktsize);
-
- dev_kfree_skb_any(skb);
-
return pktsize;
}
@@ -1370,6 +1485,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
struct io_device *iod = vnet->iod;
struct link_device *ld = get_current_link(iod);
struct raw_hdr hd;
+ struct iphdr *ip_header = NULL;
/* When use `handover' with Network Bridge,
* user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this.
@@ -1383,9 +1499,17 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_pull(skb, sizeof(struct ethhdr));
}
+ /* ip loop-back */
+ ip_header = (struct iphdr *)skb->data;
+ if (iod->msd->loopback_ipaddr &&
+ ip_header->daddr == iod->msd->loopback_ipaddr) {
+ swap(ip_header->saddr, ip_header->daddr);
+ hd.channel = DATA_LOOPBACK_CHANNEL;
+ } else {
+ hd.channel = iod->id & 0x1F;
+ }
hd.len = skb->len + sizeof(hd);
hd.control = 0;
- hd.channel = iod->id & 0x1F;
headroom = sizeof(hd) + sizeof(hdlc_start);
tailroom = sizeof(hdlc_end);
@@ -1523,9 +1647,13 @@ int sipc4_init_io_device(struct io_device *iod)
ret = device_create_file(iod->miscdev.this_device,
&attr_waketime);
if (ret)
- mif_err("failed to create sysfs file : %s\n",
+ mif_err("failed to create `waketime' file : %s\n",
+ iod->name);
+ ret = device_create_file(iod->miscdev.this_device,
+ &attr_loopback);
+ if (ret)
+ mif_err("failed to create `loopback file' : %s\n",
iod->name);
-
break;
default:
diff --git a/drivers/misc/modem_if/sipc4_modem.c b/drivers/misc/modem_if/sipc4_modem.c
index 1f9ee7c..59e2de9 100644
--- a/drivers/misc/modem_if/sipc4_modem.c
+++ b/drivers/misc/modem_if/sipc4_modem.c
@@ -41,7 +41,38 @@
#define RFS_WAKE_TIME (HZ*3)
#define RAW_WAKE_TIME (HZ*6)
-static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
+static struct modem_shared *create_modem_shared_data(void)
+{
+ struct modem_shared *msd;
+
+ msd = kzalloc(sizeof(struct modem_shared), GFP_KERNEL);
+ if (!msd)
+ return NULL;
+
+ /* initialize link device list */
+ INIT_LIST_HEAD(&msd->link_dev_list);
+
+ /* initialize tree of io devices */
+ msd->iodevs_tree_chan = RB_ROOT;
+ msd->iodevs_tree_fmt = RB_ROOT;
+
+ msd->storage.cnt = 0;
+ msd->storage.addr =
+ kzalloc(MAX_MIF_BUFF_SIZE + MAX_MIF_SEPA_SIZE, GFP_KERNEL);
+ if (!msd->storage.addr) {
+ mif_err("IPC logger buff alloc failed!!\n");
+ return NULL;
+ }
+ memset(msd->storage.addr, 0, MAX_MIF_BUFF_SIZE);
+ memcpy(msd->storage.addr, MIF_SEPARATOR, MAX_MIF_SEPA_SIZE);
+ msd->storage.addr += MAX_MIF_SEPA_SIZE;
+ spin_lock_init(&msd->lock);
+
+ return msd;
+}
+
+static struct modem_ctl *create_modemctl_device(struct platform_device *pdev,
+ struct modem_shared *msd)
{
int ret = 0;
struct modem_data *pdata;
@@ -53,6 +84,7 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
if (!modemctl)
return NULL;
+ modemctl->msd = msd;
modemctl->dev = dev;
modemctl->phone_state = STATE_OFFLINE;
@@ -60,13 +92,6 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
modemctl->mdm_data = pdata;
modemctl->name = pdata->name;
- /* initialize link device list */
- INIT_LIST_HEAD(&modemctl->commons.link_dev_list);
-
- /* initialize tree of io devices */
- modemctl->commons.iodevs_tree_chan = RB_ROOT;
- modemctl->commons.iodevs_tree_fmt = RB_ROOT;
-
/* init modemctl device for getting modemctl operations */
ret = call_modem_init_func(modemctl, pdata);
if (ret) {
@@ -74,24 +99,14 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
return NULL;
}
- modemctl->use_mif_log = pdata->use_mif_log;
- if (pdata->use_mif_log) {
- mif_err("<%s> IPC logger can be used.\n",
- pdata->name);
- }
-
- ret = mif_init_log(modemctl);
- if (ret < 0) {
- kfree(modemctl);
- return NULL;
- }
-
mif_info("%s is created!!!\n", pdata->name);
+
return modemctl;
}
static struct io_device *create_io_device(struct modem_io_t *io_t,
- struct modem_ctl *modemctl, struct modem_data *pdata)
+ struct modem_shared *msd, struct modem_ctl *modemctl,
+ struct modem_data *pdata)
{
int ret = 0;
struct io_device *iod = NULL;
@@ -124,6 +139,16 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
mif_info("Bood device = %s\n", iod->name);
}
+ /* link between io device and modem shared */
+ iod->msd = msd;
+
+ /* add iod to rb_tree */
+ if (iod->format != IPC_RAW)
+ insert_iod_with_format(msd, iod->format, iod);
+
+ if (sipc4_is_not_reserved_channel(iod->id))
+ insert_iod_with_channel(msd, iod->id, iod);
+
/* register misc device or net device */
ret = sipc4_init_io_device(iod);
if (ret) {
@@ -136,20 +161,13 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
return iod;
}
-static int attach_devices(struct modem_ctl *mc, struct io_device *iod,
- enum modem_link tx_link)
+static int attach_devices(struct io_device *iod, enum modem_link tx_link)
{
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
- /* add iod to rb_tree */
- if (iod->format != IPC_RAW)
- insert_iod_with_format(&mc->commons, iod->format, iod);
-
- if (sipc4_is_not_reserved_channel(iod->id))
- insert_iod_with_channel(&mc->commons, iod->id, iod);
-
/* find link type for this io device */
- list_for_each_entry(ld, &mc->commons.link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld)) {
/* The count 1 bits of iod->link_types is count
* of link devices of this iod.
@@ -210,14 +228,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
{
int i;
struct modem_data *pdata = pdev->dev.platform_data;
- struct modem_ctl *modemctl;
+ struct modem_shared *msd = NULL;
+ struct modem_ctl *modemctl = NULL;
struct io_device *iod[pdata->num_iodevs];
struct link_device *ld;
mif_err("%s\n", pdev->name);
memset(iod, 0, sizeof(iod));
- modemctl = create_modemctl_device(pdev);
+ msd = create_modem_shared_data();
+ if (!msd) {
+ mif_err("msd == NULL\n");
+ goto err_free_modemctl;
+ }
+
+ modemctl = create_modemctl_device(pdev, msd);
if (!modemctl) {
mif_err("modemctl == NULL\n");
goto err_free_modemctl;
@@ -235,20 +260,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
mif_err("link created: %s\n", ld->name);
ld->link_type = i;
ld->mc = modemctl;
- list_add(&ld->list, &modemctl->commons.link_dev_list);
+ ld->msd = msd;
+ list_add(&ld->list, &msd->link_dev_list);
}
}
/* create io deivces and connect to modemctl device */
for (i = 0; i < pdata->num_iodevs; i++) {
- iod[i] = create_io_device(&pdata->iodevs[i], modemctl, pdata);
+ iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl,
+ pdata);
if (!iod[i]) {
mif_err("iod[%d] == NULL\n", i);
goto err_free_modemctl;
}
- attach_devices(modemctl, iod[i],
- pdata->iodevs[i].tx_link);
+ attach_devices(iod[i], pdata->iodevs[i].tx_link);
}
platform_set_drvdata(pdev, modemctl);
@@ -264,6 +290,9 @@ err_free_modemctl:
if (modemctl != NULL)
kfree(modemctl);
+ if (msd != NULL)
+ kfree(msd);
+
return -ENOMEM;
}
diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c
index 05578f4..a9932c1 100644
--- a/drivers/misc/modem_if/sipc5_io_device.c
+++ b/drivers/misc/modem_if/sipc5_io_device.c
@@ -69,48 +69,129 @@ static ssize_t store_waketime(struct device *dev,
static struct device_attribute attr_waketime =
__ATTR(waketime, S_IRUGO | S_IWUSR, show_waketime, store_waketime);
-static inline int sipc5_check_frame_cfg(u8 *buff, struct sipc5_frame_data *frm)
+static ssize_t show_loopback(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- u8 config = buff[0];
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr;
+ char *p = buf;
- if ((config & SIPC5_START_MASK) != SIPC5_START_MASK)
- return -EBADMSG;
+ p += sprintf(buf, "%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
+
+ return p - buf;
+}
+
+static ssize_t store_loopback(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+
+ msd->loopback_ipaddr = ipv4str_to_be32(buf, count);
+
+ return count;
+}
+
+static struct device_attribute attr_loopback =
+ __ATTR(loopback, S_IRUGO | S_IWUSR, show_loopback, store_loopback);
+
+static void iodev_showtxlink(struct io_device *iod, void *args)
+{
+ char **p = (char **)args;
+ struct link_device *ld = get_current_link(iod);
+
+ if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld))
+ *p += sprintf(*p, "%s: %s\n", iod->name, ld->name);
+}
+
+static ssize_t show_txlink(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ char *p = buf;
+
+ iodevs_for_each(msd, iodev_showtxlink, &p);
+
+ return p - buf;
+}
+
+static ssize_t store_txlink(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ /* don't change without gpio dynamic switching */
+ return -EINVAL;
+}
- frm->config = config;
- frm->hdr_len = SIPC5_MIN_HEADER_SIZE;
+static struct device_attribute attr_txlink =
+ __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink);
- if (config & SIPC5_PADDING_EXIST)
+/**
+ * rx_check_frame_cfg
+ * @cfg: configuration field of a link layer header
+ * @frm: pointer to the sipc5_frame_data buffer
+ *
+ * 1) Checks whether or not an extended field exists
+ * 2) Calculates the length of a link layer header
+ *
+ * Returns the size of a link layer header
+ *
+ * Must be invoked only when the configuration field of the link layer header
+ * is validated with sipc5_start_valid() function
+ */
+static int rx_check_frame_cfg(u8 cfg, struct sipc5_frame_data *frm)
+{
+ frm->config = cfg;
+
+ if (likely(cfg & SIPC5_PADDING_EXIST))
frm->padding = true;
- if (unlikely(config & SIPC5_EXT_FIELD_EXIST)) {
- frm->ext_fld = true;
- if (config & SIPC5_CTL_FIELD_EXIST) {
+ if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) {
+ if (cfg & SIPC5_CTL_FIELD_EXIST) {
frm->ctl_fld = true;
frm->hdr_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD;
} else {
frm->ext_len = true;
frm->hdr_len = SIPC5_HEADER_SIZE_WITH_EXT_LEN;
}
+ } else {
+ frm->hdr_len = SIPC5_MIN_HEADER_SIZE;
}
- return SIPC5_CONFIG_SIZE;
+ return frm->hdr_len;
}
-static inline void sipc5_build_rx_frame_data(struct link_device *ld,
+/**
+ * rx_build_meta_data
+ * @ld: pointer to the link device
+ * @frm: pointer to the sipc5_frame_data buffer
+ *
+ * Fills each field of sipc5_frame_data from a link layer header
+ * 1) Extracts the channel ID
+ * 2) Calculates the length of a link layer frame
+ * 3) Extracts a control field if exists
+ * 4) Calculates the length of an IPC message packet in the link layer frame
+ *
+ */
+static void rx_build_meta_data(struct link_device *ld,
struct sipc5_frame_data *frm)
{
u16 *sz16 = (u16 *)(frm->hdr + SIPC5_LEN_OFFSET);
u32 *sz32 = (u32 *)(frm->hdr + SIPC5_LEN_OFFSET);
frm->ch_id = frm->hdr[SIPC5_CH_ID_OFFSET];
- frm->len = *sz16;
- if (unlikely(frm->ext_fld)) {
- if (frm->ctl_fld)
- frm->control = frm->hdr[SIPC5_CTL_OFFSET];
- else
- frm->len = *sz32;
- }
+ if (unlikely(frm->ext_len))
+ frm->len = *sz32;
+ else
+ frm->len = *sz16;
+
+ if (unlikely(frm->ctl_fld))
+ frm->control = frm->hdr[SIPC5_CTL_OFFSET];
frm->data_len = frm->len - frm->hdr_len;
@@ -118,114 +199,83 @@ static inline void sipc5_build_rx_frame_data(struct link_device *ld,
ld->name, frm->ch_id, frm->len, frm->control, frm->data_len);
}
-static inline struct sk_buff *sipc5_prepare_rx_skb(struct io_device *iod,
- struct link_device *ld, unsigned len)
-{
- struct sk_buff *skb;
-
- if (iod->format == IPC_MULTI_RAW && iod->use_handover) {
- int alloc = len + sizeof(struct ethhdr);
- skb = rx_alloc_skb(alloc, GFP_ATOMIC, iod, ld);
- if (unlikely(!skb))
- return NULL;
- skb_reserve(skb, sizeof(struct ethhdr));
- } else {
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
- }
-
- return skb;
-}
-
-/* Check and store link layer header, then alloc an skb */
-static int sipc5_recv_header(struct io_device *iod, struct link_device *ld,
- u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+/**
+ * tx_build_link_header
+ * @frm: pointer to the sipc5_frame_data buffer
+ * @iod: pointer to the IO device
+ * @ld: pointer to the link device
+ * @count: length of the data to be transmitted
+ *
+ * Builds the meta data for an SIPC5 frame and the link layer header of it
+ * Returns the link layer header length for an SIPC5 frame or 0 for other frame
+ */
+static unsigned tx_build_link_header(struct sipc5_frame_data *frm,
+ struct io_device *iod, struct link_device *ld, ssize_t count)
{
- int len = 0;
-
- mif_debug("%s: size %d\n", ld->name, size);
+ u8 *buff = frm->hdr;
+ u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET);
+ u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET);
- if (likely(!frm->config)) {
- len = sipc5_check_frame_cfg(buff, frm);
- if (len < 0) {
- mif_info("%s: ERR! wrong start (0x%02x)\n",
- ld->name, buff[0]);
- return len;
- }
+ memset(frm, 0, sizeof(struct sipc5_frame_data));
- /* Copy the link layer header to the header buffer */
- len = min(frm->hdr_len, size);
- memcpy(frm->hdr, buff, len);
- } else {
- /* Copy the link layer header to the header buffer */
- len = min((frm->hdr_len - frm->hdr_rcvd), size);
- memcpy((frm->hdr + frm->hdr_rcvd), buff, len);
+ if (iod->format == IPC_CMD ||
+ iod->format == IPC_BOOT ||
+ iod->format == IPC_RAMDUMP) {
+ frm->len = count;
+ return 0;
}
- frm->hdr_rcvd += len;
+ frm->config = SIPC5_START_MASK;
- mif_debug("%s: FRM hdr.len:%d hdr.rcvd:%d\n",
- ld->name, frm->hdr_len, frm->hdr_rcvd);
+ if (iod->format == IPC_FMT && count > 2048) {
+ frm->ctl_fld = true;
+ frm->config |= SIPC5_EXT_FIELD_EXIST;
+ frm->config |= SIPC5_CTL_FIELD_EXIST;
+ }
- if (frm->hdr_rcvd >= frm->hdr_len) {
- struct sk_buff *skb;
- sipc5_build_rx_frame_data(ld, frm);
- skb = sipc5_prepare_rx_skb(iod, ld, frm->data_len);
- fragdata(iod, ld)->skb_recv = skb;
+ if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) {
+ frm->ext_len = true;
+ frm->config |= SIPC5_EXT_FIELD_EXIST;
}
- return len;
-}
+ if (ld->aligned)
+ frm->config |= SIPC5_PADDING_EXIST;
-/* copy data to skb */
-static int sipc5_recv_payload(struct io_device *iod, struct link_device *ld,
- u8 *buff, unsigned size, struct sipc5_frame_data *frm)
-{
- struct sk_buff *skb = fragdata(iod, ld)->skb_recv;
- unsigned rest = frm->data_len - frm->data_rcvd;
- unsigned len;
+ frm->ch_id = iod->id;
- /*
- ** rest == frm->data_len - frm->data_rcvd == tailroom of skb or mifb
- */
- rest = frm->data_len - frm->data_rcvd;
- mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n",
- ld->name, frm->data_len, frm->data_rcvd, rest, size);
+ frm->hdr_len = sipc5_get_hdr_len(frm->config);
+ frm->data_len = count;
+ frm->len = frm->hdr_len + frm->data_len;
- /* If there is no skb, data must be dropped. */
- len = min(rest, size);
- if (skb)
- memcpy(skb_put(skb, len), buff, len);
+ buff[SIPC5_CONFIG_OFFSET] = frm->config;
+ buff[SIPC5_CH_ID_OFFSET] = frm->ch_id;
- frm->data_rcvd += len;
+ if (unlikely(frm->ext_len))
+ *sz32 = (u32)frm->len;
+ else
+ *sz16 = (u16)frm->len;
- mif_debug("%s: FRM data.len:%d data.rcvd:%d\n",
- ld->name, frm->data_len, frm->data_rcvd);
+ if (unlikely(frm->ctl_fld))
+ buff[SIPC5_CTL_OFFSET] = frm->control;
- return len;
+ return frm->hdr_len;
}
-static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
+static int rx_fmt_frame(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod;
struct link_device *ld = skbpriv(skb)->ld;
- struct sk_buff_head *rxq;
+ struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sipc_fmt_hdr *fh;
struct sk_buff *rx_skb;
- unsigned id;
-
- rxq = &iod->sk_rx_q;
- if (!rxq) {
- mif_debug("%s: no sk_rx_q\n", iod->name);
- return -EINVAL;
- }
-
- id = frm->control & 0x7F;
+ u8 ctrl = skbpriv(skb)->control;
+ unsigned id = ctrl & 0x7F;
if (iod->skb[id] == NULL) {
/*
** There has been no multiple frame with this ID.
*/
- if ((frm->control & 0x80) == 0) {
+ if ((ctrl & 0x80) == 0) {
/*
** It is a single frame because the "more" bit is 0.
*/
@@ -252,7 +302,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
mif_debug("%s: start multi-frame (ID:%d len:%d)\n",
iod->name, id, fh->len);
- rx_skb = rx_alloc_skb(fh->len, GFP_ATOMIC, iod, ld);
+ rx_skb = rx_alloc_skb(fh->len, iod, ld);
if (!rx_skb) {
mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name);
return -ENOMEM;
@@ -269,7 +319,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
dev_kfree_skb_any(skb);
- if (frm->control & 0x80) {
+ if (ctrl & 0x80) {
/* The last frame has not arrived yet. */
mif_debug("%s: recv multi-frame (ID:%d rcvd:%d)\n",
iod->name, id, rx_skb->len);
@@ -295,7 +345,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
return 0;
}
-static int sipc5_recv_rfs(struct sk_buff *skb)
+static int rx_rfs_frame(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod;
struct sk_buff_head *rxq = &iod->sk_rx_q;
@@ -303,8 +353,7 @@ static int sipc5_recv_rfs(struct sk_buff *skb)
skb_queue_tail(rxq, skb);
if (unlikely(rxq->qlen > 2048)) {
struct sk_buff *victim;
- mif_debug("%s: ERR! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
+ mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
victim = skb_dequeue(rxq);
dev_kfree_skb_any(victim);
} else {
@@ -316,7 +365,42 @@ static int sipc5_recv_rfs(struct sk_buff *skb)
return 0;
}
-static int sipc5_recv_misc(struct sk_buff *skb)
+static int rx_loopback(struct sk_buff *skb)
+{
+ struct io_device *iod = skbpriv(skb)->iod;
+ struct link_device *ld = get_current_link(iod);
+ struct sipc5_frame_data frm;
+ unsigned headroom;
+ unsigned tailroom = 0;
+ int ret;
+
+ headroom = tx_build_link_header(&frm, iod, ld, skb->len);
+
+ if (ld->aligned)
+ tailroom = sipc5_calc_padding_size(headroom + skb->len);
+
+ /* We need not to expand skb in here. dev_alloc_skb (in rx_alloc_skb)
+ * already alloc 32bytes padding in headroom. 32bytes are enough.
+ */
+
+ /* store IPC link header to start of skb
+ * this is skb_push not skb_put. different with misc_write.
+ */
+ memcpy(skb_push(skb, headroom), frm.hdr, headroom);
+
+ /* store padding */
+ if (tailroom)
+ skb_put(skb, tailroom);
+
+ /* forward */
+ ret = ld->send(ld, iod, skb);
+ if (ret < 0)
+ mif_err("%s->%s: ld->send fail: %d\n", iod->name,
+ ld->name, ret);
+ return ret;
+}
+
+static int rx_raw_misc(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */
struct sk_buff_head *rxq = &iod->sk_rx_q;
@@ -324,8 +408,7 @@ static int sipc5_recv_misc(struct sk_buff *skb)
skb_queue_tail(rxq, skb);
if (unlikely(rxq->qlen > 2048)) {
struct sk_buff *victim;
- mif_debug("%s: ERR! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
+ mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
victim = skb_dequeue(rxq);
dev_kfree_skb_any(victim);
} else {
@@ -337,7 +420,7 @@ static int sipc5_recv_misc(struct sk_buff *skb)
return 0;
}
-static int sipc5_recv_pdp(struct sk_buff *skb)
+static int rx_multi_pdp(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */
struct net_device *ndev;
@@ -386,69 +469,24 @@ static int sipc5_recv_pdp(struct sk_buff *skb)
return ret;
}
-/** rx_iodev_work - rx workqueue for raw data
- *
- * @work: workqueue
- *
- * If you throw packets to Network layer directly in interrupt context,
- * sometimes, you'll meet backlog buffer full of Network layer.
- * Applications need some time to get packets from Network layer.
- * And, we need to retry logic when NET_RX_DROP occured. this work ensure
- * retry when netif_rx failed.
- */
-static void rx_iodev_work(struct work_struct *work)
-{
- int ret = 0;
- struct sk_buff *skb = NULL;
- struct io_device *iod = container_of(work, struct io_device,
- rx_work.work);
-
- while ((skb = skb_dequeue(&iod->sk_rx_q)) != NULL) {
- ret = sipc5_recv_pdp(skb);
- if (ret < 0) {
- mif_err("%s: sipc5_recv_pdp fail (err %d)",
- iod->name, ret);
- dev_kfree_skb_any(skb);
- } else if (ret == NET_RX_DROP) {
- mif_err("%s: ret == NET_RX_DROP. retry later.\n",
- iod->name);
- schedule_delayed_work(&iod->rx_work,
- msecs_to_jiffies(100));
- return;
- }
- }
-}
-
-static int rx_multipdp(struct sk_buff *skb)
-{
- /* in sipc5, this iod == real_iod. not MULTIPDP's iod */
- struct io_device *iod = skbpriv(skb)->iod;
-
- if (iod->io_typ != IODEV_NET) {
- mif_info("%s: ERR! wrong io_type %d\n", iod->name, iod->io_typ);
- return -EINVAL;
- }
-
- skb_queue_tail(&iod->sk_rx_q, skb);
- mif_debug("%s: sk_rx_qlen %d\n", iod->name, iod->sk_rx_q.qlen);
-
- schedule_delayed_work(&iod->rx_work, 0);
- return 0;
-}
-
-static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb,
- struct sipc5_frame_data *frm)
+static int rx_demux(struct link_device *ld, struct sk_buff *skb)
{
struct io_device *iod = NULL;
+ char *link = ld->name;
+ u8 ch = skbpriv(skb)->ch_id;
- if (unlikely(frm->ch_id >= SIPC5_CH_ID_MAX || frm->ch_id == 0)) {
- mif_info("%s: ERR! invalid channel %d\n", ld->name, frm->ch_id);
+ if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) {
+ mif_info("%s: ERR! invalid ch# %d\n", link, ch);
return -ENODEV;
}
- iod = link_get_iod_with_channel(ld, frm->ch_id);
+ /* IP loopback */
+ if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr)
+ ch = RMNET0_CH_ID;
+
+ iod = link_get_iod_with_channel(ld, ch);
if (unlikely(!iod)) {
- mif_info("%s: ERR! no iod for ch# %d\n", ld->name, frm->ch_id);
+ mif_info("%s: ERR! no iod for ch# %d\n", link, ch);
return -ENODEV;
}
@@ -456,54 +494,127 @@ static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb,
skbpriv(skb)->iod = iod;
skbpriv(skb)->real_iod = iod;
+ /* don't care about CP2AP_LOOPBACK_CHANNEL is opened */
+ if (unlikely(iod->id == CP2AP_LOOPBACK_CHANNEL))
+ return rx_loopback(skb);
+
if (atomic_read(&iod->opened) <= 0) {
- mif_info("%s: ERR! %s is not opened\n", ld->name, iod->name);
+ mif_info("%s: ERR! %s is not opened\n", link, iod->name);
return -ENODEV;
}
- if (frm->ch_id >= SIPC5_CH_ID_RFS_0)
- return sipc5_recv_rfs(skb);
- else if (frm->ch_id >= SIPC5_CH_ID_FMT_0)
- return sipc5_recv_fmt(skb, frm);
+ if (ch >= SIPC5_CH_ID_RFS_0)
+ return rx_rfs_frame(skb);
+ else if (ch >= SIPC5_CH_ID_FMT_0)
+ return rx_fmt_frame(skb);
else if (iod->io_typ == IODEV_MISC)
- return sipc5_recv_misc(skb);
+ return rx_raw_misc(skb);
else
- return rx_multipdp(skb);
+ return rx_multi_pdp(skb);
+}
+
+/* Check and store link layer header, then alloc an skb */
+static int rx_header_from_serial(struct io_device *iod, struct link_device *ld,
+ u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+{
+ char *link = ld->name;
+ struct sk_buff *skb;
+ int len;
+ u8 cfg = buff[0];
+
+ mif_debug("%s: size %d\n", link, size);
+
+ if (!frm->config) {
+ if (unlikely(!sipc5_start_valid(cfg))) {
+ mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg);
+ return -EBADMSG;
+ }
+ rx_check_frame_cfg(cfg, frm);
+
+ /* Copy the link layer header to the header buffer */
+ len = min(frm->hdr_len, size);
+ memcpy(frm->hdr, buff, len);
+ } else {
+ /* Copy the link layer header to the header buffer */
+ len = min((frm->hdr_len - frm->hdr_rcvd), size);
+ memcpy((frm->hdr + frm->hdr_rcvd), buff, len);
+ }
+
+ frm->hdr_rcvd += len;
+
+ mif_debug("%s: FRM hdr_len:%d, hdr_rcvd:%d\n",
+ link, frm->hdr_len, frm->hdr_rcvd);
+
+ if (frm->hdr_rcvd >= frm->hdr_len) {
+ rx_build_meta_data(ld, frm);
+ skb = rx_alloc_skb(frm->data_len, iod, ld);
+ fragdata(iod, ld)->skb_recv = skb;
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
+ }
+
+ return len;
+}
+
+/* copy data to skb */
+static int rx_payload_from_serial(struct io_device *iod, struct link_device *ld,
+ u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+{
+ struct sk_buff *skb = fragdata(iod, ld)->skb_recv;
+ char *link = ld->name;
+ unsigned rest = frm->data_len - frm->data_rcvd;
+ unsigned len;
+
+ /* rest == (frm->data_len - frm->data_rcvd) == tailroom of skb */
+ rest = frm->data_len - frm->data_rcvd;
+ mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n",
+ link, frm->data_len, frm->data_rcvd, rest, size);
+
+ /* If there is no skb, data must be dropped. */
+ len = min(rest, size);
+ if (skb)
+ memcpy(skb_put(skb, len), buff, len);
+
+ frm->data_rcvd += len;
+
+ mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n",
+ link, frm->data_len, frm->data_rcvd);
+
+ return len;
}
-static int sipc5_recv_ipc_from_serial(struct io_device *iod,
- struct link_device *ld, const char *data, unsigned size)
+static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld,
+ const char *data, unsigned size)
{
struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
struct sk_buff *skb;
+ char *link = ld->name;
u8 *buff = (u8 *)data;
int rest = (int)size;
int err = 0;
int done = 0;
- mif_debug("%s: size = %d\n", ld->name, size);
+ mif_debug("%s: size = %d\n", link, size);
if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) {
/*
- There may be an skb or mifb (fragdata(iod, ld)->skb_recv) that
- is waiting for more IPC frame. In this case, sipc5_recv_header
- function must be skipped.
+ ** There is an skb that is waiting for more SIPC5 data.
+ ** In this case, rx_header_from_serial() must be skipped.
*/
mif_debug("%s: FRM data.len:%d data.rcvd:%d -> recv_data\n",
- ld->name, frm->data_len, frm->data_rcvd);
+ link, frm->data_len, frm->data_rcvd);
goto recv_data;
}
next_frame:
/* Receive and analyze header, then prepare an akb */
- err = done = sipc5_recv_header(iod, ld, buff, rest, frm);
+ err = done = rx_header_from_serial(iod, ld, buff, rest, frm);
if (err < 0)
goto err_exit;
buff += done;
rest -= done;
- mif_debug("%s: sipc5_recv_header() -> done:%d rest:%d\n",
- ld->name, done, rest);
+ mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest);
if (rest < 0)
goto err_range;
@@ -513,15 +624,13 @@ next_frame:
recv_data:
err = 0;
- mif_debug("%s: done:%d rest:%d -> recv_payload()\n",
- ld->name, done, rest);
+ mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest);
- done = sipc5_recv_payload(iod, ld, buff, rest, frm);
+ done = rx_payload_from_serial(iod, ld, buff, rest, frm);
buff += done;
rest -= done;
- mif_debug("%s: recv_payload() -> done:%d rest:%d\n",
- ld->name, done, rest);
+ mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest);
if (rest == 0 && frm->data_rcvd < frm->data_len) {
/*
@@ -538,14 +647,14 @@ recv_data:
done = sipc5_calc_padding_size(frm->len);
if (done > rest) {
mif_info("%s: ERR! padding %d > rest %d\n",
- ld->name, done, rest);
+ link, done, rest);
goto err_exit;
}
buff += done;
rest -= done;
- mif_debug("%s: padding:%d -> rest:%d\n", ld->name, done, rest);
+ mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest);
if (rest < 0)
goto err_range;
@@ -554,12 +663,12 @@ recv_data:
skb = fragdata(iod, ld)->skb_recv;
if (likely(skb)) {
- mif_debug("%s: len %d -> recv_demux()\n", ld->name, skb->len);
- err = sipc5_recv_demux(ld, skb, frm);
+ mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len);
+ err = rx_demux(ld, skb);
if (err < 0)
dev_kfree_skb_any(skb);
} else {
- mif_debug("%s: len:%d -> drop\n", ld->name, skb->len);
+ mif_debug("%s: len:%d -> drop\n", link, skb->len);
}
/* initialize the skb_recv and the frame_data buffer */
@@ -578,44 +687,61 @@ err_exit:
dev_kfree_skb_any(fragdata(iod, ld)->skb_recv);
memset(frm, 0, sizeof(struct sipc5_frame_data));
fragdata(iod, ld)->skb_recv = NULL;
- mif_info("%s: ERR! clear frag\n", ld->name);
+ mif_info("%s: ERR! clear frag\n", link);
}
return err;
err_range:
- mif_info("%s: ERR! size:%d vs. rest:%d\n", ld->name, size, rest);
+ mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest);
return size;
}
-/* Check and store link layer header */
-static int sipc5_recv_header_from_dpram(struct link_device *ld, u8 *buff,
+/**
+ * rx_header_from_mem
+ * @ld: pointer to the link device
+ * @buff: pointer to the frame
+ * @rest: size of the frame
+ * @frm: pointer to the sipc5_frame_data buffer
+ *
+ * 1) Verifies a link layer header configuration of a frame
+ * 2) Stores the link layer header to the header buffer
+ * 3) Builds and stores the meta data of the frame into a meta data buffer
+ * 4) Verifies the length of the frame
+ *
+ * Returns SIPC5 header length
+ */
+static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest,
struct sipc5_frame_data *frm)
{
- int len = sipc5_check_frame_cfg(buff, frm);
+ char *link = ld->name;
+ u8 cfg = buff[0];
- if (len < 0) {
- mif_info("%s: ERR! wrong start 0x%02x\n",
- ld->name, buff[0]);
- return len;
- } else if (len > SIPC5_MAX_HEADER_SIZE) {
- mif_info("%s: ERR! wrong header length %d\n",
- ld->name, len);
+ /* Verify link layer header configuration */
+ if (unlikely(!sipc5_start_valid(cfg))) {
+ mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg);
return -EBADMSG;
}
+ rx_check_frame_cfg(cfg, frm);
- /* Copy the link layer header to the header buffer */
- len = frm->hdr_len;
- memcpy(frm->hdr, buff, len);
+ /* Store the link layer header to the header buffer */
+ memcpy(frm->hdr, buff, frm->hdr_len);
frm->hdr_rcvd = frm->hdr_len;
- sipc5_build_rx_frame_data(ld, frm);
+ /* Build and store the meta data of this frame */
+ rx_build_meta_data(ld, frm);
- return len;
+ /* Verify frame length */
+ if (unlikely(frm->len > rest)) {
+ mif_info("%s: ERR! frame length %d > rest %d\n",
+ link, frm->len, rest);
+ return -EBADMSG;
+ }
+
+ return frm->hdr_rcvd;
}
/* copy data to skb */
-static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff,
- unsigned len)
+static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len)
{
/* If there is no skb, data must be dropped. */
if (skb)
@@ -623,60 +749,81 @@ static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff,
return len;
}
-static int sipc5_recv_ipc_from_dpram(struct io_device *iod,
- struct link_device *ld, const char *data, unsigned size)
+static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld,
+ const char *data, unsigned size)
{
struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
struct sk_buff *skb;
+ char *link = ld->name;
u8 *buff = (u8 *)data;
int rest = (int)size;
int len;
int done;
- mif_debug("%s: size = %d\n", ld->name, size);
+ mif_debug("%s: size = %d\n", link, size);
while (rest > 0) {
/* Initialize the frame data buffer */
memset(frm, 0, sizeof(struct sipc5_frame_data));
+ skb = NULL;
/* Receive and analyze link layer header */
- done = sipc5_recv_header_from_dpram(ld, buff, frm);
+ done = rx_header_from_mem(ld, buff, rest, frm);
if (unlikely(done < 0))
return -EBADMSG;
+ /* Verify rest size */
rest -= done;
- if (rest < 0)
+ if (rest < 0) {
+ mif_info("%s: ERR! rx_header -> rest %d\n", link, rest);
return -ERANGE;
+ }
+
+ /* Move buff pointer to the payload */
buff += done;
/* Prepare an akb */
len = frm->data_len;
- skb = sipc5_prepare_rx_skb(iod, ld, len);
+ skb = rx_alloc_skb(len, iod, ld);
+
+ /* Store channel ID and control fields to the CB of the skb */
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
/* Receive payload */
- mif_debug("%s: done:%d rest:%d len:%d -> recv_payload()\n",
- ld->name, done, rest, len);
- done = sipc5_recv_payload_from_dpram(skb, buff, len);
+ mif_debug("%s: done:%d rest:%d len:%d -> rx_payload()\n",
+ link, done, rest, len);
+ done = rx_payload_from_mem(skb, buff, len);
rest -= done;
- if (rest < 0)
+ if (rest < 0) {
+ mif_info("%s: ERR! rx_payload() -> rest %d\n",
+ link, rest);
+ if (skb)
+ dev_kfree_skb_any(skb);
return -ERANGE;
+ }
buff += done;
/* A padding size is applied to access the next IPC frame. */
if (frm->padding) {
done = sipc5_calc_padding_size(frm->len);
- rest -= done;
- if (rest < 0)
+ if (done > rest) {
+ mif_info("%s: ERR! padding %d > rest %d\n",
+ link, done, rest);
+ if (skb)
+ dev_kfree_skb_any(skb);
return -ERANGE;
+ }
buff += done;
+ rest -= done;
}
if (likely(skb)) {
- mif_debug("%s: len:%d -> demux\n", ld->name, skb->len);
- if (sipc5_recv_demux(ld, skb, frm) < 0)
+ mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len);
+ if (rx_demux(ld, skb) < 0)
dev_kfree_skb_any(skb);
} else {
- mif_debug("%s: len:%d -> drop\n", ld->name, skb->len);
+ mif_debug("%s: len:%d -> drop\n", link, skb->len);
}
}
@@ -689,26 +836,16 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
{
struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sk_buff *skb;
+ char *link = ld->name;
int err;
- if (!ld) {
- mif_info("ERR: !ld\n");
- return -EINVAL;
- }
-
- if (!iod) {
- mif_info("%s: ERR! !iod\n", ld->name);
- return -EINVAL;
- }
-
if (!data) {
- mif_info("%s: ERR! !data\n", ld->name);
+ mif_info("%s: ERR! !data\n", link);
return -EINVAL;
}
if (len <= 0) {
- mif_info("%s: ERR! len = %d <= 0\n",
- ld->name, len);
+ mif_info("%s: ERR! len %d <= 0\n", link, len);
return -EINVAL;
}
@@ -721,13 +858,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
wake_lock_timeout(&iod->wakelock, iod->waketime);
if (ld->link_type == LINKDEV_DPRAM && ld->aligned)
- err = sipc5_recv_ipc_from_dpram(iod, ld, data, len);
+ err = rx_frame_from_mem(iod, ld, data, len);
else
- err = sipc5_recv_ipc_from_serial(iod, ld, data, len);
+ err = rx_frame_from_serial(iod, ld, data, len);
if (err < 0)
- mif_info("%s: ERR! sipc5_recv_ipc_from_link fail "
- "(err %d)\n", ld->name, err);
+ mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n",
+ link, err);
return err;
@@ -735,13 +872,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
case IPC_BOOT:
case IPC_RAMDUMP:
/* save packet to sk_buff */
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(len, iod, ld);
if (!skb) {
- mif_info("%s: ERR! rx_alloc_skb fail\n", ld->name);
+ mif_info("%s: ERR! rx_alloc_skb fail\n", link);
return -ENOMEM;
}
- mif_debug("%s: len:%d -> iod:%s\n", ld->name, len, iod->name);
+ mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name);
memcpy(skb_put(skb, len), data, len);
skb_queue_tail(rxq, skb);
@@ -757,68 +894,84 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
return len;
default:
- mif_info("%s: ERR! unknown format %d\n", ld->name, iod->format);
+ mif_info("%s: ERR! unknown format %d\n", link, iod->format);
return -EINVAL;
}
}
-static unsigned sipc5_build_tx_link_header(struct sipc5_frame_data *frm,
- struct io_device *iod, struct link_device *ld, ssize_t count)
+static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld,
+ struct sk_buff *skb)
{
- u8 *buff = frm->hdr;
- u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET);
- u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET);
+ struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
+ u8 cfg = skb->data[0];
+ /* Initialize the frame data buffer */
memset(frm, 0, sizeof(struct sipc5_frame_data));
- if (iod->format == IPC_CMD ||
- iod->format == IPC_BOOT ||
- iod->format == IPC_RAMDUMP) {
- frm->len = count;
- return 0;
- }
+ /*
+ ** The start of a link layer header has already been checked in the
+ ** link device.
+ */
- frm->config = SIPC5_START_MASK;
+ /* Analyze the configuration of the link layer header */
+ rx_check_frame_cfg(cfg, frm);
- if (iod->format == IPC_FMT && count > 2048) {
- frm->ext_fld = true;
- frm->ctl_fld = true;
+ /* Store the link layer header to the header buffer */
+ memcpy(frm->hdr, skb->data, frm->hdr_len);
+ frm->hdr_rcvd = frm->hdr_len;
- frm->config |= SIPC5_EXT_FIELD_EXIST;
- frm->config |= SIPC5_CTL_FIELD_EXIST;
- }
+ /* Build and store the meta data of this frame */
+ rx_build_meta_data(ld, frm);
- if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) {
- frm->ext_fld = true;
- frm->ext_len = true;
+ /*
+ ** The length of the frame has already been checked in the link device.
+ */
- frm->config |= SIPC5_EXT_FIELD_EXIST;
- }
+ /* Trim the link layer header off the frame */
+ skb_pull(skb, frm->hdr_len);
- if (ld->aligned)
- frm->config |= SIPC5_PADDING_EXIST;
+ /* Store channel ID and control fields to the CB of the skb */
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
- frm->ch_id = iod->id;
+ /* Demux the frame */
+ if (rx_demux(ld, skb) < 0) {
+ mif_err("%s: ERR! rx_demux fail\n", ld->name);
+ return -EINVAL;
+ }
- frm->hdr_len = sipc5_get_hdr_size(frm->config);
- frm->data_len = count;
- frm->len = frm->hdr_len + frm->data_len;
+ return 0;
+}
- buff[SIPC5_CONFIG_OFFSET] = frm->config;
- buff[SIPC5_CH_ID_OFFSET] = frm->ch_id;
+/* called from link device when a packet arrives for this io device */
+static int io_dev_recv_skb_from_link_dev(struct io_device *iod,
+ struct link_device *ld, struct sk_buff *skb)
+{
+ char *link = ld->name;
+ enum dev_format dev = iod->format;
+ int err;
- if (frm->ext_fld) {
- if (frm->ctl_fld) {
- *sz16 = (u16)frm->len;
- buff[SIPC5_CTL_OFFSET] = frm->control;
- } else {
- *sz32 = (u32)frm->len;
+ switch (dev) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ case IPC_MULTI_RAW:
+ if (iod->waketime)
+ wake_lock_timeout(&iod->wakelock, iod->waketime);
+
+ err = rx_frame_from_skb(iod, ld, skb);
+ if (err < 0) {
+ dev_kfree_skb_any(skb);
+ mif_info("%s: ERR! rx_frame_from_skb fail (err %d)\n",
+ link, err);
}
- } else {
- *sz16 = (u16)frm->len;
- }
- return frm->hdr_len;
+ return err;
+
+ default:
+ mif_info("%s: ERR! unknown device %d\n", link, dev);
+ return -EINVAL;
+ }
}
/* inform the IO device that the modem is now online or offline or
@@ -858,18 +1011,25 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online)
}
}
+static void iodev_dump_status(struct io_device *iod, void *args)
+{
+ if (iod->format == IPC_RAW && iod->io_typ == IODEV_NET) {
+ struct link_device *ld = get_current_link(iod);
+ mif_com_log(iod->mc->msd, "%s: %s\n", iod->name, ld->name);
+ }
+}
+
static int misc_open(struct inode *inode, struct file *filp)
{
struct io_device *iod = to_io_device(filp->private_data);
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
int ret;
filp->private_data = (void *)iod;
- mif_info("%s\n", iod->name);
atomic_inc(&iod->opened);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->init_comm) {
ret = ld->init_comm(ld, iod);
if (ret < 0) {
@@ -880,24 +1040,27 @@ static int misc_open(struct inode *inode, struct file *filp)
}
}
+ mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+
return 0;
}
static int misc_release(struct inode *inode, struct file *filp)
{
struct io_device *iod = (struct io_device *)filp->private_data;
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
- mif_info("%s\n", iod->name);
atomic_dec(&iod->opened);
skb_queue_purge(&iod->sk_rx_q);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->terminate_comm)
ld->terminate_comm(ld, iod);
}
+ mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+
return 0;
}
@@ -930,6 +1093,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
char cpinfo_buf[530] = "CP Crash ";
+ unsigned long size;
+ int ret;
switch (cmd) {
case IOCTL_MODEM_ON:
@@ -952,9 +1117,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name);
return iod->mc->ops.modem_boot_off(iod->mc);
- case IOCTL_MODEM_START:
- mif_info("%s: IOCTL_MODEM_START\n", iod->name);
- return 0;
+ case IOCTL_MODEM_BOOT_DONE:
+ mif_err("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name);
+ if (iod->mc->ops.modem_boot_done)
+ return iod->mc->ops.modem_boot_done(iod->mc);
+ else
+ return 0;
case IOCTL_MODEM_STATUS:
mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name);
@@ -983,7 +1151,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0);
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
return 0;
case IOCTL_MODEM_PROTOCOL_RESUME:
@@ -993,7 +1161,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_wake, 0);
+ iodevs_for_each(iod->msd, iodev_netif_wake, 0);
return 0;
case IOCTL_MODEM_DUMP_START:
@@ -1022,6 +1190,31 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name);
return iod->mc->ops.modem_dump_reset(iod->mc);
+ case IOCTL_MIF_LOG_DUMP:
+ iodevs_for_each(iod->msd, iodev_dump_status, 0);
+ size = MAX_MIF_BUFF_SIZE;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+
+ mif_dump_log(iod->mc->msd, iod);
+ return 0;
+
+ case IOCTL_MIF_DPRAM_DUMP:
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+ if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) {
+ size = iod->mc->mdm_data->dpram_ctl->dp_size;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+ mif_dump_dpram(iod);
+ return 0;
+ }
+#endif
+ return -EINVAL;
+
default:
/* If you need to handle the ioctl for specific link device,
* then assign the link ioctl handler to ld->ioctl
@@ -1040,18 +1233,18 @@ static ssize_t misc_write(struct file *filp, const char __user *data,
{
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
- struct sipc5_frame_data *frm = &iod->meta_frame;
struct sk_buff *skb;
- u8 *buff;
- unsigned headroom;
+ int ret;
+ unsigned headroom = 0;
unsigned tailroom = 0;
size_t tx_size;
- int ret;
+ struct sipc5_frame_data frm;
+ struct timespec epoch;
if (iod->format <= IPC_RFS && iod->id == 0)
return -EINVAL;
- headroom = sipc5_build_tx_link_header(frm, iod, ld, count);
+ headroom = tx_build_link_header(&frm, iod, ld, count);
if (ld->aligned)
tailroom = sipc5_calc_padding_size(headroom + count);
@@ -1066,20 +1259,19 @@ static ssize_t misc_write(struct file *filp, const char __user *data,
}
/* store IPC link header*/
- buff = skb_put(skb, headroom);
- memcpy(buff, frm->hdr, headroom);
+ memcpy(skb_put(skb, headroom), frm.hdr, headroom);
/* store IPC message */
- buff = skb_put(skb, count);
- if (copy_from_user(buff, data, count) != 0) {
+ if (copy_from_user(skb_put(skb, count), data, count) != 0) {
if (skb)
dev_kfree_skb_any(skb);
return -EFAULT;
}
if (iod->id == SIPC5_CH_ID_FMT_0) {
- mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, buff, count);
- mif_flush_logs(ld->mc);
+ getnstimeofday(&epoch);
+ mif_time_log(iod->mc->msd, epoch, NULL, 0);
+ mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len);
}
/* store padding */
@@ -1112,6 +1304,7 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sk_buff *skb;
int copied = 0;
+ struct timespec epoch;
skb = skb_dequeue(rxq);
if (!skb) {
@@ -1120,9 +1313,9 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
}
if (iod->id == SIPC5_CH_ID_FMT_0) {
- mif_ipc_log(iod->mc, MIF_IOD_RX_EVT, iod, NULL, skb->data,
- skb->len);
- mif_flush_logs(iod->mc);
+ getnstimeofday(&epoch);
+ mif_time_log(iod->mc->msd, epoch, NULL, 0);
+ mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len);
}
copied = skb->len > count ? count : skb->len;
@@ -1200,7 +1393,7 @@ static int vnet_open(struct net_device *ndev)
{
struct vnet *vnet = netdev_priv(ndev);
- mif_info("%s\n", vnet->iod->name);
+ mif_err("%s\n", vnet->iod->name);
netif_start_queue(ndev);
atomic_inc(&vnet->iod->opened);
@@ -1211,10 +1404,11 @@ static int vnet_stop(struct net_device *ndev)
{
struct vnet *vnet = netdev_priv(ndev);
- mif_info("%s\n", vnet->iod->name);
+ mif_err("%s\n", vnet->iod->name);
atomic_dec(&vnet->iod->opened);
netif_stop_queue(ndev);
+ skb_queue_purge(&vnet->iod->sk_rx_q);
return 0;
}
@@ -1223,32 +1417,38 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
struct vnet *vnet = netdev_priv(ndev);
struct io_device *iod = vnet->iod;
struct link_device *ld = get_current_link(iod);
- struct sipc5_frame_data *frm = &iod->meta_frame;
struct sk_buff *skb_new;
- unsigned headroom;
- unsigned tailroom = 0;
int ret;
-
-#if 0
- mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, skb->data, skb->len);
- mif_flush_logs(ld->mc);
-#endif
+ unsigned headroom = 0;
+ unsigned tailroom = 0;
+ unsigned long tx_bytes = skb->len;
+ struct iphdr *ip_header = NULL;
+ struct sipc5_frame_data frm;
/* When use `handover' with Network Bridge,
- * user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this.
- *
- * We remove the one ethernet header of skb before using skb->len,
- * because the skb has two ethernet headers.
+ * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here.
+ * bridge device is ethernet device unlike xxxx_rmnet(net device).
+ * We remove the an ethernet header of skb before using skb->len,
+ * because bridge device added an ethernet header to skb.
*/
if (iod->use_handover) {
if (iod->id >= PS_DATA_CH_0 && iod->id <= PS_DATA_CH_LAST)
skb_pull(skb, sizeof(struct ethhdr));
}
- headroom = sipc5_build_tx_link_header(frm, iod, ld, skb->len);
+ headroom = tx_build_link_header(&frm, iod, ld, skb->len);
+
+ /* ip loop-back */
+ ip_header = (struct iphdr *)skb->data;
+ if (iod->msd->loopback_ipaddr &&
+ ip_header->daddr == iod->msd->loopback_ipaddr) {
+ swap(ip_header->saddr, ip_header->daddr);
+ frm.ch_id = DATA_LOOPBACK_CHANNEL;
+ frm.hdr[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL;
+ }
if (ld->aligned)
- tailroom = sipc5_calc_padding_size(frm->len);
+ tailroom = sipc5_calc_padding_size(frm.len);
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) {
mif_debug("%s: skb_copy_expand needed\n", iod->name);
@@ -1263,7 +1463,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_new = skb;
}
- memcpy(skb_push(skb_new, headroom), frm->hdr, headroom);
+ memcpy(skb_push(skb_new, headroom), frm.hdr, headroom);
if (tailroom)
skb_put(skb_new, tailroom);
@@ -1278,7 +1478,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
}
ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
+ ndev->stats.tx_bytes += tx_bytes;
return NETDEV_TX_OK;
}
@@ -1327,13 +1527,13 @@ int sipc5_init_io_device(struct io_device *iod)
/* Get data from link device */
mif_debug("%s: SIPC version = %d\n", iod->name, iod->ipc_version);
iod->recv = io_dev_recv_data_from_link_dev;
+ iod->recv_skb = io_dev_recv_skb_from_link_dev;
/* Register misc or net device */
switch (iod->io_typ) {
case IODEV_MISC:
init_waitqueue_head(&iod->wq);
skb_queue_head_init(&iod->sk_rx_q);
- INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
iod->miscdev.minor = MISC_DYNAMIC_MINOR;
iod->miscdev.name = iod->name;
@@ -1347,8 +1547,6 @@ int sipc5_init_io_device(struct io_device *iod)
case IODEV_NET:
skb_queue_head_init(&iod->sk_rx_q);
- INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
-
if (iod->use_handover)
iod->ndev = alloc_netdev(0, iod->name,
vnet_setup_ether);
@@ -1375,7 +1573,6 @@ int sipc5_init_io_device(struct io_device *iod)
case IODEV_DUMMY:
skb_queue_head_init(&iod->sk_rx_q);
- /* in sipc5, does not need rx_iodev_work on DUMMY */
iod->miscdev.minor = MISC_DYNAMIC_MINOR;
iod->miscdev.name = iod->name;
@@ -1389,7 +1586,16 @@ int sipc5_init_io_device(struct io_device *iod)
if (ret)
mif_info("%s: ERR! device_create_file fail\n",
iod->name);
-
+ ret = device_create_file(iod->miscdev.this_device,
+ &attr_loopback);
+ if (ret)
+ mif_err("failed to create `loopback file' : %s\n",
+ iod->name);
+ ret = device_create_file(iod->miscdev.this_device,
+ &attr_txlink);
+ if (ret)
+ mif_err("failed to create `txlink file' : %s\n",
+ iod->name);
break;
default:
diff --git a/drivers/misc/modem_if/sipc5_modem.c b/drivers/misc/modem_if/sipc5_modem.c
index a1287b5..f5e33d3 100644
--- a/drivers/misc/modem_if/sipc5_modem.c
+++ b/drivers/misc/modem_if/sipc5_modem.c
@@ -41,7 +41,41 @@
#define FMT_WAKE_TIME (HZ/2)
#define RAW_WAKE_TIME (HZ*6)
-static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
+static struct modem_shared *create_modem_shared_data(void)
+{
+ struct modem_shared *msd;
+ int size = MAX_MIF_BUFF_SIZE;
+
+ msd = kzalloc(sizeof(struct modem_shared), GFP_KERNEL);
+ if (!msd)
+ return NULL;
+
+ /* initialize link device list */
+ INIT_LIST_HEAD(&msd->link_dev_list);
+
+ /* initialize tree of io devices */
+ msd->iodevs_tree_chan = RB_ROOT;
+ msd->iodevs_tree_fmt = RB_ROOT;
+
+ msd->storage.cnt = 0;
+ msd->storage.addr = kzalloc(MAX_MIF_BUFF_SIZE +
+ (MAX_MIF_SEPA_SIZE * 2), GFP_KERNEL);
+ if (!msd->storage.addr) {
+ mif_err("IPC logger buff alloc failed!!\n");
+ return NULL;
+ }
+ memset(msd->storage.addr, 0, size + (MAX_MIF_SEPA_SIZE * 2));
+ memcpy(msd->storage.addr, MIF_SEPARATOR, MAX_MIF_SEPA_SIZE);
+ msd->storage.addr += MAX_MIF_SEPA_SIZE;
+ memcpy(msd->storage.addr, &size, MAX_MIF_SEPA_SIZE);
+ msd->storage.addr += MAX_MIF_SEPA_SIZE;
+ spin_lock_init(&msd->lock);
+
+ return msd;
+}
+
+static struct modem_ctl *create_modemctl_device(struct platform_device *pdev,
+ struct modem_shared *msd)
{
int ret = 0;
struct modem_data *pdata;
@@ -53,6 +87,7 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
if (!modemctl)
return NULL;
+ modemctl->msd = msd;
modemctl->dev = dev;
modemctl->phone_state = STATE_OFFLINE;
@@ -60,13 +95,6 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
modemctl->mdm_data = pdata;
modemctl->name = pdata->name;
- /* initialize link device list */
- INIT_LIST_HEAD(&modemctl->commons.link_dev_list);
-
- /* initialize tree of io devices */
- modemctl->commons.iodevs_tree_chan = RB_ROOT;
- modemctl->commons.iodevs_tree_fmt = RB_ROOT;
-
/* init modemctl device for getting modemctl operations */
ret = call_modem_init_func(modemctl, pdata);
if (ret) {
@@ -74,23 +102,14 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
return NULL;
}
- modemctl->use_mif_log = pdata->use_mif_log;
- if (pdata->use_mif_log)
- mif_err("<%s> IPC logger can be used.\n", pdata->name);
-
- ret = mif_init_log(modemctl);
- if (ret < 0) {
- kfree(modemctl);
- return NULL;
- }
-
mif_info("%s is created!!!\n", pdata->name);
return modemctl;
}
static struct io_device *create_io_device(struct modem_io_t *io_t,
- struct modem_ctl *modemctl, struct modem_data *pdata)
+ struct modem_shared *msd, struct modem_ctl *modemctl,
+ struct modem_data *pdata)
{
int ret = 0;
struct io_device *iod = NULL;
@@ -123,6 +142,16 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
mif_info("Bood device = %s\n", iod->name);
}
+ /* link between io device and modem shared */
+ iod->msd = msd;
+
+ /* add iod to rb_tree */
+ if (iod->format != IPC_RAW)
+ insert_iod_with_format(msd, iod->format, iod);
+
+ if (sipc5_is_not_reserved_channel(iod->id))
+ insert_iod_with_channel(msd, iod->id, iod);
+
/* register misc device or net device */
ret = sipc5_init_io_device(iod);
if (ret) {
@@ -135,21 +164,14 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
return iod;
}
-static int attach_devices(struct modem_ctl *mc, struct io_device *iod,
- enum modem_link tx_link)
+static int attach_devices(struct io_device *iod, enum modem_link tx_link)
{
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
unsigned ch;
- /* add iod to rb_tree */
- if (iod->format != IPC_RAW)
- insert_iod_with_format(&mc->commons, iod->format, iod);
-
- if (sipc5_is_not_reserved_channel(iod->id))
- insert_iod_with_channel(&mc->commons, iod->id, iod);
-
/* find link type for this io device */
- list_for_each_entry(ld, &mc->commons.link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld)) {
/* The count 1 bits of iod->link_types is count
* of link devices of this iod.
@@ -210,14 +232,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
{
int i;
struct modem_data *pdata = pdev->dev.platform_data;
- struct modem_ctl *modemctl;
+ struct modem_shared *msd = NULL;
+ struct modem_ctl *modemctl = NULL;
struct io_device *iod[pdata->num_iodevs];
struct link_device *ld;
mif_err("%s\n", pdev->name);
memset(iod, 0, sizeof(iod));
- modemctl = create_modemctl_device(pdev);
+ msd = create_modem_shared_data();
+ if (!msd) {
+ mif_err("msd == NULL\n");
+ goto err_free_modemctl;
+ }
+
+ modemctl = create_modemctl_device(pdev, msd);
if (!modemctl) {
mif_err("modemctl == NULL\n");
goto err_free_modemctl;
@@ -235,20 +264,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
mif_err("link created: %s\n", ld->name);
ld->link_type = i;
ld->mc = modemctl;
- list_add(&ld->list, &modemctl->commons.link_dev_list);
+ ld->msd = msd;
+ list_add(&ld->list, &msd->link_dev_list);
}
}
/* create io deivces and connect to modemctl device */
for (i = 0; i < pdata->num_iodevs; i++) {
- iod[i] = create_io_device(&pdata->iodevs[i], modemctl, pdata);
+ iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl,
+ pdata);
if (!iod[i]) {
mif_err("iod[%d] == NULL\n", i);
goto err_free_modemctl;
}
- attach_devices(modemctl, iod[i],
- pdata->iodevs[i].tx_link);
+ attach_devices(iod[i], pdata->iodevs[i].tx_link);
}
platform_set_drvdata(pdev, modemctl);
@@ -265,6 +295,9 @@ err_free_modemctl:
if (modemctl != NULL)
kfree(modemctl);
+ if (msd != NULL)
+ kfree(msd);
+
return -ENOMEM;
}