From c36d31cbc57635f7e66176d84d7b8688796a01d3 Mon Sep 17 00:00:00 2001
From: Rui Miguel Silva <rui.silva@linaro.org>
Date: Thu, 2 Jul 2015 19:11:31 +0100
Subject: [PATCH] greybus: sdio: rework of event handler

Between the time connection with module is up and the host is added,
we can receive events (card inserted/removed, write protection
switch), so until the setup is complete we queue the events received
and handle them after.

Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
---
 drivers/staging/greybus/sdio.c | 86 ++++++++++++++++++++++------------
 1 file changed, 55 insertions(+), 31 deletions(-)

diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c
index e842cae7a5bdc..53cb46f6f7c88 100644
--- a/drivers/staging/greybus/sdio.c
+++ b/drivers/staging/greybus/sdio.c
@@ -28,6 +28,7 @@ struct gb_sdio_host {
 	spinlock_t		xfer;	/* lock to cancel ongoing transfer */
 	bool			xfer_stop;
 	struct work_struct	mrqwork;
+	u8			queued_events;
 	bool			removed;
 	bool			card_present;
 	bool			read_only;
@@ -121,54 +122,40 @@ static int gb_sdio_get_caps(struct gb_sdio_host *host)
 	return 0;
 }
 
-static int gb_sdio_event_recv(u8 type, struct gb_operation *op)
+static void _gb_queue_event(struct gb_sdio_host *host, u8 event)
 {
-	struct gb_connection *connection = op->connection;
-	struct gb_sdio_host *host = connection->private;
-	struct gb_message *request;
-	struct gb_sdio_event_request *payload;
-	u8 state_changed = 0;
-	u8 event;
-
-	if (type != GB_SDIO_TYPE_EVENT) {
-		dev_err(&connection->dev,
-			"unsupported unsolicited event: %u\n", type);
-		return -EINVAL;
-	}
-
-	request = op->request;
+	if (event & GB_SDIO_CARD_INSERTED)
+		host->queued_events &= ~GB_SDIO_CARD_REMOVED;
+	else if (event & GB_SDIO_CARD_REMOVED)
+		host->queued_events &= ~GB_SDIO_CARD_INSERTED;
 
-	if (request->payload_size != sizeof(*payload)) {
-		dev_err(mmc_dev(host->mmc), "wrong event size received\n");
-		return -EINVAL;
-	}
+	host->queued_events |= event;
+}
 
-	payload = request->payload;
-	event = payload->event;
+static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event)
+{
+	u8 state_changed = 0;
 
-	switch (event) {
-	case GB_SDIO_CARD_INSERTED:
+	if (event & GB_SDIO_CARD_INSERTED) {
 		if (!mmc_card_is_removable(host->mmc))
 			return 0;
 		if (host->card_present)
 			return 0;
 		host->card_present = true;
 		state_changed = 1;
-		break;
-	case GB_SDIO_CARD_REMOVED:
+	}
+
+	if (event & GB_SDIO_CARD_REMOVED) {
 		if (!mmc_card_is_removable(host->mmc))
 			return 0;
 		if (!(host->card_present))
 			return 0;
 		host->card_present = false;
 		state_changed = 1;
-		break;
-	case GB_SDIO_WP:
+	}
+
+	if (event & GB_SDIO_WP) {
 		host->read_only = true;
-		break;
-	default:
-		dev_err(mmc_dev(host->mmc), "wrong event received %d\n", event);
-		return -EINVAL;
 	}
 
 	if (state_changed) {
@@ -180,6 +167,39 @@ static int gb_sdio_event_recv(u8 type, struct gb_operation *op)
 	return 0;
 }
 
+static int gb_sdio_event_recv(u8 type, struct gb_operation *op)
+{
+	struct gb_connection *connection = op->connection;
+	struct gb_sdio_host *host = connection->private;
+	struct gb_message *request;
+	struct gb_sdio_event_request *payload;
+	int ret =  0;
+	u8 event;
+
+	if (type != GB_SDIO_TYPE_EVENT) {
+		dev_err(&connection->dev,
+			"unsupported unsolicited event: %u\n", type);
+		return -EINVAL;
+	}
+
+	request = op->request;
+
+	if (request->payload_size != sizeof(*payload)) {
+		dev_err(mmc_dev(host->mmc), "wrong event size received\n");
+		return -EINVAL;
+	}
+
+	payload = request->payload;
+	event = payload->event;
+
+	if (host->removed)
+		_gb_queue_event(host, event);
+	else
+		ret = _gb_sdio_process_events(host, event);
+
+	return ret;
+}
+
 static int gb_sdio_set_ios(struct gb_sdio_host *host,
 			   struct gb_sdio_set_ios_request *request)
 {
@@ -649,6 +669,7 @@ static int gb_sdio_connection_init(struct gb_connection *connection)
 
 	host = mmc_priv(mmc);
 	host->mmc = mmc;
+	host->removed = true;
 
 	host->connection = connection;
 	connection->private = host;
@@ -683,6 +704,9 @@ static int gb_sdio_connection_init(struct gb_connection *connection)
 	ret = mmc_add_host(mmc);
 	if (ret < 0)
 		goto free_work;
+	host->removed = false;
+	ret = _gb_sdio_process_events(host, host->queued_events);
+	host->queued_events = 0;
 
 	return ret;
 
-- 
2.30.2