aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2007-05-26 07:48:18 -0400
committerPierre Ossman <drzeus@drzeus.cx>2007-09-23 13:45:31 -0400
commite29a7d73f4277eb92aa64e17017dea33460828ef (patch)
tree0b8cfe6d145f41c43f86b475fff86627a305af1e
parentb2bcc798bbb482b2909801280f3c4aff8cbbf5be (diff)
mmc: basic SDIO device model
Add the sdio bus type and basic device handling. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r--drivers/mmc/core/Makefile2
-rw-r--r--drivers/mmc/core/core.c27
-rw-r--r--drivers/mmc/core/sdio.c72
-rw-r--r--drivers/mmc/core/sdio_bus.c129
-rw-r--r--drivers/mmc/core/sdio_bus.h22
-rw-r--r--include/linux/mmc/card.h7
-rw-r--r--include/linux/mmc/sdio_func.h35
7 files changed, 279 insertions, 15 deletions
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 2fa5ebbc170d..71ab3d1e1eb2 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -9,5 +9,5 @@ endif
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 sdio.o sdio_ops.o sdio_bus.o
13 13
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 092fa906ab86..9747455928da 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -29,6 +29,7 @@
29#include "core.h" 29#include "core.h"
30#include "bus.h" 30#include "bus.h"
31#include "host.h" 31#include "host.h"
32#include "sdio_bus.h"
32 33
33#include "mmc_ops.h" 34#include "mmc_ops.h"
34#include "sd_ops.h" 35#include "sd_ops.h"
@@ -739,16 +740,32 @@ static int __init mmc_init(void)
739 return -ENOMEM; 740 return -ENOMEM;
740 741
741 ret = mmc_register_bus(); 742 ret = mmc_register_bus();
742 if (ret == 0) { 743 if (ret)
743 ret = mmc_register_host_class(); 744 goto destroy_workqueue;
744 if (ret) 745
745 mmc_unregister_bus(); 746 ret = mmc_register_host_class();
746 } 747 if (ret)
748 goto unregister_bus;
749
750 ret = sdio_register_bus();
751 if (ret)
752 goto unregister_host_class;
753
754 return 0;
755
756unregister_host_class:
757 mmc_unregister_host_class();
758unregister_bus:
759 mmc_unregister_bus();
760destroy_workqueue:
761 destroy_workqueue(workqueue);
762
747 return ret; 763 return ret;
748} 764}
749 765
750static void __exit mmc_exit(void) 766static void __exit mmc_exit(void)
751{ 767{
768 sdio_unregister_bus();
752 mmc_unregister_host_class(); 769 mmc_unregister_host_class();
753 mmc_unregister_bus(); 770 mmc_unregister_bus();
754 destroy_workqueue(workqueue); 771 destroy_workqueue(workqueue);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index ac0dd68df8e7..444328581cec 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -13,21 +13,49 @@
13 13
14#include <linux/mmc/host.h> 14#include <linux/mmc/host.h>
15#include <linux/mmc/card.h> 15#include <linux/mmc/card.h>
16#include <linux/mmc/sdio_func.h>
16 17
17#include "core.h" 18#include "core.h"
18#include "bus.h" 19#include "bus.h"
20#include "sdio_bus.h"
19#include "mmc_ops.h" 21#include "mmc_ops.h"
20#include "sd_ops.h" 22#include "sd_ops.h"
21#include "sdio_ops.h" 23#include "sdio_ops.h"
22 24
25static int sdio_init_func(struct mmc_card *card, unsigned int fn)
26{
27 struct sdio_func *func;
28
29 BUG_ON(fn > SDIO_MAX_FUNCS);
30
31 func = sdio_alloc_func(card);
32 if (IS_ERR(func))
33 return PTR_ERR(func);
34
35 func->num = fn;
36
37 card->sdio_func[fn - 1] = func;
38
39 return 0;
40}
41
23/* 42/*
24 * Host is being removed. Free up the current card. 43 * Host is being removed. Free up the current card.
25 */ 44 */
26static void mmc_sdio_remove(struct mmc_host *host) 45static void mmc_sdio_remove(struct mmc_host *host)
27{ 46{
47 int i;
48
28 BUG_ON(!host); 49 BUG_ON(!host);
29 BUG_ON(!host->card); 50 BUG_ON(!host->card);
30 51
52 for (i = 0;i < host->card->sdio_funcs;i++) {
53 if (host->card->sdio_func[i]) {
54 sdio_remove_func(host->card->sdio_func[i]);
55 host->card->sdio_func[i] = NULL;
56 }
57 }
58
31 mmc_remove_card(host->card); 59 mmc_remove_card(host->card);
32 host->card = NULL; 60 host->card = NULL;
33} 61}
@@ -73,7 +101,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
73int mmc_attach_sdio(struct mmc_host *host, u32 ocr) 101int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
74{ 102{
75 int err; 103 int err;
76 int funcs; 104 int i, funcs;
77 struct mmc_card *card; 105 struct mmc_card *card;
78 106
79 BUG_ON(!host); 107 BUG_ON(!host);
@@ -132,13 +160,16 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
132 } 160 }
133 161
134 card->type = MMC_TYPE_SDIO; 162 card->type = MMC_TYPE_SDIO;
163 card->sdio_funcs = funcs;
164
165 host->card = card;
135 166
136 /* 167 /*
137 * Set card RCA. 168 * Set card RCA.
138 */ 169 */
139 err = mmc_send_relative_addr(host, &card->rca); 170 err = mmc_send_relative_addr(host, &card->rca);
140 if (err) 171 if (err)
141 goto free_card; 172 goto remove;
142 173
143 mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); 174 mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
144 175
@@ -147,23 +178,46 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
147 */ 178 */
148 err = mmc_select_card(card); 179 err = mmc_select_card(card);
149 if (err) 180 if (err)
150 goto free_card; 181 goto remove;
151 182
152 host->card = card; 183 /*
184 * Initialize (but don't add) all present functions.
185 */
186 for (i = 0;i < funcs;i++) {
187 err = sdio_init_func(host->card, i + 1);
188 if (err)
189 goto remove;
190 }
153 191
154 mmc_release_host(host); 192 mmc_release_host(host);
155 193
194 /*
195 * First add the card to the driver model...
196 */
156 err = mmc_add_card(host->card); 197 err = mmc_add_card(host->card);
157 if (err) 198 if (err)
158 goto reclaim_host; 199 goto remove_added;
200
201 /*
202 * ...then the SDIO functions.
203 */
204 for (i = 0;i < funcs;i++) {
205 err = sdio_add_func(host->card->sdio_func[i]);
206 if (err)
207 goto remove_added;
208 }
159 209
160 return 0; 210 return 0;
161 211
162reclaim_host: 212
213remove_added:
214 /* Remove without lock if the device has been added. */
215 mmc_sdio_remove(host);
163 mmc_claim_host(host); 216 mmc_claim_host(host);
164free_card: 217remove:
165 mmc_remove_card(card); 218 /* And with lock if it hasn't been added. */
166 host->card = NULL; 219 if (host->card)
220 mmc_sdio_remove(host);
167err: 221err:
168 mmc_detach_bus(host); 222 mmc_detach_bus(host);
169 mmc_release_host(host); 223 mmc_release_host(host);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
new file mode 100644
index 000000000000..59c909e1c7c6
--- /dev/null
+++ b/drivers/mmc/core/sdio_bus.c
@@ -0,0 +1,129 @@
1/*
2 * linux/drivers/mmc/core/sdio_bus.c
3 *
4 * Copyright 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 * SDIO function driver model
12 */
13
14#include <linux/device.h>
15#include <linux/err.h>
16
17#include <linux/mmc/card.h>
18#include <linux/mmc/sdio_func.h>
19
20#include "sdio_bus.h"
21
22#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
23
24/*
25 * This currently matches any SDIO function to any driver in order
26 * to help initial development and testing.
27 */
28static int sdio_bus_match(struct device *dev, struct device_driver *drv)
29{
30 return 1;
31}
32
33static int
34sdio_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
35 int buf_size)
36{
37 envp[0] = NULL;
38
39 return 0;
40}
41
42static int sdio_bus_probe(struct device *dev)
43{
44 return -ENODEV;
45}
46
47static int sdio_bus_remove(struct device *dev)
48{
49 return 0;
50}
51
52static struct bus_type sdio_bus_type = {
53 .name = "sdio",
54 .match = sdio_bus_match,
55 .uevent = sdio_bus_uevent,
56 .probe = sdio_bus_probe,
57 .remove = sdio_bus_remove,
58};
59
60int sdio_register_bus(void)
61{
62 return bus_register(&sdio_bus_type);
63}
64
65void sdio_unregister_bus(void)
66{
67 bus_unregister(&sdio_bus_type);
68}
69
70static void sdio_release_func(struct device *dev)
71{
72 struct sdio_func *func = dev_to_sdio_func(dev);
73
74 kfree(func);
75}
76
77/*
78 * Allocate and initialise a new SDIO function structure.
79 */
80struct sdio_func *sdio_alloc_func(struct mmc_card *card)
81{
82 struct sdio_func *func;
83
84 func = kmalloc(sizeof(struct sdio_func), GFP_KERNEL);
85 if (!func)
86 return ERR_PTR(-ENOMEM);
87
88 memset(func, 0, sizeof(struct sdio_func));
89
90 func->card = card;
91
92 device_initialize(&func->dev);
93
94 func->dev.parent = &card->dev;
95 func->dev.bus = &sdio_bus_type;
96 func->dev.release = sdio_release_func;
97
98 return func;
99}
100
101/*
102 * Register a new SDIO function with the driver model.
103 */
104int sdio_add_func(struct sdio_func *func)
105{
106 int ret;
107
108 snprintf(func->dev.bus_id, sizeof(func->dev.bus_id),
109 "%s:%d", mmc_card_id(func->card), func->num);
110
111 ret = device_add(&func->dev);
112 if (ret == 0)
113 sdio_func_set_present(func);
114
115 return ret;
116}
117
118/*
119 * Unregister a SDIO function with the driver model, and
120 * (eventually) free it.
121 */
122void sdio_remove_func(struct sdio_func *func)
123{
124 if (sdio_func_present(func))
125 device_del(&func->dev);
126
127 put_device(&func->dev);
128}
129
diff --git a/drivers/mmc/core/sdio_bus.h b/drivers/mmc/core/sdio_bus.h
new file mode 100644
index 000000000000..567a76821ba7
--- /dev/null
+++ b/drivers/mmc/core/sdio_bus.h
@@ -0,0 +1,22 @@
1/*
2 * linux/drivers/mmc/core/sdio_bus.h
3 *
4 * Copyright 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#ifndef _MMC_CORE_SDIO_BUS_H
12#define _MMC_CORE_SDIO_BUS_H
13
14struct sdio_func *sdio_alloc_func(struct mmc_card *card);
15int sdio_add_func(struct sdio_func *func);
16void sdio_remove_func(struct sdio_func *func);
17
18int sdio_register_bus(void);
19void sdio_unregister_bus(void);
20
21#endif
22
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 43480ebebf9a..9f5f74482d98 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -56,6 +56,9 @@ struct sd_switch_caps {
56}; 56};
57 57
58struct mmc_host; 58struct mmc_host;
59struct sdio_func;
60
61#define SDIO_MAX_FUNCS 7
59 62
60/* 63/*
61 * MMC device 64 * MMC device
@@ -73,6 +76,7 @@ struct mmc_card {
73#define MMC_STATE_READONLY (1<<1) /* card is read-only */ 76#define MMC_STATE_READONLY (1<<1) /* card is read-only */
74#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ 77#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
75#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ 78#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
79
76 u32 raw_cid[4]; /* raw card CID */ 80 u32 raw_cid[4]; /* raw card CID */
77 u32 raw_csd[4]; /* raw card CSD */ 81 u32 raw_csd[4]; /* raw card CSD */
78 u32 raw_scr[2]; /* raw card SCR */ 82 u32 raw_scr[2]; /* raw card SCR */
@@ -81,6 +85,9 @@ struct mmc_card {
81 struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ 85 struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */
82 struct sd_scr scr; /* extra SD information */ 86 struct sd_scr scr; /* extra SD information */
83 struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ 87 struct sd_switch_caps sw_caps; /* switch (CMD6) caps */
88
89 unsigned int sdio_funcs; /* number of SDIO functions */
90 struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */
84}; 91};
85 92
86#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) 93#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
index 000000000000..50c78db420e6
--- /dev/null
+++ b/include/linux/mmc/sdio_func.h
@@ -0,0 +1,35 @@
1/*
2 * include/linux/mmc/sdio_func.h
3 *
4 * Copyright 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_FUNC_H
13#define MMC_SDIO_FUNC_H
14
15struct mmc_card;
16
17/*
18 * SDIO function devices
19 */
20struct sdio_func {
21 struct mmc_card *card; /* the card this device belongs to */
22 struct device dev; /* the device */
23 unsigned int num; /* function number */
24 unsigned int state; /* function state */
25#define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */
26};
27
28#define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT)
29
30#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT)
31
32#define sdio_func_id(f) ((f)->dev.bus_id)
33
34#endif
35