msm: sdio: Handle graceful shutdown
sdio_al is not notified of charm modem shutdown causing failures
in channel communication. Similar to subsystem restart, register
for a shutdown notification and notify sdio_al clients. Register
sdio_al device as a charm device and charm modem device as its
parent.
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Maya Erez <merez@codeaurora.org>
diff --git a/arch/arm/mach-msm/sdio_al.c b/arch/arm/mach-msm/sdio_al.c
index 6391284..e774508 100644
--- a/arch/arm/mach-msm/sdio_al.c
+++ b/arch/arm/mach-msm/sdio_al.c
@@ -3022,9 +3022,111 @@
return 0;
}
+static void msm_sdio_al_shutdown(struct platform_device *pdev)
+{
+ int i, j;
+ struct sdio_func *func1 = NULL;
+ int ret;
+
+ pr_info("Initiating msm_sdio_al_shutdown...");
+
+ 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;
+ }
+ sdio_al_dev = sdio_al->devices[i];
+ if (sdio_al_dev->state == CARD_REMOVED) {
+ pr_info(MODULE_NAME ": %s: card %d is already removed",
+ __func__, sdio_al_dev->card->host->index);
+ continue;
+ }
+ if (sdio_al_dev->state == MODEM_RESTART) {
+ pr_info(MODULE_NAME ": %s: card %d was already "
+ "notified for modem reset",
+ __func__, sdio_al_dev->card->host->index);
+ continue;
+ }
+
+ pr_info(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;
+
+ /* Stop mailbox timer */
+ if (sdio_al_dev->is_timer_initialized) {
+ pr_debug(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);
+ }
+ }
+
+ 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);
+ continue;
+ }
+ sdio_al_dev = sdio_al->devices[i];
+
+ 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->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) {
+ pr_info(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",
+ __func__,
+ sdio_al_dev->card->host->index);
+ sdio_release_irq(func1);
+ }
+ }
+
+ pr_debug(MODULE_NAME ": %s: Notifying SDIO clients for card %d",
+ __func__, sdio_al_dev->card->host->index);
+ if (!sdio_al->unittest_mode)
+ 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;
+ }
+
+ 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);
+ }
+ pr_info("msm_sdio_al_shutdown complete.");
+}
+
static struct platform_driver msm_sdio_al_driver = {
.probe = msm_sdio_al_probe,
.remove = __exit_p(msm_sdio_al_remove),
+ .shutdown = msm_sdio_al_shutdown,
.driver = {
.name = "msm_sdio_al",
},
@@ -3619,107 +3721,14 @@
unsigned long notif_type,
void *data)
{
- int i, j;
- struct sdio_func *func1 = NULL;
- int ret;
-
if (notif_type != SUBSYS_BEFORE_SHUTDOWN) {
pr_info(MODULE_NAME ": %s: got notification %ld",
__func__, notif_type);
return NOTIFY_DONE;
}
- 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;
- }
- sdio_al_dev = sdio_al->devices[i];
- if (sdio_al_dev->state == CARD_REMOVED) {
- pr_info(MODULE_NAME ": %s: card %d is already removed",
- __func__, sdio_al_dev->card->host->index);
- continue;
- }
- if (sdio_al_dev->state == MODEM_RESTART) {
- pr_info(MODULE_NAME ": %s: card %d was already "
- "notified for modem reset",
- __func__, sdio_al_dev->card->host->index);
- continue;
- }
- pr_info(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;
-
- /* Stop mailbox timer */
- if (sdio_al_dev->is_timer_initialized) {
- pr_debug(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);
- }
- }
-
- 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);
- continue;
- }
- sdio_al_dev = sdio_al->devices[i];
-
- 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->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) {
- pr_info(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",
- __func__,
- sdio_al_dev->card->host->index);
- sdio_release_irq(func1);
- }
- }
-
- pr_debug(MODULE_NAME ": %s: Notifying SDIO clients for card %d",
- __func__, sdio_al_dev->card->host->index);
- if (!sdio_al->unittest_mode)
- 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;
- }
-
- 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);
- }
-
+ msm_sdio_al_shutdown(NULL);
return NOTIFY_OK;
}