aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2007-05-21 14:23:20 -0400
committerPierre Ossman <drzeus@drzeus.cx>2007-09-23 13:40:07 -0400
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 9a91d9e3f1f2..c22d34b11f64 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2561,7 +2561,7 @@ L: linux-kernel@vger.kernel.org
2561W: http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html 2561W: http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html
2562S: Maintained 2562S: Maintained
2563 2563
2564MULTIMEDIA CARD (MMC) AND SECURE DIGITAL (SD) SUBSYSTEM 2564MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
2565P: Pierre Ossman 2565P: Pierre Ossman
2566M: drzeus-mmc@drzeus.cx 2566M: drzeus-mmc@drzeus.cx
2567L: linux-kernel@vger.kernel.org 2567L: linux-kernel@vger.kernel.org
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 3fdd08c7f143..2fa5ebbc170d 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -8,5 +8,6 @@ endif
8 8
9obj-$(CONFIG_MMC) += mmc_core.o 9obj-$(CONFIG_MMC) += mmc_core.o
10mmc_core-y := core.o sysfs.o bus.o host.o \ 10mmc_core-y := core.o sysfs.o bus.o host.o \
11 mmc.o mmc_ops.o sd.o sd_ops.o 11 mmc.o mmc_ops.o sd.o sd_ops.o \
12 sdio.o sdio_ops.o
12 13
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 817a79462b3d..87a6070522f8 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,
34 return sprintf(buf, "MMC\n"); 34 return sprintf(buf, "MMC\n");
35 case MMC_TYPE_SD: 35 case MMC_TYPE_SD:
36 return sprintf(buf, "SD\n"); 36 return sprintf(buf, "SD\n");
37 case MMC_TYPE_SDIO:
38 return sprintf(buf, "SDIO\n");
37 default: 39 default:
38 return -EFAULT; 40 return -EFAULT;
39 } 41 }
@@ -76,6 +78,9 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
76 case MMC_TYPE_SD: 78 case MMC_TYPE_SD:
77 add_env("MMC_TYPE=%s", "SD"); 79 add_env("MMC_TYPE=%s", "SD");
78 break; 80 break;
81 case MMC_TYPE_SDIO:
82 add_env("MMC_TYPE=%s", "SDIO");
83 break;
79 } 84 }
80 85
81 add_env("MMC_NAME=%s", mmc_card_name(card)); 86 add_env("MMC_NAME=%s", mmc_card_name(card));
@@ -221,6 +226,9 @@ int mmc_add_card(struct mmc_card *card)
221 if (mmc_card_blockaddr(card)) 226 if (mmc_card_blockaddr(card))
222 type = "SDHC"; 227 type = "SDHC";
223 break; 228 break;
229 case MMC_TYPE_SDIO:
230 type = "SDIO";
231 break;
224 default: 232 default:
225 type = "?"; 233 type = "?";
226 break; 234 break;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 51e611f2f33d..092fa906ab86 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -32,9 +32,11 @@
32 32
33#include "mmc_ops.h" 33#include "mmc_ops.h"
34#include "sd_ops.h" 34#include "sd_ops.h"
35#include "sdio_ops.h"
35 36
36extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); 37extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
37extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); 38extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
39extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
38 40
39static struct workqueue_struct *workqueue; 41static struct workqueue_struct *workqueue;
40 42
@@ -595,24 +597,38 @@ void mmc_rescan(struct work_struct *work)
595 597
596 mmc_send_if_cond(host, host->ocr_avail); 598 mmc_send_if_cond(host, host->ocr_avail);
597 599
600 /*
601 * First we search for SDIO...
602 */
603 err = mmc_send_io_op_cond(host, 0, &ocr);
604 if (!err) {
605 if (mmc_attach_sdio(host, ocr))
606 mmc_power_off(host);
607 return;
608 }
609
610 /*
611 * ...then normal SD...
612 */
598 err = mmc_send_app_op_cond(host, 0, &ocr); 613 err = mmc_send_app_op_cond(host, 0, &ocr);
599 if (!err) { 614 if (!err) {
600 if (mmc_attach_sd(host, ocr)) 615 if (mmc_attach_sd(host, ocr))
601 mmc_power_off(host); 616 mmc_power_off(host);
602 } else { 617 return;
603 /* 618 }
604 * If we fail to detect any SD cards then try 619
605 * searching for MMC cards. 620 /*
606 */ 621 * ...and finally MMC.
607 err = mmc_send_op_cond(host, 0, &ocr); 622 */
608 if (!err) { 623 err = mmc_send_op_cond(host, 0, &ocr);
609 if (mmc_attach_mmc(host, ocr)) 624 if (!err) {
610 mmc_power_off(host); 625 if (mmc_attach_mmc(host, ocr))
611 } else {
612 mmc_power_off(host); 626 mmc_power_off(host);
613 mmc_release_host(host); 627 return;
614 }
615 } 628 }
629
630 mmc_release_host(host);
631 mmc_power_off(host);
616 } else { 632 } else {
617 if (host->bus_ops->detect && !host->bus_dead) 633 if (host->bus_ops->detect && !host->bus_dead)
618 host->bus_ops->detect(host); 634 host->bus_ops->detect(host);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
new file mode 100644
index 000000000000..ac0dd68df8e7
--- /dev/null
+++ b/drivers/mmc/core/sdio.c
@@ -0,0 +1,176 @@
1/*
2 * linux/drivers/mmc/sdio.c
3 *
4 * Copyright 2006-2007 Pierre Ossman
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 */
11
12#include <linux/err.h>
13
14#include <linux/mmc/host.h>
15#include <linux/mmc/card.h>
16
17#include "core.h"
18#include "bus.h"
19#include "mmc_ops.h"
20#include "sd_ops.h"
21#include "sdio_ops.h"
22
23/*
24 * Host is being removed. Free up the current card.
25 */
26static void mmc_sdio_remove(struct mmc_host *host)
27{
28 BUG_ON(!host);
29 BUG_ON(!host->card);
30
31 mmc_remove_card(host->card);
32 host->card = NULL;
33}
34
35/*
36 * Card detection callback from host.
37 */
38static void mmc_sdio_detect(struct mmc_host *host)
39{
40 int err;
41
42 BUG_ON(!host);
43 BUG_ON(!host->card);
44
45 mmc_claim_host(host);
46
47 /*
48 * Just check if our card has been removed.
49 */
50 err = mmc_select_card(host->card);
51
52 mmc_release_host(host);
53
54 if (err) {
55 mmc_sdio_remove(host);
56
57 mmc_claim_host(host);
58 mmc_detach_bus(host);
59 mmc_release_host(host);
60 }
61}
62
63
64static const struct mmc_bus_ops mmc_sdio_ops = {
65 .remove = mmc_sdio_remove,
66 .detect = mmc_sdio_detect,
67};
68
69
70/*
71 * Starting point for SDIO card init.
72 */
73int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
74{
75 int err;
76 int funcs;
77 struct mmc_card *card;
78
79 BUG_ON(!host);
80 BUG_ON(!host->claimed);
81
82 mmc_attach_bus(host, &mmc_sdio_ops);
83
84 /*
85 * Sanity check the voltages that the card claims to
86 * support.
87 */
88 if (ocr & 0x7F) {
89 printk(KERN_WARNING "%s: card claims to support voltages "
90 "below the defined range. These will be ignored.\n",
91 mmc_hostname(host));
92 ocr &= ~0x7F;
93 }
94
95 if (ocr & MMC_VDD_165_195) {
96 printk(KERN_WARNING "%s: SDIO card claims to support the "
97 "incompletely defined 'low voltage range'. This "
98 "will be ignored.\n", mmc_hostname(host));
99 ocr &= ~MMC_VDD_165_195;
100 }
101
102 host->ocr = mmc_select_voltage(host, ocr);
103
104 /*
105 * Can we support the voltage(s) of the card(s)?
106 */
107 if (!host->ocr) {
108 err = -EINVAL;
109 goto err;
110 }
111
112 /*
113 * Inform the card of the voltage
114 */
115 err = mmc_send_io_op_cond(host, host->ocr, &ocr);
116 if (err)
117 goto err;
118
119 /*
120 * The number of functions on the card is encoded inside
121 * the ocr.
122 */
123 funcs = (ocr & 0x70000000) >> 28;
124
125 /*
126 * Allocate card structure.
127 */
128 card = mmc_alloc_card(host);
129 if (IS_ERR(card)) {
130 err = PTR_ERR(card);
131 goto err;
132 }
133
134 card->type = MMC_TYPE_SDIO;
135
136 /*
137 * Set card RCA.
138 */
139 err = mmc_send_relative_addr(host, &card->rca);
140 if (err)
141 goto free_card;
142
143 mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
144
145 /*
146 * Select card, as all following commands rely on that.
147 */
148 err = mmc_select_card(card);
149 if (err)
150 goto free_card;
151
152 host->card = card;
153
154 mmc_release_host(host);
155
156 err = mmc_add_card(host->card);
157 if (err)
158 goto reclaim_host;
159
160 return 0;
161
162reclaim_host:
163 mmc_claim_host(host);
164free_card:
165 mmc_remove_card(card);
166 host->card = NULL;
167err:
168 mmc_detach_bus(host);
169 mmc_release_host(host);
170
171 printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
172 mmc_hostname(host), err);
173
174 return err;
175}
176
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
new file mode 100644
index 000000000000..d6f9f9d85178
--- /dev/null
+++ b/drivers/mmc/core/sdio_ops.c
@@ -0,0 +1,49 @@
1/*
2 * linux/drivers/mmc/sdio_ops.c
3 *
4 * Copyright 2006-2007 Pierre Ossman
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 */
11
12#include <linux/mmc/host.h>
13#include <linux/mmc/mmc.h>
14#include <linux/mmc/sdio.h>
15
16#include "core.h"
17
18int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
19{
20 struct mmc_command cmd;
21 int i, err = 0;
22
23 BUG_ON(!host);
24
25 memset(&cmd, 0, sizeof(struct mmc_command));
26
27 cmd.opcode = SD_IO_SEND_OP_COND;
28 cmd.arg = ocr;
29 cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR;
30
31 for (i = 100; i; i--) {
32 err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
33 if (err)
34 break;
35
36 if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
37 break;
38
39 err = -ETIMEDOUT;
40
41 mmc_delay(10);
42 }
43
44 if (rocr)
45 *rocr = cmd.resp[0];
46
47 return err;
48}
49
diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h
new file mode 100644
index 000000000000..d8c982976f19
--- /dev/null
+++ b/drivers/mmc/core/sdio_ops.h
@@ -0,0 +1,18 @@
1/*
2 * linux/drivers/mmc/sdio_ops.c
3 *
4 * Copyright 2006-2007 Pierre Ossman
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 */
11
12#ifndef _MMC_SDIO_OPS_H
13#define _MMC_SDIO_OPS_H
14
15int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
16
17#endif
18
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index badf702fcff4..43480ebebf9a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -67,6 +67,7 @@ struct mmc_card {
67 unsigned int type; /* card type */ 67 unsigned int type; /* card type */
68#define MMC_TYPE_MMC 0 /* MMC card */ 68#define MMC_TYPE_MMC 0 /* MMC card */
69#define MMC_TYPE_SD 1 /* SD card */ 69#define MMC_TYPE_SD 1 /* SD card */
70#define MMC_TYPE_SDIO 2 /* SDIO card */
70 unsigned int state; /* (our) card state */ 71 unsigned int state; /* (our) card state */
71#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ 72#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
72#define MMC_STATE_READONLY (1<<1) /* card is read-only */ 73#define MMC_STATE_READONLY (1<<1) /* card is read-only */
@@ -84,6 +85,7 @@ struct mmc_card {
84 85
85#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) 86#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
86#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) 87#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
88#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO)
87 89
88#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) 90#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
89#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) 91#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 29c98ae10aff..8faa436c5571 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -41,6 +41,7 @@ struct mmc_command {
41#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) 41#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
42#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) 42#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
43#define MMC_RSP_R3 (MMC_RSP_PRESENT) 43#define MMC_RSP_R3 (MMC_RSP_PRESENT)
44#define MMC_RSP_R4 (MMC_RSP_PRESENT)
44#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) 45#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
45#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) 46#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
46 47
diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h
new file mode 100644
index 000000000000..d1a0b15cdfdf
--- /dev/null
+++ b/include/linux/mmc/sdio.h
@@ -0,0 +1,19 @@
1/*
2 * include/linux/mmc/sdio.h
3 *
4 * Copyright 2006-2007 Pierre Ossman
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 */
11
12#ifndef MMC_SDIO_H
13#define MMC_SDIO_H
14
15/* SDIO commands type argument response */
16#define SD_IO_SEND_OP_COND 5 /* bcr [23:0] OCR R4 */
17
18#endif
19