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

mmc_spi.c

开放源码实时操作系统源码.
C
第 1 页 / 共 4 页
字号:
mmc_spi_send_command(cyg_mmc_spi_disk_info_t* disk, cyg_uint32 command, cyg_uint32 arg)
{
    cyg_uint32  reply;
    reply = mmc_spi_send_command_start(disk, command, arg);
    mmc_spi_end_command(disk);
    return reply;
}

// The card will return a data block when reading a disk block, or
// for certain other commands like reading card registers. Each
// data block consists of:
//   1) some number of padding bytes 0xff while the card is still
//      processing the command and preparing the data
//   2) a data token byte, usually 0xFE for success
//   3) n bytes of data
//   4) two bytes of crc, which can be ignored.
//
// The data token byte is the only indication of success or failure,
// so that gets returned.
//
// When mounting certain types of card an extra delay may be needed
// before reading the first data block. This is handled by the
// extra_delay argument.
static cyg_uint32
mmc_spi_read_data(cyg_mmc_spi_disk_info_t* disk, cyg_uint8* buf, cyg_uint32 count, cyg_bool extra_delay)
{
    cyg_spi_device* dev = disk->mmc_spi_dev;
    int             i;
    cyg_uint8       response[2];
    cyg_uint32      retries;

    if (extra_delay) {
        retries = MMC_SPI_READ_DATA_TOKEN_RETRIES * 100;
    } else {
        retries = MMC_SPI_READ_DATA_TOKEN_RETRIES;
    }

    response[0] = 0x00FF;
    for (i = 0; (i < retries) && (0x00FF == response[0]); i++) {
        cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, response, 0);
    }
    
    if (MMC_DATA_TOKEN_SUCCESS != response[0]) {
        DEBUG1("mmc_spi_read_data(): got error response %02x after %d iterations\n", response[0], i);
        return response[0];
    }

    // Now for the actual data. There is no way of detecting a failure from
    // this point on.
    cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, count, mmc_spi_ff_data, buf, 0);
    // And the CRC, which can be ignored
    cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 2, mmc_spi_ff_data, response, 0);
    DEBUG2("mmc_spi_read_data(): got data and CRC %02x %02x\n", response[0], response[1]);
    
    return MMC_DATA_TOKEN_SUCCESS;
}

// Read one of the card registers, e.g. CSD or CID
static Cyg_ErrNo
mmc_spi_read_register(cyg_mmc_spi_disk_info_t* disk, cyg_uint32 command, cyg_uint8* buf, cyg_uint32 count)
{
    cyg_uint32      reply;
    
    reply = mmc_spi_send_command_start(disk, command, 0);
    if (MMC_REPLY_SUCCESS != reply) {
        DEBUG1("mmc_spi_read_register(): unexpected response to command %02x, reply code %02x\n", command, reply);
        mmc_spi_end_command(disk);
        return (0x00FF == reply) ? -ENODEV : -EIO;
    }
    reply = mmc_spi_read_data(disk, buf, count, false);
    mmc_spi_end_command(disk);
    if (MMC_DATA_TOKEN_SUCCESS != reply) {
        DEBUG1("mmc_spi_read_register(): unexpected response to command %02x, expected 0x00FE data token, got %02x\n", command, reply);
        return -EIO;
    }
    return ENOERR;
}

// Reading a disk block is just a combination of the above utilities.
// This code is also responsible for translating error codes, since
// higher-level code does not get to see the initial response vs. the
// data token byte.
static Cyg_ErrNo
mmc_spi_read_disk_block(cyg_mmc_spi_disk_info_t* disk, cyg_uint8* buf, cyg_uint32 block, cyg_bool extra_delay)
{
    cyg_uint32 reply;
    
    // First the command itself.
    DEBUG2("mmc_spi_read_disk_block(%d): sending command\n", block);
    reply = mmc_spi_send_command_start(disk, MMC_REQUEST_READ_SINGLE_BLOCK, block * MMC_SPI_BLOCK_SIZE);
    if (MMC_REPLY_SUCCESS != reply) {
        DEBUG1("mmc_spi_read_disk_block(%d): unexpected response to READ_SINGLE_BLOCK command, code %02x\n", block, reply);
        mmc_spi_end_command(disk);
        // A byte 0xFF indicates the card has been removed.
        if (0x00FF == reply) {
            return -ENODEV;
        }
        // Parameter or address error should not occur, higher-level
        // code should have checked the block to ensure that it is
        // in range.
        if (0 != (reply & (MMC_REPLY_PARAMETER_ERROR | MMC_REPLY_ADDRESS_ERROR))) {
            return -EINVAL;
        }
        // The disk should not be in idle state or in an erase sequence. The
        // command is definitely legal and CRCs should be disabled. So everything
        // else is an I/O error.
        return -EIO;
    }
    
    // Now read back the data block. That code can be shared with other read
    // operations, e.g. for retrieving registers.
    DEBUG2("mmc_spi_read_disk_block(%d): reading data token/data/crc\n", block);
    reply = mmc_spi_read_data(disk, buf, MMC_SPI_BLOCK_SIZE, extra_delay);
    mmc_spi_end_command(disk);
    if (MMC_DATA_TOKEN_SUCCESS != reply) {
        DEBUG1("mmc_spi_read_disk_block(%d): failed to retrieve data, error token %02x\n", block, reply);
        
        // Possibilities are password-locked, range error, ECC failure
        // if the raw data is corrupt, CC error for an internal card
        // error, or some other error. A byte 0xFF indicates the card
        // has been removed.
        if (0x00FF == reply) {
            return -ENODEV;
        } else if (0 != (MMC_DATA_ERROR_TOKEN_CARD_LOCKED & reply)) {
            // This should have been caught by a mount operation.
            return -EPERM;
        } else if (0 != (MMC_DATA_ERROR_TOKEN_OUT_OF_RANGE & reply)) {
            return -EINVAL;
        } else {
            return -EIO;
        }
    }
    return ENOERR;
}

// Writing a block involves a bit more work. Some of this could be
// moved into a utility routine if necessary, shared with code for
// e.g. updating the CSD register, but for now that other functionality
// is not needed.
static Cyg_ErrNo
mmc_spi_write_disk_block(cyg_mmc_spi_disk_info_t* disk, const cyg_uint8* buf, cyg_uint32 block)
{
    cyg_spi_device* dev = disk->mmc_spi_dev;
    cyg_uint32      reply;
    cyg_uint8       extra[4];
    int             i;
   
    // First, send the command itself and get the initial response
    DEBUG2("mmc_spi_write_disk_block(), sending command\n");
    reply = mmc_spi_send_command_start(disk, MMC_REQUEST_WRITE_BLOCK, block * MMC_SPI_BLOCK_SIZE);
    if (MMC_REPLY_SUCCESS != reply) {
        DEBUG1("mmc_spi_write_disk_block(): unexpected response to WRITE_BLOCK command, code %02x\n", reply);
        mmc_spi_end_command(disk);
        if (0x00FF == reply) {
            return -ENODEV;
        }
        // Parameter or address error should not occur, higher-level
        // code should have checked the block to ensure that it is
        // in range.
        if (0 != (reply & (MMC_REPLY_PARAMETER_ERROR | MMC_REPLY_ADDRESS_ERROR))) {
            return -EINVAL;
        }
        // The disk should not be in idle state or in an erase sequence. The
        // command is definitely legal and CRCs should be disabled. So everything
        // else is an I/O error.
        return -EIO;
    }

    // The card is now expecting a data block. This consists of a single byte
    // 0x00FE, then the data itself, and a dummy CRC. The reply from the card
    // does not contain any useful information.
    DEBUG2("mmc_spi_write_disk_block(): sending data token/data/crc\n");
    extra[0]    = 0x00FE;
    cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, extra, (cyg_uint8*)0, 0);
    cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, MMC_SPI_BLOCK_SIZE, buf, (cyg_uint8*)0, 0);
    cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 2, mmc_spi_ff_data, (cyg_uint8*)0, 0);

    // The card should respond immediately with a data response token.
    cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, extra, 0);
    DEBUG2("mmc_spi_write_disk_block(): got data response token %02x\n", extra[0]);

    // The bottom five bits contain the response. 00101 indicates success,
    // anything else is a CRC error. Everything else will have been checked
    // before the data got transferred.
    if (0x05 != (extra[0] & 0x1F)) {
        DEBUG1("mmc_spi_write_disk_block(): invalid data response token %02x\n", extra[0]);
        mmc_spi_end_command(disk);
        if (0x00FF == extra[0]) {
            return -ENODEV;
        }
        return -EIO;
    }

#ifdef MMC_SPI_BACKGROUND_WRITES    
    // Mark the card as writing. The next operation will poll for completion.
    disk->mmc_writing   = true;
#else
    // The card is now busy doing the write and will output a stream of 0's
    // while busy. The timeout should really be calculated using the CSD
    // register settings.
    //
    // It should be legal to drop the chip select here, i.e. to end
    // the current transaction and start a new one for each poll
    // operation. That would allow other SPI devices to be accessed.
    // However it appears that this does not work with all MMC cards.
    extra[0]    = 0x00;
    for (i = 0; (i < MMC_SPI_WRITE_BUSY_RETRIES) && (0x00 == extra[0]); i++) {
        cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, extra, 0);
        DEBUG2("mmc_spi_write_disk_block(), polling for ! busy, got response %02x\n", extra[0]);
    }
#endif
    
    // Assume that the loop did in fact terminate.
    mmc_spi_end_command(disk);
    return ENOERR;
}

// MMC sockets will default to a slow clockrate. During a successful mount
// the SPI device settings will be changed to the fastest supported by the
// card, as per the CSD register. This will need to be undone during an
// unmount, or if the final stages of a mount are unsuccessful.
static void
mmc_spi_restore_baud(cyg_mmc_spi_disk_info_t* disk)
{
    cyg_uint32  len = sizeof(cyg_uint32);
    (void) cyg_spi_set_config(disk->mmc_spi_dev, CYG_IO_SET_CONFIG_SPI_CLOCKRATE, (void*) &(disk->mmc_saved_baudrate), &len);
}

// check_for_disk() tries to communicate with an MMC card that is not
// currently mounted. It performs the appropriate initialization so
// that read and write operations are possible, checks the disk format,
// distinguishes between read-only and read-write cards, calculates the
// card size, stores the unique id, etc.
//
// The main error conditions are ENODEV (no card), EIO (card not
// responding sensibly to requests), ENOTDIR (wrong format), or EPERM
// (card is password-locked).
static Cyg_ErrNo
mmc_spi_check_for_disk(cyg_mmc_spi_disk_info_t* disk)
{
    cyg_spi_device*     dev = disk->mmc_spi_dev;
    int                 i;
    cyg_uint32          reply;
    Cyg_ErrNo           code;
    mmc_csd_register    csd;

#ifdef MMC_SPI_BACKGROUND_WRITES    
    // If we have unmounted a disk and are remounting it, assume that
    // any writes have completed.
    disk->mmc_writing   = false;
#endif    
    reply               = 0x00ff;
    
    for (i = 0; (i < MMC_SPI_GO_IDLE_RETRIES) && (0x01 != reply); i++) {
        // Allow platform HALs to provide additional initialization,
        // if the hardware needs it.
#ifdef HAL_MMC_SPI_INIT
        HAL_MMC_SPI_INIT(dev, reply);
        if (! reply) {
            return -ENODEV;
        }
#endif
        // MMC cards generic initialization. The card may have just
        // been plugged in so there is no guarantee that any previous
        // init() calls or other traffic will have affected this card.
        mmc_spi_send_init(disk);
    
        // Now set the card to idle state. This involves the GO_IDLE_STATE
        // command which will be accepted irrespective of whether the card is
        // currently in MMC or SPI mode, and will leave the card in SPI mode.
        reply = mmc_spi_send_command(disk, MMC_REQUEST_GO_IDLE_STATE, 0);

        // The card should reply with 0x01. FF suggests that there is
        // no card. Any other response indicates some synchronization
        // problem. For example the card might still be responding to
        // some request from a previous session which aborted at an
        // inconvenient moment. Some dummy traffic is generated in the
        // hope that this gets things back in sync.
        if (0x01 != reply) {
            DEBUG1("mmc_spi_check_for_disk(): loop %d, card did not enter idle state, code %02x\n", i, reply);
            if (0x0ff != reply) {
                cyg_spi_transfer(dev, cyg_mmc_spi_polled, 128, mmc_spi_ff_data, (cyg_uint8*) 0);
            }
        }
    }
    if (0x0ff == reply) {
        DEBUG1("mmc_spi_check_for_disk(): unable to get a response from the MMC card: code %02x\n", reply);
        // A working card should be returning some data
        return -ENODEV;
    }
    if (0x01 != reply) {
        DEBUG1("mmc_spi_check_for_disk(): card did not enter idle state, code %02x\n", reply);
        return -EIO;
    }
    
    // Next, wait for the card to initialize. This involves repeatedly
    // trying the SEND_OP_COND command until we get a reply that is
    // not idle
    reply = 0x00ff;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -