Blame view

drivers/mmc/core/sdio_cis.c 9 KB
b72612619   Nicolas Pitre   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   Nicolas Pitre   sdio: link unknow...
8
9
   * Copyright 2007 Pierre Ossman
   *
b72612619   Nicolas Pitre   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   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
b72612619   Nicolas Pitre   sdio: initial CIS...
18
19
  
  #include <linux/mmc/host.h>
1a632f8cd   Pierre Ossman   sdio: split up co...
20
  #include <linux/mmc/card.h>
b72612619   Nicolas Pitre   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   Pierre Ossman   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   David Vrabel   mmc: sdio: don't ...
31
32
  	/* Find all null-terminated (including zero length) strings in
  	   the TPLLV1_INFO field. Trailing garbage is ignored. */
759bdc7af   Pierre Ossman   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   David Vrabel   mmc: sdio: don't ...
43
  	if (nr_strings == 0)
759bdc7af   Pierre Ossman   sdio: store vendo...
44
  		return 0;
759bdc7af   Pierre Ossman   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   Pierre Ossman   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   Nicolas Pitre   sdio: initial CIS...
73
  {
1a632f8cd   Pierre Ossman   sdio: split up co...
74
  	unsigned int vendor, device;
b72612619   Nicolas Pitre   sdio: initial CIS...
75
  	/* TPLMID_MANF */
1a632f8cd   Pierre Ossman   sdio: split up co...
76
  	vendor = buf[0] | (buf[1] << 8);
b72612619   Nicolas Pitre   sdio: initial CIS...
77
78
  
  	/* TPLMID_CARD */
1a632f8cd   Pierre Ossman   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   Albert Herranz   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   Albert Herranz   sdio: pass whitel...
104
  };
4ec649606   Albert Herranz   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   Albert Herranz   sdio: pass whitel...
110
  {
4ec649606   Albert Herranz   sdio: rework cis ...
111
  	int i, ret;
ed9935f4f   Albert Herranz   sdio: pass whitel...
112

4ec649606   Albert Herranz   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   Albert Herranz   sdio: pass whitel...
117
  	}
4ec649606   Albert Herranz   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   Girish K S   mmc: replace prin...
129
130
  			pr_err("%s: bad %s tuple 0x%02x (%u bytes)
  ",
4ec649606   Albert Herranz   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   Albert Herranz   sdio: pass whitel...
139
  }
4ec649606   Albert Herranz   sdio: rework cis ...
140
  static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func,
1a632f8cd   Pierre Ossman   sdio: split up co...
141
142
  			       const unsigned char *buf, unsigned size)
  {
4ec649606   Albert Herranz   sdio: rework cis ...
143
144
  	/* Only valid for the common CIS (function 0) */
  	if (func)
1a632f8cd   Pierre Ossman   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   Albert Herranz   sdio: rework cis ...
156
  static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func,
1a632f8cd   Pierre Ossman   sdio: split up co...
157
158
159
160
  			     const unsigned char *buf, unsigned size)
  {
  	unsigned vsn;
  	unsigned min_size;
4ec649606   Albert Herranz   sdio: rework cis ...
161
162
163
  	/* Only valid for the individual function's CIS (1-7) */
  	if (!func)
  		return -EINVAL;
ed9935f4f   Albert Herranz   sdio: pass whitel...
164

4ec649606   Albert Herranz   sdio: rework cis ...
165
166
167
168
  	/*
  	 * This tuple has a different length depending on the SDIO spec
  	 * version.
  	 */
1a632f8cd   Pierre Ossman   sdio: split up co...
169
170
  	vsn = func->card->cccr.sdio_vsn;
  	min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
88ea46bcb   Wolfram Sang   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   Pierre Ossman   sdio: split up co...
177
  		return -EINVAL;
88ea46bcb   Wolfram Sang   mmc: sdio: fall b...
178
  	}
1a632f8cd   Pierre Ossman   sdio: split up co...
179
180
  
  	/* TPLFE_MAX_BLK_SIZE */
9a08f82b3   David Vrabel   sdio: set the fun...
181
  	func->max_blksize = buf[12] | (buf[13] << 8);
b72612619   Nicolas Pitre   sdio: initial CIS...
182

62a7573ee   Benzi Zbit   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   Nicolas Pitre   sdio: initial CIS...
188
189
  	return 0;
  }
4ec649606   Albert Herranz   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   Pierre Ossman   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   Albert Herranz   sdio: rework cis ...
205
206
  	if (size < 1)
  		return -EINVAL;
1a632f8cd   Pierre Ossman   sdio: split up co...
207

4ec649606   Albert Herranz   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   Pierre Ossman   sdio: split up co...
212
  }
4ec649606   Albert Herranz   sdio: rework cis ...
213
  /* Known TPL_CODEs table for CIS tuples */
b72612619   Nicolas Pitre   sdio: initial CIS...
214
  static const struct cis_tpl cis_tpl_list[] = {
759bdc7af   Pierre Ossman   sdio: store vendo...
215
  	{	0x15,	3,	cistpl_vers_1		},
b72612619   Nicolas Pitre   sdio: initial CIS...
216
217
  	{	0x20,	4,	cistpl_manfid		},
  	{	0x21,	2,	/* cistpl_funcid */	},
1a632f8cd   Pierre Ossman   sdio: split up co...
218
  	{	0x22,	0,	cistpl_funce		},
07cbeea54   Shawn Lin   mmc: sdio_cis: fi...
219
  	{	0x91,	2,	/* cistpl_sdio_std */	},
b72612619   Nicolas Pitre   sdio: initial CIS...
220
  };
1a632f8cd   Pierre Ossman   sdio: split up co...
221
  static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
b72612619   Nicolas Pitre   sdio: initial CIS...
222
223
  {
  	int ret;
b1538bcf7   Nicolas Pitre   sdio: link unknow...
224
  	struct sdio_func_tuple *this, **prev;
b72612619   Nicolas Pitre   sdio: initial CIS...
225
  	unsigned i, ptr = 0;
1a632f8cd   Pierre Ossman   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   Nicolas Pitre   sdio: initial CIS...
231
  	for (i = 0; i < 3; i++) {
1a632f8cd   Pierre Ossman   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   David Vrabel   sdio: add SDIO_FB...
240
  			SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
b72612619   Nicolas Pitre   sdio: initial CIS...
241
242
243
244
  		if (ret)
  			return ret;
  		ptr |= x << (i * 8);
  	}
1a632f8cd   Pierre Ossman   sdio: split up co...
245
246
247
248
249
250
  	if (func)
  		prev = &func->tuples;
  	else
  		prev = &card->tuples;
  
  	BUG_ON(*prev);
b72612619   Nicolas Pitre   sdio: initial CIS...
251
252
253
  
  	do {
  		unsigned char tpl_code, tpl_link;
b72612619   Nicolas Pitre   sdio: initial CIS...
254

1a632f8cd   Pierre Ossman   sdio: split up co...
255
  		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
b72612619   Nicolas Pitre   sdio: initial CIS...
256
257
258
259
260
261
  		if (ret)
  			break;
  
  		/* 0xff means we're done */
  		if (tpl_code == 0xff)
  			break;
c8d718f10   Pierre Ossman   sdio: handle null...
262
263
264
  		/* null entries have no link field or data */
  		if (tpl_code == 0x00)
  			continue;
1a632f8cd   Pierre Ossman   sdio: split up co...
265
  		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
b72612619   Nicolas Pitre   sdio: initial CIS...
266
267
  		if (ret)
  			break;
0d6132ba0   Pierre Ossman   sdio: handle cis ...
268
269
270
  		/* a size of 0xff also means we're done */
  		if (tpl_link == 0xff)
  			break;
b1538bcf7   Nicolas Pitre   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   Pierre Ossman   sdio: split up co...
276
  			ret = mmc_io_rw_direct(card, 0, 0,
b1538bcf7   Nicolas Pitre   sdio: link unknow...
277
278
  					       ptr + i, 0, &this->data[i]);
  			if (ret)
b72612619   Nicolas Pitre   sdio: initial CIS...
279
  				break;
b72612619   Nicolas Pitre   sdio: initial CIS...
280
  		}
b1538bcf7   Nicolas Pitre   sdio: link unknow...
281
282
  		if (ret) {
  			kfree(this);
b72612619   Nicolas Pitre   sdio: initial CIS...
283
284
  			break;
  		}
4ec649606   Albert Herranz   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   Albert Herranz   sdio: pass whitel...
290
  			/*
4ec649606   Albert Herranz   sdio: rework cis ...
291
292
  			 * The tuple is unknown or known but not parsed.
  			 * Queue the tuple for the function driver.
ed9935f4f   Albert Herranz   sdio: pass whitel...
293
  			 */
ed9935f4f   Albert Herranz   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   Albert Herranz   sdio: rework cis ...
299
300
301
  
  			if (ret == -ENOENT) {
  				/* warn about unknown tuples */
aa6439dad   Liu Chuansheng   mmc: sdio: Change...
302
  				pr_warn_ratelimited("%s: queuing unknown"
4ec649606   Albert Herranz   sdio: rework cis ...
303
304
305
306
307
  				       " CIS tuple 0x%02x (%u bytes)
  ",
  				       mmc_hostname(card->host),
  				       tpl_code, tpl_link);
  			}
ed9935f4f   Albert Herranz   sdio: pass whitel...
308
309
  			/* keep on analyzing tuples */
  			ret = 0;
4ec649606   Albert Herranz   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   Nicolas Pitre   sdio: initial CIS...
317
  		}
b72612619   Nicolas Pitre   sdio: initial CIS...
318

b1538bcf7   Nicolas Pitre   sdio: link unknow...
319
  		ptr += tpl_link;
b72612619   Nicolas Pitre   sdio: initial CIS...
320
  	} while (!ret);
1a632f8cd   Pierre Ossman   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   Nicolas Pitre   sdio: initial CIS...
327
328
  	return ret;
  }
b1538bcf7   Nicolas Pitre   sdio: link unknow...
329

1a632f8cd   Pierre Ossman   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   Nicolas Pitre   sdio: link unknow...
336
337
  {
  	struct sdio_func_tuple *tuple, *victim;
1a632f8cd   Pierre Ossman   sdio: split up co...
338
  	tuple = card->tuples;
b1538bcf7   Nicolas Pitre   sdio: link unknow...
339
340
341
342
343
344
  
  	while (tuple) {
  		victim = tuple;
  		tuple = tuple->next;
  		kfree(victim);
  	}
1a632f8cd   Pierre Ossman   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   Nicolas Pitre   sdio: link unknow...
385
  	func->tuples = NULL;
1a632f8cd   Pierre Ossman   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   Nicolas Pitre   sdio: link unknow...
392
  }