msm: sdio: Close all open channels on shutdown.

1.Add channel_close(ch, flush_flag) to turn off flush when shutdown
2. separate reset and shutdown callbacks
3. close all opened channels when shutdown

Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
Change-Id: I779f1802860f07eb56aef83de445072c739443dc
diff --git a/arch/arm/mach-msm/sdio_al.c b/arch/arm/mach-msm/sdio_al.c
index f5b9ad7..1a05449 100644
--- a/arch/arm/mach-msm/sdio_al.c
+++ b/arch/arm/mach-msm/sdio_al.c
@@ -2815,14 +2815,11 @@
 	return ret;
 }
 
-/**
- *  Close SDIO Channel.
- *
- */
-int sdio_close(struct sdio_channel *ch)
+static int channel_close(struct sdio_channel *ch, int flush_flag)
 {
 	int ret;
 	struct sdio_al_device *sdio_al_dev = NULL;
+	struct sdio_func *func1 = NULL;
 	int flush_len;
 	ulong flush_expires;
 
@@ -2847,19 +2844,28 @@
 				" on channel:%d\n", __func__, ch->num);
 		return -ENODEV;
 	}
-	sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+	func1 = sdio_al_dev->card->sdio_func[0];
+	sdio_claim_host(func1);
 
 	if (sdio_al_dev->is_err) {
 		SDIO_AL_ERR(__func__);
-		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto error_exit;
 	}
 	if (sdio_al_dev->state != CARD_INSERTED) {
 		sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: "
 				"sdio_al_dev is in invalid state %d\n",
 				__func__, sdio_al_dev->state);
-		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto error_exit;
+	}
+	if (ch->state != SDIO_CHANNEL_STATE_OPEN) {
+		sdio_al_loge(sdio_al_dev->dev_log,
+				MODULE_NAME ":%s: ch %s is not in "
+				"open state (%d)\n",
+				__func__, ch->name, ch->state);
+		ret = -ENODEV;
+		goto error_exit;
 	}
 	ch->state = SDIO_CHANNEL_STATE_CLOSING;
 	ret = peer_set_operation(PEER_OP_CODE_CLOSE, sdio_al_dev, ch);
@@ -2867,76 +2873,94 @@
 		sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: "
 				"peer_set_operation() failed: %d\n",
 				__func__, ret);
-		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto error_exit;
 	}
 	/* udate poll time for opened channels */
 	if  (ch->poll_delay_msec > 0) {
 		sdio_al_dev->poll_delay_msec =
 			get_min_poll_time_msec(sdio_al_dev);
 	}
-	sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+	sdio_release_host(func1);
 
 	flush_expires = jiffies +
 		msecs_to_jiffies(SDIO_CLOSE_FLUSH_TIMEOUT_MSEC);
 	/* flush rx packets of the channel */
-	do {
-		while (ch->read_avail > 0) {
-			flush_len = ch->read_avail;
-			ret = sdio_read_internal(ch, sdio_al_dev->rx_flush_buf,
-						 flush_len);
-			if (ret) {
-				sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME
-						":%s failed to sdio_read: %d, ch %s\n",
-						__func__, ret, ch->name);
-				return ret;
-			}
+	if (flush_flag) {
+		do {
+			while (ch->read_avail > 0) {
+				flush_len = ch->read_avail;
+				ret = sdio_read_internal(ch,
+						sdio_al_dev->rx_flush_buf,
+						flush_len);
+				if (ret) {
+					sdio_al_loge(&sdio_al->gen_log,
+						MODULE_NAME ":%s sdio_read"
+						" failed: %d, ch %s\n",
+						__func__, ret,
+						ch->name);
+					return ret;
+				}
 
-			if (time_after(jiffies, flush_expires) != 0) {
-				sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME
-						":%s flush rx packets"
-						" timeout: ch %s\n",
+				if (time_after(jiffies, flush_expires) != 0) {
+					sdio_al_loge(&sdio_al->gen_log,
+						MODULE_NAME ":%s flush rx "
+						"packets timeout: ch %s\n",
 						__func__, ch->name);
-				sdio_al_get_into_err_state(sdio_al_dev);
-				return -EBUSY;
+					sdio_al_get_into_err_state(sdio_al_dev);
+					return -EBUSY;
+				}
 			}
-		}
-		msleep(100);
-		if (ch->signature != SDIO_AL_SIGNATURE) {
-			sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: "
-					"after sleep, invalid signature"
-					" 0x%x\n", __func__, ch->signature);
-			return -ENODEV;
-		}
-		if (sdio_al_verify_dev(ch->sdio_al_dev, __func__))
-			return -ENODEV;
-		sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
-		ret = read_mailbox(sdio_al_dev, false);
-		if (ret) {
-			pr_err(MODULE_NAME ":%s: failed to read mailbox",
-			       __func__);
-			sdio_release_host(sdio_al_dev->card->sdio_func[0]);
-			return -ENODEV;
-		}
-		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
-	} while (ch->read_avail > 0);
+			msleep(100);
+			if (ch->signature != SDIO_AL_SIGNATURE) {
+					sdio_al_loge(&sdio_al->gen_log,
+						MODULE_NAME ":%s: after sleep,"
+						" invalid signature"
+						" 0x%x\n", __func__,
+						ch->signature);
+				return -ENODEV;
+			}
+			if (sdio_al_verify_dev(ch->sdio_al_dev, __func__))
+				return -ENODEV;
+			sdio_claim_host(func1);
 
-	sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+			ret = read_mailbox(sdio_al_dev, false);
+			if (ret) {
+				sdio_al_loge(&sdio_al->gen_log,
+						MODULE_NAME ":%s: failed to"
+						" read mailbox", __func__);
+				goto error_exit;
+			}
+			sdio_release_host(func1);
+		} while (ch->read_avail > 0);
+	}
+	sdio_claim_host(func1);
 	/* disable function to be able to open the channel again */
 	ret = sdio_disable_func(ch->func);
 	if (ret) {
-		sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to "
-				"disable Func#%d\n", ch->func->num);
-		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
-		return ret;
+		sdio_al_loge(&sdio_al->gen_log,
+			MODULE_NAME ":Fail to disable Func#%d\n",
+			ch->func->num);
+		goto error_exit;
 	}
 	ch->state = SDIO_CHANNEL_STATE_CLOSED;
 	CLOSE_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":%s: Ch %s closed "
 				"successfully\n", __func__, ch->name);
-	sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+
+error_exit:
+	sdio_release_host(func1);
 
 	return ret;
 }
+
+/**
+ *  Close SDIO Channel.
+ *
+ */
+int sdio_close(struct sdio_channel *ch)
+{
+	return channel_close(ch, true);
+}
 EXPORT_SYMBOL(sdio_close);
 
 /**
@@ -3005,7 +3029,8 @@
 	struct sdio_al_device *sdio_al_dev = NULL;
 
 	if (!ch) {
-		pr_err(MODULE_NAME ":%s: NULL channel\n",  __func__);
+		sdio_al_loge(ch->sdio_al_dev->dev_log,
+			MODULE_NAME ":%s: NULL channel\n",  __func__);
 		return -ENODEV;
 	}
 
@@ -3017,7 +3042,8 @@
 				 PIPE_RX_FIFO_ADDR, len);
 
 	if (ret) {
-		pr_err(MODULE_NAME ":ch %s: %s err=%d, len=%d\n",
+		sdio_al_loge(ch->sdio_al_dev->dev_log,
+				MODULE_NAME ":ch %s: %s err=%d, len=%d\n",
 				ch->name, __func__, -ret, len);
 		sdio_al_dev->is_err = true;
 		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
@@ -3318,56 +3344,152 @@
 	return 0;
 }
 
-static void msm_sdio_al_shutdown(struct platform_device *pdev)
+static void sdio_al_close_all_channels(struct sdio_al_device *sdio_al_dev)
 {
-	int i, j;
-	struct sdio_func *func1 = NULL;
+	int j;
 	int ret;
+	struct sdio_channel *ch = NULL;
 
-	sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Initiating "
-		"msm_sdio_al_shutdown...", __func__);
+	sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__);
 
-	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) {
-		struct sdio_al_device *sdio_al_dev = NULL;
-		if (sdio_al->devices[i] == NULL) {
-			pr_debug(MODULE_NAME ": %s: NULL device in index %d",
-			__func__, i);
-			continue;
+	if (!sdio_al_dev) {
+		sdio_al_loge(sdio_al_dev->dev_log,
+			MODULE_NAME ": %s: NULL device", __func__);
+		return;
+	}
+	for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) {
+		ch = &sdio_al_dev->channel[j];
+
+		if (ch->state == SDIO_CHANNEL_STATE_OPEN) {
+			sdio_al_loge(sdio_al_dev->dev_log,
+				MODULE_NAME ": %s: Call to sdio_close() for"
+				" ch %s\n", __func__, ch->name);
+			ret = channel_close(ch, false);
+			if (ret) {
+				sdio_al_loge(sdio_al_dev->dev_log,
+					MODULE_NAME ": %s: failed sdio_close()"
+					" for ch %s (%d)\n",
+					__func__, ch->name, ret);
+			}
+		} else {
+			pr_debug(MODULE_NAME ": %s: skip sdio_close() ch %s"
+					" (state=%d)\n", __func__,
+					ch->name, ch->state);
 		}
-		sdio_al_dev = sdio_al->devices[i];
-		if (sdio_al_dev->state == CARD_REMOVED) {
-			sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": %s: "
-				"card %d is already removed", __func__,
-				sdio_al_dev->card->host->index);
-			continue;
-		}
-		if (sdio_al_dev->state == MODEM_RESTART) {
-			sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": %s: "
-				"card %d was already notified for modem reset",
+	}
+}
+
+static void sdio_al_modem_reset_dev_operations(struct sdio_al_device
+							*sdio_al_dev)
+{
+	sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__);
+
+	if (!sdio_al_dev) {
+		sdio_al_loge(sdio_al_dev->dev_log,
+				MODULE_NAME ": %s: NULL device", __func__);
+		return;
+	}
+	if (sdio_al_dev->state == CARD_REMOVED) {
+		sdio_al_logi(sdio_al_dev->dev_log,
+				MODULE_NAME ": %s: card %d is already removed",
 				__func__, sdio_al_dev->card->host->index);
-			continue;
-		}
+		return;
+	}
+	if (sdio_al_dev->state == MODEM_RESTART) {
+		sdio_al_logi(sdio_al_dev->dev_log,
+				MODULE_NAME ": %s: card %d was already "
+				"notified for modem reset",
+				__func__, sdio_al_dev->card->host->index);
+		return;
+	}
 
 		sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": %s: Set the "
 			"state to MODEM_RESTART for card %d",
 			__func__, sdio_al_dev->card->host->index);
-		sdio_al_dev->state = MODEM_RESTART;
-		sdio_al_dev->is_ready = false;
+	sdio_al_dev->state = MODEM_RESTART;
+	sdio_al_dev->is_ready = false;
 
-		/* Stop mailbox timer */
-		if (sdio_al_dev->is_timer_initialized) {
-			pr_debug(MODULE_NAME ": %s: Stop timer for card %d",
+	/* Stop mailbox timer */
+	if (sdio_al_dev->is_timer_initialized) {
+		sdio_al_logi(sdio_al_dev->dev_log,
+				MODULE_NAME ": %s: Stop timer for card %d",
 				__func__, sdio_al_dev->card->host->index);
-			sdio_al_dev->poll_delay_msec = 0;
-			del_timer_sync(&sdio_al_dev->timer);
+		sdio_al_dev->poll_delay_msec = 0;
+		del_timer_sync(&sdio_al_dev->timer);
+		sdio_al_dev->is_timer_initialized = false;
+	} else {
+		sdio_al_logi(sdio_al_dev->dev_log,
+			MODULE_NAME ": %s: No timer. Nothing to do for card %d",
+			__func__, sdio_al_dev->card->host->index);
+	}
+	return;
+}
+
+static int sdio_al_modem_reset_bus_operations(struct sdio_al_device
+							*sdio_al_dev)
+{
+	int j;
+	int ret;
+	struct sdio_func *func1 = NULL;
+
+	sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__);
+
+	func1 = sdio_al_dev->card->sdio_func[0];
+	if ((sdio_al_dev->is_ok_to_sleep) &&
+			(!sdio_al_dev->is_err)) {
+		sdio_al_logi(&sdio_al->gen_log,
+				MODULE_NAME ": %s: wakeup modem for "
+				"card %d", __func__,
+				sdio_al_dev->card->host->index);
+		ret = sdio_al_wake_up(sdio_al_dev, 1, NULL);
+		if (ret == 0) {
+			sdio_al_logi(&sdio_al->gen_log,
+					MODULE_NAME ": %s: "
+					"sdio_release_irq"
+					" for card %d",
+					__func__,
+					sdio_al_dev->card->host->index);
+			sdio_release_irq(func1);
 		}
+	} else {
+		sdio_al_logi(&sdio_al->gen_log,
+				MODULE_NAME ": %s: sdio_release_irq"
+				" for card %d",
+				__func__,
+				sdio_al_dev->card->host->index);
+		sdio_release_irq(func1);
 	}
 
+	sdio_al_logi(&sdio_al->gen_log,
+			MODULE_NAME ": %s: Notifying SDIO clients for card %d",
+			__func__, sdio_al_dev->card->host->index);
+	for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) {
+		if (sdio_al_dev->channel[j].state ==
+				SDIO_CHANNEL_STATE_INVALID)
+			continue;
+		platform_device_unregister(
+				sdio_al_dev->channel[j].pdev);
+		sdio_al_dev->channel[j].signature = 0x0;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_MSM_SUBSYSTEM_RESTART
+static void sdio_al_reset(void)
+{
+	int i;
+	struct sdio_al_device *sdio_al_dev;
+	struct sdio_func *func1 = NULL;
+
+	sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__);
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++)
+		sdio_al_modem_reset_dev_operations(sdio_al->devices[i]);
+
 	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) {
-		struct sdio_al_device *sdio_al_dev;
 		if (sdio_al->devices[i] == NULL) {
 			pr_debug(MODULE_NAME ": %s: NULL device in index %d",
-			__func__, i);
+					__func__, i);
 			continue;
 		}
 		sdio_al_dev = sdio_al->devices[i];
@@ -3376,46 +3498,62 @@
 			func1 = sdio_al_dev->card->sdio_func[0];
 			sdio_claim_host(func1);
 
-			if ((sdio_al_dev->is_ok_to_sleep) &&
-			    (!sdio_al_dev->is_err)) {
-				pr_debug(MODULE_NAME ": %s: wakeup modem for "
-						    "card %d", __func__,
-					sdio_al_dev->card->host->index);
-				ret = sdio_al_wake_up(sdio_al_dev, 1, NULL);
-				if (ret == 0) {
-					sdio_al_logi(sdio_al_dev->dev_log,
-						MODULE_NAME ": %s: "
-						"sdio_release_irq for card %d",
-						__func__,
-						sdio_al_dev->card->host->index);
-					sdio_release_irq(func1);
-				}
-			} else {
-				pr_debug(MODULE_NAME ": %s: sdio_release_irq"
-						    " for card %d",
+			sdio_al_modem_reset_bus_operations(sdio_al_dev);
+
+			if (!sdio_al_verify_func1(sdio_al_dev, __func__))
+				sdio_release_host(func1);
+
+
+			pr_debug(MODULE_NAME ": %s: Allows sleep for card %d",
 					__func__,
 					sdio_al_dev->card->host->index);
-				sdio_release_irq(func1);
-			}
+			sdio_al_vote_for_sleep(sdio_al_dev, 1);
+		} else {
+			sdio_al_logi(&sdio_al->gen_log, MODULE_NAME
+					":%s bad function i=%d\n", __func__, i);
 		}
+	}
+	return;
+}
+#endif
 
-		pr_debug(MODULE_NAME ": %s: Notifying SDIO clients for card %d",
-				__func__, sdio_al_dev->card->host->index);
-		for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) {
-			if (sdio_al_dev->channel[j].state ==
-				SDIO_CHANNEL_STATE_INVALID)
-				continue;
-			platform_device_unregister(
-				sdio_al_dev->channel[j].pdev);
-			sdio_al_dev->channel[i].signature = 0x0;
+static void msm_sdio_al_shutdown(struct platform_device *pdev)
+{
+	int i;
+	struct sdio_func *func1 = NULL;
+	struct sdio_al_device *sdio_al_dev;
+
+	sdio_al_logi(&sdio_al->gen_log, MODULE_NAME
+			"Initiating msm_sdio_al_shutdown...");
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) {
+		if (sdio_al->devices[i] == NULL) {
+			pr_debug(MODULE_NAME ": %s: NULL device in index %d",
+					__func__, i);
+			continue;
 		}
+		sdio_al_dev = sdio_al->devices[i];
 
-		if (!sdio_al_verify_func1(sdio_al_dev, __func__))
-			sdio_release_host(sdio_al_dev->card->sdio_func[0]);
 
-		pr_debug(MODULE_NAME ": %s: Allows sleep for card %d", __func__,
-			sdio_al_dev->card->host->index);
-		sdio_al_vote_for_sleep(sdio_al_dev, 1);
+		if (!sdio_al_verify_func1(sdio_al_dev, __func__)) {
+			func1 = sdio_al_dev->card->sdio_func[0];
+			sdio_claim_host(func1);
+
+			if (sdio_al_dev->ch_close_supported)
+				sdio_al_close_all_channels(sdio_al_dev);
+
+			sdio_al_modem_reset_dev_operations(sdio_al_dev);
+
+			sdio_al_modem_reset_bus_operations(sdio_al_dev);
+
+			if (!sdio_al_verify_func1(sdio_al_dev, __func__))
+				sdio_release_host(func1);
+
+			pr_debug(MODULE_NAME ": %s: Allows sleep for card %d",
+					__func__,
+					sdio_al_dev->card->host->index);
+			sdio_al_vote_for_sleep(sdio_al_dev, 1);
+		}
 	}
 	sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: "
 		"msm_sdio_al_shutdown complete.", __func__);
@@ -3649,7 +3787,8 @@
 
 	sdio_al_dev->rx_flush_buf = kzalloc(RX_FLUSH_BUFFER_SIZE, GFP_KERNEL);
 	if (sdio_al_dev->rx_flush_buf == NULL) {
-		pr_err(MODULE_NAME ":Fail to allocate rx_flush_buf "
+		sdio_al_loge(&sdio_al->gen_log,
+				MODULE_NAME ":Fail to allocate rx_flush_buf "
 				   "for card %d\n",
 		       card->host->index);
 		return -ENOMEM;
@@ -4059,8 +4198,7 @@
 		return NOTIFY_DONE;
 	}
 
-
-	msm_sdio_al_shutdown(NULL);
+	sdio_al_reset();
 	return NOTIFY_OK;
 }