summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2004-10-06 20:43:12 +0000
committerJens Arnold <amiconn@rockbox.org>2004-10-06 20:43:12 +0000
commit6f9a7eb2c7d6f81e54b47c917be79f5126ba8982 (patch)
tree22dc11305494ed12060699821641813f6246b84a
parent30c1358f87c68d5bc78178bb3af9e8e3b8c660e6 (diff)
downloadrockbox-6f9a7eb2c7d6f81e54b47c917be79f5126ba8982.tar.gz
rockbox-6f9a7eb2c7d6f81e54b47c917be79f5126ba8982.zip
Enhanced MMC handling: Driver cleanup, timeout calculation fixed, allowed voltage check, maintain disk activity info (fixes immediate shutdown at end of playback). MMC debug menu item populated.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5193 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c132
-rw-r--r--firmware/drivers/ata_mmc.c194
-rw-r--r--firmware/export/ata_mmc.h42
3 files changed, 279 insertions, 89 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index cb6ad07c4a..663ea7bdf4 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -54,6 +54,9 @@
54#ifdef CONFIG_TUNER 54#ifdef CONFIG_TUNER
55#include "radio.h" 55#include "radio.h"
56#endif 56#endif
57#ifdef HAVE_MMC
58#include "ata_mmc.h"
59#endif
57 60
58/*---------------------------------------------------*/ 61/*---------------------------------------------------*/
59/* SPECIAL DEBUG STUFF */ 62/* SPECIAL DEBUG STUFF */
@@ -1260,9 +1263,134 @@ static bool view_runtime(void)
1260} 1263}
1261 1264
1262#ifdef HAVE_MMC 1265#ifdef HAVE_MMC
1263static bool dbg_mmc_info(void) 1266/* value is 10 * real value */
1267static unsigned char prep_value_unit(unsigned int *value,
1268 const unsigned char *units)
1269{
1270 int unit_no = 0;
1271
1272 while (*value >= 10000)
1273 {
1274 *value /= 1000;
1275 unit_no++;
1276 }
1277 return units[unit_no];
1278}
1279
1280bool dbg_mmc_info(void)
1264{ 1281{
1265 splash(HZ, true, "To be implemented."); 1282 bool done = false;
1283 int currval = 0;
1284 unsigned int value;
1285 tCardInfo *card;
1286 unsigned char pbuf[32];
1287 unsigned char card_name[7];
1288 unsigned char unit;
1289
1290 static const unsigned char i_vmin[] = { 0, 1, 5, 10, 25, 35, 60, 100 };
1291 static const unsigned char i_vmax[] = { 1, 5, 10, 25, 35, 45, 80, 200 };
1292
1293 card_name[6] = '\0';
1294
1295 lcd_setmargins(0, 0);
1296 lcd_setfont(FONT_SYSFIXED);
1297
1298 while (!done)
1299 {
1300 card = mmc_card_info(currval / 2);
1301
1302 lcd_clear_display();
1303 snprintf(pbuf, sizeof(pbuf), "[MMC%d p%d]", currval / 2,
1304 (currval % 2) + 1);
1305 lcd_puts(0, 0, pbuf);
1306
1307 if (card->initialized)
1308 {
1309 if (!(currval % 2)) /* General info */
1310 {
1311 strncpy(card_name, ((unsigned char*)card->cid) + 3, 6);
1312 snprintf(pbuf, sizeof(pbuf), "%s Rev %d.%d", card_name,
1313 mmc_extract_bits(card->cid, 72, 4),
1314 mmc_extract_bits(card->cid, 76, 4));
1315 lcd_puts(0, 1, pbuf);
1316 snprintf(pbuf, sizeof(pbuf), "Prod: %d/%d",
1317 mmc_extract_bits(card->cid, 112, 4),
1318 mmc_extract_bits(card->cid, 116, 4) + 1997);
1319 lcd_puts(0, 2, pbuf);
1320 snprintf(pbuf, sizeof(pbuf), "Ser#: 0x%08x",
1321 mmc_extract_bits(card->cid, 80, 32));
1322 lcd_puts(0, 3, pbuf);
1323 snprintf(pbuf, sizeof(pbuf), "M=%02x, O=%04x",
1324 mmc_extract_bits(card->cid, 0, 8),
1325 mmc_extract_bits(card->cid, 8, 16));
1326 lcd_puts(0, 4, pbuf);
1327 value = mmc_extract_bits(card->csd, 54, 12)
1328 * (SECTOR_SIZE << (mmc_extract_bits(card->csd, 78, 3)+2));
1329 snprintf(pbuf, sizeof(pbuf), "Size: %d MB",
1330 value / (1024*1024));
1331 lcd_puts(0, 5, pbuf);
1332 }
1333 else /* Technical details */
1334 {
1335 value = card->speed / 100;
1336 unit = prep_value_unit(&value, "kMG");
1337 if (value < 100)
1338 snprintf(pbuf, sizeof(pbuf), "Speed: %d.%01d %cBit/s",
1339 value / 10, value % 10, unit);
1340 else
1341 snprintf(pbuf, sizeof(pbuf), "Tsac: %d %cBit/s",
1342 value / 10, unit);
1343 lcd_puts(0, 1, pbuf);
1344
1345 value = card->tsac;
1346 unit = prep_value_unit(&value, "nµm");
1347 if (value < 100)
1348 snprintf(pbuf, sizeof(pbuf), "Tsac: %d.%01d %cs",
1349 value / 10, value % 10, unit);
1350 else
1351 snprintf(pbuf, sizeof(pbuf), "Tsac: %d %cs",
1352 value / 10, unit);
1353 lcd_puts(0, 1, pbuf);
1354
1355 snprintf(pbuf, sizeof(pbuf), "Nsac: %d clk", card->nsac);
1356 lcd_puts(0, 2, pbuf);
1357 snprintf(pbuf, sizeof(pbuf), "R2W: *%d", card->r2w_factor);
1358 lcd_puts(0, 3, pbuf);
1359 snprintf(pbuf, sizeof(pbuf), "IRmax: %d..%d mA",
1360 i_vmin[mmc_extract_bits(card->csd, 66, 3)],
1361 i_vmax[mmc_extract_bits(card->csd, 69, 3)]);
1362 lcd_puts(0, 4, pbuf);
1363 snprintf(pbuf, sizeof(pbuf), "IWmax: %d..%d mA",
1364 i_vmin[mmc_extract_bits(card->csd, 72, 3)],
1365 i_vmax[mmc_extract_bits(card->csd, 75, 3)]);
1366 lcd_puts(0, 5, pbuf);
1367 }
1368 }
1369 else
1370 lcd_puts(0, 1, "Not found!");
1371
1372 lcd_update();
1373
1374 switch (button_get(true))
1375 {
1376 case SETTINGS_OK:
1377 case SETTINGS_CANCEL:
1378 done = true;
1379 break;
1380
1381 case SETTINGS_DEC:
1382 currval--;
1383 if (currval < 0)
1384 currval = 3;
1385 break;
1386
1387 case SETTINGS_INC:
1388 currval++;
1389 if (currval > 3)
1390 currval = 0;
1391 break;
1392 }
1393 }
1266 1394
1267 return false; 1395 return false;
1268} 1396}
diff --git a/firmware/drivers/ata_mmc.c b/firmware/drivers/ata_mmc.c
index 7a187f092b..ea7c5f1737 100644
--- a/firmware/drivers/ata_mmc.c
+++ b/firmware/drivers/ata_mmc.c
@@ -18,6 +18,7 @@
18 ****************************************************************************/ 18 ****************************************************************************/
19#include <stdbool.h> 19#include <stdbool.h>
20#include "ata.h" 20#include "ata.h"
21#include "ata_mmc.h"
21#include "kernel.h" 22#include "kernel.h"
22#include "thread.h" 23#include "thread.h"
23#include "led.h" 24#include "led.h"
@@ -106,22 +107,6 @@ static const unsigned char dummy[] = {
106 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF 107 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
107}; 108};
108 109
109typedef struct
110{
111 bool initialized;
112 unsigned char bitrate_register;
113 unsigned char rev;
114 unsigned char rev_fract;
115 unsigned int speed; /* bps */
116 unsigned int read_timeout; /* n * 8 clock cycles */
117 unsigned int write_timeout; /* n * 8 clock cycles */
118 unsigned int size; /* in bytes */
119 unsigned int manuf_month;
120 unsigned int manuf_year;
121 unsigned long serial_number;
122 unsigned char name[7];
123} tCardInfo;
124
125static tCardInfo card_info[2]; 110static tCardInfo card_info[2];
126 111
127/* private function declarations */ 112/* private function declarations */
@@ -149,6 +134,8 @@ static int select_card(int card_no)
149 or_b(0x10, &PADRH); /* set clock gate PA12 CHECKME: mask? */ 134 or_b(0x10, &PADRH); /* set clock gate PA12 CHECKME: mask? */
150 else /* external */ 135 else /* external */
151 and_b(~0x10, &PADRH); /* clear clock gate PA12 CHECKME: mask?*/ 136 and_b(~0x10, &PADRH); /* clear clock gate PA12 CHECKME: mask?*/
137
138 last_disk_activity = current_tick;
152 139
153 if (!card_info[card_no].initialized) 140 if (!card_info[card_no].initialized)
154 { 141 {
@@ -177,6 +164,8 @@ static void deselect_card(void)
177{ 164{
178 while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ 165 while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */
179 or_b(0x06, &PADRH); /* deassert CS (both cards) */ 166 or_b(0x06, &PADRH); /* deassert CS (both cards) */
167
168 last_disk_activity = current_tick;
180} 169}
181 170
182static void setup_sci1(int bitrate_register) 171static void setup_sci1(int bitrate_register)
@@ -235,8 +224,7 @@ static void read_transfer(unsigned char *buf, int len)
235 *buf = fliptable[(signed char)(RDR1)]; /* read & bitswap */ 224 *buf = fliptable[(signed char)(RDR1)]; /* read & bitswap */
236} 225}
237 226
238/* timeout is in bytes */ 227static unsigned char poll_byte(int timeout) /* timeout is in bytes */
239static unsigned char poll_byte(int timeout)
240{ 228{
241 int i; 229 int i;
242 unsigned char data = 0; /* stop the compiler complaining */ 230 unsigned char data = 0; /* stop the compiler complaining */
@@ -254,7 +242,7 @@ static unsigned char poll_byte(int timeout)
254 return fliptable[(signed char)data]; 242 return fliptable[(signed char)data];
255} 243}
256 244
257static unsigned char poll_busy(int timeout) 245static unsigned char poll_busy(int timeout) /* timeout is in bytes */
258{ 246{
259 int i; 247 int i;
260 unsigned char data, dummy; 248 unsigned char data, dummy;
@@ -362,101 +350,134 @@ static int send_data(char start_token, const unsigned char *buf, int len,
362 return ret; 350 return ret;
363} 351}
364 352
353/* helper function to extract n (<=32) bits from an arbitrary position.
354 counting from MSB to LSB */
355unsigned long mmc_extract_bits(
356 const unsigned long *p, /* the start of the bitfield array */
357 unsigned int start, /* bit no. to start reading */
358 unsigned int size) /* how many bits to read */
359{
360 unsigned int bit_index;
361 unsigned int bits_to_use;
362 unsigned long mask;
363 unsigned long result;
364
365 if (size == 1)
366 { /* short cut */
367 return ((p[start/32] >> (31 - (start % 32))) & 1);
368 }
369
370 result = 0;
371 while (size)
372 {
373 bit_index = start % 32;
374 bits_to_use = MIN(32 - bit_index, size);
375 mask = 0xFFFFFFFF >> (32 - bits_to_use);
376
377 result <<= bits_to_use; /* start last round */
378 result |= (p[start/32] >> (32 - bits_to_use - bit_index)) & mask;
379
380 start += bits_to_use;
381 size -= bits_to_use;
382 }
383
384 return result;
385}
386
365static int initialize_card(int card_no) 387static int initialize_card(int card_no)
366{ 388{
367 int i, temp; 389 int i, temp;
368 unsigned char response; 390 unsigned char response[5];
369 unsigned char cxd[16];
370 tCardInfo *card = &card_info[card_no]; 391 tCardInfo *card = &card_info[card_no];
371 392
372 static const char mantissa[] = { /* *10 */ 393 static const char mantissa[] = { /* *10 */
373 0, 10, 12, 13, 15, 20, 25, 30, 394 0, 10, 12, 13, 15, 20, 25, 30,
374 35, 40, 45, 50, 55, 60, 70, 80 395 35, 40, 45, 50, 55, 60, 70, 80
375 }; 396 };
376 static const int speed_exponent[] = { /* /10 */ 397 static const int exponent[] = { /* use varies */
377 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 398 1, 10, 100, 1000, 10000, 100000, 1000000,
378 }; 399 10000000, 100000000, 1000000000
379
380 static const int time_exponent[] = { /* reciprocal */
381 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100
382 }; 400 };
383 401
384 /* switch to SPI mode */ 402 /* switch to SPI mode */
385 send_cmd(CMD_GO_IDLE_STATE, 0, &response); 403 send_cmd(CMD_GO_IDLE_STATE, 0, response);
386 if (response != 0x01) 404 if (response[0] != 0x01)
387 return -1; /* error response */ 405 return -1; /* error response */
388 406
389 /* initialize card */ 407 /* initialize card */
390 i = 0; 408 i = 0;
391 while (send_cmd(CMD_SEND_OP_COND, 0, &response) && (++i < 200)); 409 while (send_cmd(CMD_SEND_OP_COND, 0, response) && (++i < 200));
392 if (response != 0x00) 410 if (response[0] != 0x00)
393 return -2; /* not ready */ 411 return -2; /* not ready */
394 412
395 /* get CSD register */ 413 /* get OCR register */
396 if (send_cmd(CMD_SEND_CSD, 0, &response)) 414 if (send_cmd(CMD_READ_OCR, 0, response))
397 return -3; 415 return -3;
398 if (receive_data(cxd, 16, 50)) 416 card->ocr = (response[1] << 24) + (response[2] << 16)
399 return -4; 417 + (response[3] << 8) + response[4];
400 418
401 /* check block size */ 419 /* check voltage */
402 if (1 << (cxd[5] & 0x0F) != SECTOR_SIZE) 420 if (!(card->ocr & 0x00100000)) /* 3.2 .. 3.3 V */
421 return -4;
422
423 /* get CSD register */
424 if (send_cmd(CMD_SEND_CSD, 0, response))
403 return -5; 425 return -5;
426 if (receive_data((unsigned char*)card->csd, 16, 20))
427 return -6;
404 428
405 /* max transmission speed the card is capable of */ 429 /* check block size */
406 card->speed = mantissa[(cxd[3] & 0x78) >> 3] 430 if ((1 << mmc_extract_bits(card->csd, 44, 4)) != SECTOR_SIZE)
407 * speed_exponent[(cxd[3] & 0x07)]; 431 return -7;
408 432
409 /* calculate the clock divider */ 433 /* max transmission speed, clock divider */
434 temp = mmc_extract_bits(card->csd, 29, 3);
435 temp = (temp > 3) ? 3 : temp;
436 card->speed = mantissa[mmc_extract_bits(card->csd, 25, 4)]
437 * exponent[temp + 4];
410 card->bitrate_register = (FREQ/4-1) / card->speed; 438 card->bitrate_register = (FREQ/4-1) / card->speed;
411 439
412 /* calculate read timeout in clock cycles from TSAC, NSAC and the actual 440 /* NSAC, TSAC, read timeout */
413 * clock frequency */ 441 card->nsac = 100 * mmc_extract_bits(card->csd, 16, 8);
414 temp = (FREQ/4) / (card->bitrate_register + 1); /* actual frequency */ 442 card->tsac = mantissa[mmc_extract_bits(card->csd, 9, 4)];
415 card->read_timeout = 443 temp = mmc_extract_bits(card->csd, 13, 3);
416 (temp * mantissa[(cxd[1] & 0x78) >> 3] + (1000 * cxd[2])) 444 card->read_timeout = ((FREQ/4) / (card->bitrate_register + 1)
417 / (time_exponent[cxd[1] & 0x07] * 8); 445 * card->tsac / exponent[9 - temp]
418 446 + (10 * card->nsac));
419 /* calculate write timeout */ 447 card->read_timeout /= 8; /* clocks -> bytes */
420 temp = (cxd[12] & 0x1C) >> 2; 448 card->tsac *= exponent[temp];
421 if (temp > 5) 449
422 temp = 5; 450 /* r2w_factor, write timeout */
423 card->write_timeout = card->read_timeout * (1 << temp); 451 temp = mmc_extract_bits(card->csd, 99, 3);
424 452 temp = (temp > 5) ? 5 : temp;
425 /* calculate size */ 453 card->r2w_factor = 1 << temp;
426 card->size = ((unsigned int)(cxd[6] & 0x03) << 10) 454 card->write_timeout = card->read_timeout * card->r2w_factor;
427 + ((unsigned int)cxd[7] << 2)
428 + ((unsigned int)(cxd[8] & 0xC0) >> 6);
429 temp = ((cxd[9] & 0x03) << 1) + ((cxd[10] & 0x80) >> 7) + 2;
430 card->size *= (SECTOR_SIZE << temp);
431 455
432 /* switch to full speed */ 456 /* switch to full speed */
433 setup_sci1(card->bitrate_register); 457 setup_sci1(card->bitrate_register);
434 458
435 /* get CID register */ 459 /* get CID register */
436 if (send_cmd(CMD_SEND_CID, 0, &response)) 460 if (send_cmd(CMD_SEND_CID, 0, response))
437 return -6; 461 return -8;
438 if (receive_data(cxd, 16, 50)) 462 if (receive_data((unsigned char*)card->cid, 16, 20))
439 return -7; 463 return -9;
440
441 /* get data from CID */
442 strncpy(card->name, &cxd[3], 6);
443 card->name[6] = '\0';
444
445 card->rev = (cxd[9] & 0xF0) >> 4;
446 card->rev_fract = cxd[9] & 0x0F;
447
448 card->manuf_month = (cxd[14] & 0xF0) >> 4;
449 card->manuf_year = (cxd[14] & 0x0F) + 1997;
450
451 card->serial_number = ((unsigned long)cxd[10] << 24)
452 + ((unsigned long)cxd[11] << 16)
453 + ((unsigned long)cxd[12] << 8)
454 + (unsigned long)cxd[13];
455 464
456 card->initialized = true; 465 card->initialized = true;
457 return 0; 466 return 0;
458} 467}
459 468
469tCardInfo *mmc_card_info(int card_no)
470{
471 tCardInfo *card = &card_info[card_no];
472
473 if (!card->initialized)
474 {
475 select_card(card_no);
476 deselect_card();
477 }
478 return card;
479}
480
460int ata_read_sectors(unsigned long start, 481int ata_read_sectors(unsigned long start,
461 int incount, 482 int incount,
462 void* inbuf) 483 void* inbuf)
@@ -467,9 +488,6 @@ int ata_read_sectors(unsigned long start,
467 unsigned char response; 488 unsigned char response;
468 tCardInfo *card = &card_info[current_card]; 489 tCardInfo *card = &card_info[current_card];
469 490
470 if (incount <= 0)
471 return ret;
472
473 addr = start * SECTOR_SIZE; 491 addr = start * SECTOR_SIZE;
474 492
475 mutex_lock(&mmc_mutex); 493 mutex_lock(&mmc_mutex);
@@ -482,6 +500,7 @@ int ata_read_sectors(unsigned long start,
482 ret = send_cmd(CMD_READ_SINGLE_BLOCK, addr, &response); 500 ret = send_cmd(CMD_READ_SINGLE_BLOCK, addr, &response);
483 if (ret == 0) 501 if (ret == 0)
484 ret = receive_data(inbuf, SECTOR_SIZE, card->read_timeout); 502 ret = receive_data(inbuf, SECTOR_SIZE, card->read_timeout);
503 last_disk_activity = current_tick;
485 } 504 }
486 else 505 else
487 { 506 {
@@ -490,6 +509,7 @@ int ata_read_sectors(unsigned long start,
490 { 509 {
491 ret = receive_data(inbuf, SECTOR_SIZE, card->read_timeout); 510 ret = receive_data(inbuf, SECTOR_SIZE, card->read_timeout);
492 inbuf += SECTOR_SIZE; 511 inbuf += SECTOR_SIZE;
512 last_disk_activity = current_tick;
493 } 513 }
494 if (ret == 0) 514 if (ret == 0)
495 ret = send_cmd(CMD_STOP_TRANSMISSION, 0, &response); 515 ret = send_cmd(CMD_STOP_TRANSMISSION, 0, &response);
@@ -519,9 +539,6 @@ int ata_write_sectors(unsigned long start,
519 if (start == 0) 539 if (start == 0)
520 panicf("Writing on sector 0\n"); 540 panicf("Writing on sector 0\n");
521 541
522 if (count <= 0)
523 return ret;
524
525 addr = start * SECTOR_SIZE; 542 addr = start * SECTOR_SIZE;
526 543
527 mutex_lock(&mmc_mutex); 544 mutex_lock(&mmc_mutex);
@@ -535,6 +552,7 @@ int ata_write_sectors(unsigned long start,
535 if (ret == 0) 552 if (ret == 0)
536 ret = send_data(DT_START_BLOCK, buf, SECTOR_SIZE, 553 ret = send_data(DT_START_BLOCK, buf, SECTOR_SIZE,
537 card->write_timeout); 554 card->write_timeout);
555 last_disk_activity = current_tick;
538 } 556 }
539 else 557 else
540 { 558 {
@@ -544,12 +562,14 @@ int ata_write_sectors(unsigned long start,
544 ret = send_data(DT_START_WRITE_MULTIPLE, buf, SECTOR_SIZE, 562 ret = send_data(DT_START_WRITE_MULTIPLE, buf, SECTOR_SIZE,
545 card->write_timeout); 563 card->write_timeout);
546 buf += SECTOR_SIZE; 564 buf += SECTOR_SIZE;
565 last_disk_activity = current_tick;
547 } 566 }
548 if (ret == 0) 567 if (ret == 0)
549 { 568 {
550 response = DT_STOP_TRAN; 569 response = DT_STOP_TRAN;
551 write_transfer(&response, 1); 570 write_transfer(&response, 1);
552 poll_busy(card->write_timeout); 571 poll_busy(card->write_timeout);
572 last_disk_activity = current_tick;
553 } 573 }
554 } 574 }
555 } 575 }
diff --git a/firmware/export/ata_mmc.h b/firmware/export/ata_mmc.h
new file mode 100644
index 0000000000..afc0dc24af
--- /dev/null
+++ b/firmware/export/ata_mmc.h
@@ -0,0 +1,42 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Jens Arnold
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef __ATA_MMC_H__
20#define __ATA_MMC_H__
21
22typedef struct
23{
24 bool initialized;
25 unsigned char bitrate_register;
26 unsigned int read_timeout; /* n * 8 clock cycles */
27 unsigned int write_timeout; /* n * 8 clock cycles */
28
29 unsigned long ocr; /* OCR register */
30 unsigned long csd[4]; /* CSD register, 16 bytes */
31 unsigned long cid[4]; /* CID register, 16 bytes */
32 unsigned int speed; /* bit/s */
33 unsigned int nsac; /* clock cycles */
34 unsigned int tsac; /* n * 0.1 ns */
35 unsigned int r2w_factor;
36} tCardInfo;
37
38unsigned long mmc_extract_bits(const unsigned long *p, unsigned int start,
39 unsigned int size);
40tCardInfo *mmc_card_info(int card_no);
41
42#endif