Blame view
drivers/mmc/core/sdio_cis.c
9 KB
b72612619 sdio: initial CIS... |
1 2 3 4 5 6 7 |
/* * linux/drivers/mmc/core/sdio_cis.c * * Author: Nicolas Pitre * Created: June 11, 2007 * Copyright: MontaVista Software Inc. * |
b1538bcf7 sdio: link unknow... |
8 9 |
* Copyright 2007 Pierre Ossman * |
b72612619 sdio: initial CIS... |
10 11 12 13 14 15 16 |
* 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/kernel.h> |
5a0e3ad6a include cleanup: ... |
17 |
#include <linux/slab.h> |
b72612619 sdio: initial CIS... |
18 19 |
#include <linux/mmc/host.h> |
1a632f8cd sdio: split up co... |
20 |
#include <linux/mmc/card.h> |
b72612619 sdio: initial CIS... |
21 22 23 24 25 |
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio_func.h> #include "sdio_cis.h" #include "sdio_ops.h" |
759bdc7af sdio: store vendo... |
26 27 28 29 30 |
static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, unsigned size) { unsigned i, nr_strings; char **buffer, *string; |
a1125b1e4 mmc: sdio: don't ... |
31 32 |
/* Find all null-terminated (including zero length) strings in the TPLLV1_INFO field. Trailing garbage is ignored. */ |
759bdc7af sdio: store vendo... |
33 34 35 36 37 38 39 40 41 42 |
buf += 2; size -= 2; nr_strings = 0; for (i = 0; i < size; i++) { if (buf[i] == 0xff) break; if (buf[i] == 0) nr_strings++; } |
a1125b1e4 mmc: sdio: don't ... |
43 |
if (nr_strings == 0) |
759bdc7af sdio: store vendo... |
44 |
return 0; |
759bdc7af sdio: store vendo... |
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
size = i; buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL); if (!buffer) return -ENOMEM; string = (char*)(buffer + nr_strings); for (i = 0; i < nr_strings; i++) { buffer[i] = string; strcpy(string, buf); string += strlen(string) + 1; buf += strlen(buf) + 1; } if (func) { func->num_info = nr_strings; func->info = (const char**)buffer; } else { card->num_info = nr_strings; card->info = (const char**)buffer; } return 0; } |
1a632f8cd sdio: split up co... |
71 72 |
static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, unsigned size) |
b72612619 sdio: initial CIS... |
73 |
{ |
1a632f8cd sdio: split up co... |
74 |
unsigned int vendor, device; |
b72612619 sdio: initial CIS... |
75 |
/* TPLMID_MANF */ |
1a632f8cd sdio: split up co... |
76 |
vendor = buf[0] | (buf[1] << 8); |
b72612619 sdio: initial CIS... |
77 78 |
/* TPLMID_CARD */ |
1a632f8cd sdio: split up co... |
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
device = buf[2] | (buf[3] << 8); if (func) { func->vendor = vendor; func->device = device; } else { card->cis.vendor = vendor; card->cis.device = device; } return 0; } static const unsigned char speed_val[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; static const unsigned int speed_unit[8] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; |
4ec649606 sdio: rework cis ... |
96 97 98 99 100 101 102 103 |
typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, const unsigned char *, unsigned); struct cis_tpl { unsigned char code; unsigned char min_size; tpl_parse_t *parse; |
ed9935f4f sdio: pass whitel... |
104 |
}; |
4ec649606 sdio: rework cis ... |
105 106 107 108 109 |
static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func, const char *tpl_descr, const struct cis_tpl *tpl, int tpl_count, unsigned char code, const unsigned char *buf, unsigned size) |
ed9935f4f sdio: pass whitel... |
110 |
{ |
4ec649606 sdio: rework cis ... |
111 |
int i, ret; |
ed9935f4f sdio: pass whitel... |
112 |
|
4ec649606 sdio: rework cis ... |
113 114 115 116 |
/* look for a matching code in the table */ for (i = 0; i < tpl_count; i++, tpl++) { if (tpl->code == code) break; |
ed9935f4f sdio: pass whitel... |
117 |
} |
4ec649606 sdio: rework cis ... |
118 119 120 121 122 123 124 125 126 127 128 |
if (i < tpl_count) { if (size >= tpl->min_size) { if (tpl->parse) ret = tpl->parse(card, func, buf, size); else ret = -EILSEQ; /* known tuple, not parsed */ } else { /* invalid tuple */ ret = -EINVAL; } if (ret && ret != -EILSEQ && ret != -ENOENT) { |
a3c76eb9d mmc: replace prin... |
129 130 |
pr_err("%s: bad %s tuple 0x%02x (%u bytes) ", |
4ec649606 sdio: rework cis ... |
131 132 133 134 135 136 137 138 |
mmc_hostname(card->host), tpl_descr, code, size); } } else { /* unknown tuple */ ret = -ENOENT; } return ret; |
ed9935f4f sdio: pass whitel... |
139 |
} |
4ec649606 sdio: rework cis ... |
140 |
static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func, |
1a632f8cd sdio: split up co... |
141 142 |
const unsigned char *buf, unsigned size) { |
4ec649606 sdio: rework cis ... |
143 144 |
/* Only valid for the common CIS (function 0) */ if (func) |
1a632f8cd sdio: split up co... |
145 146 147 148 149 150 151 152 153 154 155 |
return -EINVAL; /* TPLFE_FN0_BLK_SIZE */ card->cis.blksize = buf[1] | (buf[2] << 8); /* TPLFE_MAX_TRAN_SPEED */ card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] * speed_unit[buf[3] & 7]; return 0; } |
4ec649606 sdio: rework cis ... |
156 |
static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func, |
1a632f8cd sdio: split up co... |
157 158 159 160 |
const unsigned char *buf, unsigned size) { unsigned vsn; unsigned min_size; |
4ec649606 sdio: rework cis ... |
161 162 163 |
/* Only valid for the individual function's CIS (1-7) */ if (!func) return -EINVAL; |
ed9935f4f sdio: pass whitel... |
164 |
|
4ec649606 sdio: rework cis ... |
165 166 167 168 |
/* * This tuple has a different length depending on the SDIO spec * version. */ |
1a632f8cd sdio: split up co... |
169 170 |
vsn = func->card->cccr.sdio_vsn; min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; |
88ea46bcb mmc: sdio: fall b... |
171 172 173 174 175 176 |
if (size == 28 && vsn == SDIO_SDIO_REV_1_10) { pr_warn("%s: card has broken SDIO 1.1 CIS, forcing SDIO 1.0 ", mmc_hostname(card->host)); vsn = SDIO_SDIO_REV_1_00; } else if (size < min_size) { |
1a632f8cd sdio: split up co... |
177 |
return -EINVAL; |
88ea46bcb mmc: sdio: fall b... |
178 |
} |
1a632f8cd sdio: split up co... |
179 180 |
/* TPLFE_MAX_BLK_SIZE */ |
9a08f82b3 sdio: set the fun... |
181 |
func->max_blksize = buf[12] | (buf[13] << 8); |
b72612619 sdio: initial CIS... |
182 |
|
62a7573ee sdio: fix the use... |
183 184 185 186 187 |
/* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */ if (vsn > SDIO_SDIO_REV_1_00) func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10; else func->enable_timeout = jiffies_to_msecs(HZ); |
b72612619 sdio: initial CIS... |
188 189 |
return 0; } |
4ec649606 sdio: rework cis ... |
190 191 192 193 194 195 196 197 198 199 200 201 |
/* * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples. * * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO * TPLFID_FUNCTION is always hardcoded to 0x0C. */ static const struct cis_tpl cis_tpl_funce_list[] = { { 0x00, 4, cistpl_funce_common }, { 0x01, 0, cistpl_funce_func }, { 0x04, 1+1+6, /* CISTPL_FUNCE_LAN_NODE_ID */ }, }; |
1a632f8cd sdio: split up co... |
202 203 204 |
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, unsigned size) { |
4ec649606 sdio: rework cis ... |
205 206 |
if (size < 1) return -EINVAL; |
1a632f8cd sdio: split up co... |
207 |
|
4ec649606 sdio: rework cis ... |
208 209 210 211 |
return cis_tpl_parse(card, func, "CISTPL_FUNCE", cis_tpl_funce_list, ARRAY_SIZE(cis_tpl_funce_list), buf[0], buf, size); |
1a632f8cd sdio: split up co... |
212 |
} |
4ec649606 sdio: rework cis ... |
213 |
/* Known TPL_CODEs table for CIS tuples */ |
b72612619 sdio: initial CIS... |
214 |
static const struct cis_tpl cis_tpl_list[] = { |
759bdc7af sdio: store vendo... |
215 |
{ 0x15, 3, cistpl_vers_1 }, |
b72612619 sdio: initial CIS... |
216 217 |
{ 0x20, 4, cistpl_manfid }, { 0x21, 2, /* cistpl_funcid */ }, |
1a632f8cd sdio: split up co... |
218 |
{ 0x22, 0, cistpl_funce }, |
07cbeea54 mmc: sdio_cis: fi... |
219 |
{ 0x91, 2, /* cistpl_sdio_std */ }, |
b72612619 sdio: initial CIS... |
220 |
}; |
1a632f8cd sdio: split up co... |
221 |
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) |
b72612619 sdio: initial CIS... |
222 223 |
{ int ret; |
b1538bcf7 sdio: link unknow... |
224 |
struct sdio_func_tuple *this, **prev; |
b72612619 sdio: initial CIS... |
225 |
unsigned i, ptr = 0; |
1a632f8cd sdio: split up co... |
226 227 228 229 230 |
/* * Note that this works for the common CIS (function number 0) as * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS * have the same offset. */ |
b72612619 sdio: initial CIS... |
231 |
for (i = 0; i < 3; i++) { |
1a632f8cd sdio: split up co... |
232 233 234 235 236 237 238 239 |
unsigned char x, fn; if (func) fn = func->num; else fn = 0; ret = mmc_io_rw_direct(card, 0, 0, |
7616ee95f sdio: add SDIO_FB... |
240 |
SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x); |
b72612619 sdio: initial CIS... |
241 242 243 244 |
if (ret) return ret; ptr |= x << (i * 8); } |
1a632f8cd sdio: split up co... |
245 246 247 248 249 250 |
if (func) prev = &func->tuples; else prev = &card->tuples; BUG_ON(*prev); |
b72612619 sdio: initial CIS... |
251 252 253 |
do { unsigned char tpl_code, tpl_link; |
b72612619 sdio: initial CIS... |
254 |
|
1a632f8cd sdio: split up co... |
255 |
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code); |
b72612619 sdio: initial CIS... |
256 257 258 259 260 261 |
if (ret) break; /* 0xff means we're done */ if (tpl_code == 0xff) break; |
c8d718f10 sdio: handle null... |
262 263 264 |
/* null entries have no link field or data */ if (tpl_code == 0x00) continue; |
1a632f8cd sdio: split up co... |
265 |
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); |
b72612619 sdio: initial CIS... |
266 267 |
if (ret) break; |
0d6132ba0 sdio: handle cis ... |
268 269 270 |
/* a size of 0xff also means we're done */ if (tpl_link == 0xff) break; |
b1538bcf7 sdio: link unknow... |
271 272 273 274 275 |
this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); if (!this) return -ENOMEM; for (i = 0; i < tpl_link; i++) { |
1a632f8cd sdio: split up co... |
276 |
ret = mmc_io_rw_direct(card, 0, 0, |
b1538bcf7 sdio: link unknow... |
277 278 |
ptr + i, 0, &this->data[i]); if (ret) |
b72612619 sdio: initial CIS... |
279 |
break; |
b72612619 sdio: initial CIS... |
280 |
} |
b1538bcf7 sdio: link unknow... |
281 282 |
if (ret) { kfree(this); |
b72612619 sdio: initial CIS... |
283 284 |
break; } |
4ec649606 sdio: rework cis ... |
285 286 287 288 289 |
/* Try to parse the CIS tuple */ ret = cis_tpl_parse(card, func, "CIS", cis_tpl_list, ARRAY_SIZE(cis_tpl_list), tpl_code, this->data, tpl_link); if (ret == -EILSEQ || ret == -ENOENT) { |
ed9935f4f sdio: pass whitel... |
290 |
/* |
4ec649606 sdio: rework cis ... |
291 292 |
* The tuple is unknown or known but not parsed. * Queue the tuple for the function driver. |
ed9935f4f sdio: pass whitel... |
293 |
*/ |
ed9935f4f sdio: pass whitel... |
294 295 296 297 298 |
this->next = NULL; this->code = tpl_code; this->size = tpl_link; *prev = this; prev = &this->next; |
4ec649606 sdio: rework cis ... |
299 300 301 |
if (ret == -ENOENT) { /* warn about unknown tuples */ |
aa6439dad mmc: sdio: Change... |
302 |
pr_warn_ratelimited("%s: queuing unknown" |
4ec649606 sdio: rework cis ... |
303 304 305 306 307 |
" CIS tuple 0x%02x (%u bytes) ", mmc_hostname(card->host), tpl_code, tpl_link); } |
ed9935f4f sdio: pass whitel... |
308 309 |
/* keep on analyzing tuples */ ret = 0; |
4ec649606 sdio: rework cis ... |
310 311 312 313 314 315 316 |
} else { /* * We don't need the tuple anymore if it was * successfully parsed by the SDIO core or if it is * not going to be queued for a driver. */ kfree(this); |
b72612619 sdio: initial CIS... |
317 |
} |
b72612619 sdio: initial CIS... |
318 |
|
b1538bcf7 sdio: link unknow... |
319 |
ptr += tpl_link; |
b72612619 sdio: initial CIS... |
320 |
} while (!ret); |
1a632f8cd sdio: split up co... |
321 322 323 324 325 326 |
/* * Link in all unknown tuples found in the common CIS so that * drivers don't have to go digging in two places. */ if (func) *prev = card->tuples; |
b72612619 sdio: initial CIS... |
327 328 |
return ret; } |
b1538bcf7 sdio: link unknow... |
329 |
|
1a632f8cd sdio: split up co... |
330 331 332 333 334 335 |
int sdio_read_common_cis(struct mmc_card *card) { return sdio_read_cis(card, NULL); } void sdio_free_common_cis(struct mmc_card *card) |
b1538bcf7 sdio: link unknow... |
336 337 |
{ struct sdio_func_tuple *tuple, *victim; |
1a632f8cd sdio: split up co... |
338 |
tuple = card->tuples; |
b1538bcf7 sdio: link unknow... |
339 340 341 342 343 344 |
while (tuple) { victim = tuple; tuple = tuple->next; kfree(victim); } |
1a632f8cd sdio: split up co... |
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
card->tuples = NULL; } int sdio_read_func_cis(struct sdio_func *func) { int ret; ret = sdio_read_cis(func->card, func); if (ret) return ret; /* * Since we've linked to tuples in the card structure, * we must make sure we have a reference to it. */ get_device(&func->card->dev); /* * Vendor/device id is optional for function CIS, so * copy it from the card structure as needed. */ if (func->vendor == 0) { func->vendor = func->card->cis.vendor; func->device = func->card->cis.device; } return 0; } void sdio_free_func_cis(struct sdio_func *func) { struct sdio_func_tuple *tuple, *victim; tuple = func->tuples; while (tuple && tuple != func->card->tuples) { victim = tuple; tuple = tuple->next; kfree(victim); } |
b1538bcf7 sdio: link unknow... |
385 |
func->tuples = NULL; |
1a632f8cd sdio: split up co... |
386 387 388 389 390 391 |
/* * We have now removed the link to the tuples in the * card structure, so remove the reference. */ put_device(&func->card->dev); |
b1538bcf7 sdio: link unknow... |
392 |
} |