mmc_spi.c
字号:
for (i = 0; (i < MMC_SPI_OP_COND_RETRIES) && ((0x00ff == reply) || (0 != (MMC_REPLY_IN_IDLE_STATE & reply))); i++) {
#ifdef CYGPKG_DEVS_DISK_MMC_SPI_IDLE_RETRIES_WAIT
CYGACC_CALL_IF_DELAY_US(CYGPKG_DEVS_DISK_MMC_SPI_IDLE_RETRIES_WAIT);
#endif
reply = mmc_spi_send_command(disk, MMC_REQUEST_SEND_OP_COND, 0);
}
if (MMC_REPLY_SUCCESS != reply) {
DEBUG1("mmc_spi_check_for_disk(): card has not entered operational state: reply code %02x\n", reply);
return (0x00FF == reply) ? -ENODEV : -EIO;
}
// The card has now generated sufficient responses that we don't need to
// worry about a missing card anymore.
// Get hold of the card's unique ID and store it, to allow disk changes
// to be detected.
code = mmc_spi_read_register(disk, MMC_REQUEST_SEND_CID, (cyg_uint8*) &(disk->mmc_id), 16);
if (code) {
mmc_spi_end_command(disk);
return code;
}
DEBUG2("CID data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", \
disk->mmc_id.cid_data[ 0], disk->mmc_id.cid_data[ 1], disk->mmc_id.cid_data[ 2], disk->mmc_id.cid_data[ 3], \
disk->mmc_id.cid_data[ 4], disk->mmc_id.cid_data[ 5], disk->mmc_id.cid_data[ 6], disk->mmc_id.cid_data[ 7], \
disk->mmc_id.cid_data[ 8], disk->mmc_id.cid_data[ 9], disk->mmc_id.cid_data[10], disk->mmc_id.cid_data[11], \
disk->mmc_id.cid_data[12], disk->mmc_id.cid_data[13], disk->mmc_id.cid_data[14], disk->mmc_id.cid_data[15]);
#if DEBUG > 0
DEBUG1("CID data: register\n");
DEBUG1(" : Manufacturer ID : MID = 0x%02x\n", MMC_CID_REGISTER_MID(&(disk->mmc_id)) & 0xff);
DEBUG1(" : OEM/Application ID : OID = 0x%04x\n", MMC_CID_REGISTER_OID(&(disk->mmc_id)) & 0xffff);
DEBUG1(" : Product name : PNM = 0x%02x%02x%02x%02x%02x%02x\n",
MMC_CID_REGISTER_PNM(&(disk->mmc_id))[0] & 0xff,
MMC_CID_REGISTER_PNM(&(disk->mmc_id))[1] & 0xff,
MMC_CID_REGISTER_PNM(&(disk->mmc_id))[2] & 0xff,
MMC_CID_REGISTER_PNM(&(disk->mmc_id))[3] & 0xff,
MMC_CID_REGISTER_PNM(&(disk->mmc_id))[4] & 0xff,
MMC_CID_REGISTER_PNM(&(disk->mmc_id))[5] & 0xff);
DEBUG1(" : Product revision : PRV = 0x%02x\n", MMC_CID_REGISTER_PRV(&(disk->mmc_id)) & 0xff);
DEBUG1(" : Product serial number : PSN = 0x%08x\n", MMC_CID_REGISTER_PSN(&(disk->mmc_id)) & 0xffffffff);
DEBUG1(" : Manufacturing date : MDT = 0x%02x\n", MMC_CID_REGISTER_MDT(&(disk->mmc_id)) & 0xff);
DEBUG1(" : 7-bit CRC checksum : CRC = 0x%02x\n", MMC_CID_REGISTER_CRC(&(disk->mmc_id)) & 0xff);
#endif
// And retrieve the card's configuration data.
code = mmc_spi_read_register(disk, MMC_REQUEST_SEND_CSD, (cyg_uint8*) &csd, 16);
if (code) {
mmc_spi_end_command(disk);
return code;
}
DEBUG2("CSD data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", \
csd.csd_data[ 0], csd.csd_data[ 1], csd.csd_data[ 2], csd.csd_data[3], \
csd.csd_data[ 4], csd.csd_data[ 5], csd.csd_data[ 6], csd.csd_data[7], \
csd.csd_data[ 8], csd.csd_data[ 9], csd.csd_data[10], csd.csd_data[11], \
csd.csd_data[12], csd.csd_data[13], csd.csd_data[14], csd.csd_data[15]);
// Optionally dump the whole CSD register. This takes a lot of
// code but gives a lot of info about the card. If the info looks
// correct then we really are interacting properly with an MMC card.
#if DEBUG > 0
DEBUG1("CSD data: structure 0x%02x, version 0x%02x\n", MMC_CSD_REGISTER_CSD_STRUCTURE(&csd), MMC_CSD_REGISTER_SPEC_VERS(&csd));
if (0 != MMC_CSD_REGISTER_FILE_FORMAT_GROUP(&csd)) {
DEBUG1(" : Reserved (unknown), FILE_FORMAT_GROUP %d, FILE_FORMAT %d\n", \
MMC_CSD_REGISTER_FILE_FORMAT_GROUP(&csd), MMC_CSD_REGISTER_FILE_FORMAT(&csd));
} else if (0 == MMC_CSD_REGISTER_FILE_FORMAT(&csd)) {
DEBUG1(" : Partioned disk, FILE_FORMAT_GROUP 0, FILE_FORMAT 0\n");
} else if (1 == MMC_CSD_REGISTER_FILE_FORMAT(&csd)) {
DEBUG1(" : FAT disk, FILE_FORMAT_GROUP 0, FILE_FORMAT 1\n");
} else if (2 == MMC_CSD_REGISTER_FILE_FORMAT(&csd)) {
DEBUG1(" : Universal File format, FILE_FORMAT_GROUP 0, FILE_FORMAT 2\n");
} else {
DEBUG1(" : Others/Unknown disk, FILE_FORMAT_GROUP 0, FILE_FORMAT 3\n");
}
{
static const cyg_uint32 mantissa_speeds_x10[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
static const cyg_uint32 exponent_speeds_div10[8] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
cyg_uint32 speed = mantissa_speeds_x10[MMC_CSD_REGISTER_TRAN_SPEED_MANTISSA(&csd)] *
exponent_speeds_div10[MMC_CSD_REGISTER_TRAN_SPEED_EXPONENT(&csd)];
speed /= 1000;
DEBUG1(" : TRAN_SPEED %d %d -> %d kbit/s\n", \
MMC_CSD_REGISTER_TRAN_SPEED_MANTISSA(&csd), MMC_CSD_REGISTER_TRAN_SPEED_EXPONENT(&csd), speed);
}
DEBUG1(" : READ_BL_LEN block length 2^%d (%d)\n", MMC_CSD_REGISTER_READ_BL_LEN(&csd), \
0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd));
DEBUG1(" : C_SIZE %d, C_SIZE_MULT %d\n", MMC_CSD_REGISTER_C_SIZE(&csd), MMC_CSD_REGISTER_C_SIZE_MULT(&csd));
{
cyg_uint32 block_len = 0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd);
cyg_uint32 mult = 0x01 << (MMC_CSD_REGISTER_C_SIZE_MULT(&csd) + 2);
cyg_uint32 size = block_len * mult * (MMC_CSD_REGISTER_C_SIZE(&csd) + 1);
cyg_uint32 sizeK = (cyg_uint32) (size / 1024);
cyg_uint32 sizeM = sizeK / 1024;
sizeK -= (sizeM * 1024);
DEBUG1(" : total card size %dM%dK\n", sizeM, sizeK);
}
DEBUG1(" : WR_BL_LEN block length 2^%d (%d)\n", \
MMC_CSD_REGISTER_WRITE_BL_LEN(&csd), 0x01 << MMC_CSD_REGISTER_WRITE_BL_LEN(&csd));
{
static cyg_uint32 taac_mantissa_speeds_x10[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
static cyg_uint32 taac_exponent_speeds_div10[8] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
cyg_uint32 taac_speed = taac_mantissa_speeds_x10[MMC_CSD_REGISTER_TAAC_MANTISSA(&csd)] *
taac_exponent_speeds_div10[MMC_CSD_REGISTER_TAAC_EXPONENT(&csd)];
taac_speed /= 100;
DEBUG1(" : asynchronous read access time TAAC %d %d -> %d ns\n", \
MMC_CSD_REGISTER_TAAC_MANTISSA(&csd), MMC_CSD_REGISTER_TAAC_EXPONENT(&csd), taac_speed);
}
DEBUG1(" : synchronous read access time NSAC %d * 100 cycles\n", \
MMC_CSD_REGISTER_NSAC(&csd));
DEBUG1(" : typical write program time %d * read time\n", MMC_CSD_REGISTER_R2W_FACTOR(&csd));
DEBUG1(" : CCC command classes 0x%04x\n", MMC_CSD_REGISTER_CCC(&csd));
DEBUG1(" : READ_BL_PARTIAL %d, WRITE_BLK_MISALIGN %d, READ_BLK_MISALIGN %d, DSR_IMP %d\n", \
MMC_CSD_REGISTER_READ_BL_PARTIAL(&csd), MMC_CSD_REGISTER_WRITE_BLK_MISALIGN(&csd), \
MMC_CSD_REGISTER_READ_BLK_MISALIGN(&csd), MMC_CSD_REGISTER_DSR_IMP(&csd));
DEBUG1(" : WR_BL_PARTIAL %d\n", MMC_CSD_REGISTER_WR_BL_PARTIAL(&csd));
{
static cyg_uint8 min_currents[8] = { 1, 1, 5, 10, 25, 35, 60, 100 };
static cyg_uint8 max_currents[8] = { 1, 5, 10, 25, 35, 45, 80, 200 };
DEBUG1(" : read current min %dmA, max %dmA\n", \
min_currents[MMC_CSD_REGISTER_VDD_R_CURR_MIN(&csd)], \
max_currents[MMC_CSD_REGISTER_VDD_R_CURR_MAX(&csd)]);
DEBUG1(" : write current min %dmA, max %dmA\n", \
min_currents[MMC_CSD_REGISTER_VDD_W_CURR_MIN(&csd)], \
max_currents[MMC_CSD_REGISTER_VDD_W_CURR_MAX(&csd)]);
}
DEBUG1(" : erase sector size %d, erase group size %d\n", \
MMC_CSD_REGISTER_SECTOR_SIZE(&csd) + 1, MMC_CSD_REGISTER_ERASE_GRP_SIZE(&csd) + 1);
DEBUG1(" : write group enable %d, write group size %d\n", \
MMC_CSD_REGISTER_WR_GRP_ENABLE(&csd), MMC_CSD_REGISTER_WR_GRP_SIZE(&csd) + 1);
DEBUG1(" : copy bit %d\n", MMC_CSD_REGISTER_COPY(&csd));
DEBUG1(" : permanent write protect %d, temporary write protect %d\n", \
MMC_CSD_REGISTER_PERM_WRITE_PROTECT(&csd), MMC_CSD_REGISTER_TMP_WRITE_PROTECT(&csd));
DEBUG1(" : ecc %d, default ecc %d\n", MMC_CSD_REGISTER_ECC(&csd), MMC_CSD_REGISTER_DEFAULT_ECC(&csd));
DEBUG1(" : crc 0x%08x\n", MMC_CSD_REGISTER_CRC(&csd));
#endif
// There is information available about the file format, e.g.
// partitioned vs. simple FAT. With the current version of the
// generic disk code this needs to be known statically, via
// the mbr field of the disk channel structure. If the card
// is inappropriately formatted, reject the mount request.
if ((0 != MMC_CSD_REGISTER_FILE_FORMAT_GROUP(&csd)) ||
(0 != MMC_CSD_REGISTER_FILE_FORMAT(&csd))) {
return -ENOTDIR;
}
// Look for a write-protect bit (permanent or temporary), and set
// the disk as read-only or read-write as appropriate. The
// temporary write-protect could be cleared by rewriting the CSD
// register (including recalculating the CRC) but the effort
// involves does not seem worth-while.
if ((0 != MMC_CSD_REGISTER_PERM_WRITE_PROTECT(&csd)) || (0 != MMC_CSD_REGISTER_TMP_WRITE_PROTECT(&csd))) {
disk->mmc_read_only = true;
} else {
disk->mmc_read_only = false;
}
DEBUG1("Disk read-only flag %d\n", disk->mmc_read_only);
// Calculate the disk size, primarily for assertion purposes.
// By design MMC cards are limited to 4GB, which still doesn't
// quite fit into 32 bits.
disk->mmc_block_count = (((cyg_uint64)(0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd))) *
((cyg_uint64)(0x01 << (MMC_CSD_REGISTER_C_SIZE_MULT(&csd) + 2))) *
((cyg_uint64)(MMC_CSD_REGISTER_C_SIZE(&csd) + 1))) / (cyg_uint64)MMC_SPI_BLOCK_SIZE;
DEBUG1("Disk blockcount %d (0x%08x)\n", disk->mmc_block_count, disk->mmc_block_count);
// Assume for now that the block length is 512 bytes. This is
// probably a safe assumption since we have just got the card
// initialized out of idle state. If it ever proves to be a problem
// the SET_BLOCK_LEN command can be used.
// Nevertheless store the underlying block sizes
disk->mmc_read_block_length = 0x01 << MMC_CSD_REGISTER_READ_BL_LEN(&csd);
disk->mmc_write_block_length = 0x01 << MMC_CSD_REGISTER_WRITE_BL_LEN(&csd);
// The CSD contains the maximum supported transfer speed. Adjust
// the SPI device to match, saving the old value for an unmount
// operation. It is assumed that the SPI bus driver will munge
// the supplied speed to something appropriate.
{
static const cyg_uint32 mantissa_speeds_x10[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
static const cyg_uint32 exponent_speeds_div10[8] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
cyg_uint32 speed, len;
len = sizeof(cyg_uint32);
if (cyg_spi_get_config(dev, CYG_IO_GET_CONFIG_SPI_CLOCKRATE, (void*) &disk->mmc_saved_baudrate, &len)) {
DEBUG1("Failed to retrieve current SPI device clockrate\n");
return -EIO;
}
speed = mantissa_speeds_x10[MMC_CSD_REGISTER_TRAN_SPEED_MANTISSA(&csd)] * exponent_speeds_div10[MMC_CSD_REGISTER_TRAN_SPEED_EXPONENT(&csd)];
if (speed > disk->mmc_saved_baudrate) {
DEBUG1("Old SPI speed %d, switching to %d\n", disk->mmc_saved_baudrate, speed);
cyg_spi_set_config(dev, CYG_IO_SET_CONFIG_SPI_CLOCKRATE, (void*) &speed, &len);
} else {
DEBUG1("Old SPI speed %d already greater than max speed %d, leaving it alone\n",
disk->mmc_saved_baudrate, speed);
}
}
// Read the partition table off the card. This is a way of
// checking that the card is not password-locked. It also
// provides information about the "disk geometry" which is
// needed by higher-level code.
// FIXME: the higher-level code should be made to use LBA
// addressing instead.
{
cyg_uint8 data[MMC_SPI_BLOCK_SIZE];
cyg_uint8* partition;
cyg_uint32 lba_first, lba_size, lba_end, head, cylinder, sector;
code = mmc_spi_read_disk_block(disk, data, 0, true);
if (code) {
mmc_spi_restore_baud(disk);
return code;
}
#if DEBUG > 1
{
cyg_uint8 *ptr_data;
DEBUG2("MBR dump\n");
for (i = 0; i < MMC_SPI_BLOCK_SIZE; i += 16) {
ptr_data = &data[i];
DEBUG2(" %04x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
i,
ptr_data[ 0], ptr_data[ 1], ptr_data[ 2], ptr_data[ 3],
ptr_data[ 4], ptr_data[ 5], ptr_data[ 6], ptr_data[ 7],
ptr_data[ 8], ptr_data[ 9], ptr_data[10], ptr_data[11],
ptr_data[12], ptr_data[13], ptr_data[14], ptr_data[15]);
}
}
#endif
#if DEBUG > 0
DEBUG1("Read block 0 (partition table)\n");
DEBUG1("Signature 0x%02x 0x%02x, should be 0x55 0xaa\n", data[0x1fe], data[0x1ff]);
// There should be four 16-byte partition table entries at offsets
// 0x1be, 0x1ce, 0x1de and 0x1ee. The numbers are stored little-endian
for (i = 0; i < 4; i++) {
partition = &(data[0x1be + (0x10 * i)]);
DEBUG1("Partition %d: boot %02x, first sector %02x %02x %02x, file system %02x, last sector %02x %02x %02x\n", i, \
partition[0], partition[1], partition[2], partition[3], partition[4], \
partition[5], partition[6], partition[7]);
DEBUG1(" : first sector (linear) %02x %02x %02x %02x, sector count %02x %02x %02x %02x\n", \
partition[11], partition[10], partition[9], partition[8], \
partition[15], partition[14], partition[13], partition[12]);
}
#endif
if ((0x0055 != data[0x1fe]) || (0x00aa != data[0x1ff])) {
mmc_spi_restore_baud(disk);
return -ENOTDIR;
}
partition = &(data[0x1be]);
lba_first = (partition[11] << 24) | (partition[10] << 16) | (partition[9] << 8) | partition[8];
lba_size = (partition[15] << 24) | (partition[14] << 16) | (partition[13] << 8) | partition[12];
lba_end = lba_first + lba_size - 1;
// First sector in c/h/s format
cylinder = ((partition[2] & 0xC0) << 2) | partition[3];
head = partition[1];
sector = partition[2] & 0x3F;
// lba_start == (((cylinder * Nh) + head) * Ns) + sector - 1, where (Nh == heads/cylinder) and (Ns == sectors/head)
// Strictly speaking we should be solving some simultaneous
// equations here for lba_start/lba_end, but that gets messy.
// The first partition is at the start of the card so cylinder will be 0,
// and we can ignore Nh.
CYG_ASSERT(0 == cylinder, "Driver assumption - partition 0 is at start of card\n");
CYG_ASSERT(0 != head, "Driver assumption - partition table is sensible\n");
disk->mmc_sectors_per_head = ((lba_first + 1) - sector) / head;
// Now for lba_end.
cylinder = ((partition[6] & 0xC0) << 2) | partition[7];
head = partition[5];
sector = partition[6] & 0x3F;
disk->mmc_heads_per_cylinder = ((((lba_end + 1) - sector) / disk->mmc_sectors_per_head) - head) / cylinder;
}
return ENOERR;
}
// Check that the current card is the one that was previously
// accessed. This may fail if the card has been removed and the
// slot is empty, or if the card has been removed and a different
// one inserted. It may pass incorrectly if a card is removed,
// modified elsewhere, and reinserted without eCos noticing.
// There is no way around that without some way of detecting
// disk removal in hardware.
//
// Re-reading the cid may actually be overkill. If a new card
// has been plugged in then it will not have been initialized so
// it will respond with 0xff anyway. It is very unlikely that
// an init sequence will have happened by accident.
static cyg_bool
mmc_spi_disk_changed(cyg_mmc_spi_disk_info_t* disk)
{
mmc_cid_register cid;
Cyg_ErrNo code;
code = mmc_spi_read_register(disk, MMC_REQUEST_SEND_CID, (cyg_uint8*) &cid, 16);
if (-ENODEV == code) {
return true;
}
if (0 != memcmp(&cid, &(disk->mmc_id), sizeof(mmc_cid_register))) {
return true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -