mmc_spi.c
字号:
//==========================================================================
//
// mmc_spi.c
//
// Provide a disk device driver for MMC cards over SPI
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2004, 2006 eCosCentric Limited
//
// eCos 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 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author: bartv
// Date: 2004-04-25
//
//####DESCRIPTIONEND####
//==========================================================================
#include <pkgconf/system.h>
#include <pkgconf/devs_disk_mmc.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_if.h> // delays
#include <cyg/hal/hal_intr.h>
#include <string.h>
#include <errno.h>
#include <cyg/io/io.h>
#include <cyg/io/spi.h>
#include <cyg/io/devtab.h>
#include <cyg/io/disk.h>
#include <cyg/io/mmc_protocol.h>
// Communication parameters. First some debug support
#define DEBUG 0
#if DEBUG > 0
# define DEBUG1(format, ...) diag_printf(format, ## __VA_ARGS__)
#else
# define DEBUG1(format, ...)
#endif
#if DEBUG > 1
# define DEBUG2(format, ...) diag_printf(format, ## __VA_ARGS__)
#else
# define DEBUG2(format, ...)
#endif
// Should the SPI operations run in polled or interrupt-driven mode?
// The default value is determined by CDL, but can be overridden at
// run-time if necessary. For example if configured for
// interrupt-driven I/O then it will be impossible to perform disk
// operations during system initialization, e.g. from a static
// constructor, unless this flag is changed for the duration.
#ifdef CYGIMP_DEVS_DISK_MMC_SPI_POLLED
cyg_bool cyg_mmc_spi_polled = true;
#else
cyg_bool cyg_mmc_spi_polled = false;
#endif
// Should write operations be allowed to complete in the background,
// or must the operation complete in the foreground. The latter
// requires polling for potentially a long time, up to some 100's of
// milliseconds, but the former appears unreliable if there are other
// devices on the SPI bus. In theory the MMC card should detect that
// the chip select line is dropped and tristate the output line, but
// in practice this does not always happen.
#undef MMC_SPI_BACKGROUND_WRITES
// The size of each disk block
#define MMC_SPI_BLOCK_SIZE 512
// The number of retries during a mount operation when switching to
// IDLE mode.
#define MMC_SPI_GO_IDLE_RETRIES 16
// The number of retries during a mount operation when switching from
// idle to operational
#define MMC_SPI_OP_COND_RETRIES 128
// The number of retries when waiting for a response to any command
#define MMC_SPI_COMMAND_RETRIES 32
// Retries when waiting for a data response token during a read
#define MMC_SPI_READ_DATA_TOKEN_RETRIES 32768
// Retries during a write while waiting for completion
#define MMC_SPI_WRITE_BUSY_RETRIES 32768
// ----------------------------------------------------------------------------
// SPI-specific parts of the MMC protocol.
//
// The main response byte. 0 indicates success, other bits
// indicate various error conditions.
#define MMC_REPLY_SUCCESS 0x00
#define MMC_REPLY_PARAMETER_ERROR (0x01 << 6)
#define MMC_REPLY_ADDRESS_ERROR (0x01 << 5)
#define MMC_REPLY_ERASE_SEQUENCE_ERROR (0x01 << 4)
#define MMC_REPLY_COM_CRC_ERROR (0x01 << 3)
#define MMC_REPLY_ILLEGAL_COMMAND (0x01 << 2)
#define MMC_REPLY_ERASE_RESET (0x01 << 1)
#define MMC_REPLY_IN_IDLE_STATE (0x01 << 0)
// A send-status command generates a second response byte
#define MMC_REPLY2_OUT_OF_RANGE (0x01 << 7)
#define MMC_REPLY2_ERASE_PARAM (0x01 << 6)
#define MMC_REPLY2_WP_VIOLATION (0x01 << 5)
#define MMC_REPLY2_CARD_ECC_FAILED (0x01 << 4)
#define MMC_REPLY2_CC_ERROR (0x01 << 3)
#define MMC_REPLY2_ERROR (0x01 << 2)
#define MMC_REPLY2_WP_ERASE_SKIP (0x01 << 1)
// Alias for the above
#define MMC_REPLY2_LOCK_UNLOCK_FAILED (0x01 << 1)
#define MMC_REPLY2_CARD_LOCKED (0x01 << 0)
// The data error token byte which may get sent if a read
// operation fails. The top 3 bits will be 0. A successful
// response will have these bits 1.
#define MMC_DATA_TOKEN_SUCCESS (0x00FE)
#define MMC_DATA_ERROR_TOKEN_CARD_LOCKED (0x01 << 4)
#define MMC_DATA_ERROR_TOKEN_OUT_OF_RANGE (0x01 << 3)
#define MMC_DATA_ERROR_TOKEN_CARD_ECC_FAILED (0x01 << 2)
#define MMC_DATA_ERROR_TOKEN_CC_ERROR (0x01 << 1)
#define MMC_DATA_ERROR_TOKEN_ERROR (0x01 << 0)
// ----------------------------------------------------------------------------
// Structures and statics.
//
// There should be an SPI device cyg_spi_mmc_dev0, probably provided by
// the HAL, possibly by the application. Because of the latter we cannot
// assume the variable will be defined in a header.
extern cyg_spi_device cyg_spi_mmc_dev0;
// When retrieving data it is necessary to send an 0xff byte stream,
// which the card will not confuse with further commands. The largest
// transfer is 512 bytes, too large a buffer to place on the stack.
static cyg_uint8 mmc_spi_ff_data[512];
#define MMC_SPI_INIT_FF_DATA() \
CYG_MACRO_START \
memset(mmc_spi_ff_data, 0x00FF, 512); \
CYG_MACRO_END
// Details of a specific MMC card
typedef struct cyg_mmc_spi_disk_info_t {
cyg_spi_device* mmc_spi_dev;
cyg_uint32 mmc_saved_baudrate;
cyg_uint32 mmc_block_count;
#ifdef MMC_SPI_BACKGROUND_WRITES
cyg_bool mmc_writing;
#endif
cyg_bool mmc_read_only;
cyg_bool mmc_connected;
cyg_uint32 mmc_heads_per_cylinder;
cyg_uint32 mmc_sectors_per_head;
cyg_uint32 mmc_read_block_length;
cyg_uint32 mmc_write_block_length;
mmc_cid_register mmc_id;
} cyg_mmc_spi_disk_info_t;
// There is no need for a hardware-specific disk controller structure.
// The closest equivalent is probably an SPI bus, i.e. if there were
// MMC connectors attached to different SPI buses then these would
// have separate controllers with independent locking. However that
// can be handled without a cyg_mmc_spi_controller_info_t structure.
// ----------------------------------------------------------------------------
// The low-level MMC operations
// After power-on an MMC card is in idle state and needs at least 74
// clock cycles before any communication. These might be supplied
// courtesy of another SPI device, but no guarantees, so generate some
// ticks.
static void
mmc_spi_send_init(cyg_mmc_spi_disk_info_t* disk)
{
cyg_spi_bus *bus;
cyg_spi_device *dev;
DEBUG2("mmc_spi_send_init(): dev pointer 0x%p, %d\n", disk->mmc_spi_dev, cyg_mmc_spi_polled );
dev = disk->mmc_spi_dev;
bus = dev->spi_bus;
DEBUG2(" : begin pointer %p\n", bus->spi_transaction_begin );
cyg_spi_tick(disk->mmc_spi_dev, cyg_mmc_spi_polled, 10);
}
// Send the first part of a command sequence. This consists of the
// command itself, an argument, a CRC, and then waiting for a
// reply byte from the card.
static cyg_uint32
mmc_spi_send_command_start(cyg_mmc_spi_disk_info_t* disk, cyg_uint32 command, cyg_uint32 arg)
{
cyg_spi_device* dev = disk->mmc_spi_dev;
cyg_uint8 request[7];
cyg_uint8 response[7];
cyg_uint8 reply;
int i;
#ifdef MMC_SPI_BACKGROUND_WRITES
// If the last operation was a block write, those can take a while
// to complete. Rather than wait at the end of the write(), do so
// at the beginning of the next operation i.e. here. This also
// allows the chip select to be dropped while the write comples,
// so communication is possible with other devices. The polling is
// done as a sequence of transactions rather than in a single
// transaction, again to let other threads in to communicate with
// other devices.
//
// The card will send a stream of 0x00 bytes while the write
// completes. Some cards have been observed to send a byte 0x03 at
// the end, Either way, when the card sends a byte 0xff it should
// be ready for the next command.
if (disk->mmc_writing) {
DEBUG2("mmc_spi_send_command_start(): polling for completion of previous write\n");
disk->mmc_writing = 0;
response[0] = 0x00;
for (i = 0; (i < MMC_SPI_WRITE_BUSY_RETRIES) && (0x00FF != response[0]); i++) {
cyg_spi_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, response);
}
}
#endif
request[0] = command | 0x0040;
request[1] = (arg >> 24) & 0x00FF;
request[2] = (arg >> 16) & 0x00FF;
request[3] = (arg >> 8) & 0x00FF;
request[4] = arg & 0x00FF;
// A CRC is needed for the go-idle-state command, because that
// command switches the device from MMC to SPI mode. That CRC is
// well-known. Once in SPI mode the card will not use CRCs by
// default.
request[5] = (command == 0x00) ? 0x0095 : 0x00ff;
// There will need to be at least one extra byte transfer to get
// the card's response, so send that straightaway. Extra
// outgoing data like this should be 0xff so that the card
// does not confuse it with a new incoming command.
request[6] = 0x00ff;
// Lock the SPI bus. It remains locked until a subsequent call to
// mmc_spi_end_command().
cyg_spi_transaction_begin(dev);
// Transfer the whole command, and try to read the response back
// immediately.
cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 7, request, response, 0);
DEBUG2("Sent command %02x %d: reply bytes %02x %02x %02x %02x %02x %02x %02x\n", command, arg, \
response[0], response[1], response[2], response[3], response[4], response[5], response[6]);
// The response will be a single byte with the top bit clear.
// The remaining bits are error/status flags. If the command
// involves an additional response then that will be handled
// by the calling code.
reply = response[6];
for (i = 0; (i < MMC_SPI_COMMAND_RETRIES) && (0 != (reply & 0x0080)); i++) {
cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, response, 0);
reply = response[0];
DEBUG2(" loop %d, additional reply %02x\n", i, reply);
}
// Leave the interpretation of the reply code to the caller
return (cyg_uint32) reply;
}
// At the end of each command the card needs eight clocks to finish
// its processing. A tick() call takes care of that, and will have
// the side effect of dropping the chip select. Ending the transaction
// unlocks the bus for other SPI I/O operations
static void
mmc_spi_end_command(cyg_mmc_spi_disk_info_t* disk)
{
cyg_spi_device* dev = disk->mmc_spi_dev;
cyg_spi_transaction_tick(dev, cyg_mmc_spi_polled, 1);
cyg_spi_transaction_end(dev);
}
// A utility combination of the above two for simple commands which do
// not involve any other data.
static cyg_uint32
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -