diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2007-07-30 09:15:30 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-09-23 14:44:22 -0400 |
commit | 1a632f8cdc33e7f8edca352164f0c96a75d08f08 (patch) | |
tree | 6cc3174285ee11df586ae89cf0040ba2429a83f0 /drivers | |
parent | b1538bcf75e2e11459947ec4d4329ed04fbe2b2c (diff) |
sdio: split up common and function CIS parsing
Add a more clean separation between global, common CIS information
and the function specific one as we need the common information in
places where no specific function is specified.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/core/bus.c | 3 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 9 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 2 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_cis.c | 198 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_cis.h | 7 |
5 files changed, 193 insertions, 26 deletions
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 87a6070522f8..9be11ec05d86 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include "sysfs.h" | 20 | #include "sysfs.h" |
21 | #include "core.h" | 21 | #include "core.h" |
22 | #include "sdio_cis.h" | ||
22 | #include "bus.h" | 23 | #include "bus.h" |
23 | 24 | ||
24 | #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) | 25 | #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) |
@@ -181,6 +182,8 @@ static void mmc_release_card(struct device *dev) | |||
181 | { | 182 | { |
182 | struct mmc_card *card = dev_to_mmc_card(dev); | 183 | struct mmc_card *card = dev_to_mmc_card(dev); |
183 | 184 | ||
185 | sdio_free_common_cis(card); | ||
186 | |||
184 | kfree(card); | 187 | kfree(card); |
185 | } | 188 | } |
186 | 189 | ||
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c5baf76146b2..1fb36a340468 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c | |||
@@ -66,7 +66,7 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn) | |||
66 | if (ret) | 66 | if (ret) |
67 | goto fail; | 67 | goto fail; |
68 | 68 | ||
69 | ret = sdio_read_cis(func); | 69 | ret = sdio_read_func_cis(func); |
70 | if (ret) | 70 | if (ret) |
71 | goto fail; | 71 | goto fail; |
72 | 72 | ||
@@ -287,6 +287,13 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | |||
287 | goto remove; | 287 | goto remove; |
288 | 288 | ||
289 | /* | 289 | /* |
290 | * Read the common CIS tuples. | ||
291 | */ | ||
292 | err = sdio_read_common_cis(card); | ||
293 | if (err) | ||
294 | goto remove; | ||
295 | |||
296 | /* | ||
290 | * Initialize (but don't add) all present functions. | 297 | * Initialize (but don't add) all present functions. |
291 | */ | 298 | */ |
292 | for (i = 0;i < funcs;i++) { | 299 | for (i = 0;i < funcs;i++) { |
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 78e0381f55ac..461fe4837a9f 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c | |||
@@ -95,7 +95,7 @@ static void sdio_release_func(struct device *dev) | |||
95 | { | 95 | { |
96 | struct sdio_func *func = dev_to_sdio_func(dev); | 96 | struct sdio_func *func = dev_to_sdio_func(dev); |
97 | 97 | ||
98 | sdio_free_cis(func); | 98 | sdio_free_func_cis(func); |
99 | 99 | ||
100 | kfree(func); | 100 | kfree(func); |
101 | } | 101 | } |
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index b6c7342572c1..ec806a1229b6 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c | |||
@@ -16,60 +16,152 @@ | |||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | 17 | ||
18 | #include <linux/mmc/host.h> | 18 | #include <linux/mmc/host.h> |
19 | #include <linux/mmc/card.h> | ||
19 | #include <linux/mmc/sdio.h> | 20 | #include <linux/mmc/sdio.h> |
20 | #include <linux/mmc/sdio_func.h> | 21 | #include <linux/mmc/sdio_func.h> |
21 | 22 | ||
22 | #include "sdio_cis.h" | 23 | #include "sdio_cis.h" |
23 | #include "sdio_ops.h" | 24 | #include "sdio_ops.h" |
24 | 25 | ||
25 | static int cistpl_manfid(struct sdio_func *func, | 26 | static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func, |
26 | const unsigned char *buf, | 27 | const unsigned char *buf, unsigned size) |
27 | unsigned size) | ||
28 | { | 28 | { |
29 | unsigned int vendor, device; | ||
30 | |||
29 | /* TPLMID_MANF */ | 31 | /* TPLMID_MANF */ |
30 | func->vendor = buf[0] | (buf[1] << 8); | 32 | vendor = buf[0] | (buf[1] << 8); |
31 | 33 | ||
32 | /* TPLMID_CARD */ | 34 | /* TPLMID_CARD */ |
33 | func->device = buf[2] | (buf[3] << 8); | 35 | device = buf[2] | (buf[3] << 8); |
36 | |||
37 | if (func) { | ||
38 | func->vendor = vendor; | ||
39 | func->device = device; | ||
40 | } else { | ||
41 | card->cis.vendor = vendor; | ||
42 | card->cis.device = device; | ||
43 | } | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static const unsigned char speed_val[16] = | ||
49 | { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; | ||
50 | static const unsigned int speed_unit[8] = | ||
51 | { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; | ||
52 | |||
53 | static int cistpl_funce_common(struct mmc_card *card, | ||
54 | const unsigned char *buf, unsigned size) | ||
55 | { | ||
56 | if (size < 0x04 || buf[0] != 0) | ||
57 | return -EINVAL; | ||
58 | |||
59 | /* TPLFE_FN0_BLK_SIZE */ | ||
60 | card->cis.blksize = buf[1] | (buf[2] << 8); | ||
61 | |||
62 | /* TPLFE_MAX_TRAN_SPEED */ | ||
63 | card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] * | ||
64 | speed_unit[buf[3] & 7]; | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int cistpl_funce_func(struct sdio_func *func, | ||
70 | const unsigned char *buf, unsigned size) | ||
71 | { | ||
72 | unsigned vsn; | ||
73 | unsigned min_size; | ||
74 | |||
75 | vsn = func->card->cccr.sdio_vsn; | ||
76 | min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; | ||
77 | |||
78 | if (size < min_size || buf[0] != 1) | ||
79 | return -EINVAL; | ||
80 | |||
81 | /* TPLFE_MAX_BLK_SIZE */ | ||
82 | func->blksize = buf[12] | (buf[13] << 8); | ||
34 | 83 | ||
35 | return 0; | 84 | return 0; |
36 | } | 85 | } |
37 | 86 | ||
87 | static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, | ||
88 | const unsigned char *buf, unsigned size) | ||
89 | { | ||
90 | int ret; | ||
91 | |||
92 | /* | ||
93 | * There should be two versions of the CISTPL_FUNCE tuple, | ||
94 | * one for the common CIS (function 0) and a version used by | ||
95 | * the individual function's CIS (1-7). Yet, the later has a | ||
96 | * different length depending on the SDIO spec version. | ||
97 | */ | ||
98 | if (func) | ||
99 | ret = cistpl_funce_func(func, buf, size); | ||
100 | else | ||
101 | ret = cistpl_funce_common(card, buf, size); | ||
102 | |||
103 | if (ret) { | ||
104 | printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " | ||
105 | "type %u\n", mmc_hostname(card->host), size, buf[0]); | ||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, | ||
113 | const unsigned char *, unsigned); | ||
114 | |||
38 | struct cis_tpl { | 115 | struct cis_tpl { |
39 | unsigned char code; | 116 | unsigned char code; |
40 | unsigned char min_size; | 117 | unsigned char min_size; |
41 | int (*parse)(struct sdio_func *, const unsigned char *buf, unsigned size); | 118 | tpl_parse_t *parse; |
42 | }; | 119 | }; |
43 | 120 | ||
44 | static const struct cis_tpl cis_tpl_list[] = { | 121 | static const struct cis_tpl cis_tpl_list[] = { |
45 | { 0x15, 3, /* cistpl_vers_1 */ }, | 122 | { 0x15, 3, /* cistpl_vers_1 */ }, |
46 | { 0x20, 4, cistpl_manfid }, | 123 | { 0x20, 4, cistpl_manfid }, |
47 | { 0x21, 2, /* cistpl_funcid */ }, | 124 | { 0x21, 2, /* cistpl_funcid */ }, |
48 | { 0x22, 0, /* cistpl_funce */ }, | 125 | { 0x22, 0, cistpl_funce }, |
49 | }; | 126 | }; |
50 | 127 | ||
51 | int sdio_read_cis(struct sdio_func *func) | 128 | static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) |
52 | { | 129 | { |
53 | int ret; | 130 | int ret; |
54 | struct sdio_func_tuple *this, **prev; | 131 | struct sdio_func_tuple *this, **prev; |
55 | unsigned i, ptr = 0; | 132 | unsigned i, ptr = 0; |
56 | 133 | ||
134 | /* | ||
135 | * Note that this works for the common CIS (function number 0) as | ||
136 | * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS | ||
137 | * have the same offset. | ||
138 | */ | ||
57 | for (i = 0; i < 3; i++) { | 139 | for (i = 0; i < 3; i++) { |
58 | unsigned char x; | 140 | unsigned char x, fn; |
59 | ret = mmc_io_rw_direct(func->card, 0, 0, | 141 | |
60 | func->num * 0x100 + SDIO_FBR_CIS + i, 0, &x); | 142 | if (func) |
143 | fn = func->num; | ||
144 | else | ||
145 | fn = 0; | ||
146 | |||
147 | ret = mmc_io_rw_direct(card, 0, 0, | ||
148 | fn * 0x100 + SDIO_FBR_CIS + i, 0, &x); | ||
61 | if (ret) | 149 | if (ret) |
62 | return ret; | 150 | return ret; |
63 | ptr |= x << (i * 8); | 151 | ptr |= x << (i * 8); |
64 | } | 152 | } |
65 | 153 | ||
66 | /* find the list tail */ | 154 | if (func) |
67 | for (prev = &func->tuples; *prev; prev = &(*prev)->next); | 155 | prev = &func->tuples; |
156 | else | ||
157 | prev = &card->tuples; | ||
158 | |||
159 | BUG_ON(*prev); | ||
68 | 160 | ||
69 | do { | 161 | do { |
70 | unsigned char tpl_code, tpl_link; | 162 | unsigned char tpl_code, tpl_link; |
71 | 163 | ||
72 | ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_code); | 164 | ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code); |
73 | if (ret) | 165 | if (ret) |
74 | break; | 166 | break; |
75 | 167 | ||
@@ -77,7 +169,7 @@ int sdio_read_cis(struct sdio_func *func) | |||
77 | if (tpl_code == 0xff) | 169 | if (tpl_code == 0xff) |
78 | break; | 170 | break; |
79 | 171 | ||
80 | ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_link); | 172 | ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); |
81 | if (ret) | 173 | if (ret) |
82 | break; | 174 | break; |
83 | 175 | ||
@@ -86,7 +178,7 @@ int sdio_read_cis(struct sdio_func *func) | |||
86 | return -ENOMEM; | 178 | return -ENOMEM; |
87 | 179 | ||
88 | for (i = 0; i < tpl_link; i++) { | 180 | for (i = 0; i < tpl_link; i++) { |
89 | ret = mmc_io_rw_direct(func->card, 0, 0, | 181 | ret = mmc_io_rw_direct(card, 0, 0, |
90 | ptr + i, 0, &this->data[i]); | 182 | ptr + i, 0, &this->data[i]); |
91 | if (ret) | 183 | if (ret) |
92 | break; | 184 | break; |
@@ -108,30 +200,45 @@ int sdio_read_cis(struct sdio_func *func) | |||
108 | prev = &this->next; | 200 | prev = &this->next; |
109 | printk(KERN_DEBUG | 201 | printk(KERN_DEBUG |
110 | "%s: queuing CIS tuple 0x%02x length %u\n", | 202 | "%s: queuing CIS tuple 0x%02x length %u\n", |
111 | sdio_func_id(func), tpl_code, tpl_link); | 203 | mmc_hostname(card->host), tpl_code, tpl_link); |
112 | } else { | 204 | } else { |
113 | const struct cis_tpl *tpl = cis_tpl_list + i; | 205 | const struct cis_tpl *tpl = cis_tpl_list + i; |
114 | if (tpl_link < tpl->min_size) { | 206 | if (tpl_link < tpl->min_size) { |
115 | printk(KERN_ERR | 207 | printk(KERN_ERR |
116 | "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", | 208 | "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", |
117 | sdio_func_id(func), tpl_code, tpl_link, tpl->min_size); | 209 | mmc_hostname(card->host), |
210 | tpl_code, tpl_link, tpl->min_size); | ||
118 | ret = -EINVAL; | 211 | ret = -EINVAL; |
119 | } else if (tpl->parse) | 212 | } else if (tpl->parse) { |
120 | ret = tpl->parse(func, this->data, tpl_link); | 213 | ret = tpl->parse(card, func, |
214 | this->data, tpl_link); | ||
215 | } | ||
121 | kfree(this); | 216 | kfree(this); |
122 | } | 217 | } |
123 | 218 | ||
124 | ptr += tpl_link; | 219 | ptr += tpl_link; |
125 | } while (!ret); | 220 | } while (!ret); |
126 | 221 | ||
222 | /* | ||
223 | * Link in all unknown tuples found in the common CIS so that | ||
224 | * drivers don't have to go digging in two places. | ||
225 | */ | ||
226 | if (func) | ||
227 | *prev = card->tuples; | ||
228 | |||
127 | return ret; | 229 | return ret; |
128 | } | 230 | } |
129 | 231 | ||
130 | void sdio_free_cis(struct sdio_func *func) | 232 | int sdio_read_common_cis(struct mmc_card *card) |
233 | { | ||
234 | return sdio_read_cis(card, NULL); | ||
235 | } | ||
236 | |||
237 | void sdio_free_common_cis(struct mmc_card *card) | ||
131 | { | 238 | { |
132 | struct sdio_func_tuple *tuple, *victim; | 239 | struct sdio_func_tuple *tuple, *victim; |
133 | 240 | ||
134 | tuple = func->tuples; | 241 | tuple = card->tuples; |
135 | 242 | ||
136 | while (tuple) { | 243 | while (tuple) { |
137 | victim = tuple; | 244 | victim = tuple; |
@@ -139,6 +246,53 @@ void sdio_free_cis(struct sdio_func *func) | |||
139 | kfree(victim); | 246 | kfree(victim); |
140 | } | 247 | } |
141 | 248 | ||
249 | card->tuples = NULL; | ||
250 | } | ||
251 | |||
252 | int sdio_read_func_cis(struct sdio_func *func) | ||
253 | { | ||
254 | int ret; | ||
255 | |||
256 | ret = sdio_read_cis(func->card, func); | ||
257 | if (ret) | ||
258 | return ret; | ||
259 | |||
260 | /* | ||
261 | * Since we've linked to tuples in the card structure, | ||
262 | * we must make sure we have a reference to it. | ||
263 | */ | ||
264 | get_device(&func->card->dev); | ||
265 | |||
266 | /* | ||
267 | * Vendor/device id is optional for function CIS, so | ||
268 | * copy it from the card structure as needed. | ||
269 | */ | ||
270 | if (func->vendor == 0) { | ||
271 | func->vendor = func->card->cis.vendor; | ||
272 | func->device = func->card->cis.device; | ||
273 | } | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | void sdio_free_func_cis(struct sdio_func *func) | ||
279 | { | ||
280 | struct sdio_func_tuple *tuple, *victim; | ||
281 | |||
282 | tuple = func->tuples; | ||
283 | |||
284 | while (tuple && tuple != func->card->tuples) { | ||
285 | victim = tuple; | ||
286 | tuple = tuple->next; | ||
287 | kfree(victim); | ||
288 | } | ||
289 | |||
142 | func->tuples = NULL; | 290 | func->tuples = NULL; |
291 | |||
292 | /* | ||
293 | * We have now removed the link to the tuples in the | ||
294 | * card structure, so remove the reference. | ||
295 | */ | ||
296 | put_device(&func->card->dev); | ||
143 | } | 297 | } |
144 | 298 | ||
diff --git a/drivers/mmc/core/sdio_cis.h b/drivers/mmc/core/sdio_cis.h index 863d3d516371..4d903c2e425e 100644 --- a/drivers/mmc/core/sdio_cis.h +++ b/drivers/mmc/core/sdio_cis.h | |||
@@ -14,7 +14,10 @@ | |||
14 | #ifndef _MMC_SDIO_CIS_H | 14 | #ifndef _MMC_SDIO_CIS_H |
15 | #define _MMC_SDIO_CIS_H | 15 | #define _MMC_SDIO_CIS_H |
16 | 16 | ||
17 | int sdio_read_cis(struct sdio_func *func); | 17 | int sdio_read_common_cis(struct mmc_card *card); |
18 | void sdio_free_cis(struct sdio_func *func); | 18 | void sdio_free_common_cis(struct mmc_card *card); |
19 | |||
20 | int sdio_read_func_cis(struct sdio_func *func); | ||
21 | void sdio_free_func_cis(struct sdio_func *func); | ||
19 | 22 | ||
20 | #endif | 23 | #endif |