aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if
diff options
context:
space:
mode:
authorwonhee <wonhee48.seo@samsung.com>2011-06-18 03:39:23 +0900
committerBenoit Goby <benoit@android.com>2011-06-21 00:14:10 -0700
commit5da9e51ab161246785f52ec6758d9e21c2191d21 (patch)
tree030ec328a6625f883ea986343228a4c73f02c2bc /drivers/misc/modem_if
parent744c1561fc028be58230147cc8543e2a694bdbce (diff)
downloadkernel_samsung_tuna-5da9e51ab161246785f52ec6758d9e21c2191d21.tar.gz
kernel_samsung_tuna-5da9e51ab161246785f52ec6758d9e21c2191d21.tar.bz2
kernel_samsung_tuna-5da9e51ab161246785f52ec6758d9e21c2191d21.zip
misc: modem_if: Add modem interface driver
1. Update modem interface driver based on TI HSI patch. - TI HSI patch : Change I01715849: omap4: hsi driver initial commit : Change I14d257ae: ARM: omap: tuna: add HSI support : Change Icebb37db: omap4: enable hsi hwmod : Change Iec8eef73: tuna: defcoinfig: enable HSI 2. Checked Modem BootUp and MIPI communication working. Change-Id: I75a2d960f3a66004fa81743fae931f04bd4f0c07 Signed-off-by: wonhee <wonhee48.seo@samsung.com>
Diffstat (limited to 'drivers/misc/modem_if')
-rwxr-xr-xdrivers/misc/modem_if/Kconfig40
-rwxr-xr-xdrivers/misc/modem_if/Makefile8
-rwxr-xr-xdrivers/misc/modem_if/modem.c219
-rwxr-xr-xdrivers/misc/modem_if/modem_io_device.c758
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_mipi.c1293
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_mipi.h157
-rwxr-xr-xdrivers/misc/modem_if/modem_modemctl_device_xmm6260.c195
-rwxr-xr-xdrivers/misc/modem_if/modem_prj.h202
-rwxr-xr-xdrivers/misc/modem_if/modem_variation.h118
9 files changed, 2990 insertions, 0 deletions
diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig
new file mode 100755
index 00000000000..223a2750455
--- /dev/null
+++ b/drivers/misc/modem_if/Kconfig
@@ -0,0 +1,40 @@
+menuconfig SEC_MODEM
+ bool "Modem Interface Driver"
+ default n
+ ---help---
+ Samsung Modem Interface Driver.
+
+config UMTS_LINK_MIPI
+ bool "modem driver link device MIPI-HSI"
+ depends on SEC_MODEM
+ default n
+
+config UMTS_LINK_HSIC
+ bool "modem driver link device HSIC"
+ depends on SEC_MODEM
+ default n
+
+config UMTS_MODEM_XMM6260
+ bool "modem chip : IMC xmm6260"
+ depends on SEC_MODEM
+ default n
+
+config CDMA_LINK_DPRAM
+ bool "modem driver link device DPRAM"
+ depends on SEC_MODEM
+ default n
+
+config CDMA_MODEM_CBP71
+ bool "modem chip : cbp71"
+ depends on SEC_MODEM
+ default n
+
+config LTE_LINK_USB
+ bool "modem driver link device US"
+ depends on SEC_MODEM
+ default n
+
+config LTE_MODEM_CMC221
+ bool "modem chip : cmc221"
+ depends on SEC_MODEM
+ default n
diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile
new file mode 100755
index 00000000000..3baca23bc4c
--- /dev/null
+++ b/drivers/misc/modem_if/Makefile
@@ -0,0 +1,8 @@
+obj-y += modem.o modem_io_device.o
+obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o
+obj-$(CONFIG_UMTS_LINK_MIPI) += modem_link_device_mipi.o
+obj-$(CONFIG_UMTS_LINK_HSIC) += modem_link_device_hsic.o
+obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o
+obj-$(CONFIG_CDMA_LINK_DPRAM) += modem_link_device_dpram.o
+obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o
+obj-$(CONFIG_LTE_LINK_USB) += modem_link_device_usb.o \ No newline at end of file
diff --git a/drivers/misc/modem_if/modem.c b/drivers/misc/modem_if/modem.c
new file mode 100755
index 00000000000..20eb5e23e59
--- /dev/null
+++ b/drivers/misc/modem_if/modem.c
@@ -0,0 +1,219 @@
+/* linux/drivers/modem/modem.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/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/wait.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"
+
+
+static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct modem_data *pdata;
+ struct modem_ctl *modemctl;
+ struct device *dev = &pdev->dev;
+
+ /* create modem control device */
+ modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL);
+ if (!modemctl)
+ return NULL;
+
+ modemctl->dev = dev;
+ modemctl->phone_state = STATE_OFFLINE;
+
+ /* get platform data */
+ pdata = pdev->dev.platform_data;
+
+ modemctl->name = pdata->name;
+
+ modemctl->gpio_cp_on = pdata->gpio_cp_on;
+ modemctl->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ modemctl->gpio_cp_reset = pdata->gpio_cp_reset;
+ modemctl->gpio_pda_active = pdata->gpio_pda_active;
+ modemctl->gpio_phone_active = pdata->gpio_phone_active;
+ modemctl->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ modemctl->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ modemctl->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+
+ modemctl->irq_phone_active = platform_get_irq(pdev, 0);
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+ modemctl->gpio_cp_off = pdata->gpio_cp_off;
+ modemctl->gpio_slave_wakeup = pdata->gpio_slave_wakeup;
+ modemctl->gpio_host_active = pdata->gpio_host_active;
+ modemctl->gpio_host_wakeup = pdata->gpio_host_wakeup;
+ modemctl->irq_host_wakeup = platform_get_irq(pdev, 1);
+#endif
+ /* init modemctl device for getting modemctl operations */
+ ret = call_modem_init_func(modemctl, pdata);
+ if (ret) {
+ kfree(modemctl);
+ return NULL;
+ }
+
+ pr_info("[MODEM_IF] %s:create_modemctl_device DONE\n", modemctl->name);
+ return modemctl;
+}
+
+static struct io_device *create_io_device(struct modem_io_t *io_t,
+ struct modem_ctl *modemctl)
+{
+ int ret = 0;
+ struct io_device *iod = NULL;
+
+ iod = kzalloc(sizeof(struct io_device), GFP_KERNEL);
+ if (!iod) {
+ pr_err("[MODEM_IF] io device memory alloc fail\n");
+ return NULL;
+ }
+
+ iod->name = io_t->name;
+ iod->id = io_t->id;
+ iod->format = io_t->format;
+ iod->io_typ = io_t->io_type;
+
+ /* link between io device and modem control */
+ iod->mc = modemctl;
+ modemctl->iod = iod;
+
+ /* register misc device or net device */
+ ret = init_io_device(iod);
+ if (ret) {
+ kfree(iod);
+ return NULL;
+ }
+
+ pr_info("[MODEM_IF] %s : create_io_device DONE\n", io_t->name);
+ return iod;
+}
+
+static int __devinit modem_probe(struct platform_device *pdev)
+{
+ int i;
+ struct modem_data *pdata;
+ struct modem_ctl *modemctl;
+ struct io_device *iod[MAX_NUM_IO_DEV];
+ struct link_device *ld;
+ struct io_raw_devices *io_raw_devs = NULL;
+
+ pdata = pdev->dev.platform_data;
+
+ modemctl = create_modemctl_device(pdev);
+ if (!modemctl)
+ return -ENOMEM;
+
+ /* create link device */
+ ld = call_link_init_func(pdev, pdata->link_type);
+ if (!ld)
+ goto err_free_modemctl;
+
+ io_raw_devs = kzalloc(sizeof(struct io_raw_devices), GFP_KERNEL);
+ if (!io_raw_devs)
+ return -ENOMEM;
+
+ /* 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);
+ if (!iod[i])
+ goto err_free_modemctl;
+
+ if (iod[i]->format == IPC_RAW) {
+ int ch = iod[i]->id & 0x1F;
+ io_raw_devs->raw_devices[ch] = iod[i];
+ io_raw_devs->num_of_raw_devs++;
+ iod[i]->link = ld;
+ } else {
+ /* connect io devices to one link device */
+ ld->attach(ld, iod[i]);
+ }
+
+ if (iod[i]->format == IPC_MULTI_RAW)
+ iod[i]->private_data = (void *)io_raw_devs;
+ }
+
+ platform_set_drvdata(pdev, modemctl);
+
+ pr_info("[MODEM_IF] modem_probe DONE\n");
+ return 0;
+
+err_free_modemctl:
+ for (i = 0; i < pdata->num_iodevs; i++)
+ if (iod[i] != NULL)
+ kfree(iod[i]);
+
+ if (io_raw_devs != NULL)
+ kfree(io_raw_devs);
+
+ if (modemctl != NULL)
+ kfree(modemctl);
+
+ return -ENOMEM;
+}
+
+static int modem_suspend(struct device *pdev)
+{
+ struct modem_ctl *mc = dev_get_drvdata(pdev);
+ gpio_set_value(mc->gpio_pda_active, 0);
+ return 0;
+}
+
+static int modem_resume(struct device *pdev)
+{
+ struct modem_ctl *mc = dev_get_drvdata(pdev);
+ gpio_set_value(mc->gpio_pda_active, 1);
+ return 0;
+}
+
+static const struct dev_pm_ops modem_pm_ops = {
+ .suspend = modem_suspend,
+ .resume = modem_resume,
+};
+
+static struct platform_driver modem_driver = {
+ .probe = modem_probe,
+ .driver = {
+ .name = "modem_if",
+ .pm = &modem_pm_ops,
+ },
+};
+
+static int __init modem_init(void)
+{
+ return platform_driver_register(&modem_driver);
+}
+
+module_init(modem_init);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung Modem Interface Driver");
diff --git a/drivers/misc/modem_if/modem_io_device.c b/drivers/misc/modem_if/modem_io_device.c
new file mode 100755
index 00000000000..05bbb47c836
--- /dev/null
+++ b/drivers/misc/modem_if/modem_io_device.c
@@ -0,0 +1,758 @@
+/* /linux/drivers/misc/modem_if/modem_io_device.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/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/if_arp.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+
+
+#define HDLC_START 0x7F
+#define HDLC_END 0x7E
+#define SIZE_OF_HDLC_START 1
+#define SIZE_OF_HDLC_END 1
+#define MAX_RXDATA_SIZE 0x1000 /* 4*1024 */
+
+static const char hdlc_start[1] = { HDLC_START };
+static const char hdlc_end[1] = { HDLC_END };
+
+struct fmt_hdr {
+ u16 len;
+ u8 control;
+} __attribute__ ((packed));
+
+struct raw_hdr {
+ u32 len;
+ u8 channel;
+ u8 control;
+} __attribute__ ((packed));
+
+struct rfs_hdr {
+ u32 len;
+ u8 cmd;
+ u8 id;
+} __attribute__ ((packed));
+
+static int rx_iodev_skb(struct io_device *iod);
+
+static int get_header_size(struct io_device *iod)
+{
+ switch (iod->format) {
+ case IPC_FMT:
+ return sizeof(struct fmt_hdr);
+
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ return sizeof(struct raw_hdr);
+
+ case IPC_RFS:
+ return sizeof(struct rfs_hdr);
+
+ case IPC_BOOT:
+ /* minimum size for transaction align */
+ return 4;
+
+ default:
+ return 0;
+ }
+}
+
+static int get_hdlc_size(struct io_device *iod, char *buf)
+{
+ struct fmt_hdr *fmt_header;
+ struct raw_hdr *raw_header;
+ struct rfs_hdr *rfs_header;
+
+ pr_debug("[MODEM_IF] buf : %02x %02x %02x (%d)\n", *buf, *(buf + 1),
+ *(buf + 2), __LINE__);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ fmt_header = (struct fmt_hdr *)buf;
+ return fmt_header->len;
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ raw_header = (struct raw_hdr *)buf;
+ return raw_header->len;
+ case IPC_RFS:
+ rfs_header = (struct rfs_hdr *)buf;
+ return rfs_header->len;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void *get_header(struct io_device *iod, struct misc_data_io *misc_data,
+ char *frame_header_buf)
+{
+ struct fmt_hdr *fmt_h;
+ struct raw_hdr *raw_h;
+ struct rfs_hdr *rfs_h;
+
+ switch (iod->format) {
+ case IPC_FMT:
+ fmt_h = (struct fmt_hdr *)frame_header_buf;
+
+ fmt_h->len = misc_data->size + sizeof(struct fmt_hdr);
+ fmt_h->control = 0;
+
+ return (void *)frame_header_buf;
+
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ raw_h = (struct raw_hdr *)frame_header_buf;
+
+ raw_h->len = misc_data->size + sizeof(struct raw_hdr);
+ raw_h->channel = misc_data->id;
+ raw_h->control = 0;
+
+ return (void *)frame_header_buf;
+
+ case IPC_RFS:
+ rfs_h = (struct rfs_hdr *)frame_header_buf;
+
+ rfs_h->len = misc_data->size + sizeof(struct raw_hdr);
+ rfs_h->cmd = misc_data->cmd;
+ rfs_h->id = misc_data->id;
+
+ return (void *)frame_header_buf;
+
+ default:
+ return 0;
+ }
+}
+
+static int rx_hdlc_head_start_check(char *buf)
+{
+ if (strncmp(buf, hdlc_start, sizeof(hdlc_start))) {
+ pr_err("[MODEM_IF] Wrong HDLC start: 0x%x\n", *buf);
+ return -EBADMSG;
+ }
+ return sizeof(hdlc_start);
+}
+
+static int rx_hdlc_tail_check(char *buf)
+{
+ if (strncmp(buf, hdlc_end, sizeof(hdlc_end))) {
+ pr_err("[MODEM_IF] Wrong HDLC end: 0x%x\n", *buf);
+ return -EBADMSG;
+ }
+ return sizeof(hdlc_end);
+}
+
+/* remove hdlc header and store IPC header */
+static int rx_hdlc_head_check(struct io_device *iod, char *buf, unsigned rest)
+{
+ struct header_data *hdr = &iod->h_data;
+ int head_size = get_header_size(iod);
+ int done_len = 0;
+ int len = 0;
+
+ /* first frame, remove start header 7F */
+ if (!hdr->start) {
+ len = rx_hdlc_head_start_check(buf);
+ if (len < 0)
+ return len; /*Wrong hdlc start*/
+
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len,
+ rest, __LINE__);
+
+ memcpy(&hdr->start, hdlc_start, len);
+ hdr->len = 0;
+
+ /* debug print */
+ switch (iod->format) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ case IPC_RFS:
+ /* TODO: print buf... */
+ break;
+
+ case IPC_CMD:
+ case IPC_BOOT:
+ default:
+ break;
+ }
+ buf += len;
+ done_len += len;
+ rest -= len; /* rest, call by value */
+ }
+
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ /* store the IPC header to iod priv */
+ if (hdr->len < head_size) {
+ len = min(rest, head_size - hdr->len);
+ memcpy(hdr->hdr + hdr->len, buf, len);
+ hdr->len += len;
+ done_len += len;
+ }
+
+ pr_debug("[MODEM_IF] check done_len : %d, rest : %d (%d)\n", done_len,
+ rest, __LINE__);
+ return done_len;
+}
+
+/* alloc skb and copy dat to skb */
+static int rx_hdlc_data_check(struct io_device *iod, char *buf, unsigned rest)
+{
+ struct header_data *hdr = &iod->h_data;
+ struct sk_buff *skb = iod->skb_recv;
+ int head_size = get_header_size(iod);
+ int data_size = get_hdlc_size(iod, hdr->hdr) - head_size;
+ int alloc_size = min(data_size, MAX_RXDATA_SIZE);
+ int len;
+ int done_len = 0;
+ int rest_len = data_size - hdr->flag_len;
+ struct sk_buff *skb_new;
+
+ pr_debug("[MODEM_IF] head_size : %d, data_size : %d (%d)\n", head_size,
+ data_size, __LINE__);
+
+ /* first payload data - alloc skb */
+ if (!skb) {
+ switch (iod->format) {
+ case IPC_RFS:
+ alloc_size = min(data_size, (int)rest) + head_size;
+ alloc_size = min(alloc_size, MAX_RXDATA_SIZE);
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ /* copy the RFS haeder to skb->data */
+ memcpy(skb_put(skb, head_size), hdr->hdr, head_size);
+ break;
+ default:
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ break;
+ }
+ iod->skb_recv = skb;
+ }
+
+ /* if recv packet size is larger than user space */
+ while (rest_len > MAX_RXDATA_SIZE) {
+ len = MAX_RXDATA_SIZE - skb->len;
+ memcpy(skb_put(skb, len), buf, len);
+ buf += len;
+ done_len += len;
+ rest -= len;
+ rest_len -= len;
+ rx_iodev_skb(iod);
+
+ alloc_size = min(rest_len, MAX_RXDATA_SIZE);
+ skb_new = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb_new))
+ return -ENOMEM;
+ skb = iod->skb_recv = skb_new;
+ }
+
+ /* copy data to skb */
+ len = min(rest, alloc_size - skb->len);
+ pr_debug("[MODEM_IF] rest : %d, alloc_size : %d , len : %d (%d)\n",
+ rest, alloc_size, skb->len, __LINE__);
+
+ memcpy(skb_put(skb, len), buf, len);
+ done_len += len;
+ hdr->flag_len += done_len;
+
+ return done_len;
+}
+
+static int rx_iodev_skb_raw(struct io_device *iod)
+{
+ int err;
+ struct sk_buff *skb = iod->skb_recv;
+ struct net_device *ndev;
+
+ switch (iod->io_typ) {
+ case IODEV_MISC:
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+ wake_up(&iod->wq);
+ return 0;
+
+ case IODEV_NET:
+ ndev = iod->ndev;
+ if (!ndev)
+ return NET_RX_DROP;
+
+ skb->dev = ndev;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += skb->len;
+ skb->protocol = htons(ETH_P_IP);
+
+ err = netif_rx(skb);
+ if (err != NET_RX_SUCCESS)
+ dev_err(&ndev->dev, "rx error: %d\n", err);
+ return err;
+
+ default:
+ pr_err("[MODEM_IF] wrong io_type : %d\n", iod->io_typ);
+ return -EINVAL;
+ }
+}
+
+static int rx_multipdp(struct io_device *iod)
+{
+ int ret;
+ u8 ch;
+ struct raw_hdr *raw_header = (struct raw_hdr *)&iod->h_data.hdr;
+ struct io_raw_devices *io_raw_devs =
+ (struct io_raw_devices *)iod->private_data;
+ struct io_device *real_iod;
+
+ ch = raw_header->channel;
+ real_iod = io_raw_devs->raw_devices[ch];
+
+ real_iod->skb_recv = iod->skb_recv;
+ ret = rx_iodev_skb_raw(real_iod);
+ if (ret < 0)
+ pr_err("[MODEM_IF] failed!\n");
+ return ret;
+}
+
+/* de-mux function draft */
+static int rx_iodev_skb(struct io_device *iod)
+{
+ switch (iod->format) {
+ case IPC_MULTI_RAW:
+ return rx_multipdp(iod);
+
+ case IPC_FMT:
+ case IPC_RFS:
+ default:
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+
+ pr_debug("[MODEM_IF] wake up fmt,rfs skb\n");
+ wake_up(&iod->wq);
+ return 0;
+ }
+}
+
+static int rx_hdlc_packet(struct io_device *iod, const char *data,
+ unsigned recv_size)
+{
+ unsigned rest = recv_size;
+ char *buf = (char *)data;
+ int err;
+ int len;
+
+ if (rest <= 0)
+ goto exit;
+
+ pr_info("[MODEM_IF] RX_SIZE=%d\n", rest);
+
+next_frame:
+ err = len = rx_hdlc_head_check(iod, buf, rest);
+ if (err < 0)
+ goto exit; /* buf++; rest--; goto next_frame; */
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ buf += len;
+ rest -= len;
+ if (rest <= 0)
+ goto exit;
+
+ err = len = rx_hdlc_data_check(iod, buf, rest);
+ if (err < 0)
+ goto exit;
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ buf += len;
+ rest -= len;
+ if (rest <= 0)
+ goto exit;
+
+ err = len = rx_hdlc_tail_check(buf);
+ if (err < 0)
+ goto exit;
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ buf += len;
+ rest -= len;
+ if (rest < 0)
+ goto exit;
+
+ err = rx_iodev_skb(iod);
+ if (err < 0)
+ goto exit;
+
+ /* initialize header & skb */
+ iod->skb_recv = NULL;
+ memset(&iod->h_data, 0x00, sizeof(struct header_data));
+
+ if (rest)
+ goto next_frame;
+
+exit:
+ /* free buffers. mipi-hsi re-use recv buf */
+
+ if (rest < 0)
+ err = -ERANGE;
+
+ if (err < 0 && iod->skb_recv) {
+ dev_kfree_skb_any(iod->skb_recv);
+ iod->skb_recv = NULL;
+ }
+ /* clear headers */
+ memset(&iod->h_data, 0x00, sizeof(struct header_data));
+ return err;
+}
+
+/* called from link device when a packet arrives for this io device */
+static int io_dev_recv_data_from_link_dev(struct io_device *iod,
+ const char *data, unsigned int len)
+{
+ struct sk_buff *skb;
+ int err;
+
+ switch (iod->format) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ case IPC_MULTI_RAW:
+ err = rx_hdlc_packet(iod, data, len);
+ if (err < 0)
+ pr_err("[MODEM_IF] fail process hdlc fram\n");
+ return err;
+
+ case IPC_CMD:
+ /* TODO- handle flow control command from CP */
+ return 0;
+
+ case IPC_BOOT:
+ /* save packet to sk_buff */
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MODEM_IF] fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+
+ pr_debug("[MODEM_IF] boot len : %d\n", len);
+
+ memcpy(skb_put(skb, len), data, len);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ pr_debug("[MODEM_IF] skb len : %d\n", skb->len);
+
+ wake_up(&iod->wq);
+ return len;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/* inform the IO device that the modem is now online or offline or
+ * crashing or whatever...
+ */
+static void io_dev_modem_state_changed(struct io_device *iod,
+ enum modem_state state)
+{
+}
+
+static int misc_open(struct inode *inode, struct file *filp)
+{
+ struct io_device *iod = to_io_device(filp->private_data);
+ filp->private_data = (void *)iod;
+ return 0;
+}
+
+static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+
+ poll_wait(filp, &iod->wq, wait);
+
+ if ((!skb_queue_empty(&iod->sk_rx_q))
+ && (iod->mc->phone_state != STATE_OFFLINE))
+ return POLLIN | POLLRDNORM;
+ else
+ return 0;
+}
+
+static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long _arg)
+{
+ void __user *arg = (void *) _arg;
+ struct io_device *iod = (struct io_device *)filp->private_data;
+ struct misc_data_io misc_data;
+ int frame_len = 0;
+ char frame_header_buf[sizeof(struct raw_hdr)];
+ struct sk_buff *skb;
+
+ pr_debug("[MODEM_IF] misc_ioctl : 0x%x\n", cmd);
+
+ switch (cmd) {
+ case IOCTL_MODEM_SEND:
+
+ /* TODO - check here flow control for only raw data */
+
+ if (copy_from_user(&misc_data, arg, sizeof(misc_data)) != 0)
+ return -EFAULT;
+
+ frame_len = misc_data.size + SIZE_OF_HDLC_START
+ + get_header_size(iod) + SIZE_OF_HDLC_END;
+ skb = alloc_skb(frame_len, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MODEM_IF] fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+
+ switch (iod->format) {
+ case IPC_BOOT:
+ if (copy_from_user(skb_put(skb, misc_data.size),
+ misc_data.data, misc_data.size) != 0)
+ return -EFAULT;
+ break;
+
+ case IPC_RFS:
+ memcpy(skb_put(skb, SIZE_OF_HDLC_START), hdlc_start,
+ SIZE_OF_HDLC_START);
+ if (copy_from_user(skb_put(skb, misc_data.size),
+ misc_data.data, misc_data.size) != 0)
+ return -EFAULT;
+ memcpy(skb_put(skb, SIZE_OF_HDLC_END), hdlc_end,
+ SIZE_OF_HDLC_END);
+ break;
+
+ default:
+ memcpy(skb_put(skb, SIZE_OF_HDLC_START), hdlc_start,
+ SIZE_OF_HDLC_START);
+ memcpy(skb_put(skb, get_header_size(iod)),
+ get_header(iod, &misc_data, frame_header_buf),
+ get_header_size(iod));
+ if (copy_from_user(skb_put(skb, misc_data.size),
+ misc_data.data, misc_data.size) != 0)
+ return -EFAULT;
+ memcpy(skb_put(skb, SIZE_OF_HDLC_END), hdlc_end,
+ SIZE_OF_HDLC_END);
+ break;
+ }
+
+ /* send data with sk_buff, link device will put sk_buff
+ * into the specific sk_buff_q and run work-q to send data
+ */
+ return iod->link->send(iod->link, iod, skb);
+
+ case IOCTL_MODEM_RECV:
+ if (copy_from_user(&misc_data, arg, sizeof(misc_data)) != 0)
+ return -EFAULT;
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ if (!skb) {
+ pr_info("[MODEM_IF] no data from sk_rx_q\n");
+ return 0;
+ }
+
+ if (skb->len > MAX_RXDATA_SIZE) {
+ pr_err("[MODEM_IF] skb len is too big = %d!(%d)",
+ MAX_RXDATA_SIZE, __LINE__);
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ misc_data.size = skb->len;
+ pr_debug("[MODEM_IF] skb len : %d\n", skb->len);
+
+ if (copy_to_user(misc_data.data, skb->data, skb->len) != 0)
+ return -EFAULT;
+ dev_kfree_skb_any(skb);
+
+ if (copy_to_user(arg, &misc_data, sizeof(misc_data)) != 0)
+ return -EFAULT;
+
+ pr_debug("[MODEM_IF] copy to user : %d\n", misc_data.size);
+
+ return misc_data.size;
+
+ case IOCTL_MODEM_ON:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_ON\n");
+ return iod->mc->ops.modem_on(iod->mc);
+
+ case IOCTL_MODEM_OFF:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_OFF\n");
+ return iod->mc->ops.modem_off(iod->mc);
+
+ case IOCTL_MODEM_RESET:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_RESET\n");
+ return iod->mc->ops.modem_reset(iod->mc);
+
+ case IOCTL_MODEM_BOOT_ON:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_ON\n");
+ return iod->mc->ops.modem_boot_on(iod->mc);
+
+ case IOCTL_MODEM_BOOT_OFF:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_OFF\n");
+ return iod->mc->ops.modem_boot_off(iod->mc);
+
+ case IOCTL_MODEM_START:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_START\n");
+ return iod->link->init_comm(iod->link, iod);
+
+ case IOCTL_MODEM_STATUS:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_START\n");
+ return iod->mc->phone_state;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct file_operations misc_io_fops = {
+ .owner = THIS_MODULE,
+ .open = misc_open,
+ .poll = misc_poll,
+ .unlocked_ioctl = misc_ioctl,
+};
+
+static int vnet_open(struct net_device *ndev)
+{
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int vnet_stop(struct net_device *ndev)
+{
+ netif_stop_queue(ndev);
+ return 0;
+}
+
+static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ int ret;
+ struct raw_hdr hd;
+ struct sk_buff *skb_new;
+ struct vnet *vnet = netdev_priv(ndev);
+ struct io_device *iod = vnet->iod;
+
+ hd.len = skb->len + sizeof(hd);
+ hd.control = 0;
+ hd.channel = iod->id & 0x1F;
+
+ skb_new = skb_copy_expand(skb, sizeof(hd) + sizeof(hdlc_start),
+ sizeof(hdlc_end), GFP_ATOMIC);
+ if (!skb_new) {
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ memcpy(skb_push(skb_new, sizeof(hd)), &hd, sizeof(hd));
+ memcpy(skb_push(skb_new, sizeof(hdlc_start)), hdlc_start,
+ sizeof(hdlc_start));
+ memcpy(skb_put(skb_new, sizeof(hdlc_end)), hdlc_end, sizeof(hdlc_end));
+
+ ret = iod->link->send(iod->link, iod, skb_new);
+ if (ret < 0) {
+ netif_stop_queue(ndev);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_BUSY;
+ }
+
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_ops vnet_ops = {
+ .ndo_open = vnet_open,
+ .ndo_stop = vnet_stop,
+ .ndo_start_xmit = vnet_xmit,
+};
+
+static void vnet_setup(struct net_device *ndev)
+{
+ ndev->netdev_ops = &vnet_ops;
+ ndev->type = ARPHRD_PPP;
+ ndev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ ndev->hard_header_len = 0;
+ ndev->addr_len = 0;
+ ndev->tx_queue_len = 1000;
+ ndev->mtu = ETH_DATA_LEN;
+ ndev->watchdog_timeo = 5 * HZ;
+}
+
+int init_io_device(struct io_device *iod)
+{
+ int ret = 0;
+ struct vnet *vnet;
+
+ /* get modem state from modem control device */
+ iod->modem_state_changed = io_dev_modem_state_changed;
+ /* get data from link device */
+ iod->recv = io_dev_recv_data_from_link_dev;
+
+ INIT_LIST_HEAD(&iod->list);
+
+ /* register misc or net drv */
+ switch (iod->io_typ) {
+ case IODEV_MISC:
+ init_waitqueue_head(&iod->wq);
+ skb_queue_head_init(&iod->sk_rx_q);
+
+ iod->miscdev.minor = MISC_DYNAMIC_MINOR;
+ iod->miscdev.name = iod->name;
+ iod->miscdev.fops = &misc_io_fops;
+
+ ret = misc_register(&iod->miscdev);
+ if (ret)
+ pr_err("failed to register misc io device : %s\n",
+ iod->name);
+
+ break;
+
+ case IODEV_NET:
+ iod->ndev = alloc_netdev(0, iod->name, vnet_setup);
+ if (!iod->ndev) {
+ pr_err("failed to alloc netdev\n");
+ return -ENOMEM;
+ }
+
+ ret = register_netdev(iod->ndev);
+ if (ret)
+ free_netdev(iod->ndev);
+
+ pr_err("%s: %d(iod:0x%p)", __func__, __LINE__, iod);
+ vnet = netdev_priv(iod->ndev);
+ pr_err("%s: %d(vnet:0x%p)", __func__, __LINE__, vnet);
+ vnet->iod = iod;
+
+ break;
+
+ case IODEV_DUMMY:
+ break;
+
+ default:
+ pr_err("wrong io_type : %d\n", iod->io_typ);
+ return -EINVAL;
+ }
+
+ pr_info("[MODEM_IF] %s(%d) : init_io_device() done : %d\n",
+ iod->name, iod->io_typ, ret);
+ return ret;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_mipi.c b/drivers/misc/modem_if/modem_link_device_mipi.c
new file mode 100755
index 00000000000..84ab0c3d521
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_mipi.c
@@ -0,0 +1,1293 @@
+/* /linux/drivers/new_modem_if/link_dev_mipi.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/module.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <linux/gpio.h>
+#include <linux/if_arp.h>
+
+#include <linux/hsi_driver_if.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_mipi.h"
+
+
+static int mipi_hsi_attach_io_dev(struct link_device *ld,
+ struct io_device *iod)
+{
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ iod->link = ld;
+
+ /* list up io devices */
+ list_add_tail(&iod->list, &mipi_ld->list_of_io_devices);
+ return 0;
+}
+
+static int mipi_hsi_init_communication(struct link_device *ld,
+ struct io_device *iod)
+{
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ return hsi_init_handshake(mipi_ld, HSI_INIT_MODE_NORMAL);
+
+ case IPC_BOOT:
+ return hsi_init_handshake(mipi_ld,
+ HSI_INIT_MODE_FLASHLESS_BOOT);
+
+ case IPC_RFS:
+ case IPC_RAW:
+ default:
+ return 0;
+ }
+}
+
+static int mipi_hsi_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ int ret;
+ u32 mipi_boot_len = 0;
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ struct sk_buff_head *txq;
+
+ switch (iod->format) {
+ case IPC_RAW:
+ txq = &ld->sk_raw_tx_q;
+ break;
+
+ case IPC_BOOT: /* sync operation */
+ ret = if_hsi_write(&mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL],
+ (u32 *)skb->data, skb->len);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] write fail : %d\n", ret);
+ dev_kfree_skb_any(skb);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] write Done\n");
+ dev_kfree_skb_any(skb);
+
+ ret = if_hsi_read(&mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL],
+ (const char *)&mipi_boot_len, 4);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] read len fail : %d\n", ret);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] read len Done : %d\n",
+ mipi_boot_len);
+
+ ret = if_hsi_read(&mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL],
+ (const char *)mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].rx_data,
+ mipi_boot_len);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] read len fail : %d\n", ret);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] read len Done\n");
+
+ ret = iod->recv(iod, (char *)mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].rx_data, mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].rx_count);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] iod recv fail : %d\n", ret);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] iod recv Done : %d\n", ret);
+
+ return mipi_boot_len;
+
+ case IPC_FMT:
+ case IPC_RFS:
+ default:
+ txq = &ld->sk_fmt_tx_q;
+ break;
+ }
+
+ /* save io device into cb area */
+ *((struct io_device **)skb->cb) = iod;
+ /* en queue skb data */
+ skb_queue_tail(txq, skb);
+
+ if (!work_pending(&ld->tx_work))
+ queue_work(ld->tx_wq, &ld->tx_work);
+
+ return skb->len;
+}
+
+static void mipi_hsi_tx_work(struct work_struct *work)
+{
+ int ret;
+ struct link_device *ld = container_of(work, struct link_device,
+ tx_work);
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+ struct io_device *iod;
+ struct sk_buff *fmt_skb;
+ struct sk_buff *raw_skb;
+ int send_channel = 0;
+
+ while (ld->sk_fmt_tx_q.qlen || ld->sk_raw_tx_q.qlen) {
+ pr_debug("[MIPI-HSI] fmt qlen : %d, raw qlen:%d\n",
+ ld->sk_fmt_tx_q.qlen, ld->sk_raw_tx_q.qlen);
+
+ fmt_skb = skb_dequeue(&ld->sk_fmt_tx_q);
+ if (fmt_skb) {
+ iod = *((struct io_device **)fmt_skb->cb);
+
+ pr_debug("[MIPI-HSI] dequeue. fmt qlen : %d\n",
+ ld->sk_fmt_tx_q.qlen);
+
+ if (ld->com_state != COM_ONLINE) {
+ pr_info("[MIPI-HSI] CP not ready\n");
+ skb_queue_head(&ld->sk_fmt_tx_q, fmt_skb);
+ return;
+ }
+
+ switch (iod->format) {
+ case IPC_FMT:
+ send_channel = HSI_FMT_CHANNEL;
+ break;
+
+ case IPC_RFS:
+ send_channel = HSI_RFS_CHANNEL;
+ break;
+
+ case IPC_BOOT:
+ send_channel = HSI_FLASHLESS_CHANNEL;
+ break;
+ default:
+ break;
+ }
+ ret = if_hsi_protocol_send(mipi_ld, send_channel,
+ (u32 *)fmt_skb->data, fmt_skb->len);
+ if (ret < 0) {
+ /* TODO: Re Enqueue */
+ pr_err("[MIPI-HSI] write fail : %d\n", ret);
+ } else
+ pr_debug("[MIPI-HSI] write Done\n");
+
+ dev_kfree_skb_any(fmt_skb);
+ }
+
+ raw_skb = skb_dequeue(&ld->sk_raw_tx_q);
+ if (raw_skb) {
+ if (ld->com_state != COM_ONLINE) {
+ pr_info("[MIPI-HSI] RAW CP not ready\n");
+ skb_queue_head(&ld->sk_raw_tx_q, raw_skb);
+ return;
+ }
+
+ pr_debug("[MIPI-HSI] dequeue. raw qlen:%d\n",
+ ld->sk_raw_tx_q.qlen);
+
+ ret = if_hsi_protocol_send(mipi_ld, HSI_RAW_CHANNEL,
+ (u32 *)raw_skb->data, raw_skb->len);
+ if (ret < 0) {
+ /* TODO: Re Enqueue */
+ pr_err("[MIPI-HSI] write fail : %d\n", ret);
+ } else
+ pr_debug("[MIPI-HSI] write Done\n");
+
+ dev_kfree_skb_any(raw_skb);
+ }
+ }
+}
+
+static int __devinit if_hsi_probe(struct hsi_device *dev);
+static struct hsi_device_driver if_hsi_driver = {
+ .ctrl_mask = ANY_HSI_CONTROLLER,
+ .probe = if_hsi_probe,
+ .driver = {
+ .name = "if_hsi_driver"
+ },
+};
+
+static int if_hsi_set_wakeline(struct if_hsi_channel *channel,
+ unsigned int state)
+{
+ int ret;
+
+ ret = hsi_ioctl(channel->dev, state ?
+ HSI_IOCTL_ACWAKE_UP : HSI_IOCTL_ACWAKE_DOWN, NULL);
+ if (ret) {
+ pr_debug("[MIPI-HSI] ACWAKE(%d) setting fail : %d\n", state,
+ ret);
+ return ret;
+ }
+
+ pr_info("[MIPI-HSI] ACWAKE_%d(%d)\n", channel->channel_id, state);
+ return 0;
+}
+
+static void if_hsi_acwake_down_func(unsigned long data)
+{
+ int i;
+ struct if_hsi_channel *channel;
+ struct mipi_link_device *mipi_ld = (struct mipi_link_device *)data;
+
+ pr_info("[MIPI-HSI] %s\n", __func__);
+
+ for (i = 1; i < HSI_NUM_OF_USE_CHANNELS; i++) {
+ channel = &mipi_ld->hsi_channles[i];
+
+ if (channel->send_step == STEP_IDLE) {
+ if_hsi_set_wakeline(channel, 0);
+ } else {
+ mod_timer(&mipi_ld->hsi_acwake_down_timer, jiffies +
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ pr_info("[MIPI-HSI] mod_timer done(%d)\n",
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ return;
+ }
+ }
+}
+
+static int if_hsi_open_channel(struct if_hsi_channel *channel)
+{
+ int ret;
+
+ if (channel->opened) {
+ pr_debug("[MIPI-HSI] channel %d is already opened\n",
+ channel->channel_id);
+ return 0;
+ }
+
+ ret = hsi_open(channel->dev);
+ if (ret) {
+ pr_err("[MIPI-HSI] hsi_open fail : %d\n", ret);
+ return ret;
+ }
+ channel->opened = 1;
+
+ channel->send_step = STEP_IDLE;
+ channel->recv_step = STEP_IDLE;
+
+ pr_info("[MIPI-HSI] hsi_open Done : %d\n", channel->channel_id);
+ return 0;
+}
+
+static int if_hsi_close_channel(struct if_hsi_channel *channel)
+{
+ unsigned long int flags;
+
+ if (!channel->opened) {
+ pr_debug("[MIPI-HSI] channel %d is already closed\n",
+ channel->channel_id);
+ return 0;
+ }
+
+ if_hsi_set_wakeline(channel, 0);
+ hsi_write_cancel(channel->dev);
+ hsi_read_cancel(channel->dev);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ hsi_ioctl(channel->dev, HSI_IOCTL_SW_RESET, NULL);
+
+ hsi_close(channel->dev);
+ channel->opened = 0;
+
+ channel->send_step = STEP_CLOSED;
+ channel->recv_step = STEP_CLOSED;
+
+ pr_info("[MIPI-HSI] hsi_close Done : %d\n", channel->channel_id);
+ return 0;
+}
+
+static void mipi_hsi_start_work(struct work_struct *work)
+{
+ int ret;
+ u32 start_cmd = 0xC2;
+ struct mipi_link_device *mipi_ld =
+ container_of(work, struct mipi_link_device,
+ start_work.work);
+
+ ret = if_hsi_protocol_send(mipi_ld, HSI_CMD_CHANNEL, &start_cmd, 1);
+ if (ret < 0) {
+ /* TODO: Re Enqueue */
+ pr_err("[MIPI-HSI] First write fail : %d\n", ret);
+ } else {
+ pr_info("[MIPI-HSI] First write Done : %d\n", ret);
+ mipi_ld->ld.com_state = COM_ONLINE;
+ }
+}
+
+static int hsi_init_handshake(struct mipi_link_device *mipi_ld, int mode)
+{
+ int ret;
+ int i;
+ struct hst_ctx tx_config;
+ struct hsr_ctx rx_config;
+
+ switch (mode) {
+ case HSI_INIT_MODE_NORMAL:
+ if (timer_pending(&mipi_ld->hsi_acwake_down_timer))
+ del_timer(&mipi_ld->hsi_acwake_down_timer);
+
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++) {
+ if_hsi_close_channel(&mipi_ld->hsi_channles[i]);
+ ret = if_hsi_open_channel(&mipi_ld->hsi_channles[i]);
+ if (ret)
+ return ret;
+
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 0; /* Speed : 96MHz */
+ tx_config.channels = HSI_MAX_CHANNELS;
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_SET_TX, &tx_config);
+
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 0; /* Speed : 96MHz */
+ rx_config.channels = HSI_MAX_CHANNELS;
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_SET_RX, &rx_config);
+ pr_debug("[MIPI-HSI] Set TX/RX MIPI-HSI\n");
+
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_SET_ACREADY_NORMAL, NULL);
+ pr_debug("[MIPI-HSI] ACREADY_NORMAL\n");
+ }
+
+ mipi_ld->ld.com_state = COM_HANDSHAKE;
+
+ ret = hsi_read(mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].rx_data,
+ 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ schedule_delayed_work(&mipi_ld->start_work, 3 * HZ);
+
+ pr_info("[MIPI-HSI] hsi_init_handshake Done : MODE_NORMAL\n");
+ return 0;
+
+ case HSI_INIT_MODE_FLASHLESS_BOOT:
+ case HSI_INIT_MODE_CP_RAMDUMP:
+ mipi_ld->ld.com_state = COM_BOOT;
+
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++)
+ if_hsi_close_channel(&mipi_ld->hsi_channles[i]);
+ ret = if_hsi_open_channel(
+ &mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL]);
+ if (ret)
+ return ret;
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 0; /* Speed : 96MHz */
+ tx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_TX, &tx_config);
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 0; /* Speed : 96MHz */
+ rx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_RX, &rx_config);
+ pr_debug("[MIPI-HSI] Set TX/RX MIPI-HSI\n");
+
+ if_hsi_set_wakeline(
+ &mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL], 1);
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_ACREADY_NORMAL, NULL);
+ pr_debug("[MIPI-HSI] ACREADY_NORMAL\n");
+
+ pr_info("[MIPI-HSI] hsi_init_handshake Done : FLASHLESS_BOOT\n");
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static u32 if_hsi_create_cmd(u32 cmd_type, int ch, void *arg)
+{
+ u32 cmd = 0;
+ unsigned int size = 0;
+
+ switch (cmd_type) {
+ case HSI_LL_MSG_BREAK:
+ return 0;
+
+ case HSI_LL_MSG_CONN_CLOSED:
+ cmd = ((HSI_LL_MSG_CONN_CLOSED & 0x0000000F) << 28)
+ |((ch & 0x000000FF) << 24);
+ return cmd;
+
+ case HSI_LL_MSG_ACK:
+ size = *(unsigned int *)arg;
+
+ cmd = ((HSI_LL_MSG_ACK & 0x0000000F) << 28)
+ |((ch & 0x000000FF) << 24) | ((size & 0x00FFFFFF));
+ return cmd;
+
+ case HSI_LL_MSG_NAK:
+ cmd = ((HSI_LL_MSG_NAK & 0x0000000F) << 28)
+ |((ch & 0x000000FF) << 24);
+ return cmd;
+
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ size = *(unsigned int *)arg;
+
+ cmd = ((HSI_LL_MSG_OPEN_CONN_OCTET & 0x0000000F)
+ << 28) | ((ch & 0x000000FF) << 24)
+ | ((size & 0x00FFFFFF));
+ return cmd;
+
+ case HSI_LL_MSG_OPEN_CONN:
+ case HSI_LL_MSG_CONF_RATE:
+ case HSI_LL_MSG_CANCEL_CONN:
+ case HSI_LL_MSG_CONN_READY:
+ case HSI_LL_MSG_ECHO:
+ case HSI_LL_MSG_INFO_REQ:
+ case HSI_LL_MSG_INFO:
+ case HSI_LL_MSG_CONFIGURE:
+ case HSI_LL_MSG_ALLOCATE_CH:
+ case HSI_LL_MSG_RELEASE_CH:
+ case HSI_LL_MSG_INVALID:
+ default:
+ pr_err("[MIPI-HSI] ERROR... CMD Not supported : %08x\n",
+ cmd_type);
+ return -EINVAL;
+ }
+}
+
+static void if_hsi_cmd_work(struct work_struct *work)
+{
+ int ret;
+ struct mipi_link_device *mipi_ld =
+ container_of(work, struct mipi_link_device, cmd_work);
+ struct if_hsi_channel *channel =
+ &mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL];
+ struct if_hsi_command *hsi_cmd;
+
+ pr_debug("[MIPI-HSI] cmd_work\n");
+
+ if (!list_empty(&mipi_ld->list_of_hsi_cmd))
+ if_hsi_set_wakeline(channel, 1);
+ else
+ return;
+
+ do {
+ hsi_cmd = list_entry(mipi_ld->list_of_hsi_cmd.next,
+ struct if_hsi_command, list);
+ pr_debug("[MIPI-HSI] take command : %08x\n", hsi_cmd->command);
+
+ ret = if_hsi_write(channel, &hsi_cmd->command, 4);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] write command fail : %d\n", ret);
+ if_hsi_set_wakeline(channel, 0);
+ return;
+ }
+ pr_info("[MIPI-HSI] SEND CMD : %08x\n", hsi_cmd->command);
+
+ list_del(&hsi_cmd->list);
+ kfree(hsi_cmd);
+ } while (!list_empty(&mipi_ld->list_of_hsi_cmd));
+
+ if_hsi_set_wakeline(channel, 0);
+}
+
+static int if_hsi_send_command(struct mipi_link_device *mipi_ld,
+ u32 cmd_type, int ch, u32 param)
+{
+ struct if_hsi_command *hsi_cmd;
+
+ hsi_cmd = kmalloc(sizeof(struct if_hsi_command), GFP_ATOMIC);
+ if (!hsi_cmd) {
+ pr_err("[MIPI-HSI] hsi_cmd kmalloc fail\n");
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&hsi_cmd->list);
+
+ hsi_cmd->command = if_hsi_create_cmd(cmd_type, ch, &param);
+ pr_debug("[MIPI-HSI] made command : %08x\n", hsi_cmd->command);
+
+ list_add_tail(&hsi_cmd->list, &mipi_ld->list_of_hsi_cmd);
+
+ pr_debug("[MIPI-HSI] queue_work : cmd_work\n");
+ if (!work_pending(&mipi_ld->cmd_work))
+ queue_work(mipi_ld->mipi_wq, &mipi_ld->cmd_work);
+
+ return 0;
+}
+
+static int if_hsi_decode_cmd(u32 *cmd_data, u32 *cmd, u32 *ch,
+ u32 *param)
+{
+ u32 data = *cmd_data;
+ u8 lrc_cal, lrc_act;
+ u8 val1, val2, val3;
+
+ *cmd = ((data & 0xF0000000) >> 28);
+ switch (*cmd) {
+ case HSI_LL_MSG_BREAK:
+ pr_err("[MIPI-HSI] Command MSG_BREAK Received\n");
+ return -1;
+
+ case HSI_LL_MSG_OPEN_CONN:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = ((data & 0x00FFFF00) >> 8);
+ val1 = ((data & 0xFF000000) >> 24);
+ val2 = ((data & 0x00FF0000) >> 16);
+ val3 = ((data & 0x0000FF00) >> 8);
+ lrc_act = (data & 0x000000FF);
+ lrc_cal = val1 ^ val2 ^ val3;
+
+ if (lrc_cal != lrc_act) {
+ pr_err("[MIPI-HSI] CAL is broken\n");
+ return -1;
+ }
+ return 0;
+
+ case HSI_LL_MSG_CONN_READY:
+ case HSI_LL_MSG_CONN_CLOSED:
+ case HSI_LL_MSG_CANCEL_CONN:
+ case HSI_LL_MSG_NAK:
+ *ch = ((data & 0x0F000000) >> 24);
+ return 0;
+
+ case HSI_LL_MSG_ACK:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = (data & 0x00FFFFFF);
+ return 0;
+
+ case HSI_LL_MSG_CONF_RATE:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = ((data & 0x0F000000) >> 24);
+ return 0;
+
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = (data & 0x00FFFFFF);
+ return 0;
+
+ case HSI_LL_MSG_ECHO:
+ case HSI_LL_MSG_INFO_REQ:
+ case HSI_LL_MSG_INFO:
+ case HSI_LL_MSG_CONFIGURE:
+ case HSI_LL_MSG_ALLOCATE_CH:
+ case HSI_LL_MSG_RELEASE_CH:
+ case HSI_LL_MSG_INVALID:
+ default:
+ pr_err("[MIPI-HSI] Invalid command received : %08x\n", *cmd);
+ *cmd = HSI_LL_MSG_INVALID;
+ *ch = HSI_LL_INVALID_CHANNEL;
+ return -1;
+ }
+ return 0;
+}
+
+static int if_hsi_rx_cmd_handle(struct mipi_link_device *mipi_ld, u32 cmd,
+ u32 ch, u32 param)
+{
+ int ret;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[ch];
+
+ pr_debug("[MIPI-HSI] if_hsi_rx_cmd_handle cmd=0x%x, ch=%d, param=%d\n",
+ cmd, ch, param);
+
+ switch (cmd) {
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ switch (channel->recv_step) {
+ case STEP_IDLE:
+ channel->recv_step = STEP_TO_ACK;
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_ACK, ch,
+ param);
+ if (ret) {
+ pr_err("[MIPI-HSI] if_hsi_send_command fail : %d\n",
+ ret);
+ return ret;
+ }
+
+ channel->packet_size = param;
+ channel->recv_step = STEP_RX;
+ if (param % 4)
+ param += (4 - (param % 4));
+ channel->rx_count = param;
+ ret = hsi_read(channel->dev, channel->rx_data,
+ channel->rx_count / 4);
+ if (ret) {
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+ return ret;
+ }
+ return 0;
+
+ default:
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_NAK, ch,
+ param);
+ if (ret) {
+ pr_err("[MIPI-HSI] if_hsi_send_command fail : %d\n",
+ ret);
+ return ret;
+ }
+ return 0;
+ }
+
+ case HSI_LL_MSG_ACK:
+ case HSI_LL_MSG_NAK:
+ switch (channel->send_step) {
+ case STEP_WAIT_FOR_ACK:
+ case STEP_SEND_OPEN_CONN:
+ if (cmd == HSI_LL_MSG_ACK) {
+ channel->send_step = STEP_TX;
+ channel->got_nack = 0;
+ pr_debug("[MIPI-HSI] got ack\n");
+ } else {
+ channel->send_step = STEP_WAIT_FOR_ACK;
+ channel->got_nack = 1;
+ pr_debug("[MIPI-HSI] got nack\n");
+ }
+
+ up(&channel->ack_done_sem);
+ return 0;
+
+ default:
+ pr_err("[MIPI-HSI] wrong state : %08x\n", cmd);
+ return -1;
+ }
+
+ case HSI_LL_MSG_CONN_CLOSED:
+ switch (channel->send_step) {
+ case STEP_TX:
+ case STEP_WAIT_FOR_CONN_CLOSED:
+ pr_debug("[MIPI-HSI] got close\n");
+
+ channel->send_step = STEP_IDLE;
+ up(&channel->close_conn_done_sem);
+ return 0;
+
+ default:
+ pr_err("[MIPI-HSI] wrong state : %08x\n", cmd);
+ return -1;
+ }
+
+ case HSI_LL_MSG_OPEN_CONN:
+ case HSI_LL_MSG_ECHO:
+ case HSI_LL_MSG_CANCEL_CONN:
+ case HSI_LL_MSG_CONF_RATE:
+ default:
+ pr_err("[MIPI-HSI] ERROR... CMD Not supported : %08x\n", cmd);
+ return -EINVAL;
+ }
+}
+
+static int if_hsi_protocol_send(struct mipi_link_device *mipi_ld, int ch,
+ u32 *data, unsigned int len)
+{
+ int ret;
+ int retry_count = 0;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[ch];
+
+ if (channel->send_step != STEP_IDLE) {
+ pr_err("[MIPI-HSI] send step is not IDLE : %d\n",
+ channel->send_step);
+ return -EBUSY;
+ }
+ channel->send_step = STEP_SEND_OPEN_CONN;
+
+ if_hsi_set_wakeline(channel, 1);
+ mod_timer(&mipi_ld->hsi_acwake_down_timer, jiffies +
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ pr_info("[MIPI-HSI] mod_timer done(%d)\n",
+ HSI_ACWAKE_DOWN_TIMEOUT);
+
+retry_send:
+
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_OPEN_CONN_OCTET, ch,
+ len);
+ if (ret) {
+ pr_err("[MIPI-HSI] if_hsi_send_command fail : %d\n", ret);
+ channel->send_step = STEP_IDLE;
+ return -1;
+ }
+
+ channel->send_step = STEP_WAIT_FOR_ACK;
+
+ if (down_timeout(&channel->ack_done_sem, HSI_ACK_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, ack_done timeout\n",
+ channel->channel_id);
+
+ channel->send_step = STEP_IDLE;
+ return -ETIMEDOUT;
+ }
+ pr_debug("[MIPI-HSI] ch=%d, got ack_done=%d\n", channel->channel_id,
+ channel->got_nack);
+
+ if (channel->got_nack && (retry_count < 10)) {
+ pr_info("[MIPI-HSI] ch=%d, got nack=%d retry=%d\n",
+ channel->channel_id, channel->got_nack,
+ retry_count);
+ retry_count++;
+ msleep(1);
+ goto retry_send;
+ }
+ retry_count = 0;
+
+ channel->send_step = STEP_TX;
+
+ ret = if_hsi_write(channel, data, len);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] if_hsi_write fail : %d\n", ret);
+ channel->send_step = STEP_IDLE;
+ return ret;
+ }
+ pr_info("[MIPI-HSI] SEND DATA : %08x(%d)\n", *data, len);
+
+ pr_debug("%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ *channel->tx_data, *(channel->tx_data + 1),
+ *(channel->tx_data + 2), *(channel->tx_data + 3),
+ *(channel->tx_data + 4), *(channel->tx_data + 5),
+ *(channel->tx_data + 6), *(channel->tx_data + 7));
+
+ channel->send_step = STEP_WAIT_FOR_CONN_CLOSED;
+ if (down_timeout(&channel->close_conn_done_sem,
+ HSI_CLOSE_CONN_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, close conn timeout\n",
+ channel->channel_id);
+ channel->send_step = STEP_IDLE;
+ return -ETIMEDOUT;
+ }
+ pr_debug("[MIPI-HSI] ch=%d, got close_conn_done\n",
+ channel->channel_id);
+
+ channel->send_step = STEP_IDLE;
+
+ pr_debug("[MIPI-HSI] write protocol Done : %d\n", channel->tx_count);
+ return channel->tx_count;
+}
+
+static int if_hsi_write(struct if_hsi_channel *channel, u32 *data,
+ unsigned int size)
+{
+ int ret;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ if (channel->tx_state & HSI_CHANNEL_TX_STATE_WRITING) {
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+ return -EBUSY;
+ }
+ channel->tx_state |= HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ channel->tx_data = data;
+ if (size % 4)
+ size += (4 - (size % 4));
+ channel->tx_count = size;
+
+ pr_debug("[MIPI-HSI] submit write data : 0x%x(%d)\n",
+ *(u32 *)channel->tx_data, channel->tx_count);
+ ret = hsi_write(channel->dev, channel->tx_data, channel->tx_count / 4);
+ if (ret) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_write fail : %d\n",
+ channel->channel_id, ret);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ return ret;
+ }
+
+ if (down_timeout(&channel->write_done_sem,
+ HSI_WRITE_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_write_done timeout : %d\n",
+ channel->channel_id, size);
+
+ pr_err("[MIPI-HSI] data : %08x %08x %08x %08x %08x ...\n",
+ *channel->tx_data, *(channel->tx_data + 1),
+ *(channel->tx_data + 2), *(channel->tx_data + 3),
+ *(channel->tx_data + 4));
+
+ hsi_write_cancel(channel->dev);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ return -ETIMEDOUT;
+ }
+
+ if (channel->tx_count != size)
+ pr_err("[MIPI-HSI] ch:%d,write_done fail,write_size:%d,origin_size:%d\n",
+ channel->channel_id, channel->tx_count, size);
+
+ pr_debug("[MIPI-HSI] len:%d, id:%d, data : %08x %08x %08x %08x %08x ...\n",
+ channel->tx_count, channel->channel_id, *channel->tx_data,
+ *(channel->tx_data + 1), *(channel->tx_data + 2),
+ *(channel->tx_data + 3), *(channel->tx_data + 4));
+
+ return channel->tx_count;
+}
+
+static void if_hsi_write_done(struct hsi_device *dev, unsigned int size)
+{
+ unsigned long int flags;
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[dev->n_ch];
+
+ pr_debug("[MIPI-HSI] got write data : 0x%x(%d)\n",
+ *(u32 *)channel->tx_data, size);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ pr_debug("%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ *channel->tx_data, *(channel->tx_data + 1),
+ *(channel->tx_data + 2), *(channel->tx_data + 3),
+ *(channel->tx_data + 4), *(channel->tx_data + 5),
+ *(channel->tx_data + 6), *(channel->tx_data + 7));
+
+ channel->tx_count = 4 * size;
+ up(&channel->write_done_sem);
+}
+
+static int if_hsi_read(struct if_hsi_channel *channel, const char *data,
+ unsigned int size)
+{
+ int ret;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ if (channel->rx_state & HSI_CHANNEL_RX_STATE_READING) {
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+ return -EBUSY;
+ }
+ channel->rx_state |= HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ if (size % 4)
+ size += (4 - (size % 4));
+ channel->rx_count = size;
+
+ ret = hsi_read(channel->dev, (u32 *)data, channel->rx_count / 4);
+ if (ret) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_read fail : %d\n",
+ channel->channel_id, ret);
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ return ret;
+ }
+
+ pr_debug("[MIPI-HSI] submit read data : 0x%x(%d)\n",
+ *(u32 *)channel->rx_data, channel->rx_count);
+
+ if (down_timeout(&channel->read_done_sem,
+ HSI_READ_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_read_done timeout : %d\n",
+ channel->channel_id, channel->rx_count);
+
+ hsi_read_cancel(channel->dev);
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ return -ETIMEDOUT;
+ }
+
+ if (channel->rx_count != size)
+ pr_err("[MIPI-HSI] ch:%d,read_done fail,read_size:%d,origin_size:%d\n",
+ channel->channel_id, channel->rx_count, size);
+
+ return channel->rx_count;
+}
+
+static void if_hsi_read_done(struct hsi_device *dev, unsigned int size)
+{
+ int ret;
+ unsigned long int flags;
+ u32 cmd = 0, ch = 0, param = 0;
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[dev->n_ch];
+ struct io_device *iod;
+ enum dev_format format_type = 0;
+
+ pr_debug("[MIPI-HSI] got read data : 0x%x(%d)\n",
+ *(u32 *)channel->rx_data, size);
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ channel->rx_count = 4 * size;
+
+ switch (channel->channel_id) {
+ case HSI_CONTROL_CHANNEL:
+ switch (mipi_ld->ld.com_state) {
+ case COM_HANDSHAKE:
+ case COM_ONLINE:
+ pr_info("[MIPI-HSI] RECV CMD : %08x\n",
+ *channel->rx_data);
+
+ if (channel->rx_count != 4) {
+ pr_err("[MIPI-HSI] wrong command len : %d\n",
+ channel->rx_count);
+ return;
+ }
+
+ ret = if_hsi_decode_cmd(channel->rx_data, &cmd, &ch,
+ &param);
+ if (ret)
+ pr_err("[MIPI-HSI] decode_cmd fail=%d, cmd=%x\n",
+ ret, cmd);
+ else {
+ pr_debug("[MIPI-HSI] decode_cmd : %08x\n", cmd);
+ ret = if_hsi_rx_cmd_handle(mipi_ld, cmd, ch,
+ param);
+ if (ret)
+ pr_err("[MIPI-HSI] handle cmd cmd=%x\n",
+ cmd);
+ }
+
+ ret = hsi_read(channel->dev, channel->rx_data, 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ return;
+
+ case COM_BOOT:
+ pr_debug("[MIPI-HSI] receive data : 0x%x(%d)\n",
+ *channel->rx_data, channel->rx_count);
+
+ up(&channel->read_done_sem);
+ return;
+
+ case COM_CRASH:
+ case COM_NONE:
+ default:
+ pr_info("[MIPI-HSI] receive data in wrong state : 0x%x(%d)\n",
+ *channel->rx_data, channel->rx_count);
+ up(&channel->read_done_sem);
+ return;
+ }
+ break;
+
+ case HSI_FMT_CHANNEL:
+ pr_debug("[MIPI-HSI] iodevice format : IPC_FMT\n");
+ format_type = IPC_FMT;
+ break;
+ case HSI_RAW_CHANNEL:
+ pr_debug("[MIPI-HSI] iodevice format : IPC_MULTI_RAW\n");
+ format_type = IPC_MULTI_RAW;
+ break;
+ case HSI_RFS_CHANNEL:
+ pr_debug("[MIPI-HSI] iodevice format : IPC_RFS\n");
+ format_type = IPC_RFS;
+ break;
+
+ case HSI_CMD_CHANNEL:
+ pr_info("[MIPI-HSI] receive command data : 0x%x\n",
+ *channel->rx_data);
+ return;
+
+ default:
+ up(&channel->read_done_sem);
+ return;
+ }
+
+ list_for_each_entry(iod, &mipi_ld->list_of_io_devices, list) {
+ pr_debug("[MIPI-HSI] iodevice format : %d\n", iod->format);
+
+ if (iod->format == format_type) {
+ pr_info("[MIPI-HSI] RECV DATA : %08x(%d)-%d\n",
+ *channel->rx_data, channel->packet_size,
+ iod->format);
+
+ pr_debug("%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ *channel->rx_data, *(channel->rx_data + 1),
+ *(channel->rx_data + 2), *(channel->rx_data + 3),
+ *(channel->rx_data + 4), *(channel->rx_data + 5),
+ *(channel->rx_data + 6), *(channel->rx_data + 7));
+
+ ret = iod->recv(iod, (char *)channel->rx_data,
+ channel->packet_size);
+ if (ret < 0)
+ pr_err("[MIPI-HSI] recv call fail : %d\n", ret);
+
+ if (channel->recv_step == STEP_RX) {
+ ch = channel->channel_id;
+ param = 0;
+ ret = if_hsi_send_command(mipi_ld,
+ HSI_LL_MSG_CONN_CLOSED, ch, param);
+ if (ret)
+ pr_err("[MIPI-HSI] send_cmd fail=%d\n",
+ ret);
+
+ channel->recv_step = STEP_IDLE;
+ }
+ return;
+ }
+ }
+}
+
+static void if_hsi_port_event(struct hsi_device *dev, unsigned int event,
+ void *arg)
+{
+ int acwake_level = 1;
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+
+ switch (event) {
+ case HSI_EVENT_BREAK_DETECTED:
+ pr_err("[MIPI-HSI] HSI_EVENT_BREAK_DETECTED\n");
+ return;
+
+ case HSI_EVENT_HSR_DATAAVAILABLE:
+ pr_err("[MIPI-HSI] HSI_EVENT_HSR_DATAAVAILABLE\n");
+ return;
+
+ case HSI_EVENT_CAWAKE_UP:
+ if (dev->n_ch == HSI_CONTROL_CHANNEL)
+ pr_info("[MIPI-HSI] CAWAKE_%d(1)\n", dev->n_ch);
+
+ if ((dev->n_ch == HSI_CONTROL_CHANNEL) &&
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].opened) {
+ hsi_ioctl(
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ HSI_IOCTL_SET_ACREADY_NORMAL, NULL);
+
+ pr_debug("[MIPI-HSI] ACREADY_NORMAL. Ch : %d\n",
+ dev->n_ch);
+ }
+
+ return;
+
+ case HSI_EVENT_CAWAKE_DOWN:
+ if (dev->n_ch == HSI_CONTROL_CHANNEL)
+ pr_info("[MIPI-HSI] CAWAKE_%d(0)\n", dev->n_ch);
+
+ if ((dev->n_ch == HSI_CONTROL_CHANNEL) &&
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].opened) {
+ hsi_ioctl(
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ HSI_IOCTL_GET_ACWAKE, &acwake_level);
+
+ pr_debug("[MIPI-HSI] GET_ACWAKE. Ch : %d, level : %d\n",
+ dev->n_ch, acwake_level);
+ }
+
+ if ((dev->n_ch == HSI_CONTROL_CHANNEL) &&
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].opened &&
+ !acwake_level) {
+ hsi_ioctl(
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ HSI_IOCTL_SET_ACREADY_SAFEMODE, NULL);
+
+ pr_debug("[MIPI-HSI] ACREADY_SAFEMODE. Ch : %d\n",
+ dev->n_ch);
+ }
+
+ return;
+
+ case HSI_EVENT_ERROR:
+ pr_err("[MIPI-HSI] HSI_EVENT_ERROR\n");
+ return;
+
+ default:
+ pr_err("[MIPI-HSI] Unknown Event : %d\n", event);
+ return;
+ }
+}
+
+static int __devinit if_hsi_probe(struct hsi_device *dev)
+{
+ int port = 0;
+ unsigned long *address;
+
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_driver.ch_mask[port])
+ break;
+ }
+ address = (unsigned long *)&if_hsi_driver.ch_mask[port];
+
+ if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+ /* Register callback func */
+ hsi_set_write_cb(dev, if_hsi_write_done);
+ hsi_set_read_cb(dev, if_hsi_read_done);
+ hsi_set_port_event_cb(dev, if_hsi_port_event);
+
+ /* Init device data */
+ mipi_ld->hsi_channles[dev->n_ch].dev = dev;
+ mipi_ld->hsi_channles[dev->n_ch].tx_count = 0;
+ mipi_ld->hsi_channles[dev->n_ch].rx_count = 0;
+ mipi_ld->hsi_channles[dev->n_ch].tx_state = 0;
+ mipi_ld->hsi_channles[dev->n_ch].rx_state = 0;
+ mipi_ld->hsi_channles[dev->n_ch].packet_size = 0;
+ mipi_ld->hsi_channles[dev->n_ch].send_step = STEP_UNDEF;
+ mipi_ld->hsi_channles[dev->n_ch].recv_step = STEP_UNDEF;
+ spin_lock_init(&mipi_ld->hsi_channles[dev->n_ch].tx_state_lock);
+ spin_lock_init(&mipi_ld->hsi_channles[dev->n_ch].rx_state_lock);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].write_done_sem, 0);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].read_done_sem, 0);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].ack_done_sem, 0);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].close_conn_done_sem,
+ 0);
+ }
+
+ pr_info("[MIPI-HSI] if_hsi_probe() done. ch : %d\n", dev->n_ch);
+ return 0;
+}
+
+static int if_hsi_init(struct link_device *ld)
+{
+ int ret;
+ int i = 0;
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ for (i = 0; i < HSI_MAX_PORTS; i++)
+ if_hsi_driver.ch_mask[i] = 0;
+
+ for (i = 0; i < HSI_MAX_CHANNELS; i++) {
+ mipi_ld->hsi_channles[i].dev = NULL;
+ mipi_ld->hsi_channles[i].opened = 0;
+ mipi_ld->hsi_channles[i].channel_id = i;
+ }
+ if_hsi_driver.ch_mask[0] = CHANNEL_MASK;
+
+ /* TODO - need to get priv data (request to TI) */
+ if_hsi_driver.priv_data = (void *)mipi_ld;
+ ret = hsi_register_driver(&if_hsi_driver);
+ if (ret) {
+ pr_err("[MIPI-HSI] hsi_register_driver() fail : %d\n", ret);
+ return ret;
+ }
+
+ mipi_ld->mipi_wq = create_singlethread_workqueue("mipi_cmd_wq");
+ if (!mipi_ld->mipi_wq) {
+ pr_err("[MIPI-HSI] fail to create work Q.\n");
+ return -ENOMEM;
+ }
+ INIT_WORK(&mipi_ld->cmd_work, if_hsi_cmd_work);
+ INIT_DELAYED_WORK(&mipi_ld->start_work, mipi_hsi_start_work);
+
+ setup_timer(&mipi_ld->hsi_acwake_down_timer, if_hsi_acwake_down_func,
+ (unsigned long)mipi_ld);
+ pr_info("[MIPI-HSI] setup_timer done\n");
+
+ /* TODO - allocate rx buff */
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].rx_data =
+ kmalloc(64 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_CONTROL_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_FMT_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_FMT_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_FMT_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_RAW_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_RAW_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_RAW_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_RFS_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_RFS_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_RFS_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_CMD_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_CMD_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_CMD_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+
+ pr_info("[MIPI-HSI] if_hsi_init() done : %d\n", ret);
+ return 0;
+}
+
+struct link_device *mipi_create_link_device(struct platform_device *pdev)
+{
+ int ret;
+ struct mipi_link_device *mipi_ld;
+ struct link_device *ld;
+
+ /* for dpram int */
+ /* struct modem_data *pdata = pdev->dev.platform_data; */
+
+ mipi_ld = kzalloc(sizeof(struct mipi_link_device), GFP_KERNEL);
+ if (!mipi_ld)
+ return NULL;
+
+ INIT_LIST_HEAD(&mipi_ld->list_of_io_devices);
+ INIT_LIST_HEAD(&mipi_ld->list_of_hsi_cmd);
+ skb_queue_head_init(&mipi_ld->ld.sk_fmt_tx_q);
+ skb_queue_head_init(&mipi_ld->ld.sk_raw_tx_q);
+
+ ld = &mipi_ld->ld;
+
+ ld->name = "mipi_hsi";
+ ld->attach = mipi_hsi_attach_io_dev;
+ ld->init_comm = mipi_hsi_init_communication;
+ ld->send = mipi_hsi_send;
+ ld->com_state = COM_NONE;
+
+ /* for dpram int */
+ /* ld->irq = gpio_to_irq(pdata->gpio); s*/
+
+ ld->tx_wq = create_singlethread_workqueue("mipi_tx_wq");
+ if (!ld->tx_wq) {
+ pr_err("[MIPI-HSI] fail to create work Q.\n");
+ return NULL;
+ }
+ INIT_WORK(&ld->tx_work, mipi_hsi_tx_work);
+
+ ret = if_hsi_init(ld);
+ if (ret)
+ return NULL;
+
+ pr_info("[MODEM_IF] %s : create_io_device DONE\n", mipi_ld->ld.name);
+ return ld;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_mipi.h b/drivers/misc/modem_if/modem_link_device_mipi.h
new file mode 100755
index 00000000000..0f3b3ea73ee
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_mipi.h
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef __MODEM_LINK_DEVICE_MIPI_H__
+#define __MODEM_LINK_DEVICE_MIPI_H__
+
+
+#define HSI_MAX_CHANNELS 16
+#define CHANNEL_MASK 0xFF
+
+#define HSI_CHANNEL_TX_STATE_UNAVAIL (1 << 0)
+#define HSI_CHANNEL_TX_STATE_WRITING (1 << 1)
+#define HSI_CHANNEL_RX_STATE_UNAVAIL (1 << 0)
+#define HSI_CHANNEL_RX_STATE_READING (1 << 1)
+
+#define HSI_WRITE_DONE_TIMEOUT (HZ)
+#define HSI_READ_DONE_TIMEOUT (HZ)
+#define HSI_ACK_DONE_TIMEOUT (HZ)
+#define HSI_CLOSE_CONN_DONE_TIMEOUT (HZ)
+#define HSI_ACWAKE_DOWN_TIMEOUT (HZ / 2)
+
+#define HSI_CONTROL_CHANNEL 0
+#define HSI_FLASHLESS_CHANNEL 0
+#define HSI_CP_RAMDUMP_CHANNEL 0
+#define HSI_FMT_CHANNEL 1
+#define HSI_RAW_CHANNEL 2
+#define HSI_RFS_CHANNEL 3
+#define HSI_CMD_CHANNEL 4
+#define HSI_NUM_OF_USE_CHANNELS 5
+
+#define HSI_LL_INVALID_CHANNEL 0xFF
+
+
+enum {
+ HSI_LL_MSG_BREAK, /* 0x0 */
+ HSI_LL_MSG_ECHO,
+ HSI_LL_MSG_INFO_REQ,
+ HSI_LL_MSG_INFO,
+ HSI_LL_MSG_CONFIGURE,
+ HSI_LL_MSG_ALLOCATE_CH,
+ HSI_LL_MSG_RELEASE_CH,
+ HSI_LL_MSG_OPEN_CONN,
+ HSI_LL_MSG_CONN_READY,
+ HSI_LL_MSG_CONN_CLOSED, /* 0x9 */
+ HSI_LL_MSG_CANCEL_CONN,
+ HSI_LL_MSG_ACK, /* 0xB */
+ HSI_LL_MSG_NAK, /* 0xC */
+ HSI_LL_MSG_CONF_RATE,
+ HSI_LL_MSG_OPEN_CONN_OCTET, /* 0xE */
+ HSI_LL_MSG_INVALID = 0xFF,
+};
+
+enum {
+ STEP_UNDEF,
+ STEP_CLOSED,
+ STEP_NOT_READY,
+ STEP_IDLE,
+ STEP_ERROR,
+ STEP_SEND_OPEN_CONN,
+ STEP_SEND_ACK,
+ STEP_WAIT_FOR_ACK,
+ STEP_TO_ACK,
+ STEP_SEND_NACK,
+ STEP_GET_NACK,
+ STEP_SEND_CONN_READY,
+ STEP_WAIT_FOR_CONN_READY,
+ STEP_SEND_CONF_RATE,
+ STEP_WAIT_FOR_CONF_ACK,
+ STEP_TX,
+ STEP_RX,
+ STEP_SEND_CONN_CLOSED,
+ STEP_WAIT_FOR_CONN_CLOSED,
+ STEP_SEND_BREAK,
+};
+
+
+struct if_hsi_channel {
+ struct hsi_device *dev;
+ unsigned int channel_id;
+
+ u32 *tx_data;
+ unsigned int tx_count;
+ u32 *rx_data;
+ unsigned int rx_count;
+ unsigned int packet_size;
+
+ unsigned int tx_state;
+ unsigned int rx_state;
+ spinlock_t tx_state_lock;
+ spinlock_t rx_state_lock;
+
+ unsigned int send_step;
+ unsigned int recv_step;
+
+ unsigned int got_nack;
+
+ struct semaphore write_done_sem;
+ struct semaphore read_done_sem;
+ struct semaphore ack_done_sem;
+ struct semaphore close_conn_done_sem;
+
+ unsigned int opened;
+};
+
+struct if_hsi_command {
+ u32 command;
+ struct list_head list;
+};
+
+struct mipi_link_device {
+ struct link_device ld;
+
+ /* mipi specific link data */
+ struct if_hsi_channel hsi_channles[HSI_MAX_CHANNELS];
+ struct list_head list_of_hsi_cmd;
+
+ struct workqueue_struct *mipi_wq;
+ struct work_struct cmd_work;
+ struct delayed_work start_work;
+
+ struct timer_list hsi_acwake_down_timer;
+
+ /* maybe -list of io devices for the link device to use
+ * to find where to send incoming packets to */
+ struct list_head list_of_io_devices;
+};
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_mipi_link_device(linkdev) \
+ container_of(linkdev, struct mipi_link_device, ld)
+
+
+enum {
+ HSI_INIT_MODE_NORMAL,
+ HSI_INIT_MODE_FLASHLESS_BOOT,
+ HSI_INIT_MODE_CP_RAMDUMP,
+};
+static int hsi_init_handshake(struct mipi_link_device *mipi_ld, int mode);
+static int if_hsi_write(struct if_hsi_channel *channel, u32 *data,
+ unsigned int size);
+static int if_hsi_read(struct if_hsi_channel *channel, const char *data,
+ unsigned int size);
+static int if_hsi_protocol_send(struct mipi_link_device *mipi_ld, int ch,
+ u32 *data, unsigned int len);
+
+#endif
diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c
new file mode 100755
index 00000000000..d91098822f6
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c
@@ -0,0 +1,195 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_xmm6260.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_data/modem.h>
+#include "modem_prj.h"
+
+static int xmm6260_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_on()\n");
+
+ if (!mc->gpio_cp_reset || !mc->gpio_cp_on || !mc->gpio_reset_req_n) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ udelay(160);
+ gpio_set_value(mc->gpio_pda_active, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ udelay(160);
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+ udelay(160);
+ gpio_set_value(mc->gpio_cp_on, 1);
+ msleep(20);
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int xmm6260_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_off()\n");
+
+ if (!mc->gpio_cp_reset || !mc->gpio_cp_on) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+
+ mc->phone_state = STATE_OFFLINE;
+
+ return 0;
+}
+
+
+static int xmm6260_reset(struct modem_ctl *mc)
+{
+ int ret;
+
+ pr_info("[MODEM_IF] xmm6260_reset()\n");
+
+ ret = xmm6260_off(mc);
+ if (ret)
+ return ret;
+
+ msleep(100);
+
+ ret = xmm6260_on(mc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int xmm6260_boot_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_boot_on()\n");
+
+ if (!mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_flm_uart_sel, 0);
+
+ return 0;
+}
+
+static int xmm6260_boot_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_boot_off()\n");
+
+ if (!mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_flm_uart_sel, 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);
+
+ if (!mc->gpio_cp_reset || !mc->gpio_phone_active ||
+ !mc->gpio_cp_dump_int) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return IRQ_HANDLED;
+ }
+
+ phone_reset = gpio_get_value(mc->gpio_cp_reset);
+ phone_active_value = gpio_get_value(mc->gpio_phone_active);
+ cp_dump_value = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_info("[MODEM_IF] PA EVENT : reset =%d, pa=%d, cp_dump=%d\n",
+ phone_reset, phone_active_value, cp_dump_value);
+
+ if (phone_reset && phone_active_value)
+ phone_state = STATE_ONLINE;
+ else if (phone_reset && !phone_active_value) {
+ if (cp_dump_value)
+ phone_state = STATE_CRASH_EXIT;
+ else
+ phone_state = 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);
+
+ if (phone_active_value)
+ 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);
+ enable_irq(mc->irq_phone_active);
+
+ return IRQ_HANDLED;
+}
+
+static void xmm6260_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = xmm6260_on;
+ mc->ops.modem_off = xmm6260_off;
+ mc->ops.modem_reset = xmm6260_reset;
+ mc->ops.modem_boot_on = xmm6260_boot_on;
+ mc->ops.modem_boot_off = xmm6260_boot_off;
+
+ pr_info("[MODEM_IF] xmm6260_get_ops() done\n");
+}
+
+int xmm6260_init_modemctl_device(struct modem_ctl *mc)
+{
+ int ret;
+ xmm6260_get_ops(mc);
+
+ ret = request_irq(mc->irq_phone_active, phone_active_irq_handler,
+ IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH,
+ "phone_active", mc);
+ if (ret)
+ pr_err("[MODEM_IF] failed to irq_phone_active request_irq:%d\n"
+ , ret);
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret)
+ pr_err("[MODEM_IF] failed to irq_phone_active enable_irq_wake : %d\n",
+ ret);
+
+ pr_info("[MODEM_IF] init_modemctl_device() done\n");
+ return 0;
+}
+
diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h
new file mode 100755
index 00000000000..e6f24956b45
--- /dev/null
+++ b/drivers/misc/modem_if/modem_prj.h
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef __MODEM_PRJ_H__
+#define __MODEM_PRJ_H__
+
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/skbuff.h>
+
+
+#define MAX_LINK_DEVTYPE 3
+#define MAX_RAW_DEVS 32
+#define MAX_NUM_IO_DEV (MAX_RAW_DEVS + 4)
+
+#define IOCTL_MODEM_ON _IO('o', 0x19)
+#define IOCTL_MODEM_OFF _IO('o', 0x20)
+#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_SEND _IO('o', 0x25)
+#define IOCTL_MODEM_RECV _IO('o', 0x26)
+
+#define IOCTL_MODEM_STATUS _IO('o', 0x27)
+
+/* modem status */
+#define MODEM_OFF 0
+#define MODEM_CRASHED 1
+#define MODEM_RAMDUMP 2
+#define MODEM_POWER_ON 3
+#define MODEM_BOOTING_NORMAL 4
+#define MODEM_BOOTING_RAMDUMP 5
+#define MODEM_DUMPING 6
+#define MODEM_RUNNING 7
+
+#define IPC_HEADER_MAX_SIZE 6 /* fmt 3, raw 6, rfs 6 */
+
+/* Does modem ctl structure will use state ? or status defined below ?*/
+enum modem_state {
+ STATE_OFFLINE,
+ STATE_CRASH_RESET, /* silent reset */
+ STATE_CRASH_EXIT, /* cp ramdump */
+ STATE_BOOTING,
+ STATE_ONLINE,
+};
+
+enum {
+ COM_NONE,
+ COM_ONLINE,
+ COM_HANDSHAKE,
+ COM_BOOT,
+ COM_CRASH,
+};
+
+struct header_data {
+ char hdr[IPC_HEADER_MAX_SIZE];
+ unsigned len;
+ unsigned flag_len;
+ char start; /*hdlc start header 0x7F*/
+};
+
+struct misc_data_io {
+ uint32_t size;
+ uint32_t id;
+ uint32_t cmd;
+ void *data;
+};
+
+struct vnet {
+ struct io_device *iod;
+};
+
+struct io_device {
+ struct list_head list;
+ char *name;
+
+ wait_queue_head_t wq;
+
+ struct miscdevice miscdev;
+ struct net_device *ndev;
+
+ /* ID and Format for channel on the link */
+ unsigned id;
+ enum dev_format format;
+ enum modem_io io_typ;
+
+ struct sk_buff_head sk_rx_q;
+
+ /* for fragmentation data from link device */
+ struct sk_buff *skb_recv;
+ struct header_data h_data;
+
+ /* called from linkdevice when a packet arrives for this iodevice */
+ int (*recv)(struct io_device *iod, const char *data, unsigned int len);
+
+ /* inform the IO device that the modem is now online or offline or
+ * crashing or whatever...
+ */
+ void (*modem_state_changed)(struct io_device *iod, enum modem_state);
+
+ struct link_device *link;
+ struct modem_ctl *mc;
+
+ void *private_data;
+};
+#define to_io_device(misc) container_of(misc, struct io_device, miscdev)
+
+struct io_raw_devices {
+ struct io_device *raw_devices[MAX_RAW_DEVS];
+ int num_of_raw_devs;
+};
+
+struct link_device {
+ char *name;
+
+ struct sk_buff_head sk_fmt_tx_q;
+ struct sk_buff_head sk_raw_tx_q;
+
+ struct workqueue_struct *tx_wq;
+ struct work_struct tx_work;
+ struct delayed_work tx_delayed_work;
+
+ int irq; /* for dpram int */
+ unsigned com_state;
+
+ /* called during init to associate an io device with this link */
+ int (*attach)(struct link_device *ld, struct io_device *iod);
+
+ /* init communication - setting link driver */
+ int (*init_comm)(struct link_device *ld, struct io_device *iod);
+
+ /* called by an io_device when it has a packet to send over link
+ * - the io device is passed so the link device can look at id and
+ * format fields to determine how to route/format the packet
+ */
+ int (*send)(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *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 *);
+};
+
+struct modem_ctl {
+ struct device *dev;
+ char *name;
+
+ int phone_state;
+
+ unsigned gpio_cp_on;
+ unsigned gpio_reset_req_n;
+ unsigned gpio_cp_reset;
+ unsigned gpio_pda_active;
+ unsigned gpio_phone_active;
+ unsigned gpio_cp_dump_int;
+ unsigned gpio_flm_uart_sel;
+ unsigned gpio_cp_warm_reset;
+
+ int irq_phone_active;
+
+ struct work_struct work;
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+ const struct attribute_group *group;
+ unsigned gpio_cp_off;
+ unsigned gpio_slave_wakeup;
+ unsigned gpio_host_wakeup;
+ unsigned gpio_host_active;
+ int irq_host_wakeup;
+ struct delayed_work dwork;
+ struct work_struct resume_work;
+ int wakeup_flag; /*flag for CP boot GPIO sync flag*/
+ int cpcrash_flag;
+ struct completion *l2_done;
+ int irq[3];
+#endif /*CONFIG_LTE_MODEM_CMC221*/
+
+ struct modemctl_ops ops;
+ struct io_device *iod;
+};
+
+int init_io_device(struct io_device *iod);
+
+#endif
diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h
new file mode 100755
index 00000000000..937add3ffef
--- /dev/null
+++ b/drivers/misc/modem_if/modem_variation.h
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef __MODEM_VARIATION_H__
+#define __MODEM_VARIATION_H__
+
+#define DECLARE_MODEM_INIT(type) \
+ int type ## _init_modemctl_device(struct modem_ctl *mc)
+#define DECLARE_MODEM_INIT_DUMMY(type) \
+ DECLARE_MODEM_INIT(type) { return 0; }
+
+#define DECLARE_LINK_INIT(type) \
+ struct link_device *type ## _create_link_device( \
+ struct platform_device *pdev)
+#define DECLARE_LINK_INIT_DUMMY(type) \
+ DECLARE_LINK_INIT(type) { return NULL; }
+
+#define MODEM_INIT_CALL(type) type ## _init_modemctl_device
+#define LINK_INIT_CALL(type) type ## _create_link_device
+
+/* add declaration of modem & link type */
+/* modem device support */
+#ifdef CONFIG_UMTS_MODEM_XMM6260
+DECLARE_MODEM_INIT(xmm6260);
+#else
+DECLARE_MODEM_INIT_DUMMY(xmm6260)
+#endif
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+DECLARE_MODEM_INIT(cmc221);
+#else
+DECLARE_MODEM_INIT_DUMMY(cmc221)
+#endif
+
+#ifdef CONFIG_CDMA_MODEM_CBP71
+DECLARE_MODEM_INIT(cbp71);
+#else
+DECLARE_MODEM_INIT_DUMMY(cbp71)
+#endif
+
+/* link device support */
+#ifdef CONFIG_UMTS_LINK_MIPI
+DECLARE_LINK_INIT(mipi);
+#else
+DECLARE_LINK_INIT_DUMMY(mipi)
+#endif
+
+#ifdef CONFIG_CDMA_LINK_DPRAM
+DECLARE_LINK_INIT(dpram);
+#else
+DECLARE_LINK_INIT_DUMMY(dpram)
+#endif
+
+#ifdef CONFIG_LTE_LINK_USB
+DECLARE_LINK_INIT(usb);
+#else
+DECLARE_LINK_INIT_DUMMY(usb)
+#endif
+
+#ifdef CONFIG_UMTS_LINK_HSIC
+DECLARE_LINK_INIT(hsic);
+#else
+DECLARE_LINK_INIT_DUMMY(hsic)
+#endif
+
+#ifdef CONFIG_UMTS_LINK_SPI
+DECLARE_LINK_INIT(spi);
+#else
+DECLARE_LINK_INIT_DUMMY(spi)
+#endif
+
+typedef int (*modem_init_call)(struct modem_ctl *);
+modem_init_call modem_init_func[] = {
+ MODEM_INIT_CALL(xmm6260),
+ MODEM_INIT_CALL(cbp71),
+ MODEM_INIT_CALL(cmc221),
+};
+
+typedef struct link_device *(*link_init_call)(struct platform_device *);
+link_init_call link_init_func[] = {
+ LINK_INIT_CALL(mipi),
+ LINK_INIT_CALL(dpram),
+ LINK_INIT_CALL(spi),
+ LINK_INIT_CALL(usb),
+ LINK_INIT_CALL(hsic),
+};
+
+static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata)
+{
+ if (modem_init_func[pdata->modem_type])
+ return modem_init_func[pdata->modem_type](mc);
+ else
+ return -ENOTSUPP;
+}
+
+static struct link_device *call_link_init_func(struct platform_device *pdev,
+ enum modem_link link_type)
+{
+ if (link_init_func[link_type])
+ return link_init_func[link_type](pdev);
+ else
+ return NULL;
+}
+
+#endif
+