aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/svnet
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/svnet
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
samsung update 1
Diffstat (limited to 'drivers/svnet')
-rw-r--r--drivers/svnet/Kconfig10
-rw-r--r--drivers/svnet/Makefile2
-rw-r--r--drivers/svnet/modemctl.c831
3 files changed, 843 insertions, 0 deletions
diff --git a/drivers/svnet/Kconfig b/drivers/svnet/Kconfig
new file mode 100644
index 00000000000..c0aa408410a
--- /dev/null
+++ b/drivers/svnet/Kconfig
@@ -0,0 +1,10 @@
+#
+#
+#
+
+menuconfig SAMSUNG_PHONE_SVNET
+ tristate "Samsung Phone Interface - SVN"
+ default n
+
+#if SAMSUNG_PHONE_SVNET
+#endif # SAMSUNG_PHONE_SVNET
diff --git a/drivers/svnet/Makefile b/drivers/svnet/Makefile
new file mode 100644
index 00000000000..9cf7b027eca
--- /dev/null
+++ b/drivers/svnet/Makefile
@@ -0,0 +1,2 @@
+#svnet-y := cdc-svnet.o sipc4.o
+obj-$(CONFIG_SAMSUNG_PHONE_SVNET) := modemctl.o
diff --git a/drivers/svnet/modemctl.c b/drivers/svnet/modemctl.c
new file mode 100644
index 00000000000..e6ce63d516d
--- /dev/null
+++ b/drivers/svnet/modemctl.c
@@ -0,0 +1,831 @@
+/*
+ * Modem control driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Suchang Woo <suchang.woo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/kdev_t.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+#include <linux/phone_svn/modemctl.h>
+
+#define HOST_WUP_LEVEL 0
+
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+static int sprd_boot_complete;
+#endif
+
+enum {
+ HOST_WAKEUP_LOW = 1,
+ HOST_WAKEUP_WAIT_RESET,
+} HOST_WAKEUP_STATE;
+
+enum {
+ SVNET_ERROR_RESET,
+ SVNET_ERROR_CRASH,
+} SVNET_ERROR_TYPE;
+
+/* FIXME: Don't use this except pm */
+static struct modemctl *global_mc;
+
+int mc_is_modem_on(void)
+{
+ if (!global_mc)
+ return 0;
+
+ return gpio_get_value(global_mc->gpio_phone_on);
+}
+EXPORT_SYMBOL_GPL(mc_is_modem_on);
+
+int mc_is_modem_active(void)
+{
+ if (!global_mc)
+ return 0;
+
+ return gpio_get_value(global_mc->gpio_phone_active);
+}
+
+int mc_control_active_state(int val)
+{
+ if (!global_mc)
+ return -EFAULT;
+
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#endif
+
+ if (mc_is_modem_on()) {
+ gpio_set_value(global_mc->gpio_active_state, val ? 1 : 0);
+ dev_dbg(global_mc->dev, "ACTIVE_STATE:%d\n", val ? 1 : 0);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mc_control_active_state);
+
+int mc_control_pda_active(int val)
+{
+ if (!global_mc)
+ return -EFAULT;
+
+ if (mc_is_modem_on()) {
+ gpio_set_value(global_mc->gpio_pda_active , val ? 1 : 0);
+ dev_dbg(global_mc->dev, "PDA_ACTIVE:%d\n", val ? 1 : 0);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mc_control_pda_active);
+
+int mc_control_slave_wakeup(int val)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#else
+ if (!global_mc)
+ return -EFAULT;
+
+ gpio_set_value(global_mc->gpio_ipc_slave_wakeup, val ? 1 : 0);
+ dev_dbg(global_mc->dev, ">SLAV_WUP:%d,%d\n", val ? 1 : 0,
+ gpio_get_value(global_mc->gpio_ipc_slave_wakeup));
+ return 0;
+#endif
+}
+EXPORT_SYMBOL_GPL(mc_control_slave_wakeup);
+
+int mc_is_host_wakeup(void)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#else
+ if (!global_mc)
+ return 0;
+
+ return (gpio_get_value(global_mc->gpio_ipc_host_wakeup)
+ == HOST_WUP_LEVEL) ? 1 : 0;
+#endif
+}
+EXPORT_SYMBOL_GPL(mc_is_host_wakeup);
+
+int mc_is_suspend_request(void)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#else
+ if (!global_mc)
+ return 0;
+
+ printk(KERN_DEBUG "%s:suspend requset val=%d\n", __func__,
+ gpio_get_value(global_mc->gpio_suspend_request));
+ return gpio_get_value(global_mc->gpio_suspend_request);
+#endif
+}
+
+int mc_prepare_resume(int ms_time)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#else
+ int val;
+ struct completion done;
+ struct modemctl *mc = global_mc;
+
+ if (!mc)
+ return -EFAULT;
+
+ val = gpio_get_value(mc->gpio_ipc_slave_wakeup);
+ if (val) {
+ gpio_set_value(mc->gpio_ipc_slave_wakeup, 0);
+ dev_dbg(mc->dev, "svn SLAV_WUP:reset\n");
+ }
+ val = gpio_get_value(mc->gpio_ipc_host_wakeup);
+ if (val == HOST_WUP_LEVEL) {
+ dev_dbg(mc->dev, "svn HOST_WUP:high!\n");
+ return MC_HOST_HIGH;
+ }
+
+ init_completion(&done);
+ mc->l2_done = &done;
+ gpio_set_value(mc->gpio_ipc_slave_wakeup, 1);
+ dev_dbg(mc->dev, "svn >SLAV_WUP:1,%d\n",
+ gpio_get_value(mc->gpio_ipc_slave_wakeup));
+
+ if (!wait_for_completion_timeout(&done, ms_time)) {
+ val = gpio_get_value(mc->gpio_ipc_host_wakeup);
+ if (val == HOST_WUP_LEVEL) {
+ dev_err(mc->dev, "maybe complete late.. %d\n", ms_time);
+ mc->l2_done = NULL;
+ return MC_SUCCESS;
+ }
+ dev_err(mc->dev, "Modem wakeup timeout %d\n", ms_time);
+ gpio_set_value(mc->gpio_ipc_slave_wakeup, 0);
+ dev_dbg(mc->dev, "svn >SLAV_WUP:0,%d\n",
+ gpio_get_value(mc->gpio_ipc_slave_wakeup));
+ mc->l2_done = NULL;
+ return MC_HOST_TIMEOUT;
+ }
+ return MC_SUCCESS;
+#endif
+}
+
+int mc_reconnect_gpio(void)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#else
+ struct modemctl *mc = global_mc;
+
+ if (!mc)
+ return -EFAULT;
+
+ dev_err(mc->dev, "TRY Reconnection...\n");
+
+ gpio_set_value(mc->gpio_active_state, 0);
+ msleep(20);
+ gpio_set_value(mc->gpio_ipc_slave_wakeup, 1);
+ msleep(20);
+ gpio_set_value(mc->gpio_ipc_slave_wakeup, 0);
+ msleep(20);
+ gpio_set_value(mc->gpio_active_state, 1);
+
+ return 0;
+#endif
+}
+
+int mc_is_cp_dump_int(void)
+{
+ if (!global_mc)
+ return 0;
+
+ return gpio_get_value(global_mc->gpio_cp_dump_int);
+}
+
+#ifdef CONFIG_SEC_DEBUG
+/*
+ * HSIC CP uploas scenario -
+ * 1. CP send Crash message
+ * 2. Rild save the ram data to file via HSIC
+ * 3. Rild call the kernel_upload() for AP ram dump
+ */
+static void kernel_upload(struct modemctl *mc)
+{
+ /*TODO: check the DEBUG LEVEL*/
+ if (mc->cpcrash_flag)
+ panic("CP Crash");
+ else
+ panic("HSIC Disconnected");
+}
+#else
+static void kernel_upload(struct modemctl *mc) {}
+#endif
+
+static int modem_on(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if (!mc->ops || !mc->ops->modem_on)
+ return -ENXIO;
+
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ sprd_boot_complete = 0;
+#endif
+
+ mc->ops->modem_on(mc);
+
+ return 0;
+}
+
+static int modem_off(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if (!mc->ops || !mc->ops->modem_off)
+ return -ENXIO;
+
+ mc->ops->modem_off(mc);
+
+ return 0;
+}
+
+static int modem_reset(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if (!mc->ops || !mc->ops->modem_reset)
+ return -ENXIO;
+
+ mc->ops->modem_reset(mc);
+
+ return 0;
+}
+
+static int modem_boot(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ sprd_boot_complete = 1;
+#else
+ if (!mc->ops || !mc->ops->modem_boot)
+ return -ENXIO;
+
+ mc->ops->modem_boot(mc);
+#endif
+ return 0;
+}
+
+static int modem_get_active(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if (!mc->gpio_phone_active || !mc->gpio_cp_reset)
+ return -ENXIO;
+
+ dev_dbg(mc->dev, "cp %d phone %d\n",
+ gpio_get_value(mc->gpio_cp_reset),
+ gpio_get_value(mc->gpio_phone_active));
+
+ if (gpio_get_value(mc->gpio_phone_active))
+ return 1;
+ else
+ return 0;
+
+ if (gpio_get_value(mc->gpio_cp_reset))
+ return !!gpio_get_value(mc->gpio_phone_active);
+
+ return 0;
+}
+
+static ssize_t show_control(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct modemctl *mc = dev_get_drvdata(d);
+ struct modemctl_ops *ops = mc->ops;
+
+ if (ops) {
+ if (ops->modem_on)
+ p += sprintf(p, "on ");
+ if (ops->modem_off)
+ p += sprintf(p, "off ");
+ if (ops->modem_reset)
+ p += sprintf(p, "reset ");
+ if (ops->modem_boot)
+ p += sprintf(p, "boot ");
+ } else {
+ p += sprintf(p, "(No ops)");
+ }
+
+ p += sprintf(p, "\n");
+ return p - buf;
+}
+
+static ssize_t store_control(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ if (!strncmp(buf, "on", 2)) {
+ modem_on(mc);
+ return count;
+ }
+
+ if (!strncmp(buf, "off", 3)) {
+ modem_off(mc);
+ return count;
+ }
+
+ if (!strncmp(buf, "reset", 5)) {
+ modem_reset(mc);
+ return count;
+ }
+
+ if (!strncmp(buf, "boot", 4)) {
+ modem_boot(mc);
+ return count;
+ }
+
+ if (!strncmp(buf, "daolpu", 6)) {
+ kernel_upload(mc);
+ return count;
+ }
+
+ return count;
+}
+
+static ssize_t show_status(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ p += sprintf(p, "%d\n", modem_get_active(mc));
+
+ return p - buf;
+}
+
+static ssize_t show_wakeup(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#else
+ struct modemctl *mc = dev_get_drvdata(d);
+ int count = 0;
+
+ if (!mc->gpio_ipc_host_wakeup)
+ return -ENXIO;
+
+ count += sprintf(buf + count, "%d\n",
+ mc->wakeup_flag);
+
+ return count;
+#endif
+}
+
+static ssize_t store_wakeup(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return 0;
+#else
+
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ if (!strncmp(buf, "reset", 5)) {
+ mc->wakeup_flag = HOST_WAKEUP_WAIT_RESET;
+ dev_dbg(mc->dev, "%s: wakup_flag %d\n",
+ __func__, mc->wakeup_flag);
+ return count;
+ }
+ return 0;
+#endif
+}
+
+static ssize_t show_debug(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ int i;
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ for (i = 0; i < ARRAY_SIZE(mc->irq); i++) {
+ if (mc->irq[i])
+ p += sprintf(p, "Irq %d: %d\n", i, mc->irq[i]);
+ }
+
+ p += sprintf(p, "GPIO ----\n");
+
+ if (mc->gpio_phone_on)
+ p += sprintf(p, "\t%3d %d : phone on\n", mc->gpio_phone_on,
+ gpio_get_value(mc->gpio_phone_on));
+ if (mc->gpio_phone_active)
+ p += sprintf(p, "\t%3d %d : phone active\n",
+ mc->gpio_phone_active,
+ gpio_get_value(mc->gpio_phone_active));
+ if (mc->gpio_pda_active)
+ p += sprintf(p, "\t%3d %d : pda active\n", mc->gpio_pda_active,
+ gpio_get_value(mc->gpio_pda_active));
+ if (mc->gpio_cp_reset)
+ p += sprintf(p, "\t%3d %d : CP reset\n", mc->gpio_cp_reset,
+ gpio_get_value(mc->gpio_cp_reset));
+ if (mc->gpio_usim_boot)
+ p += sprintf(p, "\t%3d %d : USIM boot\n", mc->gpio_usim_boot,
+ gpio_get_value(mc->gpio_usim_boot));
+ if (mc->gpio_flm_sel)
+ p += sprintf(p, "\t%3d %d : FLM sel\n", mc->gpio_flm_sel,
+ gpio_get_value(mc->gpio_flm_sel));
+
+ p += sprintf(p, "Support types ---\n");
+
+ return p - buf;
+}
+static DEVICE_ATTR(control, 0664, show_control, store_control);
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+static DEVICE_ATTR(wakeup, 0664, show_wakeup, store_wakeup);
+static DEVICE_ATTR(debug, S_IRUGO, show_debug, NULL);
+
+static struct attribute *modemctl_attributes[] = {
+ &dev_attr_control.attr,
+ &dev_attr_status.attr,
+ &dev_attr_wakeup.attr,
+ &dev_attr_debug.attr,
+ NULL
+};
+
+static const struct attribute_group modemctl_group = {
+ .attrs = modemctl_attributes,
+};
+
+void crash_event(int type)
+{
+ char *envs[2] = { NULL, NULL };
+
+ if (!global_mc)
+ return;
+#ifdef CONFIG_SEC_DEBUG
+ envs[0] = (type == SVNET_ERROR_CRASH) ? "MAILBOX=cp_exit"
+ : "MAILBOX=cp_reset";
+#else
+ envs[0] = "MAILBOX=cp_reset";
+#endif
+ kobject_uevent_env(&global_mc->dev->kobj, KOBJ_OFFLINE, envs);
+}
+
+/*
+ * mc_work - PHONE_ACTIVE irq
+ * After
+ * CP Crash : PHONE_ACTIVE(L) + CP_DUMP_INT(H) (0xC9)
+ * CP Reset : PHONE_ACTIVE(L) + CP_DUMP_INT(L) (0xC7)
+ * Before
+ * CP Crash : PHONE_ACTIVE(L) + SUSPEND_REQUEST(H) (0xC9)
+ * CP Reset : PHONE_ACTIVE(L) + SUSPEND_REQUEST(L) (0xC7)
+ */
+static void mc_work(struct work_struct *work_arg)
+{
+ struct modemctl *mc = container_of(work_arg, struct modemctl,
+ work.work);
+ int error;
+ int cpdump_int;
+ char *envs[2] = { NULL, NULL };
+
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ if (!sprd_boot_complete)
+ return;
+#endif
+
+ error = modem_get_active(mc);
+ if (error < 0) {
+ dev_err(mc->dev, "Not initialized\n");
+ return;
+ }
+
+ cpdump_int = gpio_get_value(mc->gpio_cp_dump_int);
+ dev_dbg(mc->dev, "PHONE ACTIVE: %d CP_DUMP_INT: %d\n",
+ error, cpdump_int);
+
+ envs[0] = cpdump_int ? "MAILBOX=cp_exit" : "MAILBOX=cp_reset";
+
+ if (error && gpio_get_value(global_mc->gpio_phone_on)) {
+ mc->cpcrash_flag = 0;
+ mc->boot_done = 1;
+ kobject_uevent(&mc->dev->kobj, KOBJ_ONLINE);
+ wake_unlock(&mc->reset_lock);
+ } else if (mc->boot_done) {
+ if (modem_get_active(mc)) {
+ wake_unlock(&mc->reset_lock);
+ return;
+ }
+ if (mc->cpcrash_flag > 3) {
+ dev_dbg(mc->dev, "send uevent to RIL [cpdump_int:%d]\n",
+ cpdump_int);
+ kobject_uevent_env(&mc->dev->kobj, KOBJ_OFFLINE, envs);
+ } else {
+ mc->cpcrash_flag++;
+ schedule_delayed_work(&mc->work, msecs_to_jiffies(50));
+ }
+ }
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ if (cpdump_int) {
+ dev_dbg(mc->dev, "send uevent to RIL [cpdump_int:%d]\n",
+ cpdump_int);
+ kobject_uevent_env(&mc->dev->kobj, KOBJ_OFFLINE, envs);
+ }
+#endif
+}
+
+static irqreturn_t modemctl_resume_thread(int irq, void *dev_id)
+{
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ return IRQ_HANDLED;
+#else
+ struct modemctl *mc = (struct modemctl *)dev_id;
+ int val = gpio_get_value(mc->gpio_ipc_host_wakeup);
+ int err;
+
+ dev_dbg(mc->dev, "svn <HOST_WUP:%d\n", val);
+ if (val != HOST_WUP_LEVEL) {
+ if (mc->l2_done) {
+ complete(mc->l2_done);
+ mc->l2_done = NULL;
+ }
+ gpio_set_value(mc->gpio_ipc_slave_wakeup, 0);
+ dev_dbg(mc->dev, "svn >SLAV_WUP:0,%d\n",
+ gpio_get_value(mc->gpio_ipc_slave_wakeup));
+ mc->debug_cnt = 0;
+ return IRQ_HANDLED;
+ }
+
+ if (val == HOST_WUP_LEVEL)
+ mc->debug_cnt++;
+
+ if (mc->debug_cnt > 30) {
+ dev_err(mc->dev, "Abnormal Host wakeup -- over 30times");
+ disable_irq(irq);
+ mc->debug_cnt = 0;
+ crash_event(SVNET_ERROR_RESET);
+ /*crash_event(SVNET_ERROR_CRASH);*/
+ /*panic("HSIC Disconnected");*/
+ msleep(1000);
+ enable_irq(irq);
+ }
+
+ if (!val
+ && mc->wakeup_flag == HOST_WAKEUP_WAIT_RESET) {
+ mc->wakeup_flag = HOST_WAKEUP_LOW;
+ dev_dbg(mc->dev, "%s: wakeup flag (%d)\n",
+ __func__, mc->wakeup_flag);
+ }
+ return IRQ_HANDLED;
+#endif
+}
+
+static irqreturn_t modemctl_irq_handler(int irq, void *dev_id)
+{
+ struct modemctl *mc = (struct modemctl *)dev_id;
+
+ wake_lock_timeout(&mc->reset_lock, HZ*30);
+ if (!work_pending(&mc->work.work))
+ schedule_delayed_work(&mc->work, msecs_to_jiffies(100));
+ /*schedule_work(&mc->work);*/
+
+ return IRQ_HANDLED;
+}
+
+static void mc_cpdump_worker(struct work_struct *work)
+{
+ struct modemctl *mc = container_of(work, struct modemctl, cpdump_work);
+ int val = gpio_get_value(mc->gpio_cp_dump_int);
+
+ dev_dbg(mc->dev, "CP_DUMP_INT:%d\n", val);
+}
+
+static irqreturn_t modemctl_cpdump_irq(int irq, void *dev_id)
+{
+ struct modemctl *mc = (struct modemctl *)dev_id;
+ int val = gpio_get_value(mc->gpio_cp_dump_int);
+
+ dev_dbg(mc->dev, "CP_DUMP_INT:%d\n", val);
+
+ if (!work_pending(&mc->cpdump_work))
+ schedule_work(&mc->cpdump_work);
+
+ return IRQ_HANDLED;
+}
+
+static void _free_all(struct modemctl *mc)
+{
+ int i;
+
+ if (mc) {
+ if (mc->ops)
+ mc->ops = NULL;
+
+ if (mc->group)
+ sysfs_remove_group(&mc->dev->kobj, mc->group);
+
+ for (i = 0; i < ARRAY_SIZE(mc->irq); i++) {
+ if (mc->irq[i])
+ free_irq(mc->irq[i], mc);
+ }
+
+ kfree(mc);
+ }
+}
+
+static int __devinit modemctl_probe(struct platform_device *pdev)
+{
+ struct modemctl_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct modemctl *mc;
+ int irq;
+ int error;
+
+ printk(KERN_ERR "[%s]\n ", __func__);
+
+ if (!pdata) {
+ dev_err(dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ mc = kzalloc(sizeof(struct modemctl), GFP_KERNEL);
+ if (!mc) {
+ dev_err(dev, "Failed to allocate device\n");
+ return -ENOMEM;
+ }
+
+ mc->gpio_phone_on = pdata->gpio_phone_on;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_cp_req_reset = pdata->gpio_cp_req_reset;
+#if !defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ mc->gpio_ipc_slave_wakeup = pdata->gpio_ipc_slave_wakeup;
+ mc->gpio_ipc_host_wakeup = pdata->gpio_ipc_host_wakeup;
+#endif
+ mc->gpio_suspend_request = pdata->gpio_suspend_request;
+ mc->gpio_active_state = pdata->gpio_active_state;
+ mc->gpio_usim_boot = pdata->gpio_usim_boot;
+ mc->gpio_flm_sel = pdata->gpio_flm_sel;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->ops = &pdata->ops;
+ mc->dev = dev;
+ dev_set_drvdata(mc->dev, mc);
+
+ error = sysfs_create_group(&mc->dev->kobj, &modemctl_group);
+ if (error) {
+ dev_err(dev, "Failed to create sysfs files\n");
+ goto fail;
+ }
+ mc->group = &modemctl_group;
+
+ INIT_DELAYED_WORK(&mc->work, mc_work);
+ INIT_WORK(&mc->cpdump_work, mc_cpdump_worker);
+ wake_lock_init(&mc->reset_lock, WAKE_LOCK_SUSPEND, "modemctl");
+
+ mc->ops->modem_cfg_gpio();
+
+ irq = gpio_to_irq(pdata->gpio_phone_active);
+ error = request_irq(irq, modemctl_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "phone_active", mc);
+ if (error) {
+ dev_err(dev, "Failed to allocate an interrupt(%d)\n", irq);
+ goto fail;
+ }
+ mc->irq[0] = irq;
+ enable_irq_wake(irq);
+#if !defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ irq = gpio_to_irq(pdata->gpio_ipc_host_wakeup);
+
+ error = request_threaded_irq(irq, NULL, modemctl_resume_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "IPC_HOST_WAKEUP", mc);
+
+ if (error) {
+ dev_err(dev, "Failed to allocate an interrupt(%d)\n", irq);
+ goto fail;
+ }
+ mc->irq[1] = irq;
+ enable_irq_wake(irq);
+#endif
+ irq = gpio_to_irq(pdata->gpio_cp_dump_int);
+#if defined(CONFIG_CHN_CMCC_SPI_SPRD)
+ error = request_irq(irq, modemctl_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "CP_DUMP_INT", mc);
+#else
+ error = request_irq(irq, modemctl_cpdump_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "CP_DUMP_INT", mc);
+#endif
+ if (error) {
+ dev_err(dev, "Failed to allocate an interrupt(%d)\n", irq);
+ goto fail;
+ }
+ mc->irq[2] = irq;
+ enable_irq_wake(irq);
+ mc->debug_cnt = 0;
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+ platform_set_drvdata(pdev, mc);
+ global_mc = mc;
+ printk("[%s Done]\n ", __func__);
+ return 0;
+
+fail:
+ _free_all(mc);
+ return error;
+}
+
+static int __devexit modemctl_remove(struct platform_device *pdev)
+{
+ struct modemctl *mc = platform_get_drvdata(pdev);
+
+ flush_work(&mc->work.work);
+ flush_work(&mc->cpdump_work);
+ platform_set_drvdata(pdev, NULL);
+ wake_lock_destroy(&mc->reset_lock);
+ _free_all(mc);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int modemctl_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct modemctl *mc = platform_get_drvdata(pdev);
+
+ if (mc->ops && mc->ops->modem_suspend)
+ mc->ops->modem_suspend(mc);
+
+ if (device_may_wakeup(dev) && mc_is_modem_on())
+ enable_irq_wake(mc->irq[1]);
+
+ return 0;
+}
+
+static int modemctl_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct modemctl *mc = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev) && mc_is_modem_on())
+ disable_irq_wake(mc->irq[1]);
+
+ if (mc->ops && mc->ops->modem_resume)
+ mc->ops->modem_resume(mc);
+
+ return 0;
+}
+
+static const struct dev_pm_ops modemctl_pm_ops = {
+ .suspend = modemctl_suspend,
+ .resume = modemctl_resume,
+};
+#endif
+
+static struct platform_driver modemctl_driver = {
+ .probe = modemctl_probe,
+ .remove = __devexit_p(modemctl_remove),
+ .driver = {
+ .name = "modemctl",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &modemctl_pm_ops,
+#endif
+ },
+};
+
+static int __init modemctl_init(void)
+{
+ int retval;
+ retval = platform_device_register(&modemctl);
+ if (retval < 0)
+ return retval;
+ return platform_driver_register(&modemctl_driver);
+}
+
+static void __exit modemctl_exit(void)
+{
+ platform_driver_unregister(&modemctl_driver);
+}
+
+module_init(modemctl_init);
+module_exit(modemctl_exit);
+
+MODULE_DESCRIPTION("Modem control driver");
+MODULE_AUTHOR("Suchang Woo <suchang.woo@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:modemctl");