Merge "msm: mdm: add mdm driver support for the sglte modem" into msm-3.0
diff --git a/arch/arm/mach-msm/include/mach/mdm2.h b/arch/arm/mach-msm/include/mach/mdm2.h
index 78ca88f..997b3be 100644
--- a/arch/arm/mach-msm/include/mach/mdm2.h
+++ b/arch/arm/mach-msm/include/mach/mdm2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,9 @@
struct mdm_platform_data {
char *mdm_version;
int ramdump_delay_ms;
+ int soft_reset_inverted;
+ int early_power_on;
+ int sfr_query;
struct platform_device *peripheral_platform_device;
};
diff --git a/arch/arm/mach-msm/mdm2.c b/arch/arm/mach-msm/mdm2.c
index b4b7ea3..bd7bd9e 100644
--- a/arch/arm/mach-msm/mdm2.c
+++ b/arch/arm/mach-msm/mdm2.c
@@ -53,11 +53,13 @@
static void mdm_peripheral_connect(struct mdm_modem_drv *mdm_drv)
{
+ if (!mdm_drv->pdata->peripheral_platform_device)
+ return;
+
mutex_lock(&hsic_status_lock);
if (hsic_peripheral_status)
goto out;
- if (mdm_drv->pdata->peripheral_platform_device)
- platform_device_add(mdm_drv->pdata->peripheral_platform_device);
+ platform_device_add(mdm_drv->pdata->peripheral_platform_device);
hsic_peripheral_status = 1;
out:
mutex_unlock(&hsic_status_lock);
@@ -65,84 +67,106 @@
static void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv)
{
+ if (!mdm_drv->pdata->peripheral_platform_device)
+ return;
+
mutex_lock(&hsic_status_lock);
if (!hsic_peripheral_status)
goto out;
- if (mdm_drv->pdata->peripheral_platform_device)
- platform_device_del(mdm_drv->pdata->peripheral_platform_device);
+ platform_device_del(mdm_drv->pdata->peripheral_platform_device);
hsic_peripheral_status = 0;
out:
mutex_unlock(&hsic_status_lock);
}
-static void power_on_mdm(struct mdm_modem_drv *mdm_drv)
+static void mdm_power_down_common(struct mdm_modem_drv *mdm_drv)
+{
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 1 : 0;
+
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction);
+ mdm_peripheral_disconnect(mdm_drv);
+}
+
+static void mdm_do_first_power_on(struct mdm_modem_drv *mdm_drv)
+{
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 0 : 1;
+
+ if (power_on_count != 1) {
+ pr_err("%s: Calling fn when power_on_count != 1\n",
+ __func__);
+ return;
+ }
+
+ pr_err("%s: Powering on modem for the first time\n", __func__);
+ mdm_peripheral_disconnect(mdm_drv);
+
+ /* If the device has a kpd pwr gpio then toggle it. */
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) {
+ /* Pull AP2MDM_KPDPWR gpio high and wait for PS_HOLD to settle,
+ * then pull it back low.
+ */
+ pr_debug("%s: Pulling AP2MDM_KPDPWR gpio high\n", __func__);
+ gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1);
+ msleep(1000);
+ gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0);
+ }
+
+ /* De-assert the soft reset line. */
+ pr_debug("%s: De-asserting soft reset gpio\n", __func__);
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction);
+
+ mdm_peripheral_connect(mdm_drv);
+ msleep(200);
+}
+
+static void mdm_do_soft_power_on(struct mdm_modem_drv *mdm_drv)
+{
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 0 : 1;
+
+ /* De-assert the soft reset line. */
+ pr_err("%s: soft resetting mdm modem\n", __func__);
+
+ mdm_peripheral_disconnect(mdm_drv);
+
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction == 1 ? 0 : 1);
+ usleep_range(5000, 10000);
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction == 1 ? 1 : 0);
+
+ mdm_peripheral_connect(mdm_drv);
+ msleep(200);
+}
+
+static void mdm_power_on_common(struct mdm_modem_drv *mdm_drv)
{
power_on_count++;
/* this gpio will be used to indicate apq readiness,
- * de-assert it now so that it can asserted later
+ * de-assert it now so that it can be asserted later.
+ * May not be used.
*/
- gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
+ if (mdm_drv->ap2mdm_wakeup_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
- /* The second attempt to power-on the mdm is the first attempt
- * from user space, but we're already powered on. Ignore this.
- * Subsequent attempts are from SSR or if something failed, in
- * which case we must always reset the modem.
+ /*
+ * If we did an "early power on" then ignore the very next
+ * power-on request because it would the be first request from
+ * user space but we're already powered on. Ignore it.
*/
- if (power_on_count == 2)
+ if (mdm_drv->pdata->early_power_on &&
+ (power_on_count == 2))
return;
- mdm_peripheral_disconnect(mdm_drv);
-
- /* Pull RESET gpio low and wait for it to settle. */
- pr_debug("Pulling RESET gpio low\n");
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 0);
- usleep_range(5000, 10000);
-
- /* Deassert RESET first and wait for it to settle. */
- pr_debug("%s: Pulling RESET gpio high\n", __func__);
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 1);
- usleep(20000);
-
- /* Pull PWR gpio high and wait for it to settle, but only
- * the first time the mdm is powered up.
- * Some targets do not use ap2mdm_kpdpwr_n_gpio.
- */
- if (power_on_count == 1) {
- if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) {
- pr_debug("%s: Powering on mdm modem\n", __func__);
- gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1);
- usleep(1000);
- }
- }
- mdm_peripheral_connect(mdm_drv);
-
- msleep(200);
-}
-
-static void power_down_mdm(struct mdm_modem_drv *mdm_drv)
-{
- int i;
-
- for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
- pet_watchdog();
- msleep(MDM_MODEM_DELTA);
- if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
- break;
- }
- if (i <= 0) {
- pr_err("%s: MDM2AP_STATUS never went low.\n",
- __func__);
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 0);
-
- for (i = MDM_HOLD_TIME; i > 0; i -= MDM_MODEM_DELTA) {
- pet_watchdog();
- msleep(MDM_MODEM_DELTA);
- }
- }
- if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
- gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0);
- mdm_peripheral_disconnect(mdm_drv);
+ if (power_on_count == 1)
+ mdm_do_first_power_on(mdm_drv);
+ else
+ mdm_do_soft_power_on(mdm_drv);
}
static void debug_state_changed(int value)
@@ -157,13 +181,15 @@
if (value) {
mdm_peripheral_disconnect(mdm_drv);
mdm_peripheral_connect(mdm_drv);
- gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 1);
+ if (mdm_drv->ap2mdm_wakeup_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 1);
}
}
static struct mdm_ops mdm_cb = {
- .power_on_mdm_cb = power_on_mdm,
- .power_down_mdm_cb = power_down_mdm,
+ .power_on_mdm_cb = mdm_power_on_common,
+ .reset_mdm_cb = mdm_power_on_common,
+ .power_down_mdm_cb = mdm_power_down_common,
.debug_state_changed_cb = debug_state_changed,
.status_cb = mdm_status_changed,
};
diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c
index f8e187d..ffff782 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -152,6 +152,14 @@
(unsigned long __user *) arg);
INIT_COMPLETION(mdm_needs_reload);
break;
+ case GET_DLOAD_STATUS:
+ pr_debug("getting status of mdm2ap_errfatal_gpio\n");
+ if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
+ !mdm_drv->mdm_ready)
+ put_user(1, (unsigned long __user *) arg);
+ else
+ put_user(0, (unsigned long __user *) arg);
+ break;
default:
pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
ret = -EINVAL;
@@ -161,31 +169,13 @@
return ret;
}
-static void mdm_fatal_fn(struct work_struct *work)
-{
- pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
- subsystem_restart(EXTERNAL_MODEM);
-}
-
-static DECLARE_WORK(mdm_fatal_work, mdm_fatal_fn);
-
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_debug("%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__);
- }
+ if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
+ mdm_drv->ops->status_cb(mdm_drv, value);
}
static DECLARE_WORK(mdm_status_work, mdm_status_fn);
@@ -194,7 +184,6 @@
{
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)
@@ -202,8 +191,9 @@
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__);
- queue_work(mdm_queue, &mdm_fatal_work);
+ pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
+ mdm_drv->mdm_ready = 0;
+ subsystem_restart(EXTERNAL_MODEM);
}
return IRQ_HANDLED;
}
@@ -242,8 +232,12 @@
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;
}
@@ -253,16 +247,23 @@
static irqreturn_t mdm_status_change(int irq, void *dev_id)
{
+ int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
+
pr_debug("%s: mdm sent status change interrupt\n", __func__);
-
- queue_work(mdm_queue, &mdm_status_work);
-
+ 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;
+ subsystem_restart(EXTERNAL_MODEM);
+ } else if (value == 1) {
+ pr_info("%s: status = 1: mdm is now ready\n", __func__);
+ queue_work(mdm_queue, &mdm_status_work);
+ }
return IRQ_HANDLED;
}
static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
{
- 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
@@ -270,7 +271,11 @@
*/
msleep(mdm_drv->pdata->ramdump_delay_ms);
}
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ if (!mdm_drv->mdm_unexpected_reset_occurred)
+ mdm_drv->ops->reset_mdm_cb(mdm_drv);
+ else
+ mdm_drv->mdm_unexpected_reset_occurred = 0;
+
return 0;
}
@@ -287,8 +292,10 @@
pr_info("%s: mdm modem restart timed out.\n", __func__);
} else {
pr_info("%s: mdm modem has been restarted\n", __func__);
+
/* Log the reason for the restart */
- queue_work(mdm_sfr_queue, &sfr_reason_work);
+ if (mdm_drv->pdata->sfr_query)
+ queue_work(mdm_sfr_queue, &sfr_reason_work);
}
INIT_COMPLETION(mdm_boot);
return mdm_drv->mdm_boot_status;
@@ -310,7 +317,6 @@
pr_info("%s: mdm modem ramdumps completed.\n",
__func__);
INIT_COMPLETION(mdm_ram_dumps);
- gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
mdm_drv->ops->power_down_mdm_cb(mdm_drv);
}
return mdm_drv->mdm_ram_dump_status;
@@ -395,11 +401,11 @@
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,
@@ -407,6 +413,12 @@
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;
+
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
mdm_drv->ops = mdm_ops;
@@ -430,11 +442,18 @@
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->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
+ "AP2MDM_PMIC_PWR_EN");
+ if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
+ gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
+ "AP2MDM_SOFT_RESET");
+
if (mdm_drv->ap2mdm_wakeup_gpio > 0)
gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
@@ -515,10 +534,18 @@
mdm_drv->mdm_status_irq = irq;
status_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);
+
/* 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);
+ 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);
@@ -526,10 +553,14 @@
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);
@@ -547,10 +578,14 @@
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);
@@ -566,5 +601,7 @@
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);
}
diff --git a/arch/arm/mach-msm/mdm_private.h b/arch/arm/mach-msm/mdm_private.h
index 206bd8b..f157d88 100644
--- a/arch/arm/mach-msm/mdm_private.h
+++ b/arch/arm/mach-msm/mdm_private.h
@@ -17,6 +17,7 @@
struct mdm_ops {
void (*power_on_mdm_cb)(struct mdm_modem_drv *mdm_drv);
+ void (*reset_mdm_cb)(struct mdm_modem_drv *mdm_drv);
void (*normal_boot_done_cb)(struct mdm_modem_drv *mdm_drv);
void (*power_down_mdm_cb)(struct mdm_modem_drv *mdm_drv);
void (*debug_state_changed_cb)(int value);
@@ -31,8 +32,9 @@
unsigned ap2mdm_status_gpio;
unsigned mdm2ap_wakeup_gpio;
unsigned ap2mdm_wakeup_gpio;
- unsigned ap2mdm_pmic_reset_n_gpio;
unsigned ap2mdm_kpdpwr_n_gpio;
+ unsigned ap2mdm_soft_reset_gpio;
+ unsigned ap2mdm_pmic_pwr_en_gpio;
int mdm_errfatal_irq;
int mdm_status_irq;
@@ -41,6 +43,7 @@
int mdm_ram_dump_status;
enum charm_boot_type boot_type;
int mdm_debug_on;
+ int mdm_unexpected_reset_occurred;
struct mdm_ops *ops;
struct mdm_platform_data *pdata;
diff --git a/include/linux/msm_charm.h b/include/linux/msm_charm.h
index 779fd38..c31e493 100644
--- a/include/linux/msm_charm.h
+++ b/include/linux/msm_charm.h
@@ -10,6 +10,7 @@
#define NORMAL_BOOT_DONE _IOW(CHARM_CODE, 5, int)
#define RAM_DUMP_DONE _IOW(CHARM_CODE, 6, int)
#define WAIT_FOR_RESTART _IOR(CHARM_CODE, 7, int)
+#define GET_DLOAD_STATUS _IOR(CHARM_CODE, 8, int)
enum charm_boot_type {
CHARM_NORMAL_BOOT = 0,