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.c481
1 files changed, 423 insertions, 58 deletions
diff --git a/arch/arm/mach-exynos/mdm_common.c b/arch/arm/mach-exynos/mdm_common.c
index 30b5f75..edd3418 100644
--- a/arch/arm/mach-exynos/mdm_common.c
+++ b/arch/arm/mach-exynos/mdm_common.c
@@ -36,7 +36,6 @@
#include <asm/uaccess.h>
#include <mach/mdm2.h>
#include <mach/restart.h>
-#include <mach/subsystem_notif.h>
#include <mach/subsystem_restart.h>
#include <linux/msm_charm.h>
#ifndef CONFIG_ARCH_EXYNOS
@@ -44,29 +43,161 @@
#endif
#include "mdm_private.h"
+#ifdef CONFIG_MDM_HSIC_PM
+#include <linux/mdm_hsic_pm.h>
+static const char rmnet_pm_dev[] = "mdm_hsic_pm0";
+#endif
+
#ifdef CONFIG_ARCH_EXYNOS
#include <linux/interrupt.h>
#include <plat/gpio-cfg.h>
+#include <mach/gpio.h>
#endif
#define MDM_MODEM_TIMEOUT 6000
#define MDM_MODEM_DELTA 100
#define MDM_BOOT_TIMEOUT 60000L
-#define MDM_RDUMP_TIMEOUT 60000L
+#define MDM_RDUMP_TIMEOUT 120000L
+#define MDM2AP_STATUS_TIMEOUT_MS 300000L
+/* declare as module param controled by cmdline parameter
+ * this value makes force ramdump even mdm gives silent reset
+ * usage : add cmdline ' mdm_common.force_dump=1 '
+ */
+static int force_dump;
+module_param(force_dump, int, S_IRUGO | S_IWUSR);
static int mdm_debug_on;
static struct workqueue_struct *mdm_queue;
+static struct workqueue_struct *mdm_sfr_queue;
+static unsigned int dump_timeout_ms;
#define EXTERNAL_MODEM "external_modem"
static struct mdm_modem_drv *mdm_drv;
+static unsigned char *mdm_read_err_report(void);
+static void mdm_disable_irqs(void);
+
DECLARE_COMPLETION(mdm_needs_reload);
DECLARE_COMPLETION(mdm_boot);
DECLARE_COMPLETION(mdm_ram_dumps);
static int first_boot = 1;
+#define RD_BUF_SIZE 100
+#define SFR_MAX_RETRIES 10
+#define SFR_RETRY_INTERVAL 1000
+
+#ifndef CONFIG_ARCH_EXYNOS
+static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
+{
+ int value = gpio_get_value(
+ mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
+
+ if (value == 0)
+ pr_info("External Modem entered Vddmin\n");
+ else
+ pr_info("External Modem exited Vddmin\n");
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static void mdm_setup_vddmin_gpios(void)
+{
+#ifdef CONFIG_ARCH_EXYNOS
+ return;
+#else
+ struct msm_rpm_iv_pair req;
+ struct mdm_vddmin_resource *vddmin_res;
+ int irq, ret;
+
+ /* This resource may not be supported by some platforms. */
+ vddmin_res = mdm_drv->pdata->vddmin_resource;
+ if (!vddmin_res)
+ return;
+
+ req.id = vddmin_res->rpm_id;
+ req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
+ << 16;
+ req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
+ req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
+
+ msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
+
+ /* Monitor low power gpio from mdm */
+ irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
+ if (irq < 0) {
+ pr_err("%s: could not get LPM POWER IRQ resource.\n",
+ __func__);
+ goto error_end;
+ }
+
+ ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "mdm lpm", NULL);
+
+ if (ret < 0)
+ pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
+ __func__, irq, ret);
+error_end:
+ return;
+#endif
+}
+
+static void mdm_restart_reason_fn(struct work_struct *work)
+{
+#ifndef CONFIG_ARCH_EXYNOS
+ int ret, ntries = 0;
+ char sfr_buf[RD_BUF_SIZE];
+ do {
+ msleep(SFR_RETRY_INTERVAL);
+ ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
+ sfr_buf, sizeof(sfr_buf));
+ if (ret) {
+ /*
+ * The sysmon device may not have been probed as yet
+ * after the restart.
+ */
+ pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
+ "%d/%d tries\n", __func__, ret,
+ ntries + 1, SFR_MAX_RETRIES);
+ } else {
+ pr_err("mdm restart reason: %s\n", sfr_buf);
+ break;
+ }
+ } while (++ntries < SFR_MAX_RETRIES);
+#endif
+}
+
+static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
+
+void mdm_set_chip_configuration(bool dload)
+{
+ if (mdm_drv) {
+ pr_info("mdm9x15 boot protocol = %s\n",
+ dload ? "DLOAD" : "SAHARA");
+ mdm_drv->proto_is_dload = dload;
+ }
+}
+
+static void mdm2ap_status_check(struct work_struct *work)
+{
+ /*
+ * If the mdm modem did not pull the MDM2AP_STATUS gpio
+ * high then call subsystem_restart.
+ */
+ if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
+ pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
+ __func__);
+ mdm_drv->mdm_ready = 0;
+ notify_modem_fatal();
+ subsystem_restart(EXTERNAL_MODEM);
+ }
+}
+
+static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
+
long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
@@ -81,6 +212,9 @@ long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
switch (cmd) {
case WAKE_CHARM:
pr_info("%s: Powering on mdm\n", __func__);
+#ifdef CONFIG_MDM_HSIC_PM
+ request_boot_lock_set(rmnet_pm_dev);
+#endif
mdm_drv->ops->power_on_mdm_cb(mdm_drv);
break;
case CHECK_FOR_BOOT:
@@ -108,6 +242,13 @@ long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
complete(&mdm_boot);
else
first_boot = 0;
+
+ /* If bootup succeeded, start a timer to check that the
+ * mdm2ap_status gpio goes high.
+ */
+ if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
+ schedule_delayed_work(&mdm2ap_status_check_work,
+ msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
break;
case RAM_DUMP_DONE:
pr_info("%s: mdm done collecting RAM dumps\n", __func__);
@@ -117,9 +258,19 @@ long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
else {
pr_info("%s: ramdump collection completed\n", __func__);
mdm_drv->mdm_ram_dump_status = 0;
+ panic("CP Crash %s", mdm_read_err_report());
}
complete(&mdm_ram_dumps);
break;
+
+ case WAIT_FOR_ERROR:
+ pr_debug("%s: wait for mdm error\n", __func__);
+ #if 0
+ ret = wait_for_completion_interruptible(&mdm_error);
+ INIT_COMPLETION(mdm_error);
+ #endif
+ break;
+
case WAIT_FOR_RESTART:
pr_info("%s: wait for mdm to need images reloaded\n",
__func__);
@@ -129,6 +280,29 @@ long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
(unsigned long __user *) arg);
INIT_COMPLETION(mdm_needs_reload);
break;
+
+ case SILENT_RESET_CONTROL:
+ pr_info("%s: mdm doing silent reset\n", __func__);
+ mdm_drv->mdm_ram_dump_status = 0;
+ complete(&mdm_ram_dumps);
+ break;
+
+ case AUTOPM_LOCK:
+ get_user(status, (unsigned long __user *) arg);
+ pr_info("%s: mdm autopm request[%s]\n", __func__,
+ status ? "lock" : "release");
+ request_autopm_lock(status);
+ break;
+
+ case GET_BOOT_PROTOCOL:
+ pr_info("%s: mdm get boot protocol %d\n", __func__,
+ mdm_drv->proto_is_dload);
+ return mdm_drv->proto_is_dload;
+
+ case GET_FORCE_RAMDUMP:
+ pr_info("%s: mdm get dump mode = %d\n", __func__, force_dump);
+ return force_dump;
+
default:
pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
ret = -EINVAL;
@@ -138,9 +312,23 @@ 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__);
+ notify_modem_fatal();
subsystem_restart(EXTERNAL_MODEM);
}
@@ -150,19 +338,15 @@ static void mdm_status_fn(struct work_struct *work)
{
int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
- if (!mdm_drv->mdm_ready)
- return;
-
- mdm_drv->ops->status_cb(mdm_drv, value);
-
- pr_err("%s: status:%d\n", __func__, value);
-
- if ((value == 0)) {
- pr_info("%s: unexpected reset external modem\n", __func__);
- subsystem_restart(EXTERNAL_MODEM);
- } else if (value == 1) {
- pr_info("%s: status = 1: mdm is now ready\n", __func__);
+ pr_debug("%s: status:%d\n", __func__, value);
+ if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
+ mdm_drv->ops->status_cb(mdm_drv, value);
+#ifdef CONFIG_MDM_HSIC_PM
+ if (value) {
+ request_boot_lock_release(rmnet_pm_dev);
+ request_active_lock_set(rmnet_pm_dev);
}
+#endif
}
static DECLARE_WORK(mdm_status_work, mdm_status_fn);
@@ -171,7 +355,6 @@ static void mdm_disable_irqs(void)
{
disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
disable_irq_nosync(mdm_drv->mdm_status_irq);
-
}
static irqreturn_t mdm_errfatal(int irq, void *dev_id)
@@ -179,12 +362,35 @@ static irqreturn_t mdm_errfatal(int irq, void *dev_id)
pr_debug("%s: mdm got errfatal interrupt\n", __func__);
if (mdm_drv->mdm_ready &&
(gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
- pr_debug("%s: scheduling work now\n", __func__);
+ pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
+ mdm_drv->mdm_ready = 0;
+ /* subsystem_restart(EXTERNAL_MODEM); */
queue_work(mdm_queue, &mdm_fatal_work);
}
return IRQ_HANDLED;
}
+static unsigned char *mdm_read_err_report(void)
+{
+ /* Read CP error report from mdm_err.log in tombstones */
+ static unsigned char buf[1000] = { 0, };
+ struct file *filp;
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ do {
+ filp = filp_open("/tombstones/mdm/mdm_err.log", \
+ O_RDWR, S_IRUSR|S_IWUSR);
+ if (IS_ERR(filp)) {
+ set_fs(old_fs);
+ return (unsigned char *) buf;
+ }
+ vfs_read(filp, buf, 1000, &filp->f_pos);
+ filp_close(filp, NULL);
+ set_fs(old_fs);
+ } while (0);
+ return (unsigned char *) buf;
+}
+
static int mdm_modem_open(struct inode *inode, struct file *file)
{
return 0;
@@ -219,21 +425,66 @@ static int mdm_panic_prep(struct notifier_block *this,
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
break;
}
- if (i <= 0)
+ if (i <= 0) {
pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
+ /* Reset the modem so that it will go into download mode. */
+ if (mdm_drv && mdm_drv->ops->reset_mdm_cb)
+ mdm_drv->ops->reset_mdm_cb(mdm_drv);
+ }
return NOTIFY_DONE;
}
static struct notifier_block mdm_panic_blk = {
.notifier_call = mdm_panic_prep,
+ .priority = 1,
+};
+
+static int mdm_reboot_notifier(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 1 : 0;
+ mdm_disable_irqs();
+ notify_modem_fatal();
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction);
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
+
+ /* give modem PMIC debounce time, spec is ~3.5 but 1s will be enough */
+ msleep(1500);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block mdm_down_block = {
+ .notifier_call = mdm_reboot_notifier,
+ .priority = 1,
};
static irqreturn_t mdm_status_change(int irq, void *dev_id)
{
int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
- pr_err("%s: mdm sent status change interrupt : %d\n", __func__, value);
- queue_work(mdm_queue, &mdm_status_work);
+ pr_debug("%s: mdm sent status change interrupt\n", __func__);
+ if (value == 0 && mdm_drv->mdm_ready == 1) {
+ pr_info("%s: unexpected reset external modem\n", __func__);
+ mdm_drv->mdm_unexpected_reset_occurred = 1;
+ mdm_drv->mdm_ready = 0;
+ notify_modem_fatal();
+ subsystem_restart(EXTERNAL_MODEM);
+ } else if (value == 1) {
+ cancel_delayed_work(&mdm2ap_status_check_work);
+ pr_info("%s: status = 1: mdm is now ready\n", __func__);
+ queue_work(mdm_queue, &mdm_status_work);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
+{
+ pr_info("%s: pbl ready:%d\n", __func__,
+ gpio_get_value(mdm_drv->mdm2ap_pblrdy));
return IRQ_HANDLED;
}
@@ -241,7 +492,7 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
{
pr_info("%s\n", __func__);
- mdm_drv->mdm_ready = 0;
+
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
if (mdm_drv->pdata->ramdump_delay_ms > 0) {
/* Wait for the external modem to complete
@@ -249,7 +500,13 @@ static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
*/
msleep(mdm_drv->pdata->ramdump_delay_ms);
}
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ #if 0
+ if (!mdm_drv->mdm_unexpected_reset_occurred)
+ mdm_drv->ops->reset_mdm_cb(mdm_drv);
+ else
+ mdm_drv->mdm_unexpected_reset_occurred = 0;
+
+ #endif
return 0;
}
@@ -257,7 +514,7 @@ static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
{
pr_info("%s\n", __func__);
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
- gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
+ gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 0);
mdm_drv->ops->power_on_mdm_cb(mdm_drv);
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
complete(&mdm_needs_reload);
@@ -265,8 +522,13 @@ static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
mdm_drv->mdm_boot_status = -ETIMEDOUT;
pr_info("%s: mdm modem restart timed out.\n", __func__);
- } else
+ } 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);
return mdm_drv->mdm_boot_status;
}
@@ -274,22 +536,25 @@ static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
static int mdm_subsys_ramdumps(int want_dumps,
const struct subsys_data *crashed_subsys)
{
- pr_info("%s\n", __func__);
+ pr_info("%s(dump = %d)\n", __func__, want_dumps);
mdm_drv->mdm_ram_dump_status = 0;
if (want_dumps) {
mdm_drv->boot_type = CHARM_RAM_DUMPS;
complete(&mdm_needs_reload);
+ pr_info("%s: waiting ramdump ...\n", __func__);
if (!wait_for_completion_timeout(&mdm_ram_dumps,
- msecs_to_jiffies(MDM_RDUMP_TIMEOUT))) {
+ msecs_to_jiffies(dump_timeout_ms))) {
mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
pr_info("%s: mdm modem ramdumps timed out.\n",
__func__);
- } else
+ } else {
pr_info("%s: mdm modem ramdumps completed.\n",
__func__);
+ mdm_drv->mdm_ram_dump_status = 0;
+ }
INIT_COMPLETION(mdm_ram_dumps);
- gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ if (!mdm_drv->pdata->no_powerdown_after_ramdumps)
+ mdm_drv->ops->power_down_mdm_cb(mdm_drv);
}
return mdm_drv->mdm_ram_dump_status;
}
@@ -319,6 +584,7 @@ DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
mdm_debug_on_get,
mdm_debug_on_set, "%llu\n");
+#ifndef CONFIG_ARCH_EXYNOS
static int mdm_debugfs_init(void)
{
struct dentry *dent;
@@ -331,6 +597,8 @@ static int mdm_debugfs_init(void)
&mdm_debug_on_fops);
return 0;
}
+#endif
+
static void mdm_modem_initialize_data(struct platform_device *pdev,
struct mdm_ops *mdm_ops)
@@ -373,11 +641,11 @@ static void mdm_modem_initialize_data(struct platform_device *pdev,
if (pres)
mdm_drv->ap2mdm_wakeup_gpio = pres->start;
- /* AP2MDM_PMIC_RESET_N */
+ /* AP2MDM_SOFT_RESET */
pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
- "AP2MDM_PMIC_RESET_N");
+ "AP2MDM_SOFT_RESET");
if (pres)
- mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
+ mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
/* AP2MDM_KPDPWR_N */
pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
@@ -385,10 +653,24 @@ static void mdm_modem_initialize_data(struct platform_device *pdev,
if (pres)
mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
+ /* AP2MDM_PMIC_PWR_EN */
+ pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "AP2MDM_PMIC_PWR_EN");
+ if (pres)
+ mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
+
+ /* MDM2AP_PBLRDY */
+ pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "MDM2AP_PBLRDY");
+ if (pres)
+ mdm_drv->mdm2ap_pblrdy = pres->start;
+
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
mdm_drv->ops = mdm_ops;
mdm_drv->pdata = pdev->dev.platform_data;
+ dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
+ mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
}
int mdm_common_create(struct platform_device *pdev,
@@ -409,31 +691,52 @@ int mdm_common_create(struct platform_device *pdev,
gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
- gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
- gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
+ 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");
+ if (mdm_drv->mdm2ap_pblrdy > 0)
+ gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
- if (mdm_drv->ap2mdm_wakeup_gpio > 0)
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0) {
+ gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
+ "AP2MDM_PMIC_PWR_EN");
+ gpio_set_value(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
+#ifdef CONFIG_ARCH_EXYNOS
+ s3c_gpio_cfgpin(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
+ S3C_GPIO_OUTPUT);
+ s3c_gpio_setpull(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
+ S3C_GPIO_PULL_UP);
+ s5p_gpio_set_drvstr(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
+ S5P_GPIO_DRVSTR_LV4);
+#endif
+ }
+ if (mdm_drv->ap2mdm_soft_reset_gpio > 0) {
+ gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
+ "AP2MDM_SOFT_RESET");
+ gpio_set_value(mdm_drv->ap2mdm_soft_reset_gpio, 0);
+#ifdef CONFIG_ARCH_EXYNOS
+ s3c_gpio_cfgpin(mdm_drv->ap2mdm_soft_reset_gpio,
+ S3C_GPIO_OUTPUT);
+ s3c_gpio_setpull(mdm_drv->ap2mdm_soft_reset_gpio,
+ S3C_GPIO_PULL_UP);
+#endif
+ }
+ if (mdm_drv->ap2mdm_wakeup_gpio > 0) {
gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
-
+ gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
#ifdef CONFIG_ARCH_EXYNOS
- gpio_set_value(mdm_drv->ap2mdm_status_gpio, 1);
- s3c_gpio_cfgpin(mdm_drv->ap2mdm_status_gpio, S3C_GPIO_OUTPUT);
- s3c_gpio_setpull(mdm_drv->ap2mdm_status_gpio, S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(mdm_drv->ap2mdm_wakeup_gpio, S3C_GPIO_OUTPUT);
+ s3c_gpio_setpull(mdm_drv->ap2mdm_wakeup_gpio,
+ S3C_GPIO_PULL_NONE);
#endif
- gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
- pr_err("%s> : right after ap2mdm_status = %d\n", __func__,
- gpio_get_value(mdm_drv->ap2mdm_status_gpio));
+ }
+ gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
#ifdef CONFIG_ARCH_EXYNOS
- gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 0);
s3c_gpio_cfgpin(mdm_drv->ap2mdm_errfatal_gpio, S3C_GPIO_OUTPUT);
- s3c_gpio_setpull(mdm_drv->ap2mdm_errfatal_gpio, S3C_GPIO_PULL_DOWN);
+ s3c_gpio_setpull(mdm_drv->ap2mdm_errfatal_gpio, S3C_GPIO_PULL_NONE);
#endif
- gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
- pr_err("%s>> : right after ap2mdm_status = %d\n", __func__,
- gpio_get_value(mdm_drv->ap2mdm_status_gpio));
if (mdm_drv->ap2mdm_wakeup_gpio > 0)
gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
@@ -450,8 +753,24 @@ int mdm_common_create(struct platform_device *pdev,
goto fatal_err;
}
+#ifndef CONFIG_ARCH_EXYNOS
+ mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
+ if (!mdm_sfr_queue) {
+ pr_err("%s: could not create workqueue mdm_sfr_queue."
+ " All mdm functionality will be disabled\n",
+ __func__);
+ ret = -ENOMEM;
+ destroy_workqueue(mdm_queue);
+ goto fatal_err;
+ }
+#endif
+
atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
+ register_reboot_notifier(&mdm_down_block);
+
+#ifndef CONFIG_ARCH_EXYNOS
mdm_debugfs_init();
+#endif
/* Register subsystem handlers */
ssr_register_subsystem(&mdm_subsystem);
@@ -459,6 +778,8 @@ int mdm_common_create(struct platform_device *pdev,
/* ERR_FATAL irq. */
#ifdef CONFIG_ARCH_EXYNOS
irq = gpio_to_irq(mdm_drv->mdm2ap_errfatal_gpio);
+ s3c_gpio_cfgpin(mdm_drv->mdm2ap_errfatal_gpio, S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(mdm_drv->mdm2ap_errfatal_gpio, S3C_GPIO_PULL_NONE);
#else
irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
#endif
@@ -478,14 +799,13 @@ int mdm_common_create(struct platform_device *pdev,
goto errfatal_err;
}
mdm_drv->mdm_errfatal_irq = irq;
+ enable_irq_wake(irq);
errfatal_err:
-
/* status irq */
#ifdef CONFIG_ARCH_EXYNOS
- ret = s5p_register_gpio_interrupt(mdm_drv->mdm2ap_status_gpio);
- if (ret)
- pr_err("%s: register MDM2AP_STATUS ret = %d\n", __func__, ret);
+ s3c_gpio_cfgpin(mdm_drv->mdm2ap_status_gpio, S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(mdm_drv->mdm2ap_status_gpio, S3C_GPIO_PULL_NONE);
irq = gpio_to_irq(mdm_drv->mdm2ap_status_gpio);
#else
irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
@@ -508,14 +828,50 @@ errfatal_err:
goto status_err;
}
mdm_drv->mdm_status_irq = irq;
+ enable_irq_wake(irq);
status_err:
+ if (mdm_drv->mdm2ap_pblrdy > 0) {
+#ifdef CONFIG_ARCH_EXYNOS
+ s3c_gpio_cfgpin(mdm_drv->mdm2ap_pblrdy, S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(mdm_drv->mdm2ap_pblrdy, S3C_GPIO_PULL_NONE);
+ irq = gpio_to_irq(mdm_drv->mdm2ap_pblrdy);
+#else
+ irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
+#endif
+ if (irq < 0) {
+ pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
+ __func__);
+ goto pblrdy_err;
+ }
+
+ ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_SHARED,
+ "mdm pbl ready", mdm_drv);
+
+ if (ret < 0) {
+ pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
+ __func__, irq, ret);
+ goto pblrdy_err;
+ }
+ }
+
+pblrdy_err:
+ /*
+ * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
+ * high until the whole phone is shut down.
+ */
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
+ /* Register VDDmin gpios with RPM */
+ mdm_setup_vddmin_gpios();
+
/* Perform early powerup of the external modem in order to
* allow tabla devices to be found.
*/
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
- pr_err("%s : ap2mdm_status = %d\n", __func__,
- gpio_get_value(mdm_drv->ap2mdm_status_gpio));
+ if (mdm_drv->pdata->early_power_on)
+ mdm_drv->ops->power_on_mdm_cb(mdm_drv);
pr_info("%s: Registering mdm modem\n", __func__);
return misc_register(&mdm_modem_misc);
@@ -523,10 +879,14 @@ status_err:
fatal_err:
gpio_free(mdm_drv->ap2mdm_status_gpio);
gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
gpio_free(mdm_drv->mdm2ap_status_gpio);
gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+ if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
if (mdm_drv->ap2mdm_wakeup_gpio > 0)
gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
@@ -544,10 +904,14 @@ int mdm_common_modem_remove(struct platform_device *pdev)
gpio_free(mdm_drv->ap2mdm_status_gpio);
gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
gpio_free(mdm_drv->mdm2ap_status_gpio);
gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+ if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
if (mdm_drv->ap2mdm_wakeup_gpio > 0)
gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
@@ -563,5 +927,6 @@ void mdm_common_modem_shutdown(struct platform_device *pdev)
mdm_disable_irqs();
mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
}
-