欢迎来到虫虫下载站 | 资源下载 资源专辑 关于我们
虫虫下载站

mmc_spi.c

开放源码实时操作系统源码.
C
第 1 页 / 共 4 页
字号:
    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 + -