aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2007-05-21 20:23:20 +0200
committerPierre Ossman <drzeus@drzeus.cx>2007-09-23 19:40:07 +0200
commit5c4e6f1301649d5b29dd0f70e6da83e728ab5ca5 (patch)
tree97d612d990f3b5255b6ea59150f91622699e124f
parent1d4de9edd6c9ad676b20729ab15c04b78e9a50c5 (diff)
mmc: detect SDIO cards
Really basic init sequence for SDIO cards. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/mmc/core/Makefile3
-rw-r--r--drivers/mmc/core/bus.c8
-rw-r--r--drivers/mmc/core/core.c40
-rw-r--r--drivers/mmc/core/sdio.c176
-rw-r--r--drivers/mmc/core/sdio_ops.c49
-rw-r--r--drivers/mmc/core/sdio_ops.h18
-rw-r--r--include/linux/mmc/card.h2
-rw-r--r--include/linux/mmc/core.h1
-rw-r--r--include/linux/mmc/sdio.h19
10 files changed, 304 insertions, 14 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 9a91d9e3f1f..c22d34b11f6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 3fdd08c7f14..2fa5ebbc170 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -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
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 817a79462b3..87a6070522f 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -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;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 51e611f2f33..092fa906ab8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -32,9 +32,11 @@
#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
index 00000000000..ac0dd68df8e
--- /dev/null
+++ b/drivers/mmc/core/sdio.c
@@ -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
index 00000000000..d6f9f9d8517
--- /dev/null
+++ b/drivers/mmc/core/sdio_ops.c
@@ -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
index 00000000000..d8c982976f1
--- /dev/null
+++ b/drivers/mmc/core/sdio_ops.h
@@ -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
+
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index badf702fcff..43480ebebf9 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -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)
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 29c98ae10af..8faa436c557 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -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
index 00000000000..d1a0b15cdfd
--- /dev/null
+++ b/include/linux/mmc/sdio.h
@@ -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
+