aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/mdm_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-exynos/mdm_common.c')
-rw-r--r--arch/arm/mach-exynos/mdm_common.c224
1 files changed, 209 insertions, 15 deletions
diff --git a/arch/arm/mach-exynos/mdm_common.c b/arch/arm/mach-exynos/mdm_common.c
index edd3418..f0c819f 100644
--- a/arch/arm/mach-exynos/mdm_common.c
+++ b/arch/arm/mach-exynos/mdm_common.c
@@ -54,6 +54,10 @@ static const char rmnet_pm_dev[] = "mdm_hsic_pm0";
#include <mach/gpio.h>
#endif
+#ifdef CONFIG_SIM_DETECT
+#include <linux/poll.h>
+#endif
+
#define MDM_MODEM_TIMEOUT 6000
#define MDM_MODEM_DELTA 100
#define MDM_BOOT_TIMEOUT 60000L
@@ -181,6 +185,23 @@ void mdm_set_chip_configuration(bool dload)
}
}
+void print_mdm_gpio_state(void)
+{
+ pr_info("ap2mdm_status is %s\n",
+ gpio_get_value(mdm_drv->ap2mdm_status_gpio) ?
+ "high" : "low");
+ pr_info("ap2mdm_errfatal is %s\n",
+ gpio_get_value(mdm_drv->ap2mdm_errfatal_gpio) ?
+ "high" : "low");
+ pr_info("mdm2ap_status is %s\n",
+ gpio_get_value(mdm_drv->mdm2ap_status_gpio) ?
+ "high" : "low");
+ pr_info("mdm2ap_errfatal is %s\n",
+ gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) ?
+ "high" : "low");
+}
+EXPORT_SYMBOL(print_mdm_gpio_state);
+
static void mdm2ap_status_check(struct work_struct *work)
{
/*
@@ -198,6 +219,27 @@ static void mdm2ap_status_check(struct work_struct *work)
static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
+static void mdm_silent_reset(void)
+{
+ pr_info("mdm: silent reset!!\n");
+
+ mdm_drv->boot_type = CHARM_NORMAL_BOOT;
+ complete(&mdm_needs_reload);
+ if (!wait_for_completion_timeout(&mdm_boot,
+ msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
+ mdm_drv->mdm_boot_status = -ETIMEDOUT;
+ pr_info("%s: mdm modem restart timed out.\n", __func__);
+ panic("%s[%p]: Failed to powerup!", __func__, current);
+ } else {
+ pr_info("%s: mdm modem has been restarted\n", __func__);
+
+ /* Log the reason for the restart */
+ if (mdm_drv->pdata->sfr_query)
+ queue_work(mdm_sfr_queue, &sfr_reason_work);
+ }
+ INIT_COMPLETION(mdm_boot);
+}
+
long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
@@ -300,9 +342,20 @@ long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
return mdm_drv->proto_is_dload;
case GET_FORCE_RAMDUMP:
+ get_user(status, (unsigned long __user *) arg);
pr_info("%s: mdm get dump mode = %d\n", __func__, force_dump);
- return force_dump;
+ if (status)
+ mdm_force_fatal();
+ else
+ mdm_silent_reset();
+ break;
+#ifdef CONFIG_SIM_DETECT
+ case GET_SIM_DETECT:
+ pr_info("%s: mdm get sim detect = %d\n", __func__,
+ mdm_drv->sim_state);
+ return mdm_drv->sim_state;
+#endif
default:
pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
ret = -EINVAL;
@@ -312,19 +365,6 @@ long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
return ret;
}
-/* temporary implemented, it should be removed at mass production */
-/* simply declare this function as extern at test point, and call it */
-void mdm_force_fatal(void)
-{
- pr_info("%s: Reseting the mdm due to AP request\n", __func__);
-
- force_dump = 1;
-
- notify_modem_fatal();
- subsystem_restart(EXTERNAL_MODEM);
-}
-EXPORT_SYMBOL(mdm_force_fatal);
-
static void mdm_fatal_fn(struct work_struct *work)
{
pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
@@ -351,6 +391,23 @@ static void mdm_status_fn(struct work_struct *work)
static DECLARE_WORK(mdm_status_work, mdm_status_fn);
+/* temporary implemented, it should be removed at mass production */
+/* simply declare this function as extern at test point, and call it */
+void mdm_force_fatal(void)
+{
+ pr_info("%s: Reseting the mdm due to AP request\n", __func__);
+
+ force_dump = 1;
+
+ if (in_irq())
+ queue_work(mdm_queue, &mdm_fatal_work);
+ else {
+ notify_modem_fatal();
+ subsystem_restart(EXTERNAL_MODEM);
+ }
+}
+EXPORT_SYMBOL(mdm_force_fatal);
+
static void mdm_disable_irqs(void)
{
disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
@@ -370,6 +427,69 @@ static irqreturn_t mdm_errfatal(int irq, void *dev_id)
return IRQ_HANDLED;
}
+#ifdef CONFIG_SIM_DETECT
+
+/*
+ * SIM state gpio shows level when SIM inserted
+ *
+ * sim_polarity == 1
+ HIGH: attach
+ sim_polarity == 0
+ * LOW : attach
+ */
+void get_sim_state_at_boot(void)
+{
+ if (mdm_drv) {
+ mdm_drv->sim_state =
+ mdm_drv->pdata->sim_polarity ==
+ gpio_get_value(mdm_drv->sim_detect_gpio);
+ mdm_drv->sim_changed = 0;
+ pr_info("%s: sim state = %s\n", __func__,
+ mdm_drv->sim_state == 1 ? "Attach" : "Detach");
+ }
+}
+
+static void sim_status_check(struct work_struct *work)
+{
+ int cur_sim_state;
+
+ if (!mdm_drv->mdm_ready)
+ return;
+
+ cur_sim_state =
+ mdm_drv->pdata->sim_polarity ==
+ gpio_get_value(mdm_drv->sim_detect_gpio);
+
+ if (cur_sim_state != mdm_drv->sim_state) {
+ mdm_drv->sim_state = cur_sim_state;
+ mdm_drv->sim_changed = 1;
+ pr_info("sim state = %s\n",
+ mdm_drv->sim_state == 1 ? "Attach" : "Detach");
+ wake_up_interruptible(&mdm_drv->wq);
+ } else
+ mdm_drv->sim_changed = 0;
+
+ mdm_drv->sim_irq = false;
+}
+
+static DECLARE_DELAYED_WORK(sim_status_check_work, sim_status_check);
+
+#define SIM_DEBOUNCE_TIME_MS 1000
+static irqreturn_t sim_detect_irq_handler(int irq, void *dev_id)
+{
+ if (mdm_drv->mdm_ready) {
+ pr_info("%s: sim gpio level = %d\n", __func__,
+ gpio_get_value(mdm_drv->sim_detect_gpio));
+
+ mdm_drv->sim_irq = true;
+ schedule_delayed_work(&sim_status_check_work,
+ msecs_to_jiffies(SIM_DEBOUNCE_TIME_MS));
+ }
+
+ return IRQ_HANDLED;
+}
+#endif
+
static unsigned char *mdm_read_err_report(void)
{
/* Read CP error report from mdm_err.log in tombstones */
@@ -391,6 +511,20 @@ static unsigned char *mdm_read_err_report(void)
return (unsigned char *) buf;
}
+#ifdef CONFIG_SIM_DETECT
+static unsigned int mdm_modem_poll(struct file *file, poll_table *wait)
+{
+ int mask = 0;
+ poll_wait(file, &mdm_drv->wq, wait);
+ if (mdm_drv->sim_changed == 1) {
+ mdm_drv->sim_changed = 0;
+ mask = POLLHUP;
+ }
+
+ return mask;
+}
+#endif
+
static int mdm_modem_open(struct inode *inode, struct file *file)
{
return 0;
@@ -400,6 +534,9 @@ static const struct file_operations mdm_modem_fops = {
.owner = THIS_MODULE,
.open = mdm_modem_open,
.unlocked_ioctl = mdm_modem_ioctl,
+#ifdef CONFIG_SIM_DETECT
+ .poll = mdm_modem_poll,
+#endif
};
@@ -444,6 +581,7 @@ static int mdm_reboot_notifier(struct notifier_block *this,
{
int soft_reset_direction =
mdm_drv->pdata->soft_reset_inverted ? 1 : 0;
+ mdm_drv->mdm_ready = 0;
mdm_disable_irqs();
notify_modem_fatal();
gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
@@ -500,6 +638,7 @@ static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
*/
msleep(mdm_drv->pdata->ramdump_delay_ms);
}
+
#if 0
if (!mdm_drv->mdm_unexpected_reset_occurred)
mdm_drv->ops->reset_mdm_cb(mdm_drv);
@@ -599,7 +738,6 @@ static int mdm_debugfs_init(void)
}
#endif
-
static void mdm_modem_initialize_data(struct platform_device *pdev,
struct mdm_ops *mdm_ops)
{
@@ -664,6 +802,17 @@ static void mdm_modem_initialize_data(struct platform_device *pdev,
"MDM2AP_PBLRDY");
if (pres)
mdm_drv->mdm2ap_pblrdy = pres->start;
+#ifdef CONFIG_SIM_DETECT
+ /* SIM_DETECT */
+ pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "SIM_DETECT");
+ if (pres)
+ mdm_drv->sim_detect_gpio = pres->start;
+ else
+ pr_err("%s: fail to get resource\n", __func__);
+
+#endif
+ mdm_drv->sim_irq = false;
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
@@ -695,6 +844,9 @@ int mdm_common_create(struct platform_device *pdev,
gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
+#ifdef CONFIG_SIM_DETECT
+ gpio_request(mdm_drv->sim_detect_gpio, "SIM_DETECT");
+#endif
if (mdm_drv->mdm2ap_pblrdy > 0)
gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
@@ -743,6 +895,10 @@ int mdm_common_create(struct platform_device *pdev,
gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
+#ifdef CONFIG_SIM_DETECT
+ gpio_direction_input(mdm_drv->sim_detect_gpio);
+ init_waitqueue_head(&mdm_drv->wq);
+#endif
mdm_queue = create_singlethread_workqueue("mdm_queue");
if (!mdm_queue) {
@@ -831,6 +987,38 @@ errfatal_err:
enable_irq_wake(irq);
status_err:
+#ifdef CONFIG_SIM_DETECT
+ /* sim detect irq */
+#ifdef CONFIG_ARCH_EXYNOS
+ s3c_gpio_cfgpin(mdm_drv->sim_detect_gpio, S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(mdm_drv->sim_detect_gpio, S3C_GPIO_PULL_NONE);
+ irq = gpio_to_irq(mdm_drv->sim_detect_gpio);
+#else
+ irq = MSM_GPIO_TO_INT(mdm_drv->sim_detect_gpio);
+#endif
+ if (irq < 0) {
+ pr_err("%s: could not get SIM DETECT IRQ resource. "
+ "error=%d No IRQ will be generated on status change.",
+ __func__, irq);
+ goto simdetect_err;
+ }
+
+ ret = request_threaded_irq(irq, NULL, sim_detect_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ "sim detect", mdm_drv);
+
+ if (ret < 0) {
+ pr_err("%s: SIM_DETECT IRQ#%d request failed with error=%d"
+ ". No IRQ will be generated on status change.",
+ __func__, irq, ret);
+ goto simdetect_err;
+ }
+
+ mdm_drv->mdm_status_irq = irq;
+ enable_irq_wake(irq);
+simdetect_err:
+#endif
+
if (mdm_drv->mdm2ap_pblrdy > 0) {
#ifdef CONFIG_ARCH_EXYNOS
s3c_gpio_cfgpin(mdm_drv->mdm2ap_pblrdy, S3C_GPIO_SFN(0xf));
@@ -885,6 +1073,9 @@ fatal_err:
gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
gpio_free(mdm_drv->mdm2ap_status_gpio);
gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+#ifdef CONFIG_MACH_SIM_DETECT
+ gpio_free(mdm_drv->sim_detect_gpio);
+#endif
if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
@@ -910,6 +1101,9 @@ int mdm_common_modem_remove(struct platform_device *pdev)
gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
gpio_free(mdm_drv->mdm2ap_status_gpio);
gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+#ifdef CONFIG_SIM_DETECT
+ gpio_free(mdm_drv->sim_detect_gpio);
+#endif
if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);