msm: mdm: Add support for remote MDM image upgrade

mdm-driver is informed that an image upgrade of the remote modem is
required, at which point it stops monitoring the error gpios and takes
the appropriate actions. Support for the "USB Switch" gpio is added.
This is needed to write a image to an empty modem flash.

Change-Id: I4c05a80955124a3eb7edc11e99f4945f9de79e6b
Signed-off-by: Ameya Thakur <ameyat@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c
index fd326f1..53e7c9e 100644
--- a/arch/arm/mach-msm/board-8960-gpiomux.c
+++ b/arch/arm/mach-msm/board-8960-gpiomux.c
@@ -240,6 +240,12 @@
 	.pull = GPIOMUX_PULL_DOWN,
 };
 
+static struct gpiomux_setting usbsw_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_DOWN,
+};
+
 static struct gpiomux_setting mdp_vsync_suspend_cfg = {
 	.func = GPIOMUX_FUNC_GPIO,
 	.drv = GPIOMUX_DRV_2MA,
@@ -760,6 +766,13 @@
 			[GPIOMUX_SUSPENDED] = &ap2mdm_cfg,
 		}
 	},
+	/* USB_SW */
+	{
+		.gpio = 25,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &usbsw_cfg,
+		}
+	}
 };
 
 static struct msm_gpiomux_config msm8960_mdp_vsync_configs[] __initdata = {
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index bb20d04..cbdc9ff 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1293,6 +1293,7 @@
 	.peripheral_platform_device = NULL,
 	.ramdump_timeout_ms = 600000,
 	.no_powerdown_after_ramdumps = 1,
+	.image_upgrade_supported = 1,
 };
 
 #define MSM_TSIF0_PHYS			(0x18200000)
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 79f8c88..d74d4aa 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -3812,6 +3812,7 @@
 #define AP2MDM_PMIC_PWR_EN		22
 #define AP2MDM_KPDPWR_N			79
 #define AP2MDM_SOFT_RESET		78
+#define USB_SW				25
 
 static struct resource sglte_resources[] = {
 	{
@@ -3856,6 +3857,12 @@
 		.name	= "AP2MDM_SOFT_RESET",
 		.flags	= IORESOURCE_IO,
 	},
+	{
+		.start	= USB_SW,
+		.end	= USB_SW,
+		.name	= "USB_SW",
+		.flags	= IORESOURCE_IO,
+	},
 };
 
 struct platform_device mdm_sglte_device = {
diff --git a/arch/arm/mach-msm/include/mach/mdm2.h b/arch/arm/mach-msm/include/mach/mdm2.h
index 637a3cc..c4877cc 100644
--- a/arch/arm/mach-msm/include/mach/mdm2.h
+++ b/arch/arm/mach-msm/include/mach/mdm2.h
@@ -31,6 +31,7 @@
 	struct mdm_vddmin_resource *vddmin_resource;
 	struct platform_device *peripheral_platform_device;
 	const unsigned int ramdump_timeout_ms;
+	int image_upgrade_supported;
 };
 
 #endif
diff --git a/arch/arm/mach-msm/mdm2.c b/arch/arm/mach-msm/mdm2.c
index 6e7086e..e74af2e 100644
--- a/arch/arm/mach-msm/mdm2.c
+++ b/arch/arm/mach-msm/mdm2.c
@@ -249,6 +249,33 @@
 	}
 }
 
+static void mdm_image_upgrade(struct mdm_modem_drv *mdm_drv, int type)
+{
+	switch (type) {
+	case APQ_CONTROLLED_UPGRADE:
+		pr_debug("%s APQ controlled modem image upgrade\n", __func__);
+		mdm_drv->mdm_ready = 0;
+		mdm_toggle_soft_reset(mdm_drv);
+		break;
+	case MDM_CONTROLLED_UPGRADE:
+		pr_debug("%s MDM controlled modem image upgrade\n", __func__);
+		mdm_drv->mdm_ready = 0;
+		/*
+		 * If we have no image currently present on the modem, then we
+		 * would be in PBL, in which case the status gpio would not go
+		 * high.
+		 */
+		mdm_drv->disable_status_check = 1;
+		if (mdm_drv->usb_switch_gpio > 0) {
+			pr_info("%s Switching usb control to MDM\n", __func__);
+			gpio_direction_output(mdm_drv->usb_switch_gpio, 1);
+		} else
+			pr_err("%s usb switch gpio unavailable\n", __func__);
+		break;
+	default:
+		pr_err("%s invalid upgrade type\n", __func__);
+	}
+}
 static struct mdm_ops mdm_cb = {
 	.power_on_mdm_cb = mdm_power_on_common,
 	.reset_mdm_cb = mdm_power_on_common,
@@ -256,6 +283,7 @@
 	.power_down_mdm_cb = mdm_power_down_common,
 	.debug_state_changed_cb = debug_state_changed,
 	.status_cb = mdm_status_changed,
+	.image_upgrade_cb = mdm_image_upgrade,
 };
 
 static int __init mdm_modem_probe(struct platform_device *pdev)
diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c
index ac9da2e..6b40cda 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -151,11 +151,13 @@
 	 * 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;
-		subsystem_restart_dev(mdm_subsys_dev);
+	if (!mdm_drv->disable_status_check) {
+		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;
+			subsystem_restart_dev(mdm_subsys_dev);
+		}
 	}
 }
 
@@ -239,6 +241,15 @@
 		else
 			put_user(0, (unsigned long __user *) arg);
 		break;
+	case IMAGE_UPGRADE:
+		pr_debug("%s Image upgrade ioctl recieved\n", __func__);
+		if (mdm_drv->pdata->image_upgrade_supported &&
+				mdm_drv->ops->image_upgrade_cb) {
+			get_user(status, (unsigned long __user *) arg);
+			mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
+		} else
+			pr_debug("%s Image upgrade not supported\n", __func__);
+		break;
 	default:
 		pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
 		ret = -EINVAL;
@@ -364,7 +375,6 @@
 		mdm_drv->ops->reset_mdm_cb(mdm_drv);
 	else
 		mdm_drv->mdm_unexpected_reset_occurred = 0;
-
 	return 0;
 }
 
@@ -515,6 +525,12 @@
 	if (pres)
 		mdm_drv->mdm2ap_pblrdy = pres->start;
 
+	/*USB_SW*/
+	pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
+							"USB_SW");
+	if (pres)
+		mdm_drv->usb_switch_gpio = pres->start;
+
 	mdm_drv->boot_type                  = CHARM_NORMAL_BOOT;
 
 	mdm_drv->ops      = mdm_ops;
@@ -557,6 +573,13 @@
 	if (mdm_drv->ap2mdm_wakeup_gpio > 0)
 		gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
 
+	if (mdm_drv->usb_switch_gpio > 0) {
+		if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
+			pr_err("%s Failed to get usb switch gpio\n", __func__);
+			mdm_drv->usb_switch_gpio = -1;
+		}
+	}
+
 	gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
 	gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
 
diff --git a/arch/arm/mach-msm/mdm_private.h b/arch/arm/mach-msm/mdm_private.h
index 7ac3727..7aba83d 100644
--- a/arch/arm/mach-msm/mdm_private.h
+++ b/arch/arm/mach-msm/mdm_private.h
@@ -23,6 +23,7 @@
 	void (*power_down_mdm_cb)(struct mdm_modem_drv *mdm_drv);
 	void (*debug_state_changed_cb)(int value);
 	void (*status_cb)(struct mdm_modem_drv *mdm_drv, int value);
+	void (*image_upgrade_cb)(struct mdm_modem_drv *mdm_drv, int type);
 };
 
 /* Private mdm2 data structure */
@@ -37,6 +38,7 @@
 	unsigned ap2mdm_soft_reset_gpio;
 	unsigned ap2mdm_pmic_pwr_en_gpio;
 	unsigned mdm2ap_pblrdy;
+	unsigned usb_switch_gpio;
 
 	int mdm_errfatal_irq;
 	int mdm_status_irq;
@@ -46,6 +48,7 @@
 	enum charm_boot_type boot_type;
 	int mdm_debug_on;
 	int mdm_unexpected_reset_occurred;
+	int disable_status_check;
 
 	struct mdm_ops *ops;
 	struct mdm_platform_data *pdata;
diff --git a/include/linux/msm_charm.h b/include/linux/msm_charm.h
index c31e493..44d2553 100644
--- a/include/linux/msm_charm.h
+++ b/include/linux/msm_charm.h
@@ -11,10 +11,15 @@
 #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)
+#define IMAGE_UPGRADE		_IOW(CHARM_CODE, 9, int)
 
 enum charm_boot_type {
 	CHARM_NORMAL_BOOT = 0,
 	CHARM_RAM_DUMPS,
 };
 
+enum image_upgrade_type {
+	APQ_CONTROLLED_UPGRADE = 0,
+	MDM_CONTROLLED_UPGRADE,
+};
 #endif