mmc: basic SDIO device model
authorPierre Ossman <drzeus@drzeus.cx>
Sat, 26 May 2007 11:48:18 +0000 (13:48 +0200)
committerPierre Ossman <drzeus@drzeus.cx>
Sun, 23 Sep 2007 17:45:31 +0000 (19:45 +0200)
Add the sdio bus type and basic device handling.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
drivers/mmc/core/Makefile
drivers/mmc/core/core.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_bus.c [new file with mode: 0644]
drivers/mmc/core/sdio_bus.h [new file with mode: 0644]
include/linux/mmc/card.h
include/linux/mmc/sdio_func.h [new file with mode: 0644]

index 2fa5ebbc170d1eb3fb610c7092cab73a51461628..71ab3d1e1eb20e1137aa0f57fa6c5094baad2b4e 100644 (file)
@@ -9,5 +9,5 @@ endif
 obj-$(CONFIG_MMC)              += mmc_core.o
 mmc_core-y                     := core.o sysfs.o bus.o host.o \
                                   mmc.o mmc_ops.o sd.o sd_ops.o \
-                                  sdio.o sdio_ops.o
+                                  sdio.o sdio_ops.o sdio_bus.o
 
index 092fa906ab86acd6b74cd550f03a9019eb8d8529..9747455928daa96129d975d1da1773b72833069b 100644 (file)
@@ -29,6 +29,7 @@
 #include "core.h"
 #include "bus.h"
 #include "host.h"
+#include "sdio_bus.h"
 
 #include "mmc_ops.h"
 #include "sd_ops.h"
@@ -739,16 +740,32 @@ static int __init mmc_init(void)
                return -ENOMEM;
 
        ret = mmc_register_bus();
-       if (ret == 0) {
-               ret = mmc_register_host_class();
-               if (ret)
-                       mmc_unregister_bus();
-       }
+       if (ret)
+               goto destroy_workqueue;
+
+       ret = mmc_register_host_class();
+       if (ret)
+               goto unregister_bus;
+
+       ret = sdio_register_bus();
+       if (ret)
+               goto unregister_host_class;
+
+       return 0;
+
+unregister_host_class:
+       mmc_unregister_host_class();
+unregister_bus:
+       mmc_unregister_bus();
+destroy_workqueue:
+       destroy_workqueue(workqueue);
+
        return ret;
 }
 
 static void __exit mmc_exit(void)
 {
+       sdio_unregister_bus();
        mmc_unregister_host_class();
        mmc_unregister_bus();
        destroy_workqueue(workqueue);
index ac0dd68df8e7846a379578ad3713d112354c3966..444328581cec1a2a7c0987abb3d0534e0386ecc1 100644 (file)
 
 #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 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
 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 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
        }
 
        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 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
         */
        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);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
new file mode 100644 (file)
index 0000000..59c909e
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ *  linux/drivers/mmc/core/sdio_bus.c
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * SDIO function driver model
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "sdio_bus.h"
+
+#define dev_to_sdio_func(d)    container_of(d, struct sdio_func, dev)
+
+/*
+ * This currently matches any SDIO function to any driver in order
+ * to help initial development and testing.
+ */
+static int sdio_bus_match(struct device *dev, struct device_driver *drv)
+{
+       return 1;
+}
+
+static int
+sdio_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
+               int buf_size)
+{
+       envp[0] = NULL;
+
+       return 0;
+}
+
+static int sdio_bus_probe(struct device *dev)
+{
+       return -ENODEV;
+}
+
+static int sdio_bus_remove(struct device *dev)
+{
+       return 0;
+}
+
+static struct bus_type sdio_bus_type = {
+       .name           = "sdio",
+       .match          = sdio_bus_match,
+       .uevent         = sdio_bus_uevent,
+       .probe          = sdio_bus_probe,
+       .remove         = sdio_bus_remove,
+};
+
+int sdio_register_bus(void)
+{
+       return bus_register(&sdio_bus_type);
+}
+
+void sdio_unregister_bus(void)
+{
+       bus_unregister(&sdio_bus_type);
+}
+
+static void sdio_release_func(struct device *dev)
+{
+       struct sdio_func *func = dev_to_sdio_func(dev);
+
+       kfree(func);
+}
+
+/*
+ * Allocate and initialise a new SDIO function structure.
+ */
+struct sdio_func *sdio_alloc_func(struct mmc_card *card)
+{
+       struct sdio_func *func;
+
+       func = kmalloc(sizeof(struct sdio_func), GFP_KERNEL);
+       if (!func)
+               return ERR_PTR(-ENOMEM);
+
+       memset(func, 0, sizeof(struct sdio_func));
+
+       func->card = card;
+
+       device_initialize(&func->dev);
+
+       func->dev.parent = &card->dev;
+       func->dev.bus = &sdio_bus_type;
+       func->dev.release = sdio_release_func;
+
+       return func;
+}
+
+/*
+ * Register a new SDIO function with the driver model.
+ */
+int sdio_add_func(struct sdio_func *func)
+{
+       int ret;
+
+       snprintf(func->dev.bus_id, sizeof(func->dev.bus_id),
+                "%s:%d", mmc_card_id(func->card), func->num);
+
+       ret = device_add(&func->dev);
+       if (ret == 0)
+               sdio_func_set_present(func);
+
+       return ret;
+}
+
+/*
+ * Unregister a SDIO function with the driver model, and
+ * (eventually) free it.
+ */
+void sdio_remove_func(struct sdio_func *func)
+{
+       if (sdio_func_present(func))
+               device_del(&func->dev);
+
+       put_device(&func->dev);
+}
+
diff --git a/drivers/mmc/core/sdio_bus.h b/drivers/mmc/core/sdio_bus.h
new file mode 100644 (file)
index 0000000..567a768
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ *  linux/drivers/mmc/core/sdio_bus.h
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+#ifndef _MMC_CORE_SDIO_BUS_H
+#define _MMC_CORE_SDIO_BUS_H
+
+struct sdio_func *sdio_alloc_func(struct mmc_card *card);
+int sdio_add_func(struct sdio_func *func);
+void sdio_remove_func(struct sdio_func *func);
+
+int sdio_register_bus(void);
+void sdio_unregister_bus(void);
+
+#endif
+
index 43480ebebf9ae4bd742338b6198d687355d0f0e4..9f5f74482d98c025c67c4aa2e5ec5ab2ca5ccbc7 100644 (file)
@@ -56,6 +56,9 @@ struct sd_switch_caps {
 };
 
 struct mmc_host;
+struct sdio_func;
+
+#define SDIO_MAX_FUNCS         7
 
 /*
  * MMC device
@@ -73,6 +76,7 @@ struct mmc_card {
 #define MMC_STATE_READONLY     (1<<1)          /* card is read-only */
 #define MMC_STATE_HIGHSPEED    (1<<2)          /* card is in high speed mode */
 #define MMC_STATE_BLOCKADDR    (1<<3)          /* card uses block-addressing */
+
        u32                     raw_cid[4];     /* raw card CID */
        u32                     raw_csd[4];     /* raw card CSD */
        u32                     raw_scr[2];     /* raw card SCR */
@@ -81,6 +85,9 @@ struct mmc_card {
        struct mmc_ext_csd      ext_csd;        /* mmc v4 extended card specific */
        struct sd_scr           scr;            /* extra SD information */
        struct sd_switch_caps   sw_caps;        /* switch (CMD6) caps */
+
+       unsigned int            sdio_funcs;     /* number of SDIO functions */
+       struct sdio_func        *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */
 };
 
 #define mmc_card_mmc(c)                ((c)->type == MMC_TYPE_MMC)
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
new file mode 100644 (file)
index 0000000..50c78db
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  include/linux/mmc/sdio_func.h
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef MMC_SDIO_FUNC_H
+#define MMC_SDIO_FUNC_H
+
+struct mmc_card;
+
+/*
+ * SDIO function devices
+ */
+struct sdio_func {
+       struct mmc_card         *card;          /* the card this device belongs to */
+       struct device           dev;            /* the device */
+       unsigned int            num;            /* function number */
+       unsigned int            state;          /* function state */
+#define SDIO_STATE_PRESENT     (1<<0)          /* present in sysfs */
+};
+
+#define sdio_func_present(f)   ((f)->state & SDIO_STATE_PRESENT)
+
+#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT)
+
+#define sdio_func_id(f)                ((f)->dev.bus_id)
+
+#endif
+