mmc: detect SDIO cards
authorPierre Ossman <drzeus@drzeus.cx>
Mon, 21 May 2007 18:23:20 +0000 (20:23 +0200)
committerPierre Ossman <drzeus@drzeus.cx>
Sun, 23 Sep 2007 17:40:07 +0000 (19:40 +0200)
Really basic init sequence for SDIO cards.

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

index 9a91d9e3f1f25c2e0aaded950fae19e4d6fa7e08..c22d34b11f648e7ef4acd6717e9f777da440f1c4 100644 (file)
@@ -2561,7 +2561,7 @@ L:        linux-kernel@vger.kernel.org
 W:     http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html
 S:     Maintained
 
-MULTIMEDIA CARD (MMC) AND SECURE DIGITAL (SD) SUBSYSTEM
+MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
 P:     Pierre Ossman
 M:     drzeus-mmc@drzeus.cx
 L:     linux-kernel@vger.kernel.org
index 3fdd08c7f143c8f874db806a948e54790f299efc..2fa5ebbc170d1eb3fb610c7092cab73a51461628 100644 (file)
@@ -8,5 +8,6 @@ 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
+                                  mmc.o mmc_ops.o sd.o sd_ops.o \
+                                  sdio.o sdio_ops.o
 
index 817a79462b3d5747e75057ac7f6819d925ae4d4e..87a6070522f829f3ff6baac77343cd62098dc64b 100644 (file)
@@ -34,6 +34,8 @@ static ssize_t mmc_type_show(struct device *dev,
                return sprintf(buf, "MMC\n");
        case MMC_TYPE_SD:
                return sprintf(buf, "SD\n");
+       case MMC_TYPE_SDIO:
+               return sprintf(buf, "SDIO\n");
        default:
                return -EFAULT;
        }
@@ -76,6 +78,9 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
        case MMC_TYPE_SD:
                add_env("MMC_TYPE=%s", "SD");
                break;
+       case MMC_TYPE_SDIO:
+               add_env("MMC_TYPE=%s", "SDIO");
+               break;
        }
 
        add_env("MMC_NAME=%s", mmc_card_name(card));
@@ -221,6 +226,9 @@ int mmc_add_card(struct mmc_card *card)
                if (mmc_card_blockaddr(card))
                        type = "SDHC";
                break;
+       case MMC_TYPE_SDIO:
+               type = "SDIO";
+               break;
        default:
                type = "?";
                break;
index 51e611f2f33d302e8c552bcd4f653a4e830005e2..092fa906ab86acd6b74cd550f03a9019eb8d8529 100644 (file)
 
 #include "mmc_ops.h"
 #include "sd_ops.h"
+#include "sdio_ops.h"
 
 extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
 extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
+extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
 
 static struct workqueue_struct *workqueue;
 
@@ -595,24 +597,38 @@ void mmc_rescan(struct work_struct *work)
 
                mmc_send_if_cond(host, host->ocr_avail);
 
+               /*
+                * First we search for SDIO...
+                */
+               err = mmc_send_io_op_cond(host, 0, &ocr);
+               if (!err) {
+                       if (mmc_attach_sdio(host, ocr))
+                               mmc_power_off(host);
+                       return;
+               }
+
+               /*
+                * ...then normal SD...
+                */
                err = mmc_send_app_op_cond(host, 0, &ocr);
                if (!err) {
                        if (mmc_attach_sd(host, ocr))
                                mmc_power_off(host);
-               } else {
-                       /*
-                        * If we fail to detect any SD cards then try
-                        * searching for MMC cards.
-                        */
-                       err = mmc_send_op_cond(host, 0, &ocr);
-                       if (!err) {
-                               if (mmc_attach_mmc(host, ocr))
-                                       mmc_power_off(host);
-                       } else {
+                       return;
+               }
+
+               /*
+                * ...and finally MMC.
+                */
+               err = mmc_send_op_cond(host, 0, &ocr);
+               if (!err) {
+                       if (mmc_attach_mmc(host, ocr))
                                mmc_power_off(host);
-                               mmc_release_host(host);
-                       }
+                       return;
                }
+
+               mmc_release_host(host);
+               mmc_power_off(host);
        } else {
                if (host->bus_ops->detect && !host->bus_dead)
                        host->bus_ops->detect(host);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
new file mode 100644 (file)
index 0000000..ac0dd68
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ *  linux/drivers/mmc/sdio.c
+ *
+ *  Copyright 2006-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.
+ */
+
+#include <linux/err.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+#include "core.h"
+#include "bus.h"
+#include "mmc_ops.h"
+#include "sd_ops.h"
+#include "sdio_ops.h"
+
+/*
+ * Host is being removed. Free up the current card.
+ */
+static void mmc_sdio_remove(struct mmc_host *host)
+{
+       BUG_ON(!host);
+       BUG_ON(!host->card);
+
+       mmc_remove_card(host->card);
+       host->card = NULL;
+}
+
+/*
+ * Card detection callback from host.
+ */
+static void mmc_sdio_detect(struct mmc_host *host)
+{
+       int err;
+
+       BUG_ON(!host);
+       BUG_ON(!host->card);
+
+       mmc_claim_host(host);
+
+       /*
+        * Just check if our card has been removed.
+        */
+       err = mmc_select_card(host->card);
+
+       mmc_release_host(host);
+
+       if (err) {
+               mmc_sdio_remove(host);
+
+               mmc_claim_host(host);
+               mmc_detach_bus(host);
+               mmc_release_host(host);
+       }
+}
+
+
+static const struct mmc_bus_ops mmc_sdio_ops = {
+       .remove = mmc_sdio_remove,
+       .detect = mmc_sdio_detect,
+};
+
+
+/*
+ * Starting point for SDIO card init.
+ */
+int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
+{
+       int err;
+       int funcs;
+       struct mmc_card *card;
+
+       BUG_ON(!host);
+       BUG_ON(!host->claimed);
+
+       mmc_attach_bus(host, &mmc_sdio_ops);
+
+       /*
+        * Sanity check the voltages that the card claims to
+        * support.
+        */
+       if (ocr & 0x7F) {
+               printk(KERN_WARNING "%s: card claims to support voltages "
+                      "below the defined range. These will be ignored.\n",
+                      mmc_hostname(host));
+               ocr &= ~0x7F;
+       }
+
+       if (ocr & MMC_VDD_165_195) {
+               printk(KERN_WARNING "%s: SDIO card claims to support the "
+                      "incompletely defined 'low voltage range'. This "
+                      "will be ignored.\n", mmc_hostname(host));
+               ocr &= ~MMC_VDD_165_195;
+       }
+
+       host->ocr = mmc_select_voltage(host, ocr);
+
+       /*
+        * Can we support the voltage(s) of the card(s)?
+        */
+       if (!host->ocr) {
+               err = -EINVAL;
+               goto err;
+       }
+
+       /*
+        * Inform the card of the voltage
+        */
+       err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+       if (err)
+               goto err;
+
+       /*
+        * The number of functions on the card is encoded inside
+        * the ocr.
+        */
+       funcs = (ocr & 0x70000000) >> 28;
+
+       /*
+        * Allocate card structure.
+        */
+       card = mmc_alloc_card(host);
+       if (IS_ERR(card)) {
+               err = PTR_ERR(card);
+               goto err;
+       }
+
+       card->type = MMC_TYPE_SDIO;
+
+       /*
+        * Set card RCA.
+        */
+       err = mmc_send_relative_addr(host, &card->rca);
+       if (err)
+               goto free_card;
+
+       mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+
+       /*
+        * Select card, as all following commands rely on that.
+        */
+       err = mmc_select_card(card);
+       if (err)
+               goto free_card;
+
+       host->card = card;
+
+       mmc_release_host(host);
+
+       err = mmc_add_card(host->card);
+       if (err)
+               goto reclaim_host;
+
+       return 0;
+
+reclaim_host:
+       mmc_claim_host(host);
+free_card:
+       mmc_remove_card(card);
+       host->card = NULL;
+err:
+       mmc_detach_bus(host);
+       mmc_release_host(host);
+
+       printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
+               mmc_hostname(host), err);
+
+       return err;
+}
+
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
new file mode 100644 (file)
index 0000000..d6f9f9d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  linux/drivers/mmc/sdio_ops.c
+ *
+ *  Copyright 2006-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.
+ */
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+
+#include "core.h"
+
+int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+       struct mmc_command cmd;
+       int i, err = 0;
+
+       BUG_ON(!host);
+
+       memset(&cmd, 0, sizeof(struct mmc_command));
+
+       cmd.opcode = SD_IO_SEND_OP_COND;
+       cmd.arg = ocr;
+       cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR;
+
+       for (i = 100; i; i--) {
+               err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+               if (err)
+                       break;
+
+               if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+                       break;
+
+               err = -ETIMEDOUT;
+
+               mmc_delay(10);
+       }
+
+       if (rocr)
+               *rocr = cmd.resp[0];
+
+       return err;
+}
+
diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h
new file mode 100644 (file)
index 0000000..d8c9829
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ *  linux/drivers/mmc/sdio_ops.c
+ *
+ *  Copyright 2006-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_OPS_H
+#define _MMC_SDIO_OPS_H
+
+int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
+
+#endif
+
index badf702fcff417c73d137ed57eba6dcd77b79be7..43480ebebf9ae4bd742338b6198d687355d0f0e4 100644 (file)
@@ -67,6 +67,7 @@ struct mmc_card {
        unsigned int            type;           /* card type */
 #define MMC_TYPE_MMC           0               /* MMC card */
 #define MMC_TYPE_SD            1               /* SD card */
+#define MMC_TYPE_SDIO          2               /* SDIO card */
        unsigned int            state;          /* (our) card state */
 #define MMC_STATE_PRESENT      (1<<0)          /* present in sysfs */
 #define MMC_STATE_READONLY     (1<<1)          /* card is read-only */
@@ -84,6 +85,7 @@ struct mmc_card {
 
 #define mmc_card_mmc(c)                ((c)->type == MMC_TYPE_MMC)
 #define mmc_card_sd(c)         ((c)->type == MMC_TYPE_SD)
+#define mmc_card_sdio(c)       ((c)->type == MMC_TYPE_SDIO)
 
 #define mmc_card_present(c)    ((c)->state & MMC_STATE_PRESENT)
 #define mmc_card_readonly(c)   ((c)->state & MMC_STATE_READONLY)
index 29c98ae10aff153156c2ebc27bba6d9c5a1d29c9..8faa436c55714fc43bb3efd5bc6217a0c1cbc0da 100644 (file)
@@ -41,6 +41,7 @@ struct mmc_command {
 #define MMC_RSP_R1B    (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
 #define MMC_RSP_R2     (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
 #define MMC_RSP_R3     (MMC_RSP_PRESENT)
+#define MMC_RSP_R4     (MMC_RSP_PRESENT)
 #define MMC_RSP_R6     (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 #define MMC_RSP_R7     (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 
diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h
new file mode 100644 (file)
index 0000000..d1a0b15
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ *  include/linux/mmc/sdio.h
+ *
+ *  Copyright 2006-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_H
+#define MMC_SDIO_H
+
+/* SDIO commands                         type  argument     response */
+#define SD_IO_SEND_OP_COND          5 /* bcr  [23:0] OCR         R4  */
+
+#endif
+