diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2007-05-26 07:48:18 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-09-23 13:45:31 -0400 |
commit | e29a7d73f4277eb92aa64e17017dea33460828ef (patch) | |
tree | 0b8cfe6d145f41c43f86b475fff86627a305af1e | |
parent | b2bcc798bbb482b2909801280f3c4aff8cbbf5be (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/Makefile | 2 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 27 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 72 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 129 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.h | 22 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 7 | ||||
-rw-r--r-- | include/linux/mmc/sdio_func.h | 35 |
7 files changed, 279 insertions, 15 deletions
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 2fa5ebbc170..71ab3d1e1eb 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile | |||
@@ -9,5 +9,5 @@ endif | |||
9 | obj-$(CONFIG_MMC) += mmc_core.o | 9 | obj-$(CONFIG_MMC) += mmc_core.o |
10 | mmc_core-y := core.o sysfs.o bus.o host.o \ | 10 | mmc_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 092fa906ab8..9747455928d 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 | |||
756 | unregister_host_class: | ||
757 | mmc_unregister_host_class(); | ||
758 | unregister_bus: | ||
759 | mmc_unregister_bus(); | ||
760 | destroy_workqueue: | ||
761 | destroy_workqueue(workqueue); | ||
762 | |||
747 | return ret; | 763 | return ret; |
748 | } | 764 | } |
749 | 765 | ||
750 | static void __exit mmc_exit(void) | 766 | static 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 ac0dd68df8e..444328581ce 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 | ||
25 | static 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 | */ |
26 | static void mmc_sdio_remove(struct mmc_host *host) | 45 | static 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 = { | |||
73 | int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | 101 | int 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 | ||
162 | reclaim_host: | 212 | |
213 | remove_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); |
164 | free_card: | 217 | remove: |
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); | ||
167 | err: | 221 | err: |
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 00000000000..59c909e1c7c --- /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 | */ | ||
28 | static int sdio_bus_match(struct device *dev, struct device_driver *drv) | ||
29 | { | ||
30 | return 1; | ||
31 | } | ||
32 | |||
33 | static int | ||
34 | sdio_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 | |||
42 | static int sdio_bus_probe(struct device *dev) | ||
43 | { | ||
44 | return -ENODEV; | ||
45 | } | ||
46 | |||
47 | static int sdio_bus_remove(struct device *dev) | ||
48 | { | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static 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 | |||
60 | int sdio_register_bus(void) | ||
61 | { | ||
62 | return bus_register(&sdio_bus_type); | ||
63 | } | ||
64 | |||
65 | void sdio_unregister_bus(void) | ||
66 | { | ||
67 | bus_unregister(&sdio_bus_type); | ||
68 | } | ||
69 | |||
70 | static 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 | */ | ||
80 | struct 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 | */ | ||
104 | int 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 | */ | ||
122 | void 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 00000000000..567a76821ba --- /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 | |||
14 | struct sdio_func *sdio_alloc_func(struct mmc_card *card); | ||
15 | int sdio_add_func(struct sdio_func *func); | ||
16 | void sdio_remove_func(struct sdio_func *func); | ||
17 | |||
18 | int sdio_register_bus(void); | ||
19 | void sdio_unregister_bus(void); | ||
20 | |||
21 | #endif | ||
22 | |||
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 43480ebebf9..9f5f74482d9 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 | ||
58 | struct mmc_host; | 58 | struct mmc_host; |
59 | struct 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 00000000000..50c78db420e --- /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 | |||
15 | struct mmc_card; | ||
16 | |||
17 | /* | ||
18 | * SDIO function devices | ||
19 | */ | ||
20 | struct 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 | |||