mmc: basic SDIO device model

Add the sdio bus type and basic device handling.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index ac0dd68..4443285 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -13,21 +13,49 @@
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
 
 #include "core.h"
 #include "bus.h"
+#include "sdio_bus.h"
 #include "mmc_ops.h"
 #include "sd_ops.h"
 #include "sdio_ops.h"
 
+static int sdio_init_func(struct mmc_card *card, unsigned int fn)
+{
+	struct sdio_func *func;
+
+	BUG_ON(fn > SDIO_MAX_FUNCS);
+
+	func = sdio_alloc_func(card);
+	if (IS_ERR(func))
+		return PTR_ERR(func);
+
+	func->num = fn;
+
+	card->sdio_func[fn - 1] = func;
+
+	return 0;
+}
+
 /*
  * Host is being removed. Free up the current card.
  */
 static void mmc_sdio_remove(struct mmc_host *host)
 {
+	int i;
+
 	BUG_ON(!host);
 	BUG_ON(!host->card);
 
+	for (i = 0;i < host->card->sdio_funcs;i++) {
+		if (host->card->sdio_func[i]) {
+			sdio_remove_func(host->card->sdio_func[i]);
+			host->card->sdio_func[i] = NULL;
+		}
+	}
+
 	mmc_remove_card(host->card);
 	host->card = NULL;
 }
@@ -73,7 +101,7 @@
 int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
 {
 	int err;
-	int funcs;
+	int i, funcs;
 	struct mmc_card *card;
 
 	BUG_ON(!host);
@@ -132,13 +160,16 @@
 	}
 
 	card->type = MMC_TYPE_SDIO;
+	card->sdio_funcs = funcs;
+
+	host->card = card;
 
 	/*
 	 * Set card RCA.
 	 */
 	err = mmc_send_relative_addr(host, &card->rca);
 	if (err)
-		goto free_card;
+		goto remove;
 
 	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
 
@@ -147,23 +178,46 @@
 	 */
 	err = mmc_select_card(card);
 	if (err)
-		goto free_card;
+		goto remove;
 
-	host->card = card;
+	/*
+	 * Initialize (but don't add) all present functions.
+	 */
+	for (i = 0;i < funcs;i++) {
+		err = sdio_init_func(host->card, i + 1);
+		if (err)
+			goto remove;
+	}
 
 	mmc_release_host(host);
 
+	/*
+	 * First add the card to the driver model...
+	 */
 	err = mmc_add_card(host->card);
 	if (err)
-		goto reclaim_host;
+		goto remove_added;
+
+	/*
+	 * ...then the SDIO functions.
+	 */
+	for (i = 0;i < funcs;i++) {
+		err = sdio_add_func(host->card->sdio_func[i]);
+		if (err)
+			goto remove_added;
+	}
 
 	return 0;
 
-reclaim_host:
+
+remove_added:
+	/* Remove without lock if the device has been added. */
+	mmc_sdio_remove(host);
 	mmc_claim_host(host);
-free_card:
-	mmc_remove_card(card);
-	host->card = NULL;
+remove:
+	/* And with lock if it hasn't been added. */
+	if (host->card)
+		mmc_sdio_remove(host);
 err:
 	mmc_detach_bus(host);
 	mmc_release_host(host);