From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- memories/MEDDdram.c | 290 +++ memories/MEDDdram.h | 48 + memories/MEDFlash.c | 281 +++ memories/MEDFlash.h | 83 + memories/MEDNandFlash.c | 510 ++++ memories/MEDNandFlash.h | 72 + memories/MEDRamDisk.c | 284 +++ memories/MEDRamDisk.h | 48 + memories/MEDSdcard.c | 694 +++++ memories/MEDSdcard.h | 50 + memories/MEDSdmmc.c | 481 ++++ memories/MEDSdmmc.h | 52 + memories/MEDSdram.c | 272 ++ memories/MEDSdram.h | 48 + memories/Media.c | 57 + memories/Media.h | 284 +++ memories/flash/flashd.dir | 37 + memories/flash/flashd.h | 128 + memories/flash/flashd_eefc.c | 494 ++++ memories/flash/flashd_efc.c | 598 +++++ memories/memories.dir | 37 + memories/nandflash/EccNandFlash.c | 328 +++ memories/nandflash/EccNandFlash.h | 101 + memories/nandflash/ManagedNandFlash.c | 862 +++++++ memories/nandflash/ManagedNandFlash.h | 154 ++ memories/nandflash/MappedNandFlash.c | 661 +++++ memories/nandflash/MappedNandFlash.h | 116 + memories/nandflash/NandCommon.h | 134 + memories/nandflash/NandFlashModel.c | 403 +++ memories/nandflash/NandFlashModel.h | 169 ++ memories/nandflash/NandFlashModelList.c | 118 + memories/nandflash/NandFlashModelList.h | 65 + memories/nandflash/NandSpareScheme.c | 230 ++ memories/nandflash/NandSpareScheme.h | 124 + memories/nandflash/NfcRawNandFlash.c | 871 +++++++ memories/nandflash/RawNandFlash.c | 792 ++++++ memories/nandflash/RawNandFlash.h | 139 + memories/nandflash/SkipBlockNandFlash.c | 444 ++++ memories/nandflash/SkipBlockNandFlash.h | 134 + memories/nandflash/TranslatedNandFlash.c | 587 +++++ memories/nandflash/TranslatedNandFlash.h | 108 + memories/nandflash/nandflash.dir | 44 + memories/norflash/NorFlashAmd.c | 435 ++++ memories/norflash/NorFlashAmd.h | 98 + memories/norflash/NorFlashApi.c | 143 ++ memories/norflash/NorFlashApi.h | 137 + memories/norflash/NorFlashCFI.c | 378 +++ memories/norflash/NorFlashCFI.h | 230 ++ memories/norflash/NorFlashCommon.c | 142 ++ memories/norflash/NorFlashCommon.h | 101 + memories/norflash/NorFlashIntel.c | 416 +++ memories/norflash/NorFlashIntel.h | 99 + memories/norflash/norflash.dir | 66 + memories/sdmmc/sdmmc.dir | 36 + memories/sdmmc/sdmmc_mci.c | 4048 ++++++++++++++++++++++++++++++ memories/sdmmc/sdmmc_mci.h | 1038 ++++++++ memories/sdmmc/sdmmc_spi.c | 1453 +++++++++++ memories/sdmmc/sdmmc_spi.h | 720 ++++++ memories/sdmmc/sdspi.c | 711 ++++++ memories/sdmmc/sdspi.h | 184 ++ memories/spi-flash/at26.c | 232 ++ memories/spi-flash/at26.h | 252 ++ memories/spi-flash/at26d.c | 409 +++ memories/spi-flash/at26d.h | 79 + memories/spi-flash/at45.c | 257 ++ memories/spi-flash/at45.h | 255 ++ memories/spi-flash/at45d.c | 223 ++ memories/spi-flash/at45d.h | 86 + memories/spi-flash/spi-flash.dir | 37 + memories/spi-flash/spid.c | 215 ++ memories/spi-flash/spid.h | 212 ++ memories/spi-flash/spid_dma.c | 392 +++ 72 files changed, 24516 insertions(+) create mode 100644 memories/MEDDdram.c create mode 100644 memories/MEDDdram.h create mode 100644 memories/MEDFlash.c create mode 100644 memories/MEDFlash.h create mode 100644 memories/MEDNandFlash.c create mode 100644 memories/MEDNandFlash.h create mode 100644 memories/MEDRamDisk.c create mode 100644 memories/MEDRamDisk.h create mode 100644 memories/MEDSdcard.c create mode 100644 memories/MEDSdcard.h create mode 100644 memories/MEDSdmmc.c create mode 100644 memories/MEDSdmmc.h create mode 100644 memories/MEDSdram.c create mode 100644 memories/MEDSdram.h create mode 100644 memories/Media.c create mode 100644 memories/Media.h create mode 100644 memories/flash/flashd.dir create mode 100644 memories/flash/flashd.h create mode 100644 memories/flash/flashd_eefc.c create mode 100644 memories/flash/flashd_efc.c create mode 100644 memories/memories.dir create mode 100644 memories/nandflash/EccNandFlash.c create mode 100644 memories/nandflash/EccNandFlash.h create mode 100644 memories/nandflash/ManagedNandFlash.c create mode 100644 memories/nandflash/ManagedNandFlash.h create mode 100644 memories/nandflash/MappedNandFlash.c create mode 100644 memories/nandflash/MappedNandFlash.h create mode 100644 memories/nandflash/NandCommon.h create mode 100644 memories/nandflash/NandFlashModel.c create mode 100644 memories/nandflash/NandFlashModel.h create mode 100644 memories/nandflash/NandFlashModelList.c create mode 100644 memories/nandflash/NandFlashModelList.h create mode 100644 memories/nandflash/NandSpareScheme.c create mode 100644 memories/nandflash/NandSpareScheme.h create mode 100644 memories/nandflash/NfcRawNandFlash.c create mode 100644 memories/nandflash/RawNandFlash.c create mode 100644 memories/nandflash/RawNandFlash.h create mode 100644 memories/nandflash/SkipBlockNandFlash.c create mode 100644 memories/nandflash/SkipBlockNandFlash.h create mode 100644 memories/nandflash/TranslatedNandFlash.c create mode 100644 memories/nandflash/TranslatedNandFlash.h create mode 100644 memories/nandflash/nandflash.dir create mode 100644 memories/norflash/NorFlashAmd.c create mode 100644 memories/norflash/NorFlashAmd.h create mode 100644 memories/norflash/NorFlashApi.c create mode 100644 memories/norflash/NorFlashApi.h create mode 100644 memories/norflash/NorFlashCFI.c create mode 100644 memories/norflash/NorFlashCFI.h create mode 100644 memories/norflash/NorFlashCommon.c create mode 100644 memories/norflash/NorFlashCommon.h create mode 100644 memories/norflash/NorFlashIntel.c create mode 100644 memories/norflash/NorFlashIntel.h create mode 100644 memories/norflash/norflash.dir create mode 100644 memories/sdmmc/sdmmc.dir create mode 100644 memories/sdmmc/sdmmc_mci.c create mode 100644 memories/sdmmc/sdmmc_mci.h create mode 100644 memories/sdmmc/sdmmc_spi.c create mode 100644 memories/sdmmc/sdmmc_spi.h create mode 100644 memories/sdmmc/sdspi.c create mode 100644 memories/sdmmc/sdspi.h create mode 100644 memories/spi-flash/at26.c create mode 100644 memories/spi-flash/at26.h create mode 100644 memories/spi-flash/at26d.c create mode 100644 memories/spi-flash/at26d.h create mode 100644 memories/spi-flash/at45.c create mode 100644 memories/spi-flash/at45.h create mode 100644 memories/spi-flash/at45d.c create mode 100644 memories/spi-flash/at45d.h create mode 100644 memories/spi-flash/spi-flash.dir create mode 100644 memories/spi-flash/spid.c create mode 100644 memories/spi-flash/spid.h create mode 100644 memories/spi-flash/spid_dma.c (limited to 'memories') diff --git a/memories/MEDDdram.c b/memories/MEDDdram.c new file mode 100644 index 0000000..6ebaf42 --- /dev/null +++ b/memories/MEDDdram.c @@ -0,0 +1,290 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MEDDdram.h" +#include +#include +#include + +#if defined(AT91C_BASE_ADDRESS_DDR) +#undef AT91C_BASE_ADDRESS_DDR +#endif +#if defined(AT91C_BASE_DDR2C) && defined(AT91C_DDR2) +#define AT91C_BASE_ADDRESS_DDR AT91C_DDR2 +#endif + +#if defined(AT91C_BASE_SDDRC) && defined(AT91C_EBI_DDRAM) +#define AT91C_BASE_ADDRESS_DDR AT91C_EBI_DDRAM +#endif + +#if defined(AT91C_BASE_ADDRESS_DDR) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Do copy and modify pointer +typedef void copyFunction(unsigned char **, unsigned char **, unsigned int); + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Do copy for 8-byte aligned data +//------------------------------------------------------------------------------ +static void AlignedCopy(unsigned char * *src, + unsigned char * *dst, + unsigned int len) +{ + unsigned int *src32, *dst32; + src32 = (unsigned int*)*src; + dst32 = (unsigned int*)*dst; + for (;len > 0; len -= 8) { + *dst32 ++ = *src32 ++; + *dst32 ++ = *src32 ++; + } + *src = (unsigned char*)src32; + *dst = (unsigned char*)dst32; +} + +//------------------------------------------------------------------------------ +/// Do copy for byte-aligned data +//------------------------------------------------------------------------------ +static void UnalignedCopy(unsigned char * *src, + unsigned char * *dst, + unsigned int len) +{ + for (;len > 0; len --) { + *(*dst) ++ = *(*src) ++; + } +} + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a DDRAM memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char MEDDdram_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char *source; + unsigned char *dest; + copyFunction *pCpy; + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + + TRACE_INFO("Media busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to read is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("MEDDdram_Read: Data too big: %u, 0x%08X\n\r", + length, address); + return MED_STATUS_ERROR; + } + + // Enter Busy state + media->state = MED_STATE_BUSY; + + // Source & Destination + source = (unsigned char *)(media->blockSize + * (address + media->baseAddress)); + dest = (unsigned char *)data; + + // Align/Unaligned copy + pCpy = (((unsigned int)source%4) == 0 && (media->blockSize%8) == 0) + ? AlignedCopy : UnalignedCopy; + + for (; length > 0; length --) { + pCpy(&source, &dest, media->blockSize); + } + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke callback + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a DDRAM media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see MediaCallback +//------------------------------------------------------------------------------ +static unsigned char MEDDdram_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char *source; + unsigned char *dest; + copyFunction *pCpy; + + //TRACE_DEBUG("MEDDdram_Write\n\r"); + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + + TRACE_INFO("MEDDdram_Write: Busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to write is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("MEDDdram_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + + // Put the media in Busy state + media->state = MED_STATE_BUSY; + + // Compute function parameters + source = (unsigned char *) data; + dest = (unsigned char *) (media->blockSize * + (media->baseAddress + address)); + + // Align/Unaligned copy + pCpy = (((unsigned int)source%4) == 0 && (media->blockSize%8) == 0) + ? AlignedCopy : UnalignedCopy; + + for (; length > 0; length --) { + pCpy(&source, &dest, media->blockSize); + } + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke the callback if it exists + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +// Exported Functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//! \brief Initializes a Media instance and the associated physical interface +//! \param media Pointer to the Media instance to initialize +//! \param blockSize Block size of the media, in bytes. +//! \param baseAddress Basic address of the media, in number of blocks. +//! \param size Size of the media, in number of blocks. +//! \see Media +//------------------------------------------------------------------------------ +void MEDDdram_Initialize(Media *media, + unsigned int blockSize, + unsigned int baseAddress, + unsigned int size) +{ + unsigned int value; + + TRACE_INFO("MEDDdram init\n\r"); + + // Initialize DDRAM if not already done + //-------------------------------------------------------------------------- + value = *((volatile unsigned int *) AT91C_BASE_ADDRESS_DDR); + *((volatile unsigned int *) AT91C_BASE_ADDRESS_DDR) = 0xDEADBEEF; + + if (*((volatile unsigned int *) AT91C_BASE_ADDRESS_DDR) == 0xDEADBEEF) { + + *((volatile unsigned int *) AT91C_BASE_ADDRESS_DDR) = value; + TRACE_INFO("DDRAM initialized\n\r"); + } + else { + + *((volatile unsigned int *) AT91C_BASE_ADDRESS_DDR) = value; + TRACE_INFO("Initialize DDRAM ...\n\r"); + BOARD_ConfigureDdram(0, BOARD_DDRAM_BUSWIDTH); + } + + // Initialize media fields + //-------------------------------------------------------------------------- + media->write = MEDDdram_Write; + media->read = MEDDdram_Read; + media->lock = 0; + media->unlock = 0; + media->handler = 0; + media->flush = 0; + + media->blockSize = blockSize; + media->baseAddress = baseAddress; + media->size = size; + + media->mappedRD = 1; + media->mappedWR = 1; + media->protected = 0; + media->removable = 0; + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; +} +#endif //#if defined(AT91C_BASE_ADDRESS_DDR) + diff --git a/memories/MEDDdram.h b/memories/MEDDdram.h new file mode 100644 index 0000000..b42cfb3 --- /dev/null +++ b/memories/MEDDdram.h @@ -0,0 +1,48 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef MEDDDRAM_H +#define MEDDDRAM_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "Media.h" + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void MEDDdram_Initialize(Media *media, + unsigned int blockSize, + unsigned int baseAddress, + unsigned int size); + +#endif //#ifndef MEDSDRAM_H diff --git a/memories/MEDFlash.c b/memories/MEDFlash.c new file mode 100644 index 0000000..5befde7 --- /dev/null +++ b/memories/MEDFlash.c @@ -0,0 +1,281 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Includes +//------------------------------------------------------------------------------ + +#include "MEDFlash.h" +#include +#include + +#include + +#if !defined (CHIP_FLASH_EFC) && !defined (CHIP_FLASH_EEFC) +#error eefc/efc not supported +#endif + +//------------------------------------------------------------------------------ +// Local definitions +//------------------------------------------------------------------------------ + +// Missing FRDY bit on the SAM7A3 +#if defined(at91sam7a3) + #define AT91C_MC_FRDY (AT91C_MC_EOP | AT91C_MC_EOL) +#endif + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a flash memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char FLA_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char *source = (unsigned char *) address; + unsigned char *dest = (unsigned char *) data; + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + + TRACE_INFO("Media busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to read is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("FLA_Read: Data too big\n\r"); + return MED_STATUS_ERROR; + } + + // Enter Busy state + media->state = MED_STATE_BUSY; + + // Read data + while (length > 0) { + + *dest = *source; + + dest++; + source++; + length--; + } + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke callback + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a flash media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see Callback_f +//------------------------------------------------------------------------------ +static unsigned char FLA_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char error; + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + + TRACE_WARNING("FLA_Write: Media is busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that address is dword-aligned + if (address%4 != 0) { + + TRACE_DEBUG("address = 0x%X\n\r", address); + TRACE_WARNING("FLA_Write: Address must be dword-aligned\n\r"); + return MED_STATUS_ERROR; + } + + // Check that length is a multiple of 4 + if (length%4 != 0) { + + TRACE_WARNING("FLA_Write: Data length must be a multiple of 4 bytes\n\r"); + return MED_STATUS_ERROR; + } + + // Check that the data to write is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("FLA_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + + // Put the media in Busy state + media->state = MED_STATE_BUSY; + + // Initialize the transfer descriptor + media->transfer.data = data; + media->transfer.address = address; + media->transfer.length = length; + media->transfer.callback = callback; + media->transfer.argument = argument; + + // Start the write operation + error = FLASHD_Write( address, data, length); + ASSERT(!error, "-F- Error when trying to write page (0x%02X)\n\r", error); + + // End of transfer + // Put the media in Ready state + media->state = MED_STATE_READY; + + // Invoke the callback if it exists + if (media->transfer.callback != 0) { + + media->transfer.callback(media->transfer.argument, 0, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Lock all the regions in the given address range. The actual unlock +//! range is reported through two output parameters. +//! \param media Pointer to Media instance. +//! \param start Start address of unlock range. +//! \param end End address of unlock range. +//! \param pActualStart Start address of the actual unlock range (optional). +//! \param pActualEnd End address of the actual unlock range (optional). +//! \return 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +static unsigned char FLA_Lock(Media *media, + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + if (FLASHD_Lock(start, end, pActualStart, pActualEnd)) + return MED_STATUS_ERROR; + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Unlock all the regions in the given address range. The actual unlock +//! range is reported through two output parameters. +//! \param media Pointer to Media instance. +//! \param start Start address of unlock range. +//! \param end End address of unlock range. +//! \param pActualStart Start address of the actual unlock range (optional). +//! \param pActualEnd End address of the actual unlock range (optional). +//! \return 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +static unsigned char FLA_Unlock(Media *media, + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + if (FLASHD_Unlock(start, end, pActualStart, pActualEnd)) + return MED_STATUS_ERROR; + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +// Exported Functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//! \brief Initializes a Media instance and the associated physical interface +//! \param media Pointer to the Media instance to initialize +//! \param efc Pointer to AT91S_EFC interface. +//! \see Media +//------------------------------------------------------------------------------ +void FLA_Initialize(Media *media, AT91S_EFC *efc) +{ + TRACE_INFO("Flash init\n\r"); + + // Initialize media fields + media->write = FLA_Write; + media->read = FLA_Read; + media->lock = FLA_Lock; + media->unlock = FLA_Unlock; + media->flush = 0; + media->handler = 0; + + media->blockSize = 1; + media->baseAddress = 0; // Address based on whole memory space + media->size = AT91C_IFLASH_SIZE; + media->interface = efc; + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = 0; + media->removable = 0; + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; + + // Initialize low-level interface + // Configure Flash Mode register + efc->EFC_FMR |= (BOARD_MCK / 666666) << 16; +} + + diff --git a/memories/MEDFlash.h b/memories/MEDFlash.h new file mode 100644 index 0000000..8ddc2f0 --- /dev/null +++ b/memories/MEDFlash.h @@ -0,0 +1,83 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Specialization of the Media class for interfacing with the internal Flash. +/// +/// !Usage +/// +/// TODO +//------------------------------------------------------------------------------ + +#ifndef MEDFLASH_H +#define MEDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "Media.h" +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +// Redefinition of EFC structure and base address. +#if !defined(AT91C_BASE_EFC) && defined(AT91C_BASE_EFC0) + #define AT91C_BASE_EFC AT91C_BASE_EFC0 + +#elif !defined(AT91C_BASE_EFC) && defined(AT91C_BASE_MC) + #define AT91S_EFC AT91S_MC + #define AT91C_BASE_EFC AT91C_BASE_MC + #define EFC_FCR MC_FCR + #define EFC_FMR MC_FMR + #define EFC_FSR MC_FSR +#endif + +// Definition for the SAM9XE (which has an EEFC) +#if defined(at91sam9xe128) || defined(at91sam9xe256) || defined(at91sam9xe512) + #define AT91C_MC_FCMD_START_PROG AT91C_EFC_FCMD_EWP + #define AT91C_MC_FRDY AT91C_EFC_FRDY +#endif + +#if defined(AT91C_BASE_EFC) + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void FLA_Initialize(Media *media, AT91S_EFC *efc); + +#endif //#if defined(AT91C_BASE_EFC) +#endif //#ifndef MEDFLASH_H + diff --git a/memories/MEDNandFlash.c b/memories/MEDNandFlash.c new file mode 100644 index 0000000..300af36 --- /dev/null +++ b/memories/MEDNandFlash.c @@ -0,0 +1,510 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MEDNandFlash.h" +#include "nandflash/NandCommon.h" +#include "nandflash/TranslatedNandFlash.h" +#include "nandflash/NandFlashModel.h" +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Casts +#define MODEL(interface) ((struct NandFlashModel *) interface) +#define TRANSLATED(interface) ((struct TranslatedNandFlash *) interface) + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +static unsigned char pageWriteBuffer[NandCommon_MAXPAGEDATASIZE]; +static signed short currentWriteBlock; +static signed short currentWritePage; + +static unsigned char pageReadBuffer[NandCommon_MAXPAGEDATASIZE]; +static signed short currentReadBlock; +static signed short currentReadPage; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Writes the current page of data on the NandFlash. +/// Returns 0 if successful; otherwise returns 1. +/// \param tnf Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +static unsigned char FlushCurrentPage(Media *media) +{ + // Make sure there is a page to flush + if (currentWritePage == -1) { + + return 0; + } + + TRACE_DEBUG("FlushCurrentPage(B#%d:P#%d)\n\r", + currentWriteBlock, currentWritePage); + + // Write page + if (TranslatedNandFlash_WritePage(TRANSLATED(media->interface), + currentWriteBlock, + currentWritePage, + pageWriteBuffer, + 0)) { + + TRACE_ERROR("FlushCurrentPage: Failed to write page.\n\r"); + return 1; + } + + // No current write page & block + currentWriteBlock = -1; + currentWritePage = -1; + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes data at an unaligned (page-wise) address and size. The address is +/// provided as the block & page number plus an offset. The data to write MUST +/// NOT span more than one page. +/// Returns 0 if the data has been written; 1 otherwise. +/// \param media Pointer to a nandflash Media instance. +/// \param block Number of the block to write. +/// \param page Number of the page to write. +/// \param offset Write offset. +/// \param buffer Data buffer. +/// \param size Number of bytes to write. +//------------------------------------------------------------------------------ +static unsigned char UnalignedWritePage( + Media *media, + unsigned short block, + unsigned short page, + unsigned short offset, + unsigned char *buffer, + unsigned int size) +{ + unsigned char error; + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface)); + unsigned char writePage = ((size + offset) == pageDataSize); + + TRACE_DEBUG("UnalignedWritePage(B%d:P%d@%d, %d)\n\r", + block, page, offset, size); + ASSERT((size + offset) <= pageDataSize, + "UnalignedWrite: Write size and offset exceed page data size\n\r"); + + // If size is 0, return immediately + if (size == 0) { + + return 0; + } + + // If this is not the current page, flush the previous one + if ((currentWriteBlock != block) || (currentWritePage != page)) { + + // Flush and make page the new current page + FlushCurrentPage(media); + TRACE_DEBUG("Current write page: B#%d:P#%d\n\r", block, page); + currentWriteBlock = block; + currentWritePage = page; + + // Read existing page data in a temporary buffer if the page is not + // entirely written + if (size != pageDataSize) { + + error = TranslatedNandFlash_ReadPage(TRANSLATED(media->interface), + block, + page, + pageWriteBuffer, + 0); + if (error) { + + TRACE_ERROR( + "UnalignedWrite: Could not read existing page data\n\r"); + return 1; + } + } + } + + // Copy data in temporary buffer + memcpy(&(pageWriteBuffer[offset]), buffer, size); + // Update read buffer if necessary + if ((currentReadPage == currentWritePage) + && (currentReadBlock == currentWriteBlock)) { + + TRACE_DEBUG("Updating current read buffer\n\r"); + memcpy(&(pageReadBuffer[offset]), buffer, size); + } + + // Flush page if it is complete + if (writePage) { + + FlushCurrentPage(media); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes a data buffer at the specified address on a NandFlash media. An +/// optional callback can be triggered after the transfer is completed. +/// Returns MED_STATUS_SUCCESS if the transfer has been started successfully; +/// otherwise returns MED_STATUS_ERROR. +/// \param media Pointer to the NandFlash Media instance. +/// \param address Address where the data shall be written. +/// \param data Data buffer. +/// \param length Number of bytes to write. +/// \param callback Optional callback to call when the write is finished. +/// \param argument Optional argument to the callback function. +//------------------------------------------------------------------------------ +static unsigned char MEDNandFlash_Write( + Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned short pageDataSize = + NandFlashModel_GetPageDataSize(MODEL(media->interface)); + unsigned short blockSize = + NandFlashModel_GetBlockSizeInPages(MODEL(media->interface)); + unsigned short block, page, offset; + unsigned int writeSize; + unsigned char *buffer = (unsigned char *) data; + unsigned int remainingLength; + unsigned char status; + + TRACE_INFO("MEDNandFlash_Write(0x%08X, %d)\n\r", address, length); + + // Translate access + if (NandFlashModel_TranslateAccess(MODEL(media->interface), + address, + length, + &block, + &page, + &offset)) { + + TRACE_ERROR("MEDNandFlash_Write: Could not start write.\n\r"); + return MED_STATUS_ERROR; + } + + TRACE_DEBUG("MEDNandFlash_Write(B#%d:P#%d@%d, %d)\n\r", + block, page, offset, length); + + // Write pages + remainingLength = length; + status = MED_STATUS_SUCCESS; + while ((status == MED_STATUS_SUCCESS) && (remainingLength > 0)) { + + // Write one page + writeSize = min(pageDataSize-offset, remainingLength); + if (UnalignedWritePage(media, block, page, offset, buffer, writeSize)) { + + TRACE_ERROR("MEDNandFlash_Write: Failed to write page\n\r"); + status = MED_STATUS_ERROR; + } + else { + + // Update addresses + remainingLength -= writeSize; + buffer += writeSize; + offset = 0; + page++; + if (page == blockSize) { + + page = 0; + block++; + } + } + } + + // Trigger callback + if (callback) { + + callback(argument, status, length - remainingLength, remainingLength); + } + + // Reset flush timer + //AT91C_BASE_NANDFLUSHTIMER->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Reads data at an unaligned address and/or size. The address is provided as +/// the block & page numbers plus an offset. +/// Returns 0 if the data has been read; otherwise returns 1. +/// \param media Pointer to a nandflash Media instance. +/// \param block Number of the block to read. +/// \param page Number of the page to read. +/// \param offset Read offset. +/// \param buffer Buffer for storing data. +/// \param size Number of bytes to read. +//------------------------------------------------------------------------------ +static unsigned char UnalignedReadPage( + Media *media, + unsigned short block, + unsigned short page, + unsigned short offset, + unsigned char *buffer, + unsigned int size) +{ + unsigned char error; + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface)); + + TRACE_DEBUG("UnalignedReadPage(B%d:P%d@%d, %d)\n\r", block, page, offset, size); + + // Check that one page is read at most + ASSERT((size + offset) <= pageDataSize, + "UnalignedReadPage: Read size & offset exceed page data size\n\r"); + + // Check if this is not the current read page + if ((block != currentReadBlock) || (page != currentReadPage)) { + + TRACE_DEBUG("Current read page: B#%d:P#%d\n\r", block, page); + currentReadBlock = block; + currentReadPage = page; + + // Check if this is the current write page + if ((currentReadBlock == currentWriteBlock) + && (currentReadPage == currentWritePage)) { + + TRACE_DEBUG("Reading current write page\n\r"); + memcpy(pageReadBuffer, pageWriteBuffer, NandCommon_MAXPAGEDATASIZE); + } + else { + + // Read whole page into a temporary buffer + error = TranslatedNandFlash_ReadPage(TRANSLATED(media->interface), + block, + page, + pageReadBuffer, + 0); + if (error) { + + TRACE_ERROR("UnalignedRead: Could not read page\n\r"); + return 1; + } + } + } + + // Copy data into buffer + memcpy(buffer, &(pageReadBuffer[offset]), size); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Reads data at the specified address of a NandFlash media. An optional +/// callback is invoked when the transfer completes. +/// Returns 1 if the transfer has been started; otherwise returns 0. +/// \param media Pointer to the NandFlash Media to read. +/// \param address Address at which the data shall be read. +/// \param data Data buffer. +/// \param length Number of bytes to read. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +//------------------------------------------------------------------------------ +static unsigned char MEDNandFlash_Read( + Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned short block, page, offset; + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(media->interface)); + unsigned short blockSizeInPages = NandFlashModel_GetBlockSizeInPages(MODEL(media->interface)); + unsigned int remainingLength; + unsigned int readSize; + unsigned char *buffer = (unsigned char *) data; + unsigned char status; + + TRACE_INFO("MEDNandFlash_Read(0x%08X, %d)\n\r", address, length); + + // Translate access into block, page and offset + if (NandFlashModel_TranslateAccess(MODEL(media->interface), + address, + length, + &block, + &page, + &offset)) { + + TRACE_ERROR("MEDNandFlash_Read: Cannot perform access\n\r"); + return MED_STATUS_ERROR; + } + + // Read + remainingLength = length; + status = MED_STATUS_SUCCESS; + while ((status == MED_STATUS_SUCCESS) && (remainingLength > 0)) { + + // Read page + readSize = min(pageDataSize-offset, remainingLength); + if (UnalignedReadPage(media, block, page, offset, buffer, readSize)) { + + TRACE_ERROR("MEDNandFlash_Read: Could not read page\n\r"); + status = MED_STATUS_ERROR; + } + else { + + // Update values + remainingLength -= readSize; + buffer += readSize; + offset = 0; + page++; + if (page == blockSizeInPages) { + + page = 0; + block++; + } + } + } + + // Trigger callback + if (callback) { + + callback(argument, status, length - remainingLength, remainingLength); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Carries out all pending operations. Returns MED_STATUS_SUCCESS if +/// succesful; otherwise, returns MED_STATUS_ERROR. +/// \param media Pointer to a NandFlash Media instance. +//------------------------------------------------------------------------------ +static unsigned char MEDNandFlash_Flush(Media *media) +{ + TRACE_INFO("MEDNandFlash_Flush()\n\r"); + + if (FlushCurrentPage(media)) { + + TRACE_ERROR("MEDNandFlash_Flush: Could not flush current page\n\r"); + return MED_STATUS_ERROR; + } + + if (TranslatedNandFlash_Flush(TRANSLATED(media->interface))) { + + TRACE_ERROR("MEDNandFlash_Flush: Could not flush translated nand\n\r"); + return MED_STATUS_ERROR; + } + + if (TranslatedNandFlash_SaveLogicalMapping(TRANSLATED(media->interface))) { + + TRACE_ERROR("MEDNandFlash_Flush: Could not save the logical mapping\n\r"); + return MED_STATUS_ERROR; + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Interrupt handler for the nandflash media. Triggered when the flush timer +/// expires, initiating a MEDNandFlash_Flush(). +/// \param media Pointer to a nandflash Media instance. +//------------------------------------------------------------------------------ +static void MEDNandFlash_InterruptHandler(Media *media) +{ + //volatile unsigned int dummy; + + TRACE_DEBUG("Flush timer expired\n\r"); + MEDNandFlash_Flush(media); + + // Acknowledge interrupt + //dummy = AT91C_BASE_NANDFLUSHTIMER->TC_SR; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a media instance to operate on the given NandFlash device. +/// \param media Pointer to a Media instance. +/// \param tnf Pointer to the TranslatedNandFlash to use. +//------------------------------------------------------------------------------ +void MEDNandFlash_Initialize(Media *media, + struct TranslatedNandFlash *translated) +{ + TRACE_INFO("MEDNandFlash_Initialize()\n\r"); + + media->write = MEDNandFlash_Write; + media->read = MEDNandFlash_Read; + media->lock = 0; + media->unlock = 0; + media->flush = MEDNandFlash_Flush; + media->handler = MEDNandFlash_InterruptHandler; + + media->interface = translated; + + media->baseAddress = 0; + media->blockSize = 1; + media->size = TranslatedNandFlash_GetDeviceSizeInBytes(translated); + + TRACE_INFO("NF Size: %d\n\r", media->size); + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = 0; + media->removable = 0; + media->state = MED_STATE_READY; + + currentWriteBlock = -1; + currentWritePage = -1; + currentReadBlock = -1; + currentReadPage = -1; + + // Configure flush timer + /* + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_NANDFLUSHTIMER); + AT91C_BASE_NANDFLUSHTIMER->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_NANDFLUSHTIMER->TC_IDR = 0xFFFFFFFF; + AT91C_BASE_NANDFLUSHTIMER->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK + | AT91C_TC_CPCSTOP + | AT91C_TC_CPCDIS + | AT91C_TC_WAVESEL_UP_AUTO + | AT91C_TC_WAVE; + AT91C_BASE_NANDFLUSHTIMER->TC_RC = 0xFFFF; + AT91C_BASE_NANDFLUSHTIMER->TC_IER = AT91C_TC_CPCS; + */ +} + diff --git a/memories/MEDNandFlash.h b/memories/MEDNandFlash.h new file mode 100644 index 0000000..ae34179 --- /dev/null +++ b/memories/MEDNandFlash.h @@ -0,0 +1,72 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef MEDNANDFLASH_H +#define MEDNANDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "Media.h" +#include "board.h" +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Timer Counter used to flush the nandflash after write operations. +#define AT91C_BASE_NANDFLUSHTIMER AT91C_BASE_TC1 +#define AT91C_ID_NANDFLUSHTIMER AT91C_ID_TC1 + +#ifndef AT91C_ID_TC1 +#if defined(AT91C_ID_TC012) + #define AT91C_ID_NANDFLUSHTIMER AT91C_ID_TC012 +#elif defined(AT91C_ID_TC) + #define AT91C_ID_NANDFLUSHTIMER AT91C_ID_TC +#else + #error Pb define ID_TC +#endif +#endif + +//------------------------------------------------------------------------------ +// Forward declarations +//------------------------------------------------------------------------------ + +struct TranslatedNandFlash; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void MEDNandFlash_Initialize( + Media *media, + struct TranslatedNandFlash *tnf); + +#endif //#ifndef MEDNANDFLASH_H + diff --git a/memories/MEDRamDisk.c b/memories/MEDRamDisk.c new file mode 100644 index 0000000..b1a8d8b --- /dev/null +++ b/memories/MEDRamDisk.c @@ -0,0 +1,284 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MEDRamDisk.h" +#include +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Do copy and modify pointer +typedef void copyFunction(unsigned char **, unsigned char **, unsigned int); + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Do copy for 8-byte aligned data +//------------------------------------------------------------------------------ +static void AlignedCopy(unsigned char * *src, + unsigned char * *dst, + unsigned int len) +{ + unsigned int *src32, *dst32; + src32 = (unsigned int*)*src; + dst32 = (unsigned int*)*dst; + for (;len > 0; len -= 8) { + *dst32 ++ = *src32 ++; + *dst32 ++ = *src32 ++; + } + *src = (unsigned char*)src32; + *dst = (unsigned char*)dst32; +} + +//------------------------------------------------------------------------------ +/// Do copy for byte-aligned data +//------------------------------------------------------------------------------ +static void UnalignedCopy(unsigned char * *src, + unsigned char * *dst, + unsigned int len) +{ + for (;len > 0; len --) { + *(*dst) ++ = *(*src) ++; + } +} + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a RAM Disk memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char MEDRamDisk_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char *source; + unsigned char *dest; + copyFunction *pCpy; + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + + TRACE_INFO("Media busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to read is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("RamDisk_Read: Data too big: %u, 0x%08X\n\r", + length, address); + return MED_STATUS_ERROR; + } + + // Enter Busy state + media->state = MED_STATE_BUSY; + + // Source & Destination + source = (unsigned char *)(media->blockSize + * (address + media->baseAddress)); + dest = (unsigned char *)data; + + // Align/Unaligned copy + pCpy = (((unsigned int)source%4) == 0 && (media->blockSize%8) == 0) + ? AlignedCopy : UnalignedCopy; + + for (; length > 0; length --) { + pCpy(&source, &dest, media->blockSize); + } + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke callback + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a Ram Disk media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see MediaCallback +//------------------------------------------------------------------------------ +static unsigned char MEDRamDisk_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char *source; + unsigned char *dest; + copyFunction *pCpy; + + //TRACE_DEBUG("RamDisk_Write\n\r"); + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + + TRACE_WARNING("RamDisk_Write: busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to write is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("RamDisk_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + + // Put the media in Busy state + media->state = MED_STATE_BUSY; + + // Compute function parameters + source = (unsigned char *) data; + dest = (unsigned char *) (media->blockSize * + (media->baseAddress + address)); + + // Align/Unaligned copy + pCpy = (((unsigned int)source%4) == 0 && (media->blockSize%8) == 0) + ? AlignedCopy : UnalignedCopy; + + for (; length > 0; length --) { + pCpy(&source, &dest, media->blockSize); + } + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke the callback if it exists + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +// Exported Functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//! \brief Initializes a Media instance and the associated physical interface +//! \param media Pointer to the Media instance to initialize +//! \return 1 if initialize sucessfully, 0 if any error. +//! \see Media +//------------------------------------------------------------------------------ +unsigned char MEDRamDisk_Initialize(Media *media, + unsigned int blockSize, + unsigned int baseAddress, + unsigned int size) +{ + unsigned int bak_val; + unsigned int baseInBytes = baseAddress * blockSize; + unsigned int sizeInBytes = size * blockSize; + + TRACE_INFO("RAM Disk init\n\r"); + + // If the address Initialized ? + bak_val = *((volatile unsigned int *) (baseInBytes)); + *((volatile unsigned int *) (baseInBytes)) = 0xDEADBEEF; + if (*((volatile unsigned int *) baseInBytes) == 0xDEADBEEF) { + + *((volatile unsigned int *) baseInBytes) = bak_val; + } + else { + + TRACE_ERROR("Un-configured memory area %x ...\n\r", baseInBytes); + return 0; + } + + baseInBytes += sizeInBytes - 4; + bak_val = *((volatile unsigned int *) (baseInBytes)); + *((volatile unsigned int *) (baseInBytes)) = 0xAECDBE9F; + if (*((volatile unsigned int *)(baseInBytes)) == 0xAECDBE9F) { + + *((volatile unsigned int *)(baseInBytes)) = bak_val; + } + else { + + TRACE_ERROR("Un-configured memory area %x ...\n\r", baseInBytes); + return 0; + } + + // Initialize media fields + media->write = MEDRamDisk_Write; + media->read = MEDRamDisk_Read; + media->lock = 0; + media->unlock = 0; + media->handler = 0; + media->flush = 0; + + media->blockSize = blockSize; + media->baseAddress = baseAddress; + media->size = size; + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = 0; + media->removable = 0; + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; + + return 1; +} diff --git a/memories/MEDRamDisk.h b/memories/MEDRamDisk.h new file mode 100644 index 0000000..b4d6561 --- /dev/null +++ b/memories/MEDRamDisk.h @@ -0,0 +1,48 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef MEDRAMDISK_H +#define MEDRAMDISK_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char MEDRamDisk_Initialize(Media *media, + unsigned int blockSize, + unsigned int baseAddress, + unsigned int size); + +#endif //#ifndef MEDRAMDISK_H diff --git a/memories/MEDSdcard.c b/memories/MEDSdcard.c new file mode 100644 index 0000000..8e46ba4 --- /dev/null +++ b/memories/MEDSdcard.c @@ -0,0 +1,694 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MEDSdcard.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(MCI2_INTERFACE) +#include "dmad/dmad.h" +#endif + +#include + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + +/// Number of SD Slots +#define NUM_SD_SLOTS 1 + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +/// MCI driver instance. +static Mci mciDrv[NUM_SD_SLOTS]; + +/// SDCard driver instance. +static SdCard sdDrv[NUM_SD_SLOTS]; + +#if MCI_BUSY_CHECK_FIX && defined(BOARD_SD_DAT0) +/// SD DAT0 pin +static const Pin pinSdDAT0 = BOARD_SD_DAT0; +#endif + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// MCI0 interrupt handler. Forwards the event to the MCI driver handler. +//------------------------------------------------------------------------------ +void MCI0_IrqHandler(void) +{ + MCI_Handler(mciDrv); +} + +//------------------------------------------------------------------------------ +// Optional: SD card detection +//------------------------------------------------------------------------------ + +/// SD card detection pin instance. +#if defined(BOARD_SD_PIN_CD) +static const Pin pinCardDetect = BOARD_SD_PIN_CD; +#endif + +#if defined(BOARD_SD_MCI1_PIN_CD) +static const Pin pinCardDetect1 = BOARD_SD_MCI1_PIN_CD; +#endif + +//------------------------------------------------------------------------------ +/// Detect if SD card is connected +//------------------------------------------------------------------------------ +static unsigned char CardIsConnected(unsigned char slot) +{ + if (slot == 0) { + #if defined(BOARD_SD_PIN_CD) + PIO_Configure(&pinCardDetect, 1); + return PIO_Get(&pinCardDetect) ? 0 : 1; + #else + return 1; + #endif + } + if (slot == 1) { + #if defined(BOARD_SD_MCI1_PIN_CD) + PIO_Configure(&pinCardDetect1, 1); + return PIO_Get(&pinCardDetect1) ? 0 : 1; + #else + return 1; + #endif + } + + return 0; +} + +//------------------------------------------------------------------------------ +// Optional: Write protection status +//------------------------------------------------------------------------------ + +/// Write protection status pin instance. +#ifdef BOARD_SD_PIN_WP +static const Pin pinMciWriteProtect = BOARD_SD_PIN_WP; +#endif +#ifdef BOARD_SD_MCI1_PIN_WP +static const Pin pinMciWriteProtect1 = BOARD_SD_MCI1_PIN_WP; +#endif + +//------------------------------------------------------------------------------ +/// Checks if the device is write protected. +/// \return 1 if protected. +//------------------------------------------------------------------------------ +static unsigned char CardIsProtected(unsigned char slot) +{ + if (slot == 0) { + #ifdef BOARD_SD_PIN_WP + PIO_Configure(&pinMciWriteProtect, 1); + return (PIO_Get(&pinMciWriteProtect) != 0); + #else + return 0; + #endif + } + + if (slot == 1) { + #ifdef BOARD_SD_MCI1_PIN_WP + PIO_Configure(&pinMciWriteProtect1, 1); + return (PIO_Get(&pinMciWriteProtect1) != 0); + #else + return 0; + #endif + } + + return 0; +} + + +//------------------------------------------------------------------------------ +/// Configure the PIO for SD +//------------------------------------------------------------------------------ +static void ConfigurePIO(unsigned char mciID) +{ + #ifdef BOARD_SD_PINS + const Pin pinSd0[] = {BOARD_SD_PINS}; + #endif + + #ifdef BOARD_SD_MCI1_PINS + const Pin pinSd1[] = {BOARD_SD_MCI1_PINS}; + #endif + + if(mciID == 0) { + #ifdef BOARD_SD_PINS + PIO_Configure(pinSd0, PIO_LISTSIZE(pinSd0)); + #endif + } else { + #ifdef BOARD_SD_MCI1_PINS + PIO_Configure(pinSd1, PIO_LISTSIZE(pinSd1)); + #endif + } +} + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a SDCARD memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char MEDSdcard_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char error; + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + + TRACE_INFO("Media busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to read is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("MEDSdcard_Read: Data too big: %d, %d\n\r", + (int)length, (int)address); + return MED_STATUS_ERROR; + } + + // Enter Busy state + media->state = MED_STATE_BUSY; + + #if 0 + // Read no more than 15KB from SD + { unsigned int rdLen, totalLen = 0; + do { + rdLen = length - totalLen; + if (rdLen > 30) rdLen = 30; + error = SD_ReadBlock(media->interface, address, rdLen, data); + totalLen += rdLen; + address += rdLen; + data = (void*)((unsigned long)data + rdLen*SD_BLOCK_SIZE); + } while(!error && totalLen < length); + } + #else + error = SD_ReadBlock((SdCard*)media->interface, address, length, data); + #endif + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke callback + if (callback != 0) { + + callback(argument, error ? MED_STATUS_ERROR : MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a SDRAM media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see MediaCallback +//------------------------------------------------------------------------------ +static unsigned char MEDSdcard_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char error; + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + + TRACE_WARNING("MEDSdcard_Write: Media is busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to write is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("MEDSdcard_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + + // Put the media in Busy state + media->state = MED_STATE_BUSY; + + #if 0 + // Write no more than 15KB + { unsigned int wrLen, totalLen = 0; + do { + wrLen = length - totalLen; + if (wrLen > 30) wrLen = 30; + error = SD_WriteBlock(media->interface, + address, wrLen, + data); + totalLen += wrLen; + address += wrLen; + data = (void*)((unsigned long)data + wrLen*SD_BLOCK_SIZE); + } while(!error && totalLen < length); + } + #else + error = SD_WriteBlock((SdCard*)media->interface, address, length, data); + #endif + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke the callback if it exists + if (callback != 0) { + + callback(argument, error ? MED_STATUS_ERROR : MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Callback invoked when SD/MMC transfer done +//------------------------------------------------------------------------------ +static void SdMmcCallback(unsigned char status, void *pCommand) +{ + SdCmd * pCmd = (SdCmd*)pCommand; + Media * pMed = pCmd->pArg; + MEDTransfer * pXfr = &pMed->transfer; + + TRACE_INFO_WP("SDCb "); + + // Error + if (status == SD_ERROR_BUSY) { + status = MED_STATUS_BUSY; + } + else if (status) { + status = MED_STATUS_ERROR; + } + + pMed->state = MED_STATE_READY; + if (pXfr->callback) { + pXfr->callback(pXfr->argument, + status, + pXfr->length * pMed->blockSize, + 0); + } +} + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a SDCARD memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char MEDSdusb_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + MEDTransfer * pXfr; + unsigned char error; + + TRACE_INFO_WP("SDuRd(%d,%d) ", (int)address, (int)length); + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + TRACE_INFO("MEDSdusb_Read: Busy\n\r"); + return MED_STATUS_BUSY; + } + // Check that the data to read is not too big + if ((length + address) > media->size) { + TRACE_WARNING("MEDSdusb_Read: Data too big: %d, %d\n\r", + (int)length, (int)address); + return MED_STATUS_ERROR; + } + // Enter Busy state + media->state = MED_STATE_BUSY; + + // Start media transfer + pXfr = &media->transfer; + pXfr->data = data; + pXfr->address = address; + pXfr->length = length; + pXfr->callback = callback; + pXfr->argument = argument; + + error = SD_Read((SdCard*)media->interface, + address, + data, + length, + SdMmcCallback, + media); + + return (error ? MED_STATUS_ERROR : MED_STATUS_SUCCESS); +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a SDRAM media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see MediaCallback +//------------------------------------------------------------------------------ +static unsigned char MEDSdusb_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + MEDTransfer * pXfr; + unsigned char error; + TRACE_INFO_WP("SDuWr(%d,%d) ", (int)address, (int)length); + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + TRACE_INFO("MEDSdusb_Write: Busy\n\r"); + return MED_STATUS_BUSY; + } + // Check that the data to write is not too big + if ((length + address) > media->size) { + TRACE_WARNING("MEDSdcard_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + // Put the media in Busy state + media->state = MED_STATE_BUSY; + + // Start media transfer + pXfr = &media->transfer; + pXfr->data = data; + pXfr->address = address; + pXfr->length = length; + pXfr->callback = callback; + pXfr->argument = argument; + + error = SD_Write((SdCard*)media->interface, + address, + data, + length, + SdMmcCallback, + media); + + return (error ? MED_STATUS_ERROR : MED_STATUS_SUCCESS); +} + +//------------------------------------------------------------------------------ +// Exported Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Detect if there is SD card in socket +//------------------------------------------------------------------------------ +unsigned char MEDSdcard_Detect(Media * media, unsigned char mciID) +{ + return CardIsConnected(mciID); +} + +//------------------------------------------------------------------------------ +/// Initializes a Media instance and the associated physical interface +/// \param media Pointer to the Media instance to initialize +/// \return 1 if success. +//------------------------------------------------------------------------------ +unsigned char MEDSdcard_Initialize(Media *media, unsigned char mciID) +{ + TRACE_INFO("MEDSdcard init\n\r"); + + // Initialize SDcard + //-------------------------------------------------------------------------- + + if (!CardIsConnected(mciID)) return 0; + + // Configure SDcard pins + ConfigurePIO(mciID); + + #if defined(MCI2_INTERFACE) + DMAD_Initialize(BOARD_MCI_DMA_CHANNEL, DMAD_NO_DEFAULT_IT); + #endif + // Initialize the MCI driver + if(mciID == 0) { + IRQ_ConfigureIT(BOARD_SD_MCI_ID, 1, MCI0_IrqHandler); + MCI_Init(mciDrv, BOARD_SD_MCI_BASE, BOARD_SD_MCI_ID, BOARD_SD_SLOT, MCI_INTERRUPT_MODE); + IRQ_EnableIT(BOARD_SD_MCI_ID); + } else { + #ifdef BOARD_SD_MCI1_ID + IRQ_ConfigureIT(BOARD_SD_MCI1_ID, 1, MCI0_IrqHandler); + MCI_Init(mciDrv, BOARD_SD_MCI1_BASE, BOARD_SD_MCI1_ID, BOARD_SD_MCI1_SLOT, MCI_INTERRUPT_MODE); + IRQ_EnableIT(BOARD_SD_MCI1_ID); + #else + TRACE_ERROR("SD/MMC card initialization failed (MCI1 not supported)\n\r"); + #endif + } +#if MCI_BUSY_CHECK_FIX && defined(BOARD_SD_DAT0) + MCI_SetBusyFix(mciDrv, &pinSdDAT0); +#endif + + // Initialize the SD card driver + if (SD_Init(sdDrv, (SdDriver *)mciDrv)) { + + TRACE_ERROR("SD/MMC card initialization failed\n\r"); + return 0; + } + else { + + //SD_DisplayRegisterCSD(&sdDrv); + TRACE_INFO("SD/MMC card initialization successful\n\r"); + TRACE_INFO("Card size: %d MB\n\r", (int)(MMC_GetTotalSizeKB(sdDrv)/1024)); + } + MCI_SetSpeed(mciDrv, sdDrv->transSpeed, sdDrv->transSpeed, BOARD_MCK); + + // Initialize media fields + //-------------------------------------------------------------------------- + media->interface = sdDrv; + #if !defined(OP_BOOTSTRAP_MCI_on) + media->write = MEDSdcard_Write; + #else + media->write = 0; + #endif + media->read = MEDSdcard_Read; + media->lock = 0; + media->unlock = 0; + media->handler = 0; + media->flush = 0; + + media->blockSize = SD_BLOCK_SIZE; + media->baseAddress = 0; + if (SD_TOTAL_SIZE(sdDrv) == 0xFFFFFFFF) + media->size = SD_TOTAL_BLOCK(sdDrv); + else + media->size = SD_TOTAL_SIZE(sdDrv)/SD_BLOCK_SIZE; + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = CardIsProtected(mciID); + media->removable = 1; + + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; + + return 1; +} + +//------------------------------------------------------------------------------ +/// Initializes a Media instance and the associated physical interface +/// \param media Pointer to the Media instance to initialize +/// \return 1 if success. +//------------------------------------------------------------------------------ +unsigned char MEDSdusb_Initialize(Media *media, unsigned char mciID) +{ + TRACE_INFO("MEDSdusb init\n\r"); + + // Initialize SDcard + //-------------------------------------------------------------------------- + + if (!CardIsConnected(mciID)) return 0; + + // Configure SDcard pins + ConfigurePIO(mciID); + + #if defined(MCI2_INTERFACE) + DMAD_Initialize(BOARD_MCI_DMA_CHANNEL, DMAD_NO_DEFAULT_IT); + #endif + // Initialize the MCI driver + if(mciID == 0) { + IRQ_ConfigureIT(BOARD_SD_MCI_ID, 1, MCI0_IrqHandler); + MCI_Init(mciDrv, BOARD_SD_MCI_BASE, BOARD_SD_MCI_ID, BOARD_SD_SLOT, MCI_INTERRUPT_MODE); + IRQ_EnableIT(BOARD_SD_MCI_ID); + } else { + #ifdef BOARD_SD_MCI1_ID + IRQ_ConfigureIT(BOARD_SD_MCI1_ID, 1, MCI0_IrqHandler); + MCI_Init(mciDrv, BOARD_SD_MCI1_BASE, BOARD_SD_MCI1_ID, BOARD_SD_SLOT, MCI_INTERRUPT_MODE); + IRQ_EnableIT(BOARD_SD_MCI1_ID); + #else + TRACE_ERROR("SD/MMC card initialization failed (MCI1 not supported)\n\r"); + #endif + } +#if MCI_BUSY_CHECK_FIX && defined(BOARD_SD_DAT0) + MCI_SetBusyFix(mciDrv, &pinSdDAT0); +#endif + + // Initialize the SD card driver + if (SD_Init(sdDrv, (SdDriver *)mciDrv)) { + + TRACE_ERROR("SD/MMC card initialization failed\n\r"); + return 0; + } + else { + + TRACE_INFO("SD/MMC card initialization successful\n\r"); + TRACE_INFO("Card size: %d MB\n\r", (int)(MMC_GetTotalSizeKB(sdDrv)/1024)); + } + MCI_SetSpeed(mciDrv, sdDrv->transSpeed, sdDrv->transSpeed, BOARD_MCK); + + // Initialize media fields + //-------------------------------------------------------------------------- + media->interface = sdDrv; + media->write = MEDSdusb_Write; + media->read = MEDSdusb_Read; + media->lock = 0; + media->unlock = 0; + media->handler = 0; + media->flush = 0; + + media->blockSize = SD_BLOCK_SIZE; + media->baseAddress = 0; + if (SD_TOTAL_SIZE(sdDrv) == 0xFFFFFFFF) + media->size = SD_TOTAL_BLOCK(sdDrv); + else + media->size = SD_TOTAL_SIZE(sdDrv)/SD_BLOCK_SIZE; + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = CardIsProtected(mciID); + media->removable = 1; + + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; + + return 1; +} + +//------------------------------------------------------------------------------ +/// erase all the Sdcard +/// \param media Pointer to the Media instance to initialize +//------------------------------------------------------------------------------ +void MEDSdcard_EraseAll(Media *media) +{ + unsigned char buffer[SD_BLOCK_SIZE]; + unsigned int block; + unsigned int multiBlock = 1; // change buffer size for multiblocks + unsigned char error; + + TRACE_INFO("MEDSdcard Erase All ...\n\r"); + + // Clear the block buffer + memset(buffer, 0, media->blockSize * multiBlock); + + for (block=0; + block < (SD_TOTAL_BLOCK((SdCard*)media->interface)-multiBlock); + block += multiBlock) { + + error = SD_WriteBlock((SdCard*)media->interface, block, multiBlock, buffer); + ASSERT(!error, "\n\r-F- Failed to write block (%d) #%u\n\r", error, block); + } +} + +//------------------------------------------------------------------------------ +/// erase block +/// \param media Pointer to the Media instance to initialize +/// \param block to erase +//------------------------------------------------------------------------------ +void MEDSdcard_EraseBlock(Media *media, unsigned int block) +{ + unsigned char buffer[SD_BLOCK_SIZE]; + unsigned char error; + + // Clear the block buffer + memset(buffer, 0, media->blockSize); + + error = SD_WriteBlock((SdCard*)media->interface, block, 1, buffer); + ASSERT(!error, "\n\r-F- Failed to write block (%d) #%u\n\r", error, block); +} + +//------------------------------------------------------------------------------ +/// Get driver pointer +//------------------------------------------------------------------------------ +SdCard* MEDSdcard_GetDriver(unsigned int slot) +{ + if (slot >= NUM_SD_SLOTS) return 0; + return &sdDrv[slot]; +} diff --git a/memories/MEDSdcard.h b/memories/MEDSdcard.h new file mode 100644 index 0000000..f15a08e --- /dev/null +++ b/memories/MEDSdcard.h @@ -0,0 +1,50 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef MEDSDCARD_H +#define MEDSDCARD_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char MEDSdcard_Detect(Media *media, unsigned char mciID); +extern unsigned char MEDSdcard_Initialize(Media *media, unsigned char mciID); +extern unsigned char MEDSdusb_Initialize(Media *media, unsigned char mciID); +extern void MEDSdcard_EraseAll(Media *media); +extern void MEDSdcard_EraseBlock(Media *media, unsigned int block); +extern SdCard* MEDSdcard_GetDriver(unsigned int slot); +#endif //#ifndef MEDSDCARD_H diff --git a/memories/MEDSdmmc.c b/memories/MEDSdmmc.c new file mode 100644 index 0000000..46a0550 --- /dev/null +++ b/memories/MEDSdmmc.c @@ -0,0 +1,481 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MEDSdmmc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(MCI2_INTERFACE) +#include +#else +#include +#include "dmad/dmad.h" +#endif + + +#include + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a SDCARD memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char MEDSdmmc_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char error; + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + + TRACE_INFO("Media busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to read is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("MEDSdmmc_Read: Data too big: %d, %d\n\r", + length, address); + return MED_STATUS_ERROR; + } + + // Enter Busy state + media->state = MED_STATE_BUSY; + + error = SD_ReadBlock((SdCard*)media->interface, address, length, data); + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke callback + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a SDRAM media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see MediaCallback +//------------------------------------------------------------------------------ +static unsigned char MEDSdmmc_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char error; + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + + TRACE_WARNING("MEDSdmmc_Write: Media is busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the card is not protected + if (media->protected) { + + return MED_STATUS_PROTECTED; + } + + // Check that the data to write is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("MEDSdmmc_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + + // Put the media in Busy state + media->state = MED_STATE_BUSY; + + error = SD_WriteBlock((SdCard*)media->interface, address, length, data); + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke the callback if it exists + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Callback invoked when SD/MMC transfer done +//------------------------------------------------------------------------------ +static void SdMmcCallback(unsigned char status, void *pCommand) +{ + SdCmd * pCmd = (SdCmd*)pCommand; + Media * pMed = pCmd->pArg; + MEDTransfer * pXfr = &pMed->transfer; + volatile unsigned char medStatus = MED_STATUS_SUCCESS; + + TRACE_INFO_WP("SDCb "); + + // Error + if (status == SD_ERROR_BUSY) { + //TRACE_WARNING("SD BUSY\n\r"); + medStatus = MED_STATUS_BUSY; + } + else if (status != MED_STATUS_SUCCESS) { + //TRACE_WARNING("SD st%d\n\r", status); + medStatus = MED_STATUS_ERROR; + } + + pMed->state = MED_STATE_READY; + if (pXfr->callback) { + pXfr->callback(pXfr->argument, + medStatus, + pXfr->length * pMed->blockSize, + 0); + } +} + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a SDCARD memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char MEDSdusb_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + MEDTransfer * pXfr; + unsigned char error; + + TRACE_INFO_WP("SDuRd(%d,%d) ", address, length); + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + TRACE_INFO("MEDSdusb_Read: Busy\n\r"); + return MED_STATUS_BUSY; + } + // Check that the data to read is not too big + if ((length + address) > media->size) { + TRACE_WARNING("MEDSdusb_Read: Data too big: %d, %d\n\r", + length, address); + return MED_STATUS_ERROR; + } + // Enter Busy state + media->state = MED_STATE_BUSY; + + // Start media transfer + pXfr = &media->transfer; + pXfr->data = data; + pXfr->address = address; + pXfr->length = length; + pXfr->callback = callback; + pXfr->argument = argument; + + error = SD_Read((SdCard*)media->interface, + address, + data, + length, + SdMmcCallback, + media); + + return (error ? MED_STATUS_ERROR : MED_STATUS_SUCCESS); +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a SDRAM media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see MediaCallback +//------------------------------------------------------------------------------ +static unsigned char MEDSdusb_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + MEDTransfer * pXfr; + unsigned char error; + TRACE_INFO_WP("SDuWr(%d,%d) ", address, length); + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + TRACE_INFO("MEDSdusb_Write: Busy\n\r"); + return MED_STATUS_BUSY; + } + // Check that the card is not protected + if (media->protected) { + return MED_STATUS_PROTECTED; + } + // Check that the data to write is not too big + if ((length + address) > media->size) { + TRACE_WARNING("MEDSdmmc_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + // Put the media in Busy state + media->state = MED_STATE_BUSY; + + // Start media transfer + pXfr = &media->transfer; + pXfr->data = data; + pXfr->address = address; + pXfr->length = length; + pXfr->callback = callback; + pXfr->argument = argument; + + error = SD_Write((SdCard*)media->interface, + address, + data, + length, + SdMmcCallback, + media); + + return (error ? MED_STATUS_ERROR : MED_STATUS_SUCCESS); +} + +//------------------------------------------------------------------------------ +// Exported Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a Media instance and the associated physical interface +/// \param media Pointer to the Media instance to initialize +/// \param sdDrv Pointer to the SdCard instance that initialized. +/// \param protected SD card protected status. +/// \return 1 if success. +//------------------------------------------------------------------------------ +void MEDSdcard_Initialize(Media *media, SdCard * sdDrv, unsigned char protected) +{ + TRACE_INFO("MEDSdcard init\n\r"); + + // Initialize SDcard: Should have been done before media interface init + //-------------------------------------------------------------------------- + + // Initialize media fields + //-------------------------------------------------------------------------- + media->interface = sdDrv; + media->write = MEDSdmmc_Write; + media->read = MEDSdmmc_Read; + media->cancelIo = 0; + media->lock = 0; + media->unlock = 0; + media->handler = 0; + media->flush = 0; + + media->baseAddress = 0; + if (SD_TOTAL_SIZE(sdDrv) == 0xFFFFFFFF) { + media->blockSize = SD_BLOCK_SIZE; + media->size = SD_TOTAL_BLOCK(sdDrv); + } + else { + media->blockSize = SD_TOTAL_SIZE(sdDrv) / SD_TOTAL_BLOCK(sdDrv); + if (media->blockSize != SD_BLOCK_SIZE) { + media->blockSize = SD_BLOCK_SIZE; + media->size = SD_TOTAL_SIZE(sdDrv) / SD_BLOCK_SIZE; + } + else { + media->size = SD_TOTAL_BLOCK(sdDrv); + } + } + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = protected; + media->removable = 1; + + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; +} + +//------------------------------------------------------------------------------ +/// Initializes a Media instance and the associated physical interface +/// \param media Pointer to the Media instance to initialize +/// \param sdDrv Pointer to the SdCard instance that initialized. +/// \param protected SD card protected status. +/// \return 1 if success. +//------------------------------------------------------------------------------ +void MEDSdusb_Initialize(Media *media, SdCard * sdDrv, unsigned char protected) +{ + TRACE_INFO("MEDSdusb init\n\r"); + + // Initialize SDcard: Should have been done before media interface init + //-------------------------------------------------------------------------- + + // Initialize media fields + //-------------------------------------------------------------------------- + media->interface = sdDrv; +#if defined(AT91C_MCI_WRPROOF) + media->write = MEDSdusb_Write; +#else + media->write = MEDSdmmc_Write; +#endif +#if defined(AT91C_MCI_RDPROOF) + media->read = MEDSdusb_Read; +#else + media->read = MEDSdmmc_Read; +#endif + media->cancelIo = 0; // Cancel pending IO, add later. + media->lock = 0; + media->unlock = 0; + media->handler = 0; + media->flush = 0; + + media->baseAddress = 0; + if (SD_TOTAL_SIZE(sdDrv) == 0xFFFFFFFF) { + media->blockSize = SD_BLOCK_SIZE; + media->size = SD_TOTAL_BLOCK(sdDrv); + } + else { + media->blockSize = SD_TOTAL_SIZE(sdDrv) / SD_TOTAL_BLOCK(sdDrv); + if (media->blockSize != SD_BLOCK_SIZE) { + media->blockSize = SD_BLOCK_SIZE; + media->size = SD_TOTAL_SIZE(sdDrv) / SD_BLOCK_SIZE; + } + else { + media->size = SD_TOTAL_BLOCK(sdDrv); + } + } + + media->mappedRD = 0; + media->mappedWR = 0; + media->protected = protected; + media->removable = 1; + + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; +} + +//------------------------------------------------------------------------------ +/// erase all the Sdcard +/// \param media Pointer to the Media instance to initialize +//------------------------------------------------------------------------------ +void MEDSdcard_EraseAll(Media *media) +{ + unsigned char buffer[SD_BLOCK_SIZE]; + unsigned int block; + unsigned int multiBlock = 1; // change buffer size for multiblocks + unsigned char error; + + TRACE_INFO("MEDSdcard Erase All ...\n\r"); + + // Clear the block buffer + memset(buffer, 0, media->blockSize * multiBlock); + + for (block=0; + block < (SD_TOTAL_BLOCK((SdCard*)media->interface)-multiBlock); + block += multiBlock) { + + error = SD_WriteBlock((SdCard*)media->interface, block, multiBlock, buffer); + ASSERT(!error, "\n\r-F- Failed to write block (%d) #%u\n\r", error, block); + } +} + +//------------------------------------------------------------------------------ +/// erase block +/// \param media Pointer to the Media instance to initialize +/// \param block to erase +//------------------------------------------------------------------------------ +void MEDSdcard_EraseBlock(Media *media, unsigned int block) +{ + unsigned char buffer[SD_BLOCK_SIZE]; + unsigned char error; + + // Clear the block buffer + memset(buffer, 0, media->blockSize); + + error = SD_WriteBlock((SdCard*)media->interface, block, 1, buffer); + ASSERT(!error, "\n\r-F- Failed to write block (%d) #%u\n\r", error, block); +} diff --git a/memories/MEDSdmmc.h b/memories/MEDSdmmc.h new file mode 100644 index 0000000..077c53f --- /dev/null +++ b/memories/MEDSdmmc.h @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef MEDSDMMC_H +#define MEDSDMMC_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "Media.h" +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void MEDSdcard_Initialize(Media *media, + SdCard * sdDrv, + unsigned char protected); +extern void MEDSdusb_Initialize(Media *media, + SdCard * sdDrv, + unsigned char protected); +extern void MEDSdcard_EraseAll(Media *media); +extern void MEDSdcard_EraseBlock(Media *media, unsigned int block); +#endif //#ifndef MEDSDCARD_H diff --git a/memories/MEDSdram.c b/memories/MEDSdram.c new file mode 100644 index 0000000..044330e --- /dev/null +++ b/memories/MEDSdram.c @@ -0,0 +1,272 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MEDSdram.h" +#include +#include +#include + +#if defined(AT91C_EBI_SDRAM) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Do copy and modify pointer +typedef void copyFunction(unsigned char **, unsigned char **, unsigned int); + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Do copy for 8-byte aligned data +//------------------------------------------------------------------------------ +static void AlignedCopy(unsigned char * *src, + unsigned char * *dst, + unsigned int len) +{ + unsigned int *src32, *dst32; + src32 = (unsigned int*)*src; + dst32 = (unsigned int*)*dst; + for (;len > 0; len -= 8) { + *dst32 ++ = *src32 ++; + *dst32 ++ = *src32 ++; + } + *src = (unsigned char*)src32; + *dst = (unsigned char*)dst32; +} + +//------------------------------------------------------------------------------ +/// Do copy for byte-aligned data +//------------------------------------------------------------------------------ +static void UnalignedCopy(unsigned char * *src, + unsigned char * *dst, + unsigned int len) +{ + for (;len > 0; len --) { + *(*dst) ++ = *(*src) ++; + } +} + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a SDRAM memory +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//------------------------------------------------------------------------------ +static unsigned char MEDSdram_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char *source; + unsigned char *dest; + copyFunction *pCpy; + + // Check that the media is ready + if (media->state != MED_STATE_READY) { + + TRACE_INFO("SDRAM busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to read is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("SdRamD_Read: Data too big: %u, 0x%08X\n\r", + length, address); + return MED_STATUS_ERROR; + } + + // Enter Busy state + media->state = MED_STATE_BUSY; + + // Source & Destination + source = (unsigned char *)(media->blockSize + * (address + media->baseAddress)); + dest = (unsigned char *)data; + + // Align/Unaligned copy + pCpy = (((unsigned int)source%4) == 0 && (media->blockSize%8) == 0) + ? AlignedCopy : UnalignedCopy; + + for (; length > 0; length --) { + pCpy(&source, &dest, media->blockSize); + } + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke callback + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on a SDRAM media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see Media +//! \see MediaCallback +//------------------------------------------------------------------------------ +static unsigned char MEDSdram_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + unsigned char *source; + unsigned char *dest; + copyFunction *pCpy; + + //TRACE_DEBUG("SdRamD_Write\n\r"); + + // Check that the media if ready + if (media->state != MED_STATE_READY) { + + TRACE_WARNING("SdRamD_Write: Media is busy\n\r"); + return MED_STATUS_BUSY; + } + + // Check that the data to write is not too big + if ((length + address) > media->size) { + + TRACE_WARNING("SdRamD_Write: Data too big\n\r"); + return MED_STATUS_ERROR; + } + + // Compute function parameters + source = (unsigned char *) data; + dest = (unsigned char *) (media->blockSize * + (media->baseAddress + address)); + + // Align/Unaligned copy + pCpy = (((unsigned int)source%4) == 0 && (media->blockSize%8) == 0) + ? AlignedCopy : UnalignedCopy; + + for (; length > 0; length --) { + pCpy(&source, &dest, media->blockSize); + } + + // Leave the Busy state + media->state = MED_STATE_READY; + + // Invoke the callback if it exists + if (callback != 0) { + + callback(argument, MED_STATUS_SUCCESS, 0, 0); + } + + return MED_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +// Exported Functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//! \brief Initializes a Media instance and the associated physical interface +//! \param media Pointer to the Media instance to initialize +//! \see Media +//------------------------------------------------------------------------------ +void MEDSdram_Initialize(Media *media, + unsigned int blockSize, + unsigned int baseAddress, + unsigned int size) +{ + unsigned int value; + + TRACE_INFO("MEDSdram init\n\r"); + + // Initialize SDRAM if not already done + //-------------------------------------------------------------------------- + value = *((volatile unsigned int *) AT91C_EBI_SDRAM); + *((volatile unsigned int *) AT91C_EBI_SDRAM) = 0xDEADBEEF; + + if (*((volatile unsigned int *) AT91C_EBI_SDRAM) == 0xDEADBEEF) { + + *((volatile unsigned int *) AT91C_EBI_SDRAM) = value; + TRACE_INFO("SDRAM already initialized\n\r"); + } + else { + + TRACE_INFO("Initializing the SDRAM ...\n\r"); + BOARD_ConfigureSdram(BOARD_SDRAM_BUSWIDTH); + } + + // Initialize media fields + //-------------------------------------------------------------------------- + media->write = MEDSdram_Write; + media->read = MEDSdram_Read; + media->lock = 0; + media->unlock = 0; + media->handler = 0; + media->flush = 0; + + media->blockSize = blockSize; + media->baseAddress = baseAddress; + media->size = size; + + media->mappedRD = 1; + media->mappedWR = 1; + media->protected = 0; + media->removable = 0; + media->state = MED_STATE_READY; + + media->transfer.data = 0; + media->transfer.address = 0; + media->transfer.length = 0; + media->transfer.callback = 0; + media->transfer.argument = 0; +} +#endif //#if defined(AT91C_EBI_SDRAM) + diff --git a/memories/MEDSdram.h b/memories/MEDSdram.h new file mode 100644 index 0000000..96aa2ea --- /dev/null +++ b/memories/MEDSdram.h @@ -0,0 +1,48 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef MEDSDRAM_H +#define MEDSDRAM_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "Media.h" + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void MEDSdram_Initialize(Media *media, + unsigned int blockSize, + unsigned int baseAddress, + unsigned int size); + +#endif //#ifndef MEDSDRAM_H diff --git a/memories/Media.c b/memories/Media.c new file mode 100644 index 0000000..f099b17 --- /dev/null +++ b/memories/Media.c @@ -0,0 +1,57 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "Media.h" + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +/// Number of medias which are effectively used. +unsigned int numMedias = 0; + +//------------------------------------------------------------------------------ +//! \brief Handle interrupts on specified media +//! \param pMedia List of media +//! \param bNumMedia Number of media in list +//! \see S_media +//------------------------------------------------------------------------------ +void MED_HandleAll(Media *pMedia, unsigned char bNumMedia) +{ + // Check each media for interrupts to handle + unsigned int i; + for (i = 0; i < bNumMedia; i++) { + + MED_Handler(&(pMedia[i])); + } +} diff --git a/memories/Media.h b/memories/Media.h new file mode 100644 index 0000000..09700f8 --- /dev/null +++ b/memories/Media.h @@ -0,0 +1,284 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Generic Media type, which provides transparent access to all types of +/// memories. +/// +/// \note The physical or HW related media operations (physical device +/// connection & protection detecting, PIO configurations and interface +/// driver initialization) are excluded. +/// +/// !Usage +/// -# Do PIO initialization for peripheral interfaces. +/// -# Initialize peripheral interface driver & device driver. +/// -# Initialize specific media interface and link to this initialized driver. +/// +//------------------------------------------------------------------------------ + +#ifndef MEDIA_H +#define MEDIA_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//! \brief Operation result code returned by media methods +#define MED_STATUS_SUCCESS 0x00 +#define MED_STATUS_ERROR 0x01 +#define MED_STATUS_BUSY 0x02 +#define MED_STATUS_PROTECTED 0x04 + +//! \brief Media statuses +#define MED_STATE_READY 0x00 /// Media is ready for access +#define MED_STATE_BUSY 0x01 /// Media is busy + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ +typedef struct _Media Media; + +typedef void (*MediaCallback)(void *argument, + unsigned char status, + unsigned int transferred, + unsigned int remaining); + +typedef unsigned char (*Media_write)(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument); + +typedef unsigned char (*Media_read)(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument); + +typedef unsigned char (*Media_cancelIo)(Media *media); + +typedef unsigned char (*Media_lock)(Media *media, + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd); + +typedef unsigned char (*Media_unlock)(Media *media, + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd); + +typedef unsigned char (*Media_ioctl)(Media *media, + unsigned char ctrl, + void *buff); + +typedef unsigned char (*Media_flush)(Media *media); + +typedef void (*Media_handler)(Media *media); + +//! \brief Media transfer +//! \see TransferCallback +typedef struct { + + void *data; //!< Pointer to the data buffer + unsigned int address; //!< Address where to read/write the data + unsigned int length; //!< Size of the data to read/write + MediaCallback callback; //!< Callback to invoke when the transfer done + void *argument; //!< Callback argument + +} MEDTransfer; + +//! \brief Media object +//! \see MEDTransfer +struct _Media { + + Media_write write; //!< Write method + Media_read read; //!< Read method + Media_cancelIo cancelIo; //!< Cancel pending IO method + Media_lock lock; //!< lock method if possible + Media_unlock unlock; //!< unlock method if possible + Media_flush flush; //!< Flush method + Media_handler handler; //!< Interrupt handler + unsigned int blockSize; //!< Block size in bytes (1, 512, 1K, 2K ...) + unsigned int baseAddress; //!< Base address of media in number of blocks + unsigned int size; //!< Size of media in number of blocks + MEDTransfer transfer; //!< Current transfer operation + void *interface; //!< Pointer to the physical interface used + unsigned char bReserved:4, + mappedRD:1, //!< Mapped to memory space to read + mappedWR:1, //!< Mapped to memory space to write + protected:1, //!< Protected media? + removable:1; //!< Removable/Fixed media? + unsigned char state; //!< Status of media + unsigned short reserved; +}; + +/// Available medias. +extern Media medias[]; + +/// Number of medias which are effectively used. +/// Defined by Media, shared usage by USB MSD & FS ... +extern unsigned int numMedias; + +//------------------------------------------------------------------------------ +// Inline Functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//! \brief Writes data on a media +//! \param media Pointer to a Media instance +//! \param address Address at which to write +//! \param data Pointer to the data to write +//! \param length Size of the data buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the write operation terminates +//! \param argument Optional argument for the callback function +//! \return Operation result code +//! \see TransferCallback +//------------------------------------------------------------------------------ +static inline unsigned char MED_Write(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + return media->write(media, address, data, length, callback, argument); +} + +//------------------------------------------------------------------------------ +//! \brief Reads a specified amount of data from a media +//! \param media Pointer to a Media instance +//! \param address Address of the data to read +//! \param data Pointer to the buffer in which to store the retrieved +//! data +//! \param length Length of the buffer +//! \param callback Optional pointer to a callback function to invoke when +//! the operation is finished +//! \param argument Optional pointer to an argument for the callback +//! \return Operation result code +//! \see TransferCallback +//------------------------------------------------------------------------------ +static inline unsigned char MED_Read(Media *media, + unsigned int address, + void *data, + unsigned int length, + MediaCallback callback, + void *argument) +{ + return media->read(media, address, data, length, callback, argument); +} + +//------------------------------------------------------------------------------ +//! \brief Locks all the regions in the given address range. +//! \param media Pointer to a Media instance +/// \param start Start address of lock range. +/// \param end End address of lock range. +/// \param pActualStart Start address of the actual lock range (optional). +/// \param pActualEnd End address of the actual lock range (optional). +/// \return 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +static inline unsigned char MED_Lock(Media *media, + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + if( media->lock ) { + return media->lock(media, start, end, pActualStart, pActualEnd); + } + else { + return MED_STATUS_SUCCESS; + } +} + +//------------------------------------------------------------------------------ +//! \brief Unlocks all the regions in the given address range +//! \param media Pointer to a Media instance +/// \param start Start address of unlock range. +/// \param end End address of unlock range. +/// \param pActualStart Start address of the actual unlock range (optional). +/// \param pActualEnd End address of the actual unlock range (optional). +/// \return 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +static inline unsigned char MED_Unlock(Media *media, + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + if( media->unlock ) { + return media->unlock(media, start, end, pActualStart, pActualEnd); + } + else { + return MED_STATUS_SUCCESS; + } +} + +//------------------------------------------------------------------------------ +//! \brief +//! \param media Pointer to the Media instance to use +//------------------------------------------------------------------------------ +static inline unsigned char MED_Flush(Media *media) +{ + if (media->flush) { + + return media->flush(media); + } + else { + + return MED_STATUS_SUCCESS; + } +} + +//------------------------------------------------------------------------------ +//! \brief Invokes the interrupt handler of the specified media +//! \param media Pointer to the Media instance to use +//------------------------------------------------------------------------------ +static inline void MED_Handler(Media *media) +{ + if (media->handler) { + + media->handler(media); + } +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void MED_HandleAll(Media *medias, unsigned char numMedias); + +#endif // _MEDIA_H + diff --git a/memories/flash/flashd.dir b/memories/flash/flashd.dir new file mode 100644 index 0000000..29505a2 --- /dev/null +++ b/memories/flash/flashd.dir @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// Contains the %flashd driver for the Embedded Flash Controller EFC(EEFC). +//------------------------------------------------------------------------------ + diff --git a/memories/flash/flashd.h b/memories/flash/flashd.h new file mode 100644 index 0000000..b1d9a50 --- /dev/null +++ b/memories/flash/flashd.h @@ -0,0 +1,128 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !Purpose +/// +/// The goal of FLASHD driver is to provide support for ATMEL Embedded Flash +/// devices. The FLASHD driver provides the unified interface of functions +/// and operations for all supported Embedded Flash Memory Devices. The driver +/// accesses the Flash memory by calling the low-level code which provided in +/// EFC(EEFC) peripheral,o users do not have to concern themselves with the +/// details of the special command sequences. +/// +/// !Usage +/// +/// -# Initializes the %flash driver with FLASHD_Initialize(). +/// -# Provide all neccessary operations such as %flash write, erase, lock, +/// unlock, and set/clear GPNVM. +/// - They are able to check function arguments, address boundary etc. +/// - All %flash address argument is the unique address, as the result, +/// user no need to take care about the address is in which EFC, +/// in which page and in which lock region for difference Flash device, +/// so that can help develop your uppon appcations with maximum efficiency. +/// -# Writes data in embedded %flash using FLASHD_Write(). +/// -# Erase data in embedded %flash using FLASHD_Write(). +/// -# Lock region in embedded %flash using FLASHD_Lock(). +/// -# Unlock region in embedded %flash using FLASHD_Unlock(). +/// -# Set GPNVM in embedded %flash using FLASHD_SetGPNVM(). +/// -# Clear GPNVM in embedded %flash using FLASHD_ClearGPNVM(). +/// +/// !See also +/// - efc: EFC peripheral interface. +/// - eefc: EEFC peripheral interface. +//------------------------------------------------------------------------------ +#ifndef FLASHD_H +#define FLASHD_H + +#include + +#if defined (CHIP_FLASH_EFC) +#include +#elif defined (CHIP_FLASH_EEFC) +#include +#endif + +//------------------------------------------------------------------------------ +// Functions +//------------------------------------------------------------------------------ + +extern void FLASHD_Initialize(unsigned int mck); + +extern unsigned char FLASHD_Erase(unsigned int address); + +extern unsigned char FLASHD_Write( + unsigned int address, + const void *pBuffer, + unsigned int size); + +extern unsigned char FLASHD_Lock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd); + +extern unsigned char FLASHD_Unlock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd); + +extern unsigned char FLASHD_IsLocked( + unsigned int start, + unsigned int end); + +extern unsigned char FLASHD_SetGPNVM(unsigned char gpnvm); + +extern unsigned char FLASHD_ClearGPNVM(unsigned char gpnvm); + +extern unsigned char FLASHD_IsGPNVMSet(unsigned char gpnvm); + +#if defined(CHIP_FLASH_EFC) + +#if !defined EFC_NO_SECURITY_BIT +extern unsigned char FLASHD_IsSecurityBitSet(void); + +extern unsigned char FLASHD_SetSecurityBit(void); +#endif + +#elif defined(CHIP_FLASH_EEFC) + +#define FLASHD_IsSecurityBitSet() FLASHD_IsGPNVMSet(0) + +#define FLASHD_SetSecurityBit() FLASHD_SetGPNVM(0) + +extern unsigned char FLASHD_ReadUniqueID(unsigned long * uniqueID); + +#endif //#if defined(CHIP_FLASH_EFC) + + +#endif //#ifndef FLASHD_H + diff --git a/memories/flash/flashd_eefc.c b/memories/flash/flashd_eefc.c new file mode 100644 index 0000000..94fea3c --- /dev/null +++ b/memories/flash/flashd_eefc.c @@ -0,0 +1,494 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2009, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "flashd.h" +#include + +#if !defined (CHIP_FLASH_EEFC) +#error eefc not supported +#endif + +#include +#include +#include +#include + +#include + + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +#if defined(AT91C_BASE_EFC) && !defined(AT91C_BASE_EFC0) + #define AT91C_BASE_EFC0 AT91C_BASE_EFC +#endif + + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Computes the lock range associated with the given address range. +/// \param start Start address of lock range. +/// \param end End address of lock range. +/// \param pActualStart Actual start address of lock range. +/// \param pActualEnd Actual end address of lock range. +//------------------------------------------------------------------------------ +static void ComputeLockRange( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pStartEfc, *pEndEfc; + unsigned short startPage, endPage; + unsigned short numPagesInRegion; + unsigned short actualStartPage, actualEndPage; + + // Convert start and end address in page numbers + EFC_TranslateAddress(&pStartEfc, start, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, end, &endPage, 0); + + // Find out the first page of the first region to lock + numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + actualStartPage = startPage - (startPage % numPagesInRegion); + actualEndPage = endPage; + if ((endPage % numPagesInRegion) != 0) { + + actualEndPage += numPagesInRegion - (endPage % numPagesInRegion); + } + + // Store actual page numbers + EFC_ComputeAddress(pStartEfc, actualStartPage, 0, pActualStart); + EFC_ComputeAddress(pEndEfc, actualEndPage, 0, pActualEnd); + TRACE_DEBUG("Actual lock range is 0x%06X - 0x%06X\n\r", *pActualStart, *pActualEnd); +} + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the flash driver. +/// \param mck Master clock frequency in Hz. +//------------------------------------------------------------------------------ +void FLASHD_Initialize(unsigned int mck) +{ + EFC_DisableFrdyIt(AT91C_BASE_EFC0); +#ifdef AT91C_BASE_EFC1 + EFC_DisableFrdyIt(AT91C_BASE_EFC1); +#endif +} + +//------------------------------------------------------------------------------ +/// Erases the entire flash. +/// Returns 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +unsigned char FLASHD_Erase(unsigned int address) +{ + AT91S_EFC *pEfc; + unsigned short page; + unsigned short offset; + unsigned char error; + +#ifdef AT91C_BASE_EFC1 + // Convert wrapped address to physical address. + address &= 0x19FFFF; + SANITY_CHECK((address >=AT91C_IFLASH) || (address >=AT91C_IFLASH1)); + // Check EFC crossover 2 bank + if (address >=AT91C_IFLASH1) { + SANITY_CHECK(address <= (AT91C_IFLASH1 + AT91C_IFLASH1_SIZE)); + } + else { + SANITY_CHECK(address <= (AT91C_IFLASH + AT91C_IFLASH_SIZE)); + } +#else + SANITY_CHECK((address >=AT91C_IFLASH) || (address <= (AT91C_IFLASH + AT91C_IFLASH_SIZE))); +#endif + // Translate write address + EFC_TranslateAddress(&pEfc, address, &page, &offset); + + error = EFC_PerformCommand(pEfc, AT91C_EFC_FCMD_EA, 0); + + return error; +} + + +static unsigned char pPageBuffer[AT91C_IFLASH_PAGE_SIZE]; + +//------------------------------------------------------------------------------ +/// Writes a data buffer in the internal flash. This function works in polling +/// mode, and thus only returns when the data has been effectively written. +/// Returns 0 if successful; otherwise returns an error code. +/// \param address Write address. +/// \param pBuffer Data buffer. +/// \param size Size of data buffer in bytes. +//------------------------------------------------------------------------------ +unsigned char FLASHD_Write( + unsigned int address, + const void *pBuffer, + unsigned int size) +{ + AT91S_EFC *pEfc; + unsigned short page; + unsigned short offset; + unsigned int writeSize; + unsigned int pageAddress; + unsigned short padding; + unsigned char error; + + unsigned int sizeTmp; + unsigned int *pAlignedDestination; + unsigned int *pAlignedSource; + + SANITY_CHECK(pBuffer); +#ifdef AT91C_BASE_EFC1 + // Convert wrapped address to physical address. + address &= 0x19FFFF; + SANITY_CHECK((address >=AT91C_IFLASH) || (address >=AT91C_IFLASH1)); + // Check EFC crossover 2 bank + if (address >=AT91C_IFLASH1) { + SANITY_CHECK((address + size) <= (AT91C_IFLASH1 + AT91C_IFLASH1_SIZE)); + } + else { + SANITY_CHECK((address + size) <= (AT91C_IFLASH + AT91C_IFLASH_SIZE)); + } +#else + SANITY_CHECK(address >=AT91C_IFLASH); + SANITY_CHECK((address + size) <= (AT91C_IFLASH + AT91C_IFLASH_SIZE)); +#endif + // Translate write address + EFC_TranslateAddress(&pEfc, address, &page, &offset); + + // Write all pages + while (size > 0) { + + // Copy data in temporary buffer to avoid alignment problems + writeSize = min(AT91C_IFLASH_PAGE_SIZE - offset, size); + EFC_ComputeAddress(pEfc, page, 0, &pageAddress); + padding = AT91C_IFLASH_PAGE_SIZE - offset - writeSize; + + // Pre-buffer data + memcpy(pPageBuffer, (void *) pageAddress, offset); + + // Buffer data + memcpy(pPageBuffer + offset, pBuffer, writeSize); + + // Post-buffer data + memcpy(pPageBuffer + offset + writeSize, (void *) (pageAddress + offset + writeSize), padding); + + // Write page + // Writing 8-bit and 16-bit data is not allowed + // and may lead to unpredictable data corruption + pAlignedDestination = (unsigned int*)pageAddress; + pAlignedSource = (unsigned int*)pPageBuffer; + sizeTmp = AT91C_IFLASH_PAGE_SIZE; + while (sizeTmp >= 4) { + + *pAlignedDestination++ = *pAlignedSource++; + sizeTmp -= 4; + } + + // Send writing command + error = EFC_PerformCommand(pEfc, AT91C_EFC_FCMD_EWP, page); + if (error) { + + return error; + } + + // Progression + address += AT91C_IFLASH_PAGE_SIZE; + pBuffer = (void *) ((unsigned int) pBuffer + writeSize); + size -= writeSize; + page++; + offset = 0; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Locks all the regions in the given address range. The actual lock range is +/// reported through two output parameters. +/// Returns 0 if successful; otherwise returns an error code. +/// \param start Start address of lock range. +/// \param end End address of lock range. +/// \param pActualStart Start address of the actual lock range (optional). +/// \param pActualEnd End address of the actual lock range (optional). +//------------------------------------------------------------------------------ +unsigned char FLASHD_Lock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pEfc; + unsigned int actualStart, actualEnd; + unsigned short startPage, endPage; + unsigned char error; + unsigned short numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + + // Compute actual lock range and store it + ComputeLockRange(start, end, &actualStart, &actualEnd); + if (pActualStart) { + + *pActualStart = actualStart; + } + if (pActualEnd) { + + *pActualEnd = actualEnd; + } + + // Compute page numbers + EFC_TranslateAddress(&pEfc, actualStart, &startPage, 0); + EFC_TranslateAddress(0, actualEnd, &endPage, 0); + + // Lock all pages + while (startPage < endPage) { + + error = EFC_PerformCommand(pEfc, AT91C_EFC_FCMD_SLB, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Unlocks all the regions in the given address range. The actual unlock range is +/// reported through two output parameters. +/// Returns 0 if successful; otherwise returns an error code. +/// \param start Start address of unlock range. +/// \param end End address of unlock range. +/// \param pActualStart Start address of the actual unlock range (optional). +/// \param pActualEnd End address of the actual unlock range (optional). +//------------------------------------------------------------------------------ +unsigned char FLASHD_Unlock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pEfc; + unsigned int actualStart, actualEnd; + unsigned short startPage, endPage; + unsigned char error; + unsigned short numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + + // Compute actual unlock range and store it + ComputeLockRange(start, end, &actualStart, &actualEnd); + if (pActualStart) { + + *pActualStart = actualStart; + } + if (pActualEnd) { + + *pActualEnd = actualEnd; + } + + // Compute page numbers + EFC_TranslateAddress(&pEfc, actualStart, &startPage, 0); + EFC_TranslateAddress(0, actualEnd, &endPage, 0); + + // Unlock all pages + while (startPage < endPage) { + + error = EFC_PerformCommand(pEfc, AT91C_EFC_FCMD_CLB, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns the number of locked regions inside the given address range. +/// \param start Start address of range. +/// \param end End address of range. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsLocked(unsigned int start, unsigned int end) +{ + AT91S_EFC *pEfc; + unsigned short startPage, endPage; + unsigned char startRegion, endRegion; + unsigned int numPagesInRegion; + unsigned int status; + unsigned char error; + unsigned int numLockedRegions = 0; + + SANITY_CHECK(end >= start); +#ifdef AT91C_BASE_EFC1 + // Convert wrapped address to physical address. + start &= 0x19FFFF; + end &= 0x19FFFF; + // Check EFC crossover 2 bank + SANITY_CHECK(((start >=AT91C_IFLASH) && (end <= AT91C_IFLASH + AT91C_IFLASH_SIZE)) + || ((start >=AT91C_IFLASH1) && (end <= AT91C_IFLASH1 + AT91C_IFLASH1_SIZE))); +#else + SANITY_CHECK((start >=AT91C_IFLASH) && (end <= AT91C_IFLASH + AT91C_IFLASH_SIZE)); +#endif + + // Compute page numbers + EFC_TranslateAddress(&pEfc, start, &startPage, 0); + EFC_TranslateAddress(0, end, &endPage, 0); + + // Compute region numbers + numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + startRegion = startPage / numPagesInRegion; + endRegion = endPage / numPagesInRegion; + if ((endPage % numPagesInRegion) != 0) { + + endRegion++; + } + + // Retrieve lock status + error = EFC_PerformCommand(pEfc, AT91C_EFC_FCMD_GLB, 0); + ASSERT(!error, "-F- Error while trying to fetch lock bits status (0x%02X)\n\r", error); + status = EFC_GetResult(pEfc); + + // Check status of each involved region + while (startRegion < endRegion) { + + if ((status & (1 << startRegion)) != 0) { + + numLockedRegions++; + } + startRegion++; + } + + return numLockedRegions; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the given GPNVM bit is currently set; otherwise returns 0. +/// \param gpnvm GPNVM bit index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsGPNVMSet(unsigned char gpnvm) +{ + unsigned char error; + unsigned int status; + + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + + // Get GPNVMs status + error = EFC_PerformCommand(AT91C_BASE_EFC, AT91C_EFC_FCMD_GFB, 0); + ASSERT(!error, "-F- Error while trying to fetch GPNVMs status (0x%02X)\n\r", error); + status = EFC_GetResult(AT91C_BASE_EFC); + + // Check if GPNVM is set + if ((status & (1 << gpnvm)) != 0) { + + return 1; + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Sets the selected GPNVM bit. +/// Returns 0 if successful; otherwise returns an error code. +/// \param gpnvm GPNVM index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_SetGPNVM(unsigned char gpnvm) +{ + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + + if (!FLASHD_IsGPNVMSet(gpnvm)) { + + return EFC_PerformCommand(AT91C_BASE_EFC, AT91C_EFC_FCMD_SFB, gpnvm); + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Clears the selected GPNVM bit. +/// Returns 0 if successful; otherwise returns an error code. +/// \param gpnvm GPNVM index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_ClearGPNVM(unsigned char gpnvm) +{ + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + + if (FLASHD_IsGPNVMSet(gpnvm)) { + + return EFC_PerformCommand(AT91C_BASE_EFC, AT91C_EFC_FCMD_CFB, gpnvm); + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Read the unique ID. +/// Returns 0 if successful; otherwise returns an error code. +/// \param uniqueID pointer on a 4bytes char containing the unique ID value. +//------------------------------------------------------------------------------ +#ifdef AT91C_EFC_FCMD_STUI +unsigned char FLASHD_ReadUniqueID (unsigned long * uniqueID) +{ + unsigned char error; + + SANITY_CHECK(uniqueID != NULL); + + uniqueID[0] = 0; + uniqueID[1] = 0; + uniqueID[2] = 0; + uniqueID[3] = 0; + + EFC_StartCommand(AT91C_BASE_EFC, AT91C_EFC_FCMD_STUI, 0); + + uniqueID[0] = *(unsigned int *)AT91C_IFLASH; + uniqueID[1] = *(unsigned int *)(AT91C_IFLASH + 4); + uniqueID[2] = *(unsigned int *)(AT91C_IFLASH + 8); + uniqueID[3] = *(unsigned int *)(AT91C_IFLASH + 12); + + error = EFC_PerformCommand(AT91C_BASE_EFC, AT91C_EFC_FCMD_SPUI, 0); + if (error) return error; + + return 0; +} +#endif diff --git a/memories/flash/flashd_efc.c b/memories/flash/flashd_efc.c new file mode 100644 index 0000000..f3fb2ee --- /dev/null +++ b/memories/flash/flashd_efc.c @@ -0,0 +1,598 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "flashd.h" +#include + +#if !defined(CHIP_FLASH_EFC) +#error efc not supported +#endif + +#include +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +#if defined(AT91C_BASE_EFC) && !defined(AT91C_BASE_EFC0) + #define AT91C_BASE_EFC0 AT91C_BASE_EFC +#endif + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Computes the lock range associated with the given address range. +/// \param start Start address of lock range. +/// \param end End address of lock range. +/// \param pActualStart Actual start address of lock range. +/// \param pActualEnd Actual end address of lock range. +//------------------------------------------------------------------------------ +static void ComputeLockRange( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pStartEfc, *pEndEfc; + unsigned short startPage, endPage; + unsigned short numPagesInRegion; + unsigned short actualStartPage, actualEndPage; + + // Convert start and end address in page numbers + EFC_TranslateAddress(&pStartEfc, start, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, end, &endPage, 0); + + // Find out the first page of the first region to lock + numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + actualStartPage = startPage - (startPage % numPagesInRegion); + actualEndPage = endPage; + if ((endPage % numPagesInRegion) != 0) { + + actualEndPage += numPagesInRegion - (endPage % numPagesInRegion); + } + + // Store actual page numbers + EFC_ComputeAddress(pStartEfc, actualStartPage, 0, pActualStart); + EFC_ComputeAddress(pEndEfc, actualEndPage, 0, pActualEnd); + TRACE_DEBUG("Actual lock range is 0x%06X - 0x%06X\n\r", *pActualStart, *pActualEnd); +} + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the flash driver. +/// \param mck Master clock frequency in Hz. +//------------------------------------------------------------------------------ +void FLASHD_Initialize(unsigned int mck) +{ + EFC_SetMasterClock(mck); + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC0, 1); + EFC_DisableIt(AT91C_BASE_EFC0, AT91C_MC_FRDY | AT91C_MC_LOCKE | AT91C_MC_PROGE); +#ifdef AT91C_BASE_EFC1 + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC1, 1); + EFC_DisableIt(AT91C_BASE_EFC1, AT91C_MC_FRDY | AT91C_MC_LOCKE | AT91C_MC_PROGE); +#endif +} + +//------------------------------------------------------------------------------ +/// Erases the entire flash. +/// Returns 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +unsigned char FLASHD_Erase(unsigned int address) +{ + AT91S_EFC *pEfc; + unsigned short page; + unsigned short offset; + unsigned char error; + + SANITY_CHECK((address >=AT91C_IFLASH) || (address <= (AT91C_IFLASH + AT91C_IFLASH_SIZE))); + + // Translate write address + EFC_TranslateAddress(&pEfc, address, &page, &offset); + + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_ERASE_ALL, 0); + + return error; +} + +static unsigned char pPageBuffer[AT91C_IFLASH_PAGE_SIZE]; + +//------------------------------------------------------------------------------ +/// Writes a data buffer in the internal flash. This function works in polling +/// mode, and thus only returns when the data has been effectively written. +/// Returns 0 if successful; otherwise returns an error code. +/// \param address Write address. +/// \param pBuffer Data buffer. +/// \param size Size of data buffer in bytes. +//------------------------------------------------------------------------------ +unsigned char FLASHD_Write( + unsigned int address, + const void *pBuffer, + unsigned int size) +{ + AT91S_EFC *pEfc; + unsigned short page; + unsigned short offset; + unsigned int writeSize; + unsigned int pageAddress; + unsigned short padding; + unsigned char error; + + unsigned int sizeTmp; + unsigned int *pAlignedDestination; + unsigned int *pAlignedSource; + + SANITY_CHECK(address >= AT91C_IFLASH); + SANITY_CHECK(pBuffer); + SANITY_CHECK((address + size) <= (AT91C_IFLASH + AT91C_IFLASH_SIZE)); + + // Translate write address + EFC_TranslateAddress(&pEfc, address, &page, &offset); + + // Write all pages + while (size > 0) { + + // Copy data in temporary buffer to avoid alignment problems + writeSize = min(AT91C_IFLASH_PAGE_SIZE - offset, size); + EFC_ComputeAddress(pEfc, page, 0, &pageAddress); + padding = AT91C_IFLASH_PAGE_SIZE - offset - writeSize; + + // Pre-buffer data (mask with 0xFF) + memcpy(pPageBuffer, (void *) pageAddress, offset); + + // Buffer data + memcpy(pPageBuffer + offset, pBuffer, writeSize); + + // Post-buffer data + memcpy(pPageBuffer + offset + writeSize, (void *) (pageAddress + offset + writeSize), padding); + + // Write page + // Writing 8-bit and 16-bit data is not allowed + // and may lead to unpredictable data corruption +#ifdef EFC_EVEN_ODD_PROG + // Write even words first with auto erase + pAlignedDestination = (unsigned int*)pageAddress; + pAlignedSource = (unsigned int*)pPageBuffer; + sizeTmp = AT91C_IFLASH_PAGE_SIZE; + while (sizeTmp >= 4) { + + *pAlignedDestination = *pAlignedSource; + pAlignedDestination += 2; + pAlignedSource += 2; + sizeTmp -= 8; + } + // Send writing command + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_START_PROG, page); + if (error) { + + return error; + } + + // Then write odd words without auto erase + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC0, 0); +#ifdef AT91C_BASE_EFC1 + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC1, 0); +#endif + pAlignedDestination = (unsigned int*)pageAddress + 1; + pAlignedSource = (unsigned int*)pPageBuffer + 1; + sizeTmp = AT91C_IFLASH_PAGE_SIZE; + while (sizeTmp >= 4) { + + *pAlignedDestination = *pAlignedSource; + pAlignedDestination += 2; + pAlignedSource += 2; + sizeTmp -= 8; + } + + // Send writing command + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_START_PROG, page); + if (error) { + + return error; + } + + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC0, 1); +#ifdef AT91C_BASE_EFC1 + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC1, 1); +#endif + +#else + pAlignedDestination = (unsigned int*)pageAddress; + pAlignedSource = (unsigned int*)pPageBuffer; + sizeTmp = AT91C_IFLASH_PAGE_SIZE; + while (sizeTmp >= 4) { + + *pAlignedDestination++ = *pAlignedSource++; + sizeTmp -= 4; + } + + // Send writing command + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_START_PROG, page); + if (error) { + + return error; + } +#endif + // Progression + address += AT91C_IFLASH_PAGE_SIZE; + pBuffer = (void *) ((unsigned int) pBuffer + writeSize); + size -= writeSize; + page++; + offset = 0; + +#if defined(AT91C_BASE_EFC1) + // Handle EFC crossover + if ((pEfc == AT91C_BASE_EFC0) && (page >= (AT91C_IFLASH_NB_OF_PAGES / 2))) { + + pEfc = AT91C_BASE_EFC1; + page = 0; + } +#endif + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Locks all the regions in the given address range. The actual lock range is +/// reported through two output parameters. +/// Returns 0 if successful; otherwise returns an error code. +/// \param start Start address of lock range. +/// \param end End address of lock range. +/// \param pActualStart Start address of the actual lock range (optional). +/// \param pActualEnd End address of the actual lock range (optional). +//------------------------------------------------------------------------------ +unsigned char FLASHD_Lock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pStartEfc, *pEndEfc, *pEfc; + unsigned int actualStart, actualEnd; + unsigned short startPage, endPage; + unsigned char error; + unsigned short numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + + // Compute actual lock range and store it + ComputeLockRange(start, end, &actualStart, &actualEnd); + if (pActualStart) { + + *pActualStart = actualStart; + } + if (pActualEnd) { + + *pActualEnd = actualEnd; + } + + // Compute page numbers + EFC_TranslateAddress(&pStartEfc, actualStart, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, actualEnd, &endPage, 0); + + // Lock all pages + // If there is an EFC crossover, lock all pages from first EFC +#if defined(AT91C_BASE_EFC1) + if (pStartEfc != pEndEfc) { + + while (startPage < (AT91C_IFLASH_NB_OF_PAGES / 2)) { + + error = EFC_PerformCommand(pStartEfc, AT91C_MC_FCMD_LOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + startPage = 0; + } +#endif + pEfc = pEndEfc; + + // Lock remaining pages + while (startPage < endPage) { + + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_LOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Unlocks all the regions in the given address range. The actual unlock range is +/// reported through two output parameters. +/// Returns 0 if successful; otherwise returns an error code. +/// \param start Start address of unlock range. +/// \param end End address of unlock range. +/// \param pActualStart Start address of the actual unlock range (optional). +/// \param pActualEnd End address of the actual unlock range (optional). +//------------------------------------------------------------------------------ +unsigned char FLASHD_Unlock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pStartEfc, *pEndEfc, *pEfc; + unsigned int actualStart, actualEnd; + unsigned short startPage, endPage; + unsigned char error; + unsigned short numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + + // Compute actual unlock range and store it + ComputeLockRange(start, end, &actualStart, &actualEnd); + if (pActualStart) { + + *pActualStart = actualStart; + } + if (pActualEnd) { + + *pActualEnd = actualEnd; + } + + // Compute page numbers + EFC_TranslateAddress(&pStartEfc, actualStart, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, actualEnd, &endPage, 0); + + // Unlock all pages + // If there is an EFC crossover, unlock all pages from first EFC +#if defined(AT91C_BASE_EFC1) + if (pStartEfc != pEndEfc) { + + while (startPage < (AT91C_IFLASH_NB_OF_PAGES / 2)) { + + error = EFC_PerformCommand(pStartEfc, AT91C_MC_FCMD_UNLOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + startPage = 0; + } +#endif + pEfc = pEndEfc; + + // Unlock remaining pages + while (startPage < endPage) { + + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_UNLOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns the number of locked regions inside the given address range. +/// \param start Start address of range. +/// \param end End address of range. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsLocked(unsigned int start, unsigned int end) +{ + AT91S_EFC *pStartEfc, *pEndEfc, *pEfc; + unsigned short startPage, endPage; + unsigned char startRegion, endRegion; + unsigned int numPagesInRegion; + unsigned int numLockedRegions = 0; + unsigned int status; + + // Get EFC & page values + EFC_TranslateAddress(&pStartEfc, start, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, end, &endPage, 0); + + // Compute region indexes + numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + startRegion = startPage / numPagesInRegion; + endRegion = endPage / numPagesInRegion; + if ((endPage % numPagesInRegion) != 0) { + + endRegion++; + } + + // EFC cross-over, handle starting page -> end page of EFC0 +#if defined(AT91C_BASE_EFC1) + if (pStartEfc != pEndEfc) { + + status = EFC_GetStatus(pStartEfc); + while (startRegion < 16) { + + if ((status & (1 << startRegion << 16)) != 0) { + + numLockedRegions++; + } + startRegion++; + } + startRegion = 0; + } +#endif + pEfc = pEndEfc; + + // Remaining regions / no EFC cross-over + status = EFC_GetStatus(pEfc); + while (startRegion < endRegion) { + + if ((status & (1 << startRegion << 16)) != 0) { + + numLockedRegions++; + } + startRegion++; + } + + return numLockedRegions; +} + +#if (CHIP_EFC_NUM_GPNVMS > 0) +//------------------------------------------------------------------------------ +/// Returns 1 if the given GPNVM bit is currently set; otherwise returns 0. +/// \param gpnvm GPNVM bit index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsGPNVMSet(unsigned char gpnvm) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + unsigned int status; + + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + +#ifdef AT91C_BASE_EFC1 + // GPNVM in EFC1 + if (gpnvm >= 8) { + + pEfc = AT91C_BASE_EFC1; + gpnvm -= 8; + } +#endif + + // Check if GPNVM is set + status = EFC_GetStatus(pEfc); + if ((status & (1 << gpnvm << 8)) != 0) { + + return 1; + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Sets the selected GPNVM bit. +/// Returns 0 if successful; otherwise returns an error code. +/// \param gpnvm GPNVM index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_SetGPNVM(unsigned char gpnvm) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + + if (!FLASHD_IsGPNVMSet(gpnvm)) { + +#ifdef AT91C_BASE_EFC1 + // GPNVM in EFC1 + if (gpnvm >= 8) { + + pEfc = AT91C_BASE_EFC1; + gpnvm -= 8; + } +#endif + + return EFC_PerformCommand(pEfc, AT91C_MC_FCMD_SET_GP_NVM, gpnvm); + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Clears the selected GPNVM bit. +/// Returns 0 if successful; otherwise returns an error code. +/// \param gpnvm GPNVM index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_ClearGPNVM(unsigned char gpnvm) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + + if (FLASHD_IsGPNVMSet(gpnvm)) { + +#ifdef AT91C_BASE_EFC1 + // GPNVM in EFC1 + if (gpnvm >= 8) { + + pEfc = AT91C_BASE_EFC1; + gpnvm -= 8; + } +#endif + + return EFC_PerformCommand(pEfc, AT91C_MC_FCMD_CLR_GP_NVM, gpnvm); + } + else { + + return 0; + } +} +#endif //#if (CHIP_EFC_NUM_GPNVMS > 0) + +#if !defined EFC_NO_SECURITY_BIT +//------------------------------------------------------------------------------ +/// Returns 1 if the Security bit is currently set; otherwise returns 0. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsSecurityBitSet(void) +{ + + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + unsigned int status; + + status = EFC_GetStatus(pEfc); + return ( ((status & AT91C_MC_SECURITY) != 0)?1:0 ); + +} + +//------------------------------------------------------------------------------ +/// Set Security Bit Command (SSB). +/// Returns 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +unsigned char FLASHD_SetSecurityBit(void) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + + if( FLASHD_IsSecurityBitSet() == 0) { + return EFC_PerformCommand(pEfc, AT91C_MC_FCMD_SET_SECURITY, 0); + } + else { + return 0; + } +} + +#endif //#if (!defined EFC_NO_SECURITY_BIT) + diff --git a/memories/memories.dir b/memories/memories.dir new file mode 100644 index 0000000..1093673 --- /dev/null +++ b/memories/memories.dir @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory contains different memory access APIs and drivers +/// +//------------------------------------------------------------------------------ \ No newline at end of file diff --git a/memories/nandflash/EccNandFlash.c b/memories/nandflash/EccNandFlash.c new file mode 100644 index 0000000..dad5c95 --- /dev/null +++ b/memories/nandflash/EccNandFlash.c @@ -0,0 +1,328 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "EccNandFlash.h" +#include "NandCommon.h" +#include "NandSpareScheme.h" +#include +#include +#include +#ifdef HARDWARE_ECC +#include +#endif +#include + +//------------------------------------------------------------------------------ +// Variables +//------------------------------------------------------------------------------ +/// Not using ECC +static unsigned char noEcc = 0; + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Casts +#define MODEL(ecc) ((struct NandFlashModel *) ecc) +#define RAW(ecc) ((struct RawNandFlash *) ecc) + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an EccNandFlash instance. +/// \param ecc Pointer to an EccNandFlash instance. +/// \param model Pointer to the underlying nand chip model. Can be 0. +/// \param commandAddress Address at which commands are sent. +/// \param addressAddress Address at which addresses are sent. +/// \param dataAddress Address at which data is sent. +/// \param pinChipEnable Pin controlling the CE signal of the NandFlash. +/// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. +//------------------------------------------------------------------------------ +unsigned char EccNandFlash_Initialize( + struct EccNandFlash *ecc, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy) +{ + unsigned char rc; + rc = RawNandFlash_Initialize(RAW(ecc), + model, + commandAddress, + addressAddress, + dataAddress, + pinChipEnable, + pinReadyBusy); +#if defined(HARDWARE_ECC) + { unsigned int ecc_page; + switch(NandFlashModel_GetPageDataSize(MODEL(ecc))) { + case 512: ecc_page = AT91C_HSMC4_PAGESIZE_528_Bytes; break; + case 1024: ecc_page = AT91C_HSMC4_PAGESIZE_1056_Bytes; break; + case 2048: ecc_page = AT91C_HSMC4_PAGESIZE_2112_Bytes; break; + case 4096: ecc_page = AT91C_HSMC4_PAGESIZE_4224_Bytes; break; + default: + TRACE_ERROR("PageSize %d not compatible with ECC\n\r", + NandFlashModel_GetPageDataSize(MODEL(ecc))); + return NandCommon_ERROR_ECC_NOT_COMPATIBLE; + } + HSMC4_EccConfigure(AT91C_ECC_TYPCORRECT_ONE_EVERY_256_BYTES, + ecc_page); + } +#endif + return rc; +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or spare of a page of a nandflash chip, and verify that +/// the data is valid using the ECC information contained in the spare. If one +/// buffer pointer is 0, the corresponding area is not saved. +/// Returns 0 if the data has been read and is valid; otherwise returns either +/// NandCommon_ERROR_CORRUPTEDDATA or ... +/// \param ecc Pointer to an EccNandFlash instance. +/// \param block Number of block to read from. +/// \param page Number of page to read inside given block. +/// \param data Data area buffer. +/// \param spare Spare area buffer. +//------------------------------------------------------------------------------ +unsigned char EccNandFlash_ReadPage( + const struct EccNandFlash *ecc, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; + unsigned char error; +#ifndef HARDWARE_ECC + unsigned char tmpData[NandCommon_MAXPAGEDATASIZE]; + unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; +#else + unsigned char hsiaoInSpare[NandCommon_MAXSPAREECCBYTES]; + unsigned char hsiao[NandCommon_MAXSPAREECCBYTES]; +#endif + unsigned char tmpNoEcc; + + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); + unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); + + TRACE_DEBUG("EccNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); +#ifndef HARDWARE_ECC + // Start by reading the spare data + error = RawNandFlash_ReadPage(RAW(ecc), block, page, 0, tmpSpare); + if (error) { + + TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); + return error; + } + + // Then reading the data + error = RawNandFlash_ReadPage(RAW(ecc), block, page, tmpData, 0); + if (error) { + + TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); + return error; + } + + tmpNoEcc = EccNandlfash_GetNoECC(); + if(!tmpNoEcc){ + // Retrieve ECC information from page and verify the data + NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hamming); + error = Hamming_Verify256x(tmpData, pageDataSize, hamming); + } +#else + // Start by reading the spare area + // Note: Can't read data and spare at the same time, otherwise, the ECC parity generation will be incorrect. + error = RawNandFlash_ReadPage(RAW(ecc), block, page, 0, tmpSpare); + if (error) { + + TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", + block, page); + return error; + } + // Retrieve ECC information from page and verify the data + NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hsiaoInSpare); + + // Reading the main data area + error = RawNandFlash_ReadPage(RAW(ecc), block, page, (unsigned char*)data, 0); + if (error) { + + TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", + block, page); + return error; + } + HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); + error = HSMC4_VerifyHsiao((unsigned char*) data, + pageDataSize, + hsiaoInSpare, + hsiao, + NandFlashModel_GetDataBusWidth(MODEL(ecc))); +#endif + if (error && (error != Hamming_ERROR_SINGLEBIT) && (!tmpNoEcc)) { + + TRACE_ERROR("EccNandFlash_ReadPage: at B%d.P%d Unrecoverable data\n\r", + block, page); + return NandCommon_ERROR_CORRUPTEDDATA; + } +#ifndef HARDWARE_ECC + // Copy data and/or spare into final buffers + if (data) { + + memcpy(data, tmpData, pageDataSize); + } + if (spare) { + + memcpy(spare, tmpSpare, pageSpareSize); + } +#else + if (spare) { + + memcpy(spare, tmpSpare, pageSpareSize); + } +#endif + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or spare area of a nandflash page, after calculating an +/// ECC for the data area and storing it in the spare. If no data buffer is +/// provided, the ECC is read from the existing page spare. If no spare buffer +/// is provided, the spare area is still written with the ECC information +/// calculated on the data buffer. +/// Returns 0 if successful; otherwise returns an error code. +/// \param ecc Pointer to an EccNandFlash instance. +/// \param block Number of the block to write in. +/// \param page Number of the page to write inside the given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char EccNandFlash_WritePage( + const struct EccNandFlash *ecc, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char error; + unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); + unsigned short pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); + unsigned char tmpNoEcc; +#ifndef HARDWARE_ECC + unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; +#else + unsigned char hsiao[NandCommon_MAXSPAREECCBYTES]; +#endif + + ASSERT(data || spare, "EccNandFlash_WritePage: At least one area must be written\n\r"); + TRACE_DEBUG("EccNandFlash_WritePage(B#%d:P#%d)\n\r", block, page); +#ifndef HARDWARE_ECC + + tmpNoEcc = EccNandlfash_GetNoECC(); + // Compute ECC on the new data, if provided + // If not provided, hamming code set to 0xFFFF.. to keep existing bytes + memset(hamming, 0xFF, NandCommon_MAXSPAREECCBYTES); + if (data && !tmpNoEcc) { + + // Compute hamming code on data + Hamming_Compute256x(data, pageDataSize, hamming); + } + + // Store code in spare buffer (if no buffer provided, use a temp. one) + if (!spare) { + + spare = tmpSpare; + memset(spare, 0xFF, pageSpareSize); + } + NandSpareScheme_WriteEcc(NandFlashModel_GetScheme(MODEL(ecc)), spare, hamming); + + // Perform write operation + error = RawNandFlash_WritePage(RAW(ecc), block, page, data, spare); + if (error) { + + TRACE_ERROR("EccNandFlash_WritePage: Failed to write page\n\r"); + return error; + } + +#else + // Store code in spare buffer (if no buffer provided, use a temp. one) + if (!spare) { + spare = tmpSpare; + memset(spare, 0xFF, pageSpareSize); + } + // Perform write operation + error = RawNandFlash_WritePage(RAW(ecc), block, page, data, spare); + if (error) { + + TRACE_ERROR("EccNandFlash_WritePage: Failed to write page\n\r"); + return error; + } + HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); + // Perform write operation + NandSpareScheme_WriteEcc(NandFlashModel_GetScheme(MODEL(ecc)), spare, hsiao); + error = RawNandFlash_WritePage(RAW(ecc), block, page, 0, spare); + if (error) { + TRACE_ERROR("EccNandFlash_WritePage: Failed to write page\n\r"); + return error; + } +#endif + return 0; +} + +//------------------------------------------------------------------------------ +/// Set no ecc flag. +//------------------------------------------------------------------------------ +void EccNandlfash_SetNoECC(void) +{ + noEcc = 1; +} + +//------------------------------------------------------------------------------ +/// Clear no ecc flag. +//------------------------------------------------------------------------------ +void EccNandlfash_ClearNoECC(void) +{ + noEcc = 0; +} + +//------------------------------------------------------------------------------ +/// Get no ecc flag. +//------------------------------------------------------------------------------ +unsigned char EccNandlfash_GetNoECC(void) +{ + return noEcc; +} + diff --git a/memories/nandflash/EccNandFlash.h b/memories/nandflash/EccNandFlash.h new file mode 100644 index 0000000..39358e8 --- /dev/null +++ b/memories/nandflash/EccNandFlash.h @@ -0,0 +1,101 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "EccNandFlash" +/// +/// !!!Purpose +/// +/// EccNandFlash layer is called by SkipBlockNandFlash driver, it will call the bl driver (RawNandFlash) +/// to do write/read operations, and do ECC check to the write/read result, it then will feedback the +/// ecc check result to the upper SkipBlockNandFlash layer driver. +/// +/// !!!Usage +/// +/// -# EccNandFlash_Initialize is used to initializes an EccNandFlash instance. +/// -# EccNandFlash_WritePage is used to write a Nandflash page with ecc result, the function +/// will calculate ecc for the data that is going to be written, and write data and spare(with +/// calculated ecc) to Nandflash device. +/// -# EccNandFlash_ReadPage is uese to read a Nandflash page with ecc check, the function +/// will read out data and spare first, then it calculates ecc with data and then compare with +/// the readout ecc, and feedback the ecc check result to dl driver. +//------------------------------------------------------------------------------ + +#ifndef ECCNANDFLASH_H +#define ECCNANDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "RawNandFlash.h" +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +struct EccNandFlash { + + struct RawNandFlash raw; +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char EccNandFlash_Initialize( + struct EccNandFlash *ecc, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy); + +extern unsigned char EccNandFlash_ReadPage( + const struct EccNandFlash *ecc, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char EccNandFlash_WritePage( + const struct EccNandFlash *ecc, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern void EccNandlfash_SetNoECC(void); +extern void EccNandlfash_ClearNoECC(void); +extern unsigned char EccNandlfash_GetNoECC(void); + +#endif //#ifndef ECCNANDFLASH_H + diff --git a/memories/nandflash/ManagedNandFlash.c b/memories/nandflash/ManagedNandFlash.c new file mode 100644 index 0000000..dcc39d6 --- /dev/null +++ b/memories/nandflash/ManagedNandFlash.c @@ -0,0 +1,862 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "ManagedNandFlash.h" +#include "NandSpareScheme.h" +#include "NandFlashModel.h" +#include "RawNandFlash.h" +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +// Casts +#define ECC(managed) ((struct EccNandFlash *) managed) +#define RAW(managed) ((struct RawNandFlash *) managed) +#define MODEL(managed) ((struct NandFlashModel *) managed) + +// Values returned by the CheckBlock() function +#define BADBLOCK 255 +#define GOODBLOCK 254 + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Returns 1 if a nandflash device is virgin (i.e. has never been used as a +/// managed nandflash); otherwise return 0. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param spare Pointer to allocated spare area (must be assigned) +//------------------------------------------------------------------------------ +static unsigned char IsDeviceVirgin(const struct ManagedNandFlash *managed, + unsigned char *spare) +{ + struct NandBlockStatus blockStatus; + const struct NandSpareScheme *scheme = + NandFlashModel_GetScheme(MODEL(managed)); + unsigned short baseBlock = managed->baseBlock; + unsigned char badBlockMarker; + + unsigned char error; + + ASSERT(spare, "ManagedNandFlash_IsDeviceVirgin: spare\n\r"); + + // Read spare area of page #0 + error = RawNandFlash_ReadPage(RAW(managed), baseBlock, 0, 0, spare); + ASSERT(!error, "ManagedNandFlash_IsDeviceVirgin: Failed to read page #0\n\r"); + + // Retrieve bad block marker and block status from spare area + NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); + NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); + + // Check if block is marked as bad + if (badBlockMarker != 0xFF) { + + // Device is not virgin, since page #0 is guaranteed to be good + return 0; + } + // If device is not virgin, then block status will be set to either + // FREE, DIRTY or LIVE + else if (blockStatus.status != NandBlockStatus_DEFAULT) { + + // Device is not virgin + return 0; + } + + return 1; +} + +//------------------------------------------------------------------------------ +/// Returns BADBLOCK if the given block of a nandflash device is bad; returns +/// GOODBLOCK if the block is good; or returns a NandCommon_ERROR code. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Raw block to check. +/// \param spare Pointer to allocated spare area (must be assigned) +//------------------------------------------------------------------------------ +static unsigned char CheckBlock( + const struct ManagedNandFlash *managed, + unsigned short block, + unsigned char *spare) +{ + unsigned char error; + unsigned int i; + unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(managed)); + + ASSERT(spare, "ManagedNandFlash_CheckBlock: spare\n\r"); + + // Read spare area of first page of block + error = RawNandFlash_ReadPage(RAW(managed), block, 0, 0, spare); + if (error) { + + TRACE_ERROR("CheckBlock: Cannot read page #0 of block #%d\n\r", block); + return error; + } + + // Make sure it is all 0xFF + for (i=0; i < pageSpareSize; i++) { + + if (spare[i] != 0xFF) { + + return BADBLOCK; + } + } + + // Read spare area of second page of block + error = RawNandFlash_ReadPage(RAW(managed), block, 1, 0, spare); + if (error) { + + TRACE_ERROR("CheckBlock: Cannot read page #1 of block #%d\n\r", block); + return error; + } + + // Make sure it is all 0xFF + for (i=0; i < pageSpareSize; i++) { + + if (spare[i] != 0xFF) { + + return BADBLOCK; + } + } + + return GOODBLOCK; +} + +//------------------------------------------------------------------------------ +/// Physically writes the status of a block inside its first page spare area. +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xx code. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Raw block number. +/// \param pStatus Pointer to status data. +/// \param spare Pointer to allocated spare area (must be assigned). +//------------------------------------------------------------------------------ +static unsigned char WriteBlockStatus( + const struct ManagedNandFlash *managed, + unsigned short block, + struct NandBlockStatus *pStatus, + unsigned char *spare) +{ + ASSERT(spare, "ManagedNandFlash_WriteBlockStatus: spare\n\r"); + + memset(spare, 0xFF, NandCommon_MAXPAGESPARESIZE); + NandSpareScheme_WriteExtra(NandFlashModel_GetScheme(MODEL(managed)), + spare, + pStatus, + 4, + 0); + return RawNandFlash_WritePage(RAW(managed), + block, 0, 0, spare); +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a ManagedNandFlash instance. Scans the device to retrieve or +/// create block status information. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param model Pointer to the underlying nand chip model. Can be 0. +/// \param commandAddress Address at which commands are sent. +/// \param addressAddress Address at which addresses are sent. +/// \param dataAddress Address at which data is sent. +/// \param pinChipEnable Pin controlling the CE signal of the NandFlash. +/// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. +/// \param baseBlock Base physical block address of managed area, managed 0. +/// \param sizeInBlocks Number of blocks that is managed. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_Initialize( + struct ManagedNandFlash *managed, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy, + unsigned short baseBlock, + unsigned short sizeInBlocks) +{ + unsigned char error; + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + unsigned int numBlocks; + unsigned int pageSpareSize; + const struct NandSpareScheme *scheme; + unsigned int block, phyBlock; + struct NandBlockStatus blockStatus; + unsigned char badBlockMarker; + unsigned int eraseCount, minEraseCount, maxEraseCount; + + TRACE_DEBUG("ManagedNandFlash_Initialize()\n\r"); + + // Initialize EccNandFlash + error = EccNandFlash_Initialize(ECC(managed), + model, + commandAddress, + addressAddress, + dataAddress, + pinChipEnable, + pinReadyBusy); + if (error) { + + return error; + } + + // Retrieve model information + numBlocks = NandFlashModel_GetDeviceSizeInBlocks(MODEL(managed)); + pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(managed)); + scheme = NandFlashModel_GetScheme(MODEL(managed)); + + // Initialize base & size + if (sizeInBlocks == 0) sizeInBlocks = numBlocks; + if (baseBlock > numBlocks) { + baseBlock = 0; + } + else if (baseBlock + sizeInBlocks > numBlocks) { + sizeInBlocks = numBlocks - baseBlock; + } + TRACE_INFO("Managed NF area: %d + %d\n\r", baseBlock, sizeInBlocks); + + if (sizeInBlocks > NandCommon_MAXNUMBLOCKS) { + TRACE_ERROR("Out of Maxmized Managed Size: %d > %d\n\r", + sizeInBlocks, NandCommon_MAXNUMBLOCKS); + TRACE_INFO("Change NandCommon_MAXNUMBLOCKS or sizeInBlocks\n\r"); + return NandCommon_ERROR_OUTOFBOUNDS; + } + + managed->baseBlock = baseBlock; + managed->sizeInBlocks = sizeInBlocks; + + // Initialize block statuses + // First, check if device is virgin + if (IsDeviceVirgin(managed, spare)) { + + TRACE_WARNING("Device is virgin, doing initial block scanning ...\n\r"); + + // Perform initial scan of the device area + for (block=0; block < sizeInBlocks; block++) { + + phyBlock = baseBlock + block; + + // Check if physical block is bad + error = CheckBlock(managed, phyBlock, spare); + if (error == BADBLOCK) { + + // Mark block as bad + TRACE_DEBUG("Block #%d is bad\n\r", block); + managed->blockStatuses[block].status = NandBlockStatus_BAD; + } + else if (error == GOODBLOCK) { + + // Mark block as free with erase count 0 + TRACE_DEBUG("Block #%d is free\n\r", block); + managed->blockStatuses[block].status = NandBlockStatus_FREE; + managed->blockStatuses[block].eraseCount = 0; + + // Write status in spare of block first page + error = WriteBlockStatus(managed, + phyBlock, + &(managed->blockStatuses[block]), + spare); + if (error) { + + TRACE_ERROR("ManagedNandFlash_Initialize: WR spare\n\r"); + return error; + } + } + else { + + TRACE_ERROR("ManagedNandFlash_Initialize: Scan device\n\r"); + return error; + } + } + } + else { + + TRACE_INFO("Managed, retrieving information ...\n\r"); + + // Retrieve block statuses from their first page spare area + // (find maximum and minimum wear at the same time) + minEraseCount = 0xFFFFFFFF; + maxEraseCount = 0; + for (block=0; block < sizeInBlocks; block++) { + + phyBlock = baseBlock + block; + + // Read spare of first page + error = RawNandFlash_ReadPage(RAW(managed), phyBlock, 0, 0, spare); + if (error) { + + TRACE_ERROR("ManagedNandFlash_Initialize: Read block #%d(%d)\n\r", + block, phyBlock); + } + + // Retrieve bad block marker and block status + NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); + NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); + + // If they do not match, block must be bad + if ( (badBlockMarker != 0xFF) + && (blockStatus.status != NandBlockStatus_BAD)) { + + TRACE_DEBUG("Block #%d(%d) is bad\n\r", block, phyBlock); + managed->blockStatuses[block].status = NandBlockStatus_BAD; + } + // Check that block status is not default + // (meaning block is not managed) + else if (blockStatus.status == NandBlockStatus_DEFAULT) { + + ASSERT(0, "Block #%d(%d) is not managed\n\r", block, phyBlock); + } + // Otherwise block status is accurate + else { + + TRACE_DEBUG("Block #%03d(%d) : status = %2d | eraseCount = %d\n\r", + block, phyBlock, + blockStatus.status, blockStatus.eraseCount); + managed->blockStatuses[block] = blockStatus; + + // Check for min/max erase counts + if (blockStatus.eraseCount < minEraseCount) { + + minEraseCount = blockStatus.eraseCount; + } + if (blockStatus.eraseCount > maxEraseCount) { + + maxEraseCount = blockStatus.eraseCount; + } + + //// Clean block + //// Release LIVE blocks + //if (managed->blockStatuses[block].status == NandBlockStatus_LIVE) { + // + // ManagedNandFlash_ReleaseBlock(managed, block); + //} + //// Erase DIRTY blocks + //if (managed->blockStatuses[block].status == NandBlockStatus_DIRTY) { + // + // ManagedNandFlash_EraseBlock(managed, block); + //} + } + } + + // Display erase count information + TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); + TRACE_ERROR_WP("| Wear | Count | Free | Live | Dirty |\n\r"); + TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); + + for (eraseCount=minEraseCount; eraseCount <= maxEraseCount; eraseCount++) { + + unsigned int count = 0, live = 0, dirty = 0, free = 0; + for (block=0; block < sizeInBlocks; block++) { + + if ((managed->blockStatuses[block].eraseCount == eraseCount) + && (managed->blockStatuses[block].status != NandBlockStatus_BAD)) { + + count++; + + switch (managed->blockStatuses[block].status) { + case NandBlockStatus_LIVE: live++; break; + case NandBlockStatus_DIRTY: dirty++; break; + case NandBlockStatus_FREE: free++; break; + } + } + } + + if (count > 0) { + + TRACE_ERROR_WP("| %4d | %8d | %4d | %4d | %4d |\n\r", + eraseCount, count, free, live, dirty); + } + } + TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Allocates a FREE block of a managed nandflash and marks it as LIVE. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if +/// the block is not FREE. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Block to allocate, in managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_AllocateBlock( + struct ManagedNandFlash *managed, + unsigned short block) +{ + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + TRACE_INFO("ManagedNandFlash_AllocateBlock(%d)\n\r", block); + + // Check that block is FREE + if (managed->blockStatuses[block].status != NandBlockStatus_FREE) { + + TRACE_ERROR("ManagedNandFlash_AllocateBlock: Block must be FREE\n\r"); + return NandCommon_ERROR_WRONGSTATUS; + } + + // Change block status to LIVE + managed->blockStatuses[block].status = NandBlockStatus_LIVE; + return WriteBlockStatus(managed, + managed->baseBlock + block, + &(managed->blockStatuses[block]), + spare); +} + +//------------------------------------------------------------------------------ +/// Releases a LIVE block of a nandflash and marks it as DIRTY. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if +/// the block is not LIVE, or a RawNandFlash_WritePage error. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Block to release, based on managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_ReleaseBlock( + struct ManagedNandFlash *managed, + unsigned short block) +{ + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + TRACE_INFO("ManagedNandFlash_ReleaseBlock(%d)\n\r", block); + + // Check that block is LIVE + if (managed->blockStatuses[block].status != NandBlockStatus_LIVE) { + + TRACE_ERROR("ManagedNandFlash_ReleaseBlock: Block must be LIVE\n\r"); + return NandCommon_ERROR_WRONGSTATUS; + } + + // Change block status to DIRTY + managed->blockStatuses[block].status = NandBlockStatus_DIRTY; + return WriteBlockStatus(managed, + managed->baseBlock + block, + &(managed->blockStatuses[block]), + spare); +} + +//------------------------------------------------------------------------------ +/// Erases a DIRTY block of a managed NandFlash. +/// Returns the RawNandFlash_EraseBlock code or NandCommon_ERROR_WRONGSTATUS. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Block to erase, in managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_EraseBlock( + struct ManagedNandFlash *managed, + unsigned short block) +{ + unsigned int phyBlock = managed->baseBlock + block; + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + unsigned char error; + TRACE_INFO("ManagedNandFlash_EraseBlock(%d)\n\r", block); + + // Check block status + if (managed->blockStatuses[block].status != NandBlockStatus_DIRTY) { + + TRACE_ERROR("ManagedNandFlash_EraseBlock: Block must be DIRTY\n\r"); + return NandCommon_ERROR_WRONGSTATUS; + } + + // Erase block + error = RawNandFlash_EraseBlock(RAW(managed), phyBlock); + if (error) { + + return error; + } + + // Update block status + managed->blockStatuses[block].status = NandBlockStatus_FREE; + managed->blockStatuses[block].eraseCount++; + return WriteBlockStatus(managed, + phyBlock, + &(managed->blockStatuses[block]), + spare); +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or the spare area of a page on a managed nandflash. If +/// the data pointer is not 0, then the block MUST be LIVE. +/// Returns NandCommon_ERROR_WRONGSTATUS if the block is not LIVE and the data +/// pointer is not null; Otherwise, returns EccNandFlash_ReadPage(). +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Block to read page from, based on managed area. +/// \param page Number of page to read inside the given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_ReadPage( + const struct ManagedNandFlash *managed, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + // Check that the block is LIVE if data is requested + if ((managed->blockStatuses[block].status != NandBlockStatus_LIVE) + && (managed->blockStatuses[block].status != NandBlockStatus_DIRTY)) { + + TRACE_ERROR("ManagedNandFlash_ReadPage: Block must be LIVE or DIRTY.\n\r"); + return NandCommon_ERROR_WRONGSTATUS; + } + + // Read data with ECC verification + return EccNandFlash_ReadPage(ECC(managed), + managed->baseBlock + block, + page, data, spare); +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or spare area of a LIVE page on a managed NandFlash. +/// Returns NandCommon_ERROR_WRONGSTATUS if the page is not LIVE; otherwise, +/// returns EccNandFlash_WritePage(). +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block The block to write, in managed area. +/// \param page Number of the page to write inside the given block. +/// \param data Data area buffer. +/// \param spare Spare area buffer. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_WritePage( + const struct ManagedNandFlash *managed, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + // Check that the block is LIVE + if (managed->blockStatuses[block].status != NandBlockStatus_LIVE) { + + TRACE_ERROR("ManagedNandFlash_WritePage: Block must be LIVE.\n\r"); + return NandCommon_ERROR_WRONGSTATUS; + } + + // Write data with ECC calculation + return EccNandFlash_WritePage(ECC(managed), + managed->baseBlock + block, + page, data, spare); +} + +//------------------------------------------------------------------------------ +/// Copy the data & spare area of one page to another page. The source block +/// can be either LIVE or DIRTY, and the destination block must be LIVE; they +/// must both have the same parity. +/// Returns 0 if successful; NandCommon_ERROR_WRONGSTATUS if one or more page +/// is not live; otherwise returns an NandCommon_ERROR_xxx code. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param sourceBlock Source block number based on managed area. +/// \param sourcePage Number of source page inside the source block. +/// \param destBlock Destination block number based on managed area. +/// \param destPage Number of destination page inside the dest block. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_CopyPage( + const struct ManagedNandFlash *managed, + unsigned short sourceBlock, + unsigned short sourcePage, + unsigned short destBlock, + unsigned short destPage) +{ + unsigned char error; + + ASSERT((sourcePage & 1) == (destPage & 1), + "ManagedNandFlash_CopyPage: source & dest pages must have the same parity\n\r"); + + TRACE_INFO("ManagedNandFlash_CopyPage(B#%d:P#%d -> B#%d:P#%d)\n\r", + sourceBlock, sourcePage, destBlock, destPage); + + // Check block statuses + if ((managed->blockStatuses[sourceBlock].status != NandBlockStatus_LIVE) + && (managed->blockStatuses[sourceBlock].status != NandBlockStatus_DIRTY)) { + + TRACE_ERROR("ManagedNandFlash_CopyPage: Source block must be LIVE or DIRTY.\n\r"); + return NandCommon_ERROR_WRONGSTATUS; + } + if (managed->blockStatuses[destBlock].status != NandBlockStatus_LIVE) { + + TRACE_ERROR("ManagedNandFlash_CopyPage: Destination block must be LIVE.\n\r"); + return NandCommon_ERROR_WRONGSTATUS; + } + + // If destination page is page #0, block status information must not be + // overwritten + if (destPage == 0) { + + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + + // Read data & spare to copy + error = EccNandFlash_ReadPage(ECC(managed), + managed->baseBlock + sourceBlock, + sourcePage, + data, spare); + if (error) { + + return error; + } + + // Write destination block status information in spare + NandSpareScheme_WriteExtra(NandFlashModel_GetScheme(MODEL(managed)), + spare, + &(managed->blockStatuses[destBlock]), + 4, + 0); + + // Write page + error = RawNandFlash_WritePage(RAW(managed), + managed->baseBlock + destBlock, + destPage, + data, spare); + if (error) { + + return error; + } + } + // Otherwise, a normal copy can be done + else { + + return RawNandFlash_CopyPage(RAW(managed), + managed->baseBlock + sourceBlock, + sourcePage, + managed->baseBlock + destBlock, + destPage); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Copies the data from a whole block to another block on a nandflash. Both +/// blocks must be LIVE. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if +/// at least one of the blocks is not free, or a NandCommon_ERROR_xxx code. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param sourceBlock Source block number. +/// \param destBlock Destination block number. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_CopyBlock( + const struct ManagedNandFlash *managed, + unsigned short sourceBlock, + unsigned short destBlock) +{ + unsigned short numPages = NandFlashModel_GetBlockSizeInPages(MODEL(managed)); + unsigned char error; + unsigned short page; + + ASSERT(sourceBlock != destBlock, + "ManagedNandFlash_CopyBlock: Source block must be different from dest. block\n\r"); + + TRACE_INFO("ManagedNandFlash_CopyBlock(B#%d->B#%d)\n\r", + sourceBlock, destBlock); + + // Copy all pages + for (page=0; page < numPages; page++) { + + error = ManagedNandFlash_CopyPage(managed, + sourceBlock, + page, + destBlock, + page); + if (error) { + + TRACE_ERROR("ManagedNandFlash_CopyPage: Failed to copy page %d\n\r", page); + return error; + } + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Erases all the blocks which are currently marked as DIRTY. +/// Returns 0 if successful; otherwise, returns a NandCommon_ERROR code. +/// \param managed Pointer to a ManagedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_EraseDirtyBlocks( + struct ManagedNandFlash *managed) +{ + unsigned int i; + unsigned char error; + + // Erase all dirty blocks + for (i=0; i < managed->sizeInBlocks; i++) { + + if (managed->blockStatuses[i].status == NandBlockStatus_DIRTY) { + + error = ManagedNandFlash_EraseBlock(managed, i); + if (error) { + + return error; + } + } + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Looks for the youngest block having the desired status among the blocks +/// of a managed nandflash. If a block is found, its index is stored inside +/// the provided variable (if pointer is not 0). +/// Returns 0 if a block has been found; otherwise returns either +/// NandCommon_ERROR_NOBLOCKFOUND if there are no blocks having the desired +/// status. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Pointer to the block number variable, based on managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_FindYoungestBlock( + const struct ManagedNandFlash *managed, + unsigned char status, + unsigned short *block) +{ + unsigned char found = 0; + unsigned short bestBlock = 0; + unsigned int i; + + // Go through the block array + for (i=0; i < managed->sizeInBlocks; i++) { + + // Check status + if (managed->blockStatuses[i].status == status) { + + // If no block was found, i becomes the best block + if (!found) { + + found = 1; + bestBlock = i; + } + // Compare the erase counts otherwise + else if (managed->blockStatuses[i].eraseCount + < managed->blockStatuses[bestBlock].eraseCount) { + + bestBlock = i; + } + } + } + + if (found) { + + if (block) { + + *block = bestBlock; + } + return 0; + } + else { + + return NandCommon_ERROR_NOBLOCKFOUND; + } +} + +//------------------------------------------------------------------------------ +/// Counts and returns the number of blocks having the given status in a +/// managed nandflash. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param status Desired block status. +//------------------------------------------------------------------------------ +unsigned short ManagedNandFlash_CountBlocks( + const struct ManagedNandFlash *managed, + unsigned char status) +{ + unsigned int i; + unsigned short count = 0; + + // Examine each block + for (i=0; i < managed->sizeInBlocks; i++) { + + if (managed->blockStatuses[i].status == status) { + + count++; + } + } + + return count; +} + +//------------------------------------------------------------------------------ +/// Returns the number of available blocks in a managed nandflash. +/// \param managed Pointer to a ManagedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned short ManagedNandFlash_GetDeviceSizeInBlocks( + const struct ManagedNandFlash *managed) +{ + return managed->sizeInBlocks; +} + +//------------------------------------------------------------------------------ +/// Erase all blocks in the managed area of nand flash. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param level Erase level. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_EraseAll(struct ManagedNandFlash *managed, + unsigned char level) +{ + unsigned int i; + unsigned char error = 0; + + if (level == NandEraseFULL) { + for (i=0; i < managed->sizeInBlocks; i++) { + error = RawNandFlash_EraseBlock(RAW(managed), + managed->baseBlock + i); + // Reset block status + managed->blockStatuses[i].eraseCount = 0; + if (error) { + TRACE_WARNING("Managed_FullErase: %d(%d)\n\r", + i, managed->baseBlock + i); + managed->blockStatuses[i].status = NandBlockStatus_BAD; + continue; + } + managed->blockStatuses[i].status = NandBlockStatus_FREE; + } + } + else if (level == NandEraseDATA) { + for (i=0; i < managed->sizeInBlocks; i++) { + error = ManagedNandFlash_EraseBlock(managed, i); + if (error) { + TRACE_WARNING("Managed_DataErase: %d(%d)\n\r", + i, managed->baseBlock + i); + } + } + } + else { + for (i=0; i < managed->sizeInBlocks; i++) { + if (managed->blockStatuses[i].status == NandBlockStatus_DIRTY) { + error = ManagedNandFlash_EraseBlock(managed, i); + if (error) { + TRACE_WARNING("Managed_DirtyErase: %d(%d)\n\r", + i, managed->baseBlock + i); + } + } + } + } + + return error; +} diff --git a/memories/nandflash/ManagedNandFlash.h b/memories/nandflash/ManagedNandFlash.h new file mode 100644 index 0000000..2a3f054 --- /dev/null +++ b/memories/nandflash/ManagedNandFlash.h @@ -0,0 +1,154 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "ManagedNandFlash" +/// +/// !!!Purpose +/// +/// The lower layer of nandflash block management, it is called by MappedNandFlash layer, and +/// it will call EccNandFlash layer. +//------------------------------------------------------------------------------ + +#ifndef MANAGEDNANDFLASH_H +#define MANAGEDNANDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandCommon.h" +#include "EccNandFlash.h" + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +#define NandBlockStatus_DEFAULT 0xF +#define NandBlockStatus_FREE 0xE +#define NandBlockStatus_LIVE 0xC +#define NandBlockStatus_DIRTY 0x8 +#define NandBlockStatus_BAD 0x0 + +#define NandEraseDIRTY 0 // Erase dirty blocks only +#define NandEraseDATA 1 // Erase all data, calculate count +#define NandEraseFULL 2 // Erase all, reset erase count + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +struct NandBlockStatus { + + unsigned int status:4, + eraseCount:28; +}; + +struct ManagedNandFlash { + + struct EccNandFlash ecc; + struct NandBlockStatus blockStatuses[NandCommon_MAXNUMBLOCKS]; + unsigned short baseBlock; + unsigned short sizeInBlocks; +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char ManagedNandFlash_Initialize( + struct ManagedNandFlash *managed, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy, + unsigned short baseBlock, + unsigned short sizeInBlocks); + +extern unsigned char ManagedNandFlash_AllocateBlock( + struct ManagedNandFlash *managed, + unsigned short block); + +extern unsigned char ManagedNandFlash_ReleaseBlock( + struct ManagedNandFlash *managed, + unsigned short block); + +extern unsigned char ManagedNandFlash_EraseBlock( + struct ManagedNandFlash *managed, + unsigned short block); + +extern unsigned char ManagedNandFlash_ReadPage( + const struct ManagedNandFlash *managed, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char ManagedNandFlash_WritePage( + const struct ManagedNandFlash *managed, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char ManagedNandFlash_CopyPage( + const struct ManagedNandFlash *managed, + unsigned short sourceBlock, + unsigned short sourcePage, + unsigned short destBlock, + unsigned short destPage); + +extern unsigned char ManagedNandFlash_CopyBlock( + const struct ManagedNandFlash *managed, + unsigned short sourceBlock, + unsigned short destBlock); + +extern unsigned char ManagedNandFlash_EraseDirtyBlocks( + struct ManagedNandFlash *managed); + +extern unsigned char ManagedNandFlash_FindYoungestBlock( + const struct ManagedNandFlash *managed, + unsigned char status, + unsigned short *block); + +extern unsigned short ManagedNandFlash_CountBlocks( + const struct ManagedNandFlash *managed, + unsigned char status); + +extern unsigned short ManagedNandFlash_GetDeviceSizeInBlocks( + const struct ManagedNandFlash *managed); + +extern unsigned char ManagedNandFlash_EraseAll( + struct ManagedNandFlash *managed, + unsigned char level); + +#endif //#ifndef MANAGEDNANDFLASH_H + diff --git a/memories/nandflash/MappedNandFlash.c b/memories/nandflash/MappedNandFlash.c new file mode 100644 index 0000000..9baf83e --- /dev/null +++ b/memories/nandflash/MappedNandFlash.c @@ -0,0 +1,661 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MappedNandFlash.h" +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Casts +#define MANAGED(mapped) ((struct ManagedNandFlash *) mapped) +#define ECC(mapped) ((struct EccNandFlash *) mapped) +#define RAW(mapped) ((struct RawNandFlash *) mapped) +#define MODEL(mapped) ((struct NandFlashModel *) mapped) + +/// Logical block mapping pattern +#define PATTERN(i) ((i << 1) & 0x73) + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Scans a mapped nandflash to find an existing logical block mapping. If a +/// block contains the mapping, its index is stored in the provided variable (if +/// pointer is not 0). +/// Returns 0 if mapping has been found; otherwise returns +/// NandCommon_ERROR_NOMAPPING if no mapping exists, or another +/// NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalMappingBlock Pointer to a variable for storing the block +/// number. +//------------------------------------------------------------------------------ +static unsigned char FindLogicalMappingBlock( + const struct MappedNandFlash *mapped, + signed short *logicalMappingBlock) +{ + unsigned short block; + unsigned char found; + unsigned short numBlocks = ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(mapped)); + unsigned char error; + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned int i; + + TRACE_INFO("FindLogicalMappingBlock()~%d\n\r", numBlocks); + + // Search each LIVE block + found = 0; + block = 0; + while (!found && (block < numBlocks)) { + + // Check that block is LIVE + if (MANAGED(mapped)->blockStatuses[block].status == NandBlockStatus_LIVE) { + + // Read block + TRACE_INFO("Checking LIVE block #%d\n\r", block); + error = ManagedNandFlash_ReadPage(MANAGED(mapped), block, 0, data, 0); + if (!error) { + + // Compare data with logical mapping pattern + i = 0; + found = 1; + while ((i < pageDataSize) && found) { + + if (data[i] != PATTERN(i)) { + + found = 0; + } + i++; + } + + // If this is the mapping, stop looking + if (found) { + + TRACE_WARNING_WP("-I- Logical mapping in block #%d\n\r", + block); + if (logicalMappingBlock) { + + *logicalMappingBlock = block; + } + return 0; + } + } + else if (error != NandCommon_ERROR_WRONGSTATUS) { + + TRACE_ERROR( + "FindLogicalMappingBlock: Failed to scan block #%d\n\r", + block); + return error; + } + } + + block++; + } + + TRACE_WARNING("No logical mapping found in device\n\r"); + return NandCommon_ERROR_NOMAPPING; +} + +//------------------------------------------------------------------------------ +/// Loads the logical mapping contained in the given physical block. +/// Returns 0 if successful; otherwise, returns a NandCommon_ERROR code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param physicalBlock Physical block number. +//------------------------------------------------------------------------------ +static unsigned char LoadLogicalMapping( + struct MappedNandFlash *mapped, + unsigned short physicalBlock) +{ + unsigned char error; + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned short pageDataSize = + NandFlashModel_GetPageDataSize(MODEL(mapped)); + unsigned short numBlocks = + ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + unsigned int remainingSize; + unsigned char *currentBuffer; + unsigned short currentPage; + unsigned int readSize; + unsigned int i; + unsigned char status; + signed short logicalBlock; + //signed short firstBlock, lastBlock; + + TRACE_INFO("LoadLogicalMapping(B#%d)\n\r", physicalBlock); + + // Load mapping from pages #1 - #XXX of block + currentBuffer = (unsigned char *) mapped->logicalMapping; + remainingSize = sizeof(mapped->logicalMapping); + currentPage = 1; + while (remainingSize > 0) { + + // Read page + readSize = min(remainingSize, pageDataSize); + error = ManagedNandFlash_ReadPage(MANAGED(mapped), + physicalBlock, + currentPage, + data, + 0); + if (error) { + + TRACE_ERROR( + "LoadLogicalMapping: Failed to load mapping\n\r"); + return error; + } + + // Copy page info + memcpy(currentBuffer, data, readSize); + + currentBuffer += readSize; + remainingSize -= readSize; + currentPage++; + } + + // Store mapping block index + mapped->logicalMappingBlock = physicalBlock; + + // Power-loss recovery + for (i=0; i < numBlocks; i++) { + + // Check that this is not the logical mapping block + if (i != physicalBlock) { + + status = mapped->managed.blockStatuses[i].status; + logicalBlock = MappedNandFlash_PhysicalToLogical(mapped, i); + + // Block is LIVE + if (status == NandBlockStatus_LIVE) { + + // Block is not mapped -> release it + if (logicalBlock == -1) { + + TRACE_WARNING_WP("-I- Release unmapped LIVE #%d\n\r", + i); + ManagedNandFlash_ReleaseBlock(MANAGED(mapped), i); + } + } + // Block is DIRTY + else if (status == NandBlockStatus_DIRTY) { + + // Block is mapped -> fake it as live + if (logicalBlock != -1) { + + TRACE_WARNING_WP("-I- Mark mapped DIRTY #%d -> LIVE\n\r", + i); + mapped->managed.blockStatuses[i].status = + NandBlockStatus_LIVE; + } + } + // Block is FREE or BAD + else { + + // Block is mapped -> remove it from mapping + if (logicalBlock != -1) { + + TRACE_WARNING_WP("-I- Unmap FREE or BAD #%d\n\r", i); + mapped->logicalMapping[logicalBlock] = -1; + } + } + } + } + + TRACE_WARNING_WP("-I- Mapping loaded from block #%d\n\r", physicalBlock); + + return 0; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a MappedNandFlash instance. Scans the device to look for and +/// existing logical block mapping; otherwise starts from scratch (no block +/// mapped). +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param model Pointer to the underlying nand chip model. Can be 0. +/// \param commandAddress Address at which commands are sent. +/// \param addressAddress Address at which addresses are sent. +/// \param dataAddress Address at which data is sent. +/// \param pinChipEnable Pin controlling the CE signal of the NandFlash. +/// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. +/// \param baseBlock Basic physical block address of mapped area. +/// \param sizeInBlocks Number of blocks that is mapped. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_Initialize( + struct MappedNandFlash *mapped, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy, + unsigned short baseBlock, + unsigned short sizeInBlocks) +{ + unsigned char error; + unsigned short numBlocks; + unsigned short block; + signed short logicalMappingBlock = 0; + + TRACE_INFO("MappedNandFlash_Initialize()\n\r"); + + // Initialize ManagedNandFlash + error = ManagedNandFlash_Initialize(MANAGED(mapped), + model, + commandAddress, + addressAddress, + dataAddress, + pinChipEnable, + pinReadyBusy, + baseBlock, + sizeInBlocks); + if (error) { + + return error; + } + + // Scan to find logical mapping + mapped->mappingModified = 0; + error = FindLogicalMappingBlock(mapped, &logicalMappingBlock); + if (!error) { + + // Extract mapping from block + mapped->logicalMappingBlock = logicalMappingBlock; + return LoadLogicalMapping(mapped, logicalMappingBlock); + } + else if (error == NandCommon_ERROR_NOMAPPING) { + + // Start with no block mapped + mapped->logicalMappingBlock = -1; + numBlocks = ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + for (block=0; block < numBlocks; block++) { + + mapped->logicalMapping[block] = -1; + } + } + else { + + TRACE_ERROR("MappedNandFlash_Initialize: Initialize device\n\r"); + return error; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or spare area of a page in a mapped logical block. +/// Returns 0 if successful; otherwise, returns NandCommon_ERROR_BLOCKNOTMAPPED +/// if the block is not mapped, or a NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param block Number of logical block to read page from. +/// \param page Number of page to read inside given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_ReadPage( + const struct MappedNandFlash *mapped, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + signed short physicalBlock; + + TRACE_INFO("MappedNandFlash_ReadPage(LB#%d:P#%d)\n\r", block, page); + + // Check if block is mapped + physicalBlock = mapped->logicalMapping[block]; + if (physicalBlock == -1) { + + TRACE_INFO( "MappedNandFlash_ReadPage: Block %d not mapped\n\r", block); + return NandCommon_ERROR_BLOCKNOTMAPPED; + } + + // Read page from corresponding physical block + return ManagedNandFlash_ReadPage(MANAGED(mapped), + physicalBlock, + page, + data, + spare); +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or spare area of a page in the given mapped logical +/// block. +/// Returns 0 if successful; otherwise, returns NandCommon_ERROR_BLOCKNOTMAPPED +/// (indicating the block is not logically mapped) or another NandCommon_ERROR +/// code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param block Number of logical block to write on. +/// \param page Number of page to write inside given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_WritePage( + const struct MappedNandFlash *mapped, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + signed short physicalBlock; + + TRACE_INFO("MappedNandFlash_WritePage(LB#%d:P#%d)\n\r", block, page); + + // Check if block is mapped + physicalBlock = mapped->logicalMapping[block]; + if (physicalBlock == -1) { + + TRACE_ERROR("MappedNandFlash_WritePage: Block must be mapped\n\r"); + return NandCommon_ERROR_BLOCKNOTMAPPED; + } + + // Write page on physical block + return ManagedNandFlash_WritePage(MANAGED(mapped), + physicalBlock, + page, + data, + spare); +} + +//------------------------------------------------------------------------------ +/// Maps a logical block number to an actual physical block. This allocates +/// the physical block (meaning it must be FREE), and releases the previous +/// block being replaced (if any). +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalBlock Logical block number to map. +/// \param physicalBlock Physical block to map to the logical one. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_Map( + struct MappedNandFlash *mapped, + unsigned short logicalBlock, + unsigned short physicalBlock) +{ + unsigned char error; + signed short oldPhysicalBlock; + + TRACE_INFO("MappedNandFlash_Map(LB#%d -> PB#%d)\n\r", + logicalBlock, physicalBlock); + ASSERT( + logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_Map: logicalBlock out-of-range\n\r"); + ASSERT( + physicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_Map: physicalBlock out-of-range\n\r"); + + // Allocate physical block + error = ManagedNandFlash_AllocateBlock(MANAGED(mapped), physicalBlock); + if (error) { + + return error; + } + + // Release currently mapped block (if any) + oldPhysicalBlock = mapped->logicalMapping[logicalBlock]; + if (oldPhysicalBlock != -1) { + + error = + ManagedNandFlash_ReleaseBlock(MANAGED(mapped), oldPhysicalBlock); + if (error) { + + return error; + } + } + + // Set mapping + mapped->logicalMapping[logicalBlock] = physicalBlock; + mapped->mappingModified = 1; + + return 0; +} + +//------------------------------------------------------------------------------ +/// Unmaps a logical block by releasing the corresponding physical block (if +/// any). +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalBlock Number of logical block to unmap. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_Unmap( + struct MappedNandFlash *mapped, + unsigned short logicalBlock) +{ + signed short physicalBlock = mapped->logicalMapping[logicalBlock]; + unsigned char error; + + TRACE_INFO("MappedNandFlash_Unmap(LB#%d)\n\r", logicalBlock); + ASSERT( + logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_Unmap: logicalBlock out-of-range\n\r"); + + if (physicalBlock != -1) { + + error = ManagedNandFlash_ReleaseBlock(MANAGED(mapped), physicalBlock); + if (error) { + + return error; + } + } + mapped->logicalMapping[logicalBlock] = -1; + mapped->mappingModified = 1; + + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns the physical block mapped with the given logical block, or -1 if it +/// is not mapped. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalBlock Logical block number. +//------------------------------------------------------------------------------ +signed short MappedNandFlash_LogicalToPhysical( + const struct MappedNandFlash *mapped, + unsigned short logicalBlock) +{ + ASSERT( + logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_LogicalToPhysical: logicalBlock out-of-range\n\r"); + + return mapped->logicalMapping[logicalBlock]; +} + +//------------------------------------------------------------------------------ +/// Returns the logical block which is mapped to given physical block, or -1 if +/// the latter is not mapped. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param physicalBlock Physical block number. +//------------------------------------------------------------------------------ +signed short MappedNandFlash_PhysicalToLogical( + const struct MappedNandFlash *mapped, + unsigned short physicalBlock) +{ + unsigned short numBlocks = + ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + signed short logicalBlock; + + ASSERT( + physicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_PhysicalToLogical: physicalBlock out-of-range\n\r"); + + // Search the mapping for the desired physical block + for (logicalBlock=0; logicalBlock < numBlocks; logicalBlock++) { + + if (mapped->logicalMapping[logicalBlock] == physicalBlock) { + + return logicalBlock; + } + } + + return -1; +} + +//------------------------------------------------------------------------------ +/// Saves the logical mapping on a FREE, unmapped physical block. Allocates the +/// new block, releases the previous one (if any) and save the mapping. +/// Returns 0 if successful; otherwise, returns NandCommon_ERROR_WRONGSTATUS +/// if the block is not LIVE, or a NandCommon_ERROR code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param physicalBlock Physical block number. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_SaveLogicalMapping( + struct MappedNandFlash *mapped, + unsigned short physicalBlock) +{ + unsigned char error; + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned short pageDataSize = + NandFlashModel_GetPageDataSize(MODEL(mapped)); + //unsigned short numBlocks = + // ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + unsigned int i; + unsigned int remainingSize; + unsigned char *currentBuffer; + unsigned short currentPage; + unsigned int writeSize; + signed short previousPhysicalBlock; + + TRACE_INFO("MappedNandFlash_SaveLogicalMapping(B#%d)\n\r", physicalBlock); + + // If mapping has not been modified, do nothing + if (!mapped->mappingModified) { + + return 0; + } + + // Allocate new block + error = ManagedNandFlash_AllocateBlock(MANAGED(mapped), physicalBlock); + if (error) { + + return error; + } + + // Save mapping + previousPhysicalBlock = mapped->logicalMappingBlock; + mapped->logicalMappingBlock = physicalBlock; + + // Save actual mapping in pages #1-#XXX + currentBuffer = (unsigned char *) mapped->logicalMapping; + remainingSize = sizeof(mapped->logicalMapping); + currentPage = 1; + while (remainingSize > 0) { + + writeSize = min(remainingSize, pageDataSize); + memset(data, 0xFF, pageDataSize); + memcpy(data, currentBuffer, writeSize); + error = ManagedNandFlash_WritePage(MANAGED(mapped), + physicalBlock, + currentPage, + data, + 0); + if (error) { + + TRACE_ERROR( + "MappedNandFlash_SaveLogicalMapping: Failed to write mapping\n\r"); + return error; + } + + currentBuffer += writeSize; + remainingSize -= writeSize; + currentPage++; + } + + // Mark page #0 of block with a distinguishible pattern, so the mapping can + // be retrieved at startup + for (i=0; i < pageDataSize; i++) { + + data[i] = PATTERN(i); + } + error = ManagedNandFlash_WritePage(MANAGED(mapped), + physicalBlock, 0, + data, 0); + if (error) { + + TRACE_ERROR( + "MappedNandFlash_SaveLogicalMapping: Failed to write pattern\n\r"); + return error; + } + + // Mapping is not modified anymore + mapped->mappingModified = 0; + + // Release previous block (if any) + if (previousPhysicalBlock != -1) { + + TRACE_DEBUG("Previous physical block was #%d\n\r", + previousPhysicalBlock); + error = ManagedNandFlash_ReleaseBlock(MANAGED(mapped), + previousPhysicalBlock); + if (error) { + + return error; + } + } + + TRACE_INFO("Mapping saved on block #%d\n\r", physicalBlock); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Erase all blocks in the mapped area of nand flash. +/// \param managed Pointer to a MappedNandFlash instance. +/// \param level Erase level. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_EraseAll( + struct MappedNandFlash *mapped, + unsigned char level) +{ + unsigned int block; + ManagedNandFlash_EraseAll(MANAGED(mapped), level); + // Reset to no block mapped + if (level > NandEraseDIRTY) { + mapped->logicalMappingBlock = -1; + mapped->mappingModified = 0; + for (block=0; + block < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + block++) { + mapped->logicalMapping[block] = -1; + } + } + return 0; +} diff --git a/memories/nandflash/MappedNandFlash.h b/memories/nandflash/MappedNandFlash.h new file mode 100644 index 0000000..c34bfc1 --- /dev/null +++ b/memories/nandflash/MappedNandFlash.h @@ -0,0 +1,116 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "MappedNandFlash" +/// +/// !!!Purpose +/// +/// MappedNandFlash layer will do operations on logical blocks of nandflash, it is called by +/// TranslatedNandFlash layer +//------------------------------------------------------------------------------ + +#ifndef MAPPEDNANDFLASH_H +#define MAPPEDNANDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "ManagedNandFlash.h" + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +struct MappedNandFlash { + + struct ManagedNandFlash managed; + signed short logicalMapping[NandCommon_MAXNUMBLOCKS]; + signed short logicalMappingBlock; + unsigned char mappingModified; + unsigned char reserved; +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char MappedNandFlash_Initialize( + struct MappedNandFlash *mapped, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy, + unsigned short baseBlock, + unsigned short sizeInBlocks); + +extern unsigned char MappedNandFlash_ReadPage( + const struct MappedNandFlash *mapped, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char MappedNandFlash_WritePage( + const struct MappedNandFlash *mapped, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char MappedNandFlash_Map( + struct MappedNandFlash *mapped, + unsigned short logicalBlock, + unsigned short physicalBlock); + +extern unsigned char MappedNandFlash_Unmap( + struct MappedNandFlash *mapped, + unsigned short logicalBlock); + +extern signed short MappedNandFlash_LogicalToPhysical( + const struct MappedNandFlash *mapped, + unsigned short logicalBlock); + +extern signed short MappedNandFlash_PhysicalToLogical( + const struct MappedNandFlash *mapped, + unsigned short physicalBlock); + +extern unsigned char MappedNandFlash_SaveLogicalMapping( + struct MappedNandFlash *mapped, + unsigned short physicalBlock); + +extern unsigned char MappedNandFlash_EraseAll( + struct MappedNandFlash *mapped, + unsigned char level); + +#endif //#ifndef MAPPEDNANDFLASH_H + diff --git a/memories/nandflash/NandCommon.h b/memories/nandflash/NandCommon.h new file mode 100644 index 0000000..47fefa7 --- /dev/null +++ b/memories/nandflash/NandCommon.h @@ -0,0 +1,134 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef NANDCOMMON_H +#define NANDCOMMON_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ +#if defined(at91sam3u1) || defined(at91sam3u2) || defined(at91sam7se32) +#define NF_MAXPAGESIZE_SUPPORT_2K +#endif + +//------------------------------------------------------------------------------ +/// \page "NandFlash Maximum Supported Values" +/// Since no memory allocation is available, limits have been set on various +/// characteristics of a NandFlash chip: +/// +/// !Maximums +/// - NandCommon_MAXNUMBLOCKS +/// - NandCommon_MAXNUMPAGESPERBLOCK +/// - NandCommon_MAXPAGESIZE + +/// Maximum number of blocks in a device +#define NandCommon_MAXNUMBLOCKS 1024 //2048 + +/// Maximum number of pages in one block +#define NandCommon_MAXNUMPAGESPERBLOCK 256 //64 + +/// Maximum size of the data area of one page, in bytes. +#if !defined(NF_MAXPAGESIZE_SUPPORT_2K) +#define NandCommon_MAXPAGEDATASIZE 4096 //2048 +#else +#define NandCommon_MAXPAGEDATASIZE 2048 +#endif + +/// Maximum size of the spare area of one page, in bytes. +#define NandCommon_MAXPAGESPARESIZE 128 //64 + +/// Maximum number of ecc bytes stored in the spare for one single page. +#define NandCommon_MAXSPAREECCBYTES 48 //24 + +/// Maximum number of extra free bytes inside the spare area of a page. +#define NandCommon_MAXSPAREEXTRABYTES 78 //38 + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "NandFlash Error Codes" +/// NandFlash API methods return either 0 (indicating that there was no error), +/// or one of the following error codes: +/// +/// !Codes +/// - NandCommon_ERROR_NOMOREBLOCKS +/// - NandCommon_ERROR_BLOCKNOTMAPPED +/// - NandCommon_ERROR_OUTOFBOUNDS +/// - NandCommon_ERROR_MAPPINGNOTFOUND +/// - NandCommon_ERROR_CANNOTREAD + +/// No more blocks can be allocated for a write operation. +#define NandCommon_ERROR_NOMOREBLOCKS 1 + +/// The desired logical block has no current physical mapping. +#define NandCommon_ERROR_BLOCKNOTMAPPED 2 + +/// Access if out-of-bounds. +#define NandCommon_ERROR_OUTOFBOUNDS 3 + +/// There are no block having the desired property. +#define NandCommon_ERROR_NOBLOCKFOUND 4 + +/// The nandflash device has no logical mapping information on it. +#define NandCommon_ERROR_MAPPINGNOTFOUND 5 + +/// A read operation cannot be carried out. +#define NandCommon_ERROR_CANNOTREAD 6 + +/// A write operation cannot be carried out. +#define NandCommon_ERROR_CANNOTWRITE 7 + +/// NandFlash chip model cannot be recognized. +#define NandCommon_ERROR_UNKNOWNMODEL 8 + +/// Page data is corrupted according to ECC +#define NandCommon_ERROR_CORRUPTEDDATA 9 + +/// Block is not in the required status. +#define NandCommon_ERROR_WRONGSTATUS 10 + +/// Device has no logical mapping stored in it +#define NandCommon_ERROR_NOMAPPING 11 + +/// The block being accessed is bad and must be replaced +#define NandCommon_ERROR_BADBLOCK 12 + +/// Failed to perform an erase operation +#define NandCommon_ERROR_CANNOTERASE 13 + +/// A hardware copyback operation failed. +#define NandCommon_ERROR_CANNOTCOPY 14 + +/// HW Ecc Not compatible with the Nand Model +#define NandCommon_ERROR_ECC_NOT_COMPATIBLE 15 + +//------------------------------------------------------------------------------ + +#endif //#ifndef NANDCOMMON_H + diff --git a/memories/nandflash/NandFlashModel.c b/memories/nandflash/NandFlashModel.c new file mode 100644 index 0000000..759a4ca --- /dev/null +++ b/memories/nandflash/NandFlashModel.c @@ -0,0 +1,403 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandFlashModel.h" +#include "NandCommon.h" +#if defined(CHIP_NAND_CTRL) +#include +#endif +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +/// Get the power of input, given a certain result, i.e. input^(power) = result. +/// returns the value of "power" if succesfully find the power. +/// \param result a certain output we want to calculate. +/// \param input the input of the power. +//------------------------------------------------------------------------------ +#if defined(OP_BOOTSTRAP_on) +unsigned int CALPOW(unsigned int result, unsigned int input) +{ + unsigned int i=0; + + while(i<32) + { + if(result == (input << i)) + return i; + i++; + } + + return 0; +} +#endif + +//------------------------------------------------------------------------------ +/// Get the interger part of input, given a certain result, i.e. return = result / input. +/// returns the value of interger part of the result/input. +/// \param result a certain output we want to calculate. +/// \param input the input of the division. +//------------------------------------------------------------------------------ +#if defined(OP_BOOTSTRAP_on) +unsigned int CALINT(unsigned int result, unsigned int input) +{ + unsigned int i=0; + unsigned int tmpInput=0; + + while(1) + { + tmpInput +=input; + i++; + if(tmpInput == result) + return i; + else if (tmpInput > result) + return (i-1); + } + +} +#endif + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Looks for a NandFlashModel corresponding to the given ID inside a list of +/// model. If found, the model variable is filled with the correct values. +/// This function returns 0 if a matching model has been found; otherwise it +/// returns NandCommon_ERROR_UNKNOWNMODEL. +/// \param modelList List of NandFlashModel instances. +/// \param size Number of models in list. +/// \param chipId Identifier returned by the Nand(id1|(id2<<8)|(id3<<16)|(id4<<24)). +/// \param model NandFlashModel instance to update with the model parameters. +//------------------------------------------------------------------------------ +unsigned char NandFlashModel_Find( + const struct NandFlashModel *modelList, + unsigned int size, + unsigned int chipId, + struct NandFlashModel *model) +{ + unsigned char found = 0, id2, id4; + unsigned int i; + #if defined(CHIP_NAND_CTRL) + unsigned char pageSize = 0; + #endif + id2 = (unsigned char)(chipId>>8); + id4 = (unsigned char)(chipId>>24); + + TRACE_INFO("Nandflash ID is 0x%08X\n\r", chipId); + + for(i=0; iblockSizeInKBytes == 0 || model->pageSizeInBytes == 0) { + TRACE_DEBUG("Fetch from ID4(0x%.2x):\r\n", id4); + /// Fetch from the extended ID4 + /// ID4 D5 D4 BlockSize || D1 D0 PageSize + /// 0 0 64K || 0 0 1K + /// 0 1 128K || 0 1 2K + /// 1 0 256K || 1 0 4K + /// 1 1 512K || 1 1 8k + #if !defined(OP_BOOTSTRAP_on) + switch(id4 & 0x03) { + case 0x00: model->pageSizeInBytes = 1024; break; + case 0x01: model->pageSizeInBytes = 2048; break; + case 0x02: model->pageSizeInBytes = 4096; break; + case 0x03: model->pageSizeInBytes = 8192; break; + } + switch(id4 & 0x30) { + case 0x00: model->blockSizeInKBytes = 64; break; + case 0x10: model->blockSizeInKBytes = 128; break; + case 0x20: model->blockSizeInKBytes = 256; break; + case 0x30: model->blockSizeInKBytes = 512; break; + } + #else + model->pageSizeInBytes = 1024 << (id4 & 0x03); + model->blockSizeInKBytes = (64) << ((id4 & 0x30) >>4); + #endif + } + #if defined(CHIP_NAND_CTRL) + switch(model->pageSizeInBytes) { + case 1024: pageSize = AT91C_HSMC4_PAGESIZE_1056_Bytes; break; + case 2048: pageSize = AT91C_HSMC4_PAGESIZE_2112_Bytes; break; + case 4096: pageSize = AT91C_HSMC4_PAGESIZE_4224_Bytes; break; + default: TRACE_ERROR("Unsupportted page size for NAND Flash Controller\n\r"); + } + HSMC4_SetMode(pageSize | AT91C_HSMC4_DTOMUL_1048576 | AT91C_HSMC4_EDGECTRL |AT91C_HSMC4_DTOCYC | AT91C_HSMC4_RSPARE); + #endif + } + TRACE_DEBUG("NAND Model found:\r\n"); + TRACE_DEBUG(" * deviceId = 0x%02X\r\n", model->deviceId); + TRACE_DEBUG(" * deviceSizeInMegaBytes = %d\r\n", model->deviceSizeInMegaBytes); + TRACE_DEBUG(" * blockSizeInkBytes = %d\r\n", model->blockSizeInKBytes); + TRACE_DEBUG(" * pageSizeInBytes = %d\r\n", model->pageSizeInBytes); + TRACE_DEBUG(" * options = 0x%02X\r\n", model->options); + break; + } + } + + // Check if chip has been detected + if (found) { + + return 0; + } + else { + + return NandCommon_ERROR_UNKNOWNMODEL; + } +} + +//------------------------------------------------------------------------------ +/// Translates address/size access of a NandFlashModel to block, page and +/// offset values. The values are stored in the provided variables if their +/// pointer is not 0. +/// Returns 0 if the access is correct; otherwise returns +/// NandCommon_ERROR_OUTOFBOUNDS. +/// \param model Pointer to a NandFlashModel instance. +/// \param address Access address. +/// \param size Access size in bytes. +/// \param block Stores the first accessed block number. +/// \param page Stores the first accessed page number inside the first block. +/// \param offset Stores the byte offset inside the first accessed page. +//------------------------------------------------------------------------------ +unsigned char NandFlashModel_TranslateAccess( + const struct NandFlashModel *model, + unsigned int address, + unsigned int size, + unsigned short *block, + unsigned short *page, + unsigned short *offset) +{ + // Check that access is not too big + #if !defined(OP_BOOTSTRAP_on) + if ((address + size) > NandFlashModel_GetDeviceSizeInBytes(model)) { + + TRACE_DEBUG("NandFlashModel_TranslateAccess: out-of-bounds access.\n\r"); + return NandCommon_ERROR_OUTOFBOUNDS; + } + #endif + + // Get Nand info + unsigned int blockSize = NandFlashModel_GetBlockSizeInBytes(model); + unsigned int pageSize = NandFlashModel_GetPageDataSize(model); + + // Translate address + #if !defined(OP_BOOTSTRAP_on) + unsigned short tmpBlock = address / blockSize; + address -= tmpBlock * blockSize; + unsigned short tmpPage = address / pageSize; + address -= tmpPage * pageSize; + unsigned short tmpOffset = address; + #else + unsigned short tmpBlock = CALINT(address, blockSize); + address -= tmpBlock * blockSize; + unsigned short tmpPage = CALINT(address, pageSize); + address -= tmpPage * pageSize; + unsigned short tmpOffset= address; + #endif + + // Save results + if (block) { + + *block = tmpBlock; + } + if (page) { + + *page = tmpPage; + } + if (offset) { + + *offset = tmpOffset; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns the spare area placement scheme used by a particular nandflash +/// model. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +const struct NandSpareScheme * NandFlashModel_GetScheme( + const struct NandFlashModel *model) +{ + return model->scheme; +} + +//------------------------------------------------------------------------------ +/// Returns the device ID of a particular NandFlash model. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned char NandFlashModel_GetDeviceId( + const struct NandFlashModel *model) +{ + return model->deviceId; +} + +//------------------------------------------------------------------------------ +/// Returns the number of blocks in the entire device. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned short NandFlashModel_GetDeviceSizeInBlocks( + const struct NandFlashModel *model) +{ + #if !defined(OP_BOOTSTRAP_on) + return ((1024) / model->blockSizeInKBytes) * model->deviceSizeInMegaBytes; + #else + unsigned int pow; + pow = CALPOW((1024 * model->deviceSizeInMegaBytes), model->blockSizeInKBytes); + return (0x1 << pow); + #endif +} + +//------------------------------------------------------------------------------ +/// Returns the number of pages in the entire device. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned int NandFlashModel_GetDeviceSizeInPages( + const struct NandFlashModel *model) +{ + return (unsigned int) NandFlashModel_GetDeviceSizeInBlocks(model) //* 8 // HACK + * NandFlashModel_GetBlockSizeInPages(model); +} + +//------------------------------------------------------------------------------ +/// Returns the size of the whole device in bytes (this does not include the +/// size of the spare zones). +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned long long NandFlashModel_GetDeviceSizeInBytes( + const struct NandFlashModel *model) +{ + return ((unsigned long long) model->deviceSizeInMegaBytes) << 20; +} + +//------------------------------------------------------------------------------ +/// Returns the size of the whole device in Mega bytes (this does not include the +/// size of the spare zones). +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned int NandFlashModel_GetDeviceSizeInMBytes( + const struct NandFlashModel *model) +{ + return ((unsigned int) model->deviceSizeInMegaBytes); +} + + +//------------------------------------------------------------------------------ +/// Returns the number of pages in one single block of a device. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned short NandFlashModel_GetBlockSizeInPages( + const struct NandFlashModel *model) +{ + #if !defined(OP_BOOTSTRAP_on) + return model->blockSizeInKBytes * 1024 / model->pageSizeInBytes; + #else + unsigned int pow; + pow = CALPOW((model->blockSizeInKBytes * 1024), model->pageSizeInBytes); + return (0x1 << pow); + #endif +} + +//------------------------------------------------------------------------------ +/// Returns the size in bytes of one single block of a device. This does not +/// take into account the spare zones size. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned int NandFlashModel_GetBlockSizeInBytes( + const struct NandFlashModel *model) +{ + return (model->blockSizeInKBytes *1024); +} + +//------------------------------------------------------------------------------ +/// Returns the size of the data area of a page in bytes. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned short NandFlashModel_GetPageDataSize( + const struct NandFlashModel *model) +{ + return model->pageSizeInBytes; +} + +//------------------------------------------------------------------------------ +/// Returns the size of the spare area of a page in bytes. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned char NandFlashModel_GetPageSpareSize( + const struct NandFlashModel *model) +{ + return (model->pageSizeInBytes>>5); /// Spare size is 16/512 of data size +} + +//------------------------------------------------------------------------------ +/// Returns the number of bits used by the data bus of a NandFlash device. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned char NandFlashModel_GetDataBusWidth( + const struct NandFlashModel *model) +{ + return (model->options&NandFlashModel_DATABUS16)? 16: 8; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the given NandFlash model uses the "small blocks/pages" +/// command set; otherwise returns 0. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned char NandFlashModel_HasSmallBlocks( + const struct NandFlashModel *model) +{ + return (model->pageSizeInBytes <= 512 )? 1: 0; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the device supports the copy-back operation. Otherwise returns +/// 0. +/// \param model Pointer to a NandFlashModel instance. +//------------------------------------------------------------------------------ +unsigned char NandFlashModel_SupportsCopyBack( + const struct NandFlashModel *model) +{ + return ((model->options & NandFlashModel_COPYBACK) != 0); +} diff --git a/memories/nandflash/NandFlashModel.h b/memories/nandflash/NandFlashModel.h new file mode 100644 index 0000000..9c21a45 --- /dev/null +++ b/memories/nandflash/NandFlashModel.h @@ -0,0 +1,169 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Type and methods for manipulating NandFlash models. +/// +/// !Usage +/// +/// -# Find the model of a NandFlash using its device ID with the +/// NandFlashModel_Find function. +/// +/// -# Retrieve parameters of a NandFlash model using the following functions: +/// - NandFlashModel_GetDeviceId +/// - NandFlashModel_GetDeviceSizeInBlocks +/// - NandFlashModel_GetDeviceSizeInPages +/// - NandFlashModel_GetDeviceSizeInBytes +/// - NandFlashModel_GetBlockSizeInPages +/// - NandFlashModel_GetBlockSizeInBytes +/// - NandFlashModel_GetPageDataSize +/// - NandFlashModel_GetPageSpareSize +/// - NandFlashModel_GetDataBusWidth +/// - NandFlashModel_UsesSmallBlocksRead +/// - NandFlashModel_UsesSmallBlocksWrite +//------------------------------------------------------------------------------ + +#ifndef NANDFLASHMODEL_H +#define NANDFLASHMODEL_H + +//------------------------------------------------------------------------------ +// Forward declarations +//------------------------------------------------------------------------------ + +struct NandSpareScheme; + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "NandFlashModel options" +/// This page lists the possible options for a NandFlash chip. +/// +/// !Options +/// - NandFlashModel_DATABUS8 +/// - NandFlashModel_DATABUS16 +/// - NandFlashModel_COPYBACK + +/// Indicates the Nand uses an 8-bit databus. +#define NandFlashModel_DATABUS8 (0 << 0) + +/// Indicates the Nand uses a 16-bit databus. +#define NandFlashModel_DATABUS16 (1 << 0) + +/// The Nand supports the copy-back function (internal page-to-page copy). +#define NandFlashModel_COPYBACK (1 << 1) + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Describes a particular model of NandFlash device. +//------------------------------------------------------------------------------ +struct NandFlashModel { + + /// Identifier for the device. + unsigned char deviceId; + /// Special options for the NandFlash. + unsigned char options; + /// Size of the data area of a page, in bytes. + unsigned short pageSizeInBytes; + /// Size of the device in MB. + unsigned short deviceSizeInMegaBytes; + /// Size of one block in kilobytes. + unsigned short blockSizeInKBytes; + /// Spare area placement scheme + const struct NandSpareScheme *scheme; +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char NandFlashModel_Find( + const struct NandFlashModel *modelList, + unsigned int size, + unsigned int id, + struct NandFlashModel *model); + +extern unsigned char NandFlashModel_TranslateAccess( + const struct NandFlashModel *model, + unsigned int address, + unsigned int size, + unsigned short *block, + unsigned short *page, + unsigned short *offset); + +extern const struct NandSpareScheme * NandFlashModel_GetScheme( + const struct NandFlashModel *model); + +extern unsigned char NandFlashModel_GetDeviceId( + const struct NandFlashModel *model); + +extern unsigned short NandFlashModel_GetDeviceSizeInBlocks( + const struct NandFlashModel *model); + +extern unsigned int NandFlashModel_GetDeviceSizeInPages( + const struct NandFlashModel *model); + +extern unsigned long long NandFlashModel_GetDeviceSizeInBytes( + const struct NandFlashModel *model); + +extern unsigned int NandFlashModel_GetDeviceSizeInMBytes( + const struct NandFlashModel *model); + +extern unsigned short NandFlashModel_GetBlockSizeInPages( + const struct NandFlashModel *model); + +extern unsigned int NandFlashModel_GetBlockSizeInBytes( + const struct NandFlashModel *model); + +extern unsigned short NandFlashModel_GetPageDataSize( + const struct NandFlashModel *model); + +extern unsigned char NandFlashModel_GetPageSpareSize( + const struct NandFlashModel *model); + +extern unsigned char NandFlashModel_GetDataBusWidth( + const struct NandFlashModel *model); + +extern unsigned char NandFlashModel_HasSmallBlocks( + const struct NandFlashModel *model); + +extern unsigned char NandFlashModel_SupportsCopyBack( + const struct NandFlashModel *model); + +#endif //#ifndef NANDFLASHMODEL_H + diff --git a/memories/nandflash/NandFlashModelList.c b/memories/nandflash/NandFlashModelList.c new file mode 100644 index 0000000..78b25f6 --- /dev/null +++ b/memories/nandflash/NandFlashModelList.c @@ -0,0 +1,118 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandFlashModelList.h" +#include "NandSpareScheme.h" + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of NandFlash models which can be recognized by the software. +const struct NandFlashModel nandFlashModelList[NandFlashModelList_SIZE] = { + +// | ID | Options | Page | Mo | Block |BlkPg |DevBlk + {0x6e, NandFlashModel_DATABUS8, 256, 1, 4, &nandSpareScheme256}, + {0x64, NandFlashModel_DATABUS8, 256, 2, 4, &nandSpareScheme256}, + {0x6b, NandFlashModel_DATABUS8, 512, 4, 8, &nandSpareScheme512}, + {0xe8, NandFlashModel_DATABUS8, 256, 1, 4, &nandSpareScheme256}, + {0xec, NandFlashModel_DATABUS8, 256, 1, 4, &nandSpareScheme256}, + {0xea, NandFlashModel_DATABUS8, 256, 2, 4, &nandSpareScheme256}, + {0xd5, NandFlashModel_DATABUS8, 512, 4, 8, &nandSpareScheme512}, + {0xe3, NandFlashModel_DATABUS8, 512, 4, 8, &nandSpareScheme512}, + {0xe5, NandFlashModel_DATABUS8, 512, 4, 8, &nandSpareScheme512}, + {0xd6, NandFlashModel_DATABUS8, 512, 8, 8, &nandSpareScheme512}, + + {0x39, NandFlashModel_DATABUS8, 512, 8, 8, &nandSpareScheme512}, + {0xe6, NandFlashModel_DATABUS8, 512, 8, 8, &nandSpareScheme512}, + {0x49, NandFlashModel_DATABUS16, 512, 8, 8, &nandSpareScheme512}, + {0x59, NandFlashModel_DATABUS16, 512, 8, 8, &nandSpareScheme512}, + + {0x33, NandFlashModel_DATABUS8, 512, 16, 16, &nandSpareScheme512}, + {0x73, NandFlashModel_DATABUS8, 512, 16, 16, &nandSpareScheme512}, + {0x43, NandFlashModel_DATABUS16, 512, 16, 16, &nandSpareScheme512}, + {0x53, NandFlashModel_DATABUS16, 512, 16, 16, &nandSpareScheme512}, + + {0x35, NandFlashModel_DATABUS8, 512, 32, 16, &nandSpareScheme512}, + {0x75, NandFlashModel_DATABUS8, 512, 32, 16, &nandSpareScheme512}, + {0x45, NandFlashModel_DATABUS16, 512, 32, 16, &nandSpareScheme512}, + {0x55, NandFlashModel_DATABUS16, 512, 32, 16, &nandSpareScheme512}, + + {0x36, NandFlashModel_DATABUS8, 512, 64, 16, &nandSpareScheme512}, + {0x76, NandFlashModel_DATABUS8, 512, 64, 16, &nandSpareScheme512}, + {0x46, NandFlashModel_DATABUS16, 512, 64, 16, &nandSpareScheme512}, + {0x56, NandFlashModel_DATABUS16, 512, 64, 16, &nandSpareScheme512}, + + {0x78, NandFlashModel_DATABUS8, 512, 128, 16, &nandSpareScheme512}, + {0x39, NandFlashModel_DATABUS8, 512, 128, 16, &nandSpareScheme512}, + {0x79, NandFlashModel_DATABUS8, 512, 128, 16, &nandSpareScheme512}, + {0x72, NandFlashModel_DATABUS16, 512, 128, 16, &nandSpareScheme512}, + {0x49, NandFlashModel_DATABUS16, 512, 128, 16, &nandSpareScheme512}, + {0x74, NandFlashModel_DATABUS16, 512, 128, 16, &nandSpareScheme512}, + {0x59, NandFlashModel_DATABUS16, 512, 128, 16, &nandSpareScheme512}, + + {0x71, NandFlashModel_DATABUS8, 512, 256, 16, &nandSpareScheme512}, + +// Large blocks devices. Parameters must be fetched from the extended I +#define OPTIONS NandFlashModel_COPYBACK + + {0xA2, NandFlashModel_DATABUS8 | OPTIONS, 0, 64, 0, &nandSpareScheme2048}, + {0xF2, NandFlashModel_DATABUS8 | OPTIONS, 0, 64, 0, &nandSpareScheme2048}, + {0xB2, NandFlashModel_DATABUS16 | OPTIONS, 0, 64, 0, &nandSpareScheme2048}, + {0xC2, NandFlashModel_DATABUS16 | OPTIONS, 0, 64, 0, &nandSpareScheme2048}, + + {0xA1, NandFlashModel_DATABUS8 | OPTIONS, 0, 128, 0, &nandSpareScheme2048}, + {0xF1, NandFlashModel_DATABUS8 | OPTIONS, 0, 128, 0, &nandSpareScheme2048}, + {0xB1, NandFlashModel_DATABUS16 | OPTIONS, 0, 128, 0, &nandSpareScheme2048}, + {0xC1, NandFlashModel_DATABUS16 | OPTIONS, 0, 128, 0, &nandSpareScheme2048}, + + {0xAA, NandFlashModel_DATABUS8 | OPTIONS, 0, 256, 0, &nandSpareScheme2048}, + {0xDA, NandFlashModel_DATABUS8 | OPTIONS, 0, 256, 0, &nandSpareScheme2048}, + {0xBA, NandFlashModel_DATABUS16 | OPTIONS, 0, 256, 0, &nandSpareScheme2048}, + {0xCA, NandFlashModel_DATABUS16 | OPTIONS, 0, 256, 0, &nandSpareScheme2048}, + + {0xAC, NandFlashModel_DATABUS8 | OPTIONS, 0, 512, 0, &nandSpareScheme2048}, + {0xDC, NandFlashModel_DATABUS8 | OPTIONS, 0, 512, 0, &nandSpareScheme2048}, + {0xBC, NandFlashModel_DATABUS16 | OPTIONS, 0, 512, 0, &nandSpareScheme2048}, + {0xCC, NandFlashModel_DATABUS16 | OPTIONS, 0, 512, 0, &nandSpareScheme2048}, + + {0xA3, NandFlashModel_DATABUS8 | OPTIONS, 0, 1024, 0, &nandSpareScheme2048}, + {0xD3, NandFlashModel_DATABUS8 | OPTIONS, 0, 1024, 0, &nandSpareScheme2048}, + {0xB3, NandFlashModel_DATABUS16 | OPTIONS, 0, 1024, 0, &nandSpareScheme2048}, + {0xC3, NandFlashModel_DATABUS16 | OPTIONS, 0, 1024, 0, &nandSpareScheme2048}, + + {0xA5, NandFlashModel_DATABUS8 | OPTIONS, 0, 2048, 0, &nandSpareScheme2048}, + {0xD5, NandFlashModel_DATABUS8 | OPTIONS, 0, 2048, 0, &nandSpareScheme2048}, + {0xB5, NandFlashModel_DATABUS16 | OPTIONS, 0, 2048, 0, &nandSpareScheme2048}, + {0xC5, NandFlashModel_DATABUS16 | OPTIONS, 0, 2048, 0, &nandSpareScheme2048}, +}; diff --git a/memories/nandflash/NandFlashModelList.h b/memories/nandflash/NandFlashModelList.h new file mode 100644 index 0000000..e451271 --- /dev/null +++ b/memories/nandflash/NandFlashModelList.h @@ -0,0 +1,65 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Static array of the various NandFlashModels which are supported. +/// +/// !Usage +/// +/// -# Uses the NandFlashModel_Find function to look for a particular model in +/// the nandFlashModelList array. +//------------------------------------------------------------------------------ + +#ifndef NANDFLASHMODELLIST_H +#define NANDFLASHMODELLIST_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandFlashModel.h" + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Number of NandFlash models inside the list. +#define NandFlashModelList_SIZE 58 + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +extern const struct NandFlashModel nandFlashModelList[NandFlashModelList_SIZE]; + +#endif //#ifndef NANDFLASHMODELLIST_H + diff --git a/memories/nandflash/NandSpareScheme.c b/memories/nandflash/NandSpareScheme.c new file mode 100644 index 0000000..a816f9c --- /dev/null +++ b/memories/nandflash/NandSpareScheme.c @@ -0,0 +1,230 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandSpareScheme.h" +#include + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ +/// Spare area placement scheme for 256 byte pages. +const struct NandSpareScheme nandSpareScheme256 = { + + // Bad block marker is at position #5 + 5, + // 3 ecc bytes + 3, + // Ecc bytes positions + {0, 1, 2}, + // 4 extra bytes + 4, + // Extra bytes positions + {3, 4, 6, 7} +}; + +/// Spare area placement scheme for 512 byte pages. +const struct NandSpareScheme nandSpareScheme512 = { + + // Bad block marker is at position #5 + 5, + // 6 ecc bytes + 6, + // Ecc bytes positions + {0, 1, 2, 3, 6, 7}, + // 8 extra bytes + 8, + // Extra bytes positions + {8, 9, 10, 11, 12, 13, 14, 15} +}; + +/// Spare area placement scheme for 2048 byte pages. +const struct NandSpareScheme nandSpareScheme2048 = { + + // Bad block marker is at position #0 + 0, + // 24 ecc bytes + 24, + // Ecc bytes positions + {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63}, + // 38 extra bytes + 38, + // Extra bytes positions + { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39} +}; + +/// Spare area placement scheme for 4096 byte pages. +const struct NandSpareScheme nandSpareScheme4096 = { + + // Bad block marker is at position #0 + 0, + // 48 ecc bytes + 48, + // Ecc bytes positions + { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127}, + // 78 extra bytes + 78, + // Extra bytes positions + { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79} +}; + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Reads the bad block marker inside a spare area buffer using the provided +/// scheme. +/// \param scheme Pointer to a NandSpareScheme instance. +/// \param spare Spare area buffer. +/// \param marker Pointer to the variable to store the bad block marker. +//------------------------------------------------------------------------------ +void NandSpareScheme_ReadBadBlockMarker( + const struct NandSpareScheme *scheme, + const unsigned char *spare, + unsigned char *marker) +{ + *marker = spare[scheme->badBlockMarkerPosition]; +} + +//------------------------------------------------------------------------------ +/// Modifies the bad block marker inside a spare area, using the given scheme. +/// \param scheme Pointer to a NandSpareScheme instance. +/// \param spare Spare area buffer. +/// \param marker Bad block marker to write. +//------------------------------------------------------------------------------ +void NandSpareScheme_WriteBadBlockMarker( + const struct NandSpareScheme *scheme, + unsigned char *spare, + unsigned char marker) +{ + spare[scheme->badBlockMarkerPosition] = marker; +} + +//------------------------------------------------------------------------------ +/// Reads ECC information from a spare area using the provided scheme. +/// \param scheme Pointer to a NandSpareScheme instance. +/// \param spare Spare area buffer. +/// \param ecc ECC buffer. +//------------------------------------------------------------------------------ +void NandSpareScheme_ReadEcc( + const struct NandSpareScheme *scheme, + const unsigned char *spare, + unsigned char *ecc) +{ + unsigned int i; + for (i=0; i < scheme->numEccBytes; i++) { + + ecc[i] = spare[scheme->eccBytesPositions[i]]; + } +} + +//------------------------------------------------------------------------------ +/// Writes ECC information in a spare area, using a particular scheme. +/// \param scheme Pointer to a NandSpareScheme instance. +/// \param spare Spare area buffer. +/// \param ecc ECC buffer. +//------------------------------------------------------------------------------ +void NandSpareScheme_WriteEcc( + const struct NandSpareScheme *scheme, + unsigned char *spare, + const unsigned char *ecc) +{ + unsigned int i; + for (i=0; i < scheme->numEccBytes; i++) { + + spare[scheme->eccBytesPositions[i]] = ecc[i]; + } +} + +//------------------------------------------------------------------------------ +/// Reads extra bytes of information from a spare area, using the provided +/// scheme. +/// \param scheme Pointer to a NandSpareScheme instance. +/// \param spare Spare area buffer. +/// \param extra Extra bytes buffer. +/// \param size Number of extra bytes to read. +/// \param offset Index where to read the first extra byte. +//------------------------------------------------------------------------------ +void NandSpareScheme_ReadExtra( + const struct NandSpareScheme *scheme, + const unsigned char *spare, + void *extra, + unsigned char size, + unsigned char offset) +{ + ASSERT((size + offset) < scheme->numExtraBytes, + "NandSpareScheme_ReadExtra: Too many bytes\n\r"); + + unsigned int i; + for (i=0; i < size; i++) { + + ((unsigned char *) extra)[i] = spare[scheme->extraBytesPositions[i+offset]]; + } +} + +//------------------------------------------------------------------------------ +/// Write extra bytes of information inside a spare area, using the provided +/// scheme. +/// \param scheme Pointer to a NandSpareScheme instance. +/// \param spare Spare area buffer. +/// \param extra Extra bytes to write. +/// \param size Number of extra bytes to write. +/// \param offset Index where to write the first extra byte. +//------------------------------------------------------------------------------ +void NandSpareScheme_WriteExtra( + const struct NandSpareScheme *scheme, + unsigned char *spare, + const void *extra, + unsigned char size, + unsigned char offset) +{ + ASSERT((size + offset) < scheme->numExtraBytes, + "NandSpareScheme_WriteExtra: Too many bytes\n\r"); + + unsigned int i; + for (i=0; i < size; i++) { + + spare[scheme->extraBytesPositions[i+offset]] = ((unsigned char *) extra)[i]; + } +} + diff --git a/memories/nandflash/NandSpareScheme.h b/memories/nandflash/NandSpareScheme.h new file mode 100644 index 0000000..8bdb1c6 --- /dev/null +++ b/memories/nandflash/NandSpareScheme.h @@ -0,0 +1,124 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "NandSpareScheme" +/// +/// !!!Purpose +/// +/// NandSpareScheme layer is used to do Nandflash device's spare area operations. It is called by +/// upper layer drivers, such as SkipBlockNandFlash layer. +/// +/// !!!Usage +/// +/// -# NandSpareScheme_WriteBadBlockMarker is used to mark a badblock marker inside a spare +/// area. +/// -# NandSpareScheme_ReadBadBlockMarker is used to readout the marker. +/// -# NandSpareScheme_ReadEcc is used to read out ecc from spare area using the provided +/// spare scheme. +/// -# NandSpareScheme_WriteEcc is used to write ecc to spare area using the provided +/// spare scheme. +/// -# NandSpareScheme_ReadExtra is used to read extra bytes from spare area using the provided +/// spare scheme. +/// -# NandSpareScheme_WriteExtra is used to write extra bytes to spare area using the provided +/// spare scheme. +//------------------------------------------------------------------------------ + + +#ifndef NANDSPARESCHEME_H +#define NANDSPARESCHEME_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandCommon.h" + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +struct NandSpareScheme { + + unsigned char badBlockMarkerPosition; + unsigned char numEccBytes; + unsigned char eccBytesPositions[NandCommon_MAXSPAREECCBYTES]; + unsigned char numExtraBytes; + unsigned char extraBytesPositions[NandCommon_MAXSPAREEXTRABYTES]; +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +extern const struct NandSpareScheme nandSpareScheme256; +extern const struct NandSpareScheme nandSpareScheme512; +extern const struct NandSpareScheme nandSpareScheme2048; +extern const struct NandSpareScheme nandSpareScheme4096; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void NandSpareScheme_ReadBadBlockMarker( + const struct NandSpareScheme *scheme, + const unsigned char *spare, + unsigned char *marker); + +extern void NandSpareScheme_WriteBadBlockMarker( + const struct NandSpareScheme *scheme, + unsigned char *spare, + unsigned char marker); + +extern void NandSpareScheme_ReadEcc( + const struct NandSpareScheme *scheme, + const unsigned char *spare, + unsigned char *ecc); + +extern void NandSpareScheme_WriteEcc( + const struct NandSpareScheme *scheme, + unsigned char *spare, + const unsigned char *ecc); + +extern void NandSpareScheme_ReadExtra( + const struct NandSpareScheme *scheme, + const unsigned char *spare, + void *extra, + unsigned char size, + unsigned char offset); + +extern void NandSpareScheme_WriteExtra( + const struct NandSpareScheme *scheme, + unsigned char *spare, + const void *extra, + unsigned char size, + unsigned char offset); + +#endif //#ifndef NANDSPARESCHEME_H + diff --git a/memories/nandflash/NfcRawNandFlash.c b/memories/nandflash/NfcRawNandFlash.c new file mode 100644 index 0000000..6dc015f --- /dev/null +++ b/memories/nandflash/NfcRawNandFlash.c @@ -0,0 +1,871 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//#define DMA_TRANSFER + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "RawNandFlash.h" +#include "NandCommon.h" +#include "NandFlashModelList.h" +#include +#include +#include +#include +#include + +#include +#if defined(CHIP_NAND_CTRL) + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Nand flash chip status codes +#define STATUS_READY (1 << 6) +#define STATUS_ERROR (1 << 0) + +/// Nand flash commands +#define COMMAND_READ_1 0x00 +#define COMMAND_READ_2 0x30 +#define COMMAND_COPYBACK_READ_1 0x00 +#define COMMAND_COPYBACK_READ_2 0x35 +#define COMMAND_COPYBACK_PROGRAM_1 0x85 +#define COMMAND_COPYBACK_PROGRAM_2 0x10 +#define COMMAND_RANDOM_OUT 0x05 +#define COMMAND_RANDOM_OUT_2 0xE0 +#define COMMAND_RANDOM_IN 0x85 +#define COMMAND_READID 0x90 +#define COMMAND_WRITE_1 0x80 +#define COMMAND_WRITE_2 0x10 +#define COMMAND_ERASE_1 0x60 +#define COMMAND_ERASE_2 0xD0 +#define COMMAND_STATUS 0x70 +#define COMMAND_RESET 0xFF + +/// Nand flash commands (small blocks) +#define COMMAND_READ_A 0x00 +#define COMMAND_READ_C 0x50 + +/// Using DMA +static unsigned int bNfcDMA = 0; + +//------------------------------------------------------------------------------ +// Internal macros +//------------------------------------------------------------------------------ +#define READ_DATA8(raw) \ + (*((volatile unsigned char *) raw->dataAddress)) +#define READ_DATA16(raw) \ + (*((volatile unsigned short *) raw->dataAddress)) + +/// Internal cast macros +#define MODEL(raw) ((struct NandFlashModel *) raw) + +/// Number of tries for erasing a block +#define NUMERASETRIES 2 +/// Number of tries for writing a block +#define NUMWRITETRIES 2 +/// Number of tries for copying a block +#define NUMCOPYTRIES 2 + + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Translates the given column and row address into first and other (1-4) address +/// cycles. The resulting values are stored in the provided variables if they are not null. +/// \param columnAddress Column address to translate. +/// \param rowAddress Row address to translate. +/// \param pAddressCycle0 First address cycle. +/// \param pAddressCycle1234 four address cycles. +//------------------------------------------------------------------------------ +void NFC_TranslateAddress( + const struct RawNandFlash *raw, + unsigned short columnAddress, + unsigned int rowAddress, + unsigned int *pAddressCycle0, + unsigned int *pAddressCycle1234, + unsigned char useFiveAddress) +{ + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(raw)); + unsigned int numPages = NandFlashModel_GetDeviceSizeInPages(MODEL(raw)); + unsigned char numAddressCycles = 0; + unsigned int addressCycle0 = 0; + unsigned int addressCycle1234 = 0; + + // Check the data bus width of the NandFlash + if (NandFlashModel_GetDataBusWidth(MODEL(raw)) == 16) { + // Div 2 is because we address in word and not in byte + columnAddress >>= 1; + } + // When 5 address cycle are used. + if (useFiveAddress) { + + while (pageDataSize > 0) { + if (numAddressCycles == 0) { + addressCycle0 = (columnAddress & 0xFF); + } + else { + addressCycle1234 |= (columnAddress & 0xFF) << ((numAddressCycles - 1) * 8); + } + pageDataSize >>= 8; + columnAddress >>= 8; + numAddressCycles ++; + } + while (numPages > 0) { + if (numAddressCycles == 0) { + addressCycle0 = (rowAddress & 0xFF); + } + else { + addressCycle1234 |= (rowAddress & 0xFF) << ((numAddressCycles - 1) * 8); + } + numPages >>= 8; + rowAddress >>= 8; + numAddressCycles ++; + } + } + // When less than 5 address cycle are used. + else { + while (numPages > 0) { + addressCycle1234 |= (rowAddress & 0xFF) << ((numAddressCycles) * 8); + numPages >>= 8; + rowAddress >>= 8; + numAddressCycles ++; + } + } + // Store values + if (pAddressCycle0) { + *pAddressCycle0 = addressCycle0; + } + if (pAddressCycle1234) { + *pAddressCycle1234 = addressCycle1234; + } + return; +} + + +//------------------------------------------------------------------------------ +/// Return 1 if program or erase operation is completed. +/// and the program or erase operation is completed successfully, otherwise return 0. +/// \param raw Pointer to a RawNandFlash instance. +//------------------------------------------------------------------------------ +static unsigned char IsOperationComplete(const struct RawNandFlash *raw) +{ + unsigned char status; + + HSMC4_SendCommand(AT91C_HSMC4_HOSTCMD | // Command. + 0 | // Host read data. + 0 | // Host is disabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_NONE | // No address cycle. + (COMMAND_STATUS << 2), // CMD1 (COMMAND_STATUS). + 0, // Dummy address cylce 1,2,3,4. + 0 // Dummy address cylce 0. + ); + status = READ_DATA8(raw); + if (((status & STATUS_READY) != STATUS_READY) || ((status & STATUS_ERROR) != 0)) { + return 0; + } + return 1; +} + +//------------------------------------------------------------------------------ +/// Sends data to the NandFlash chip from the provided buffer. +/// \param raw Pointer to a RawNandFlash instance. +/// \param buffer Buffer where the data area will be stored. +/// \param sramOffset NFC internal sram start offset. +/// \param size Number of data bytes to send. +//------------------------------------------------------------------------------ +static void CopyDataToNfcInternalSram( + const struct RawNandFlash *raw, + unsigned char *data, + unsigned short sramOffset, + unsigned short size) +{ + if(bNfcDMA){ + unsigned int startSourceAddr; + unsigned int startDestAddr; + // Initialize DMA controller. + DMAD_Initialize(BOARD_NAND_DMA_CHANNEL, 0); + startSourceAddr = (unsigned int)data; + startDestAddr = (unsigned int)(NFC_SRAM_BASE_ADDRESS + sramOffset); + // Configure transfer size and width per transfer. + DMAD_Configure_TransferController(BOARD_NAND_DMA_CHANNEL, size / 4, 2, 2, startSourceAddr, startDestAddr); + // Configure single buffer transfer. + DMAD_Configure_Buffer(BOARD_NAND_DMA_CHANNEL, DMA_TRANSFER_SINGLE, DMA_TRANSFER_SINGLE, 0, 0); + // Start channel 0 transfer. + DMAD_BufferTransfer(BOARD_NAND_DMA_CHANNEL, size / 4, 0 , 1); + + }else { + unsigned char * pBuffer; + unsigned int i; + pBuffer = (unsigned char *)(NFC_SRAM_BASE_ADDRESS + sramOffset); + for (i = 0; i < size; i++) { + *pBuffer++ = *data++; + } + } +} +//------------------------------------------------------------------------------ +/// Sends data to the NandFlash chip from the provided buffer. +/// \param raw Pointer to a RawNandFlash instance. +/// \param data Buffer where the data area will be stored. +/// \param sramOffset NFC internal sram start offset. +/// \param size Number of data bytes to receive. +//------------------------------------------------------------------------------ +static void CopyDataFromNfcInternalSram( + const struct RawNandFlash *raw, + unsigned char *data, + unsigned short sramOffset, + unsigned short size) +{ + if(bNfcDMA){ + + unsigned int startSourceAddr; + unsigned int startDestAddr; + + // Initialize DMA controller. + DMAD_Initialize(BOARD_NAND_DMA_CHANNEL, 0); + + startSourceAddr = (unsigned int)(NFC_SRAM_BASE_ADDRESS + sramOffset); + startDestAddr = (unsigned int)data; + + // Configure transfer size and width per transfer. + DMAD_Configure_TransferController(BOARD_NAND_DMA_CHANNEL, size / 4, 2, 2, startSourceAddr, startDestAddr); + // Configure single buffer transfer. + DMAD_Configure_Buffer(BOARD_NAND_DMA_CHANNEL, DMA_TRANSFER_SINGLE, DMA_TRANSFER_SINGLE, 0, 0); + // Start channel 0 transfer. + DMAD_BufferTransfer(BOARD_NAND_DMA_CHANNEL, size / 4, 0 , 1); + }else { + unsigned char * pBuffer; + unsigned int i; + pBuffer = (unsigned char *)(NFC_SRAM_BASE_ADDRESS + sramOffset); + for (i = 0; i < size; i++) { + *data++ = *pBuffer++; + } + } +} + +//------------------------------------------------------------------------------ +/// Erases the specified block of the device. Returns 0 if the operation was +/// successful; otherwise returns an error code. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the physical block to erase. +//------------------------------------------------------------------------------ +static unsigned char EraseBlock( + const struct RawNandFlash *raw, + unsigned short block) +{ + unsigned char error = 0; + unsigned int rowAddress; + unsigned int addressCycle1234; + + TRACE_DEBUG("EraseBlock(%d)\r\n", block); + + // Calculate address used for erase + rowAddress = block * NandFlashModel_GetBlockSizeInPages(MODEL(raw)); + + NFC_TranslateAddress(raw, 0, rowAddress, 0, &addressCycle1234, 0); + + // Start erase + HSMC4_SendCommand (AT91C_HSMC4_HOSTCMD | // Command. + 0 | // Host read data. + 0 | // Host is disabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_THREE | // Number of address cycle. + AT91C_HSMC4_VCMD2 | // CMD2 enabled. + (COMMAND_ERASE_2 << 10) | // CMD2 (ERASE CONFIRME). + (COMMAND_ERASE_1 << 2), // CMD1 (ERASE). + addressCycle1234, // Address cylce 1, 2, 3, 4. + 0 // Dummy address cylce 0. + ); + + while( !HSMC4_isReadyBusy() ); + #if !defined (OP_BOOTSTRAP_on) + if (!IsOperationComplete(raw)) { + TRACE_ERROR( + "EraseBlock: Could not erase block %d.\n\r", + block); + error = NandCommon_ERROR_CANNOTERASE; + } + #endif + return error; +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or the spare area of a page on a NandFlash chip. If one +/// of the buffer pointer is 0, the corresponding area is not written. +/// Returns 0 if the write operation is successful; otherwise returns 1. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the block where the page to write resides. +/// \param page Number of the page to write inside the given block. +/// \param data Buffer containing the data area. +/// \param spare Buffer containing the spare area. +//------------------------------------------------------------------------------ +static unsigned char WritePage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + + unsigned char error = 0; + unsigned int pageDataSize = NandFlashModel_GetPageDataSize(MODEL(raw)); + unsigned int spareDataSize = NandFlashModel_GetPageSpareSize(MODEL(raw)); + unsigned int rowAddress; + unsigned int addressCycle0; + unsigned int addressCycle1234; + + TRACE_DEBUG("WritePage(B#%d:P#%d)\r\n", block, page); + + // Calculate physical address of the page + rowAddress = block * NandFlashModel_GetBlockSizeInPages(MODEL(raw)) + page; + /* + if (spare){ + HSMC4_EnableSpareWrite(); + } + else { + HSMC4_DisableSpareWrite(); + } + */ + // Note: special case when ECC parity generation. + // ECC results are available as soon as the counter reaches the end of the main area. + // But when reach PageSize for an example, it could not generate last ECC_PR, The + // workaround enable SPARE_WRITE, whatever real spare area write or not. + HSMC4_EnableSpareWrite(); + + // Write data area if needed + if (data) { + CopyDataToNfcInternalSram(raw, (unsigned char *) data, 0, pageDataSize); + if (spare) { + CopyDataToNfcInternalSram(raw, (unsigned char *) spare, pageDataSize, spareDataSize); + } + NFC_TranslateAddress(raw, 0, rowAddress, &addressCycle0, &addressCycle1234, 1); + } + if (spare && !data) { + CopyDataToNfcInternalSram(raw, (unsigned char *) spare, 0, spareDataSize); + NFC_TranslateAddress(raw, pageDataSize, rowAddress, &addressCycle0, &addressCycle1234, 1); + } + + if (data || spare) { + // Start write operation + HSMC4_SendCommand (AT91C_HSMC4_HOSTCMD | // Command. + AT91C_HSMC4_HOST_WR | // Host write data. + AT91C_HSMC4_HOST_EN | // Host is enabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_FIVE | // Number of address cycle. + 0 | // CMD2 disabled. + 0 | // CMD2. + (COMMAND_WRITE_1 << 2), // CMD1. + addressCycle1234, // Address cylce 1, 2, 3, 4. + addressCycle0 // Address cylce 0. + ); + while( !HSMC4_TransferComplete()); + + HSMC4_SendCommand (AT91C_HSMC4_HOSTCMD | // Command. + 0 | // No data Transfer. + 0 | // Host is disabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_NONE | // No address cycle. + 0 | // CMD2 disabled. + 0 | // CMD2. + (COMMAND_WRITE_2 << 2), // CMD1. + 0, // Dummy address cylce 1, 2, 3, 4. + 0 // Dummy address cylce 0. + ); + while( !HSMC4_isReadyBusy()); + if (!IsOperationComplete(raw)) { + TRACE_ERROR("WritePage: Failed writing data area.\n\r"); + error = NandCommon_ERROR_CANNOTWRITE; + } + } + HSMC4_DisableSpareWrite(); + return error; +} + + +//------------------------------------------------------------------------------ +/// Copies the data in a page of the NandFlash device to an other page on that +/// same chip. Both pages must have be even or odd; it is not possible to copy +/// and even page to an odd page and vice-versa. +/// Returns 0 if the operation is successful; otherwise returns a +/// NandCommon_ERROR code. +/// \param raw Pointer to a RawNandFlash instance. +/// \param sourceBlock Source block number. +/// \param sourcePage Source page number inside the source block. +/// \param destBlock Destination block number. +/// \param destPage Destination page number inside the destination block. +//------------------------------------------------------------------------------ +static unsigned char CopyPage( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short sourcePage, + unsigned short destBlock, + unsigned short destPage) +{ + unsigned short numPages = NandFlashModel_GetBlockSizeInPages(MODEL(raw)); + unsigned int sourceRow = sourceBlock * numPages + sourcePage; + unsigned int destRow = destBlock * numPages + destPage; + unsigned char error = 0; + unsigned int addressCycle0; + unsigned int addressCycle1234; + + ASSERT((sourcePage & 1) == (destPage & 1), + "CopyPage: Source and destination page must have the same parity.\n\r"); + + TRACE_DEBUG("CopyPage(B#%d:P#%d -> B#%d:P#%d)\n\r", + sourceBlock, sourcePage, destBlock, destPage); + + // Use the copy-back facility if available + if (NandFlashModel_SupportsCopyBack(MODEL(raw))) { + + // Start operation + NFC_TranslateAddress(raw, 0, sourceRow, &addressCycle0, &addressCycle1234, 1); + // Start copy-back read + HSMC4_SendCommand (AT91C_HSMC4_HOSTCMD | // Command. + 0 | // Host read data. + 0 | // Host is disabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_FIVE | // Number of address cycle. + AT91C_HSMC4_VCMD2 | // CMD2 enabled. + (COMMAND_COPYBACK_READ_2 << 10)| // CMD2. + (COMMAND_COPYBACK_READ_1 << 2), // CMD1. + addressCycle1234, // Address cylce 1, 2, 3, 4. + addressCycle0 // Address cylce 0. + ); + while( !HSMC4_isReadyBusy() ); + + // Start copy-back write + NFC_TranslateAddress(raw, 0, destRow, &addressCycle0, &addressCycle1234, 1); + HSMC4_SendCommand (AT91C_HSMC4_HOSTCMD | // Command. + 0 | // No data transfer. + 0 | // Host is disabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_FIVE | // Number of address cycle. + AT91C_HSMC4_VCMD2 | // CMD2 enabled. + (COMMAND_COPYBACK_PROGRAM_2 << 10)| // CMD2. + (COMMAND_COPYBACK_PROGRAM_1 << 2), // CMD1. + addressCycle1234, // Address cylce 1, 2, 3, 4. + addressCycle0 // Address cylce 0. + ); + while( !HSMC4_isReadyBusy() ); + + // Check status + if (!IsOperationComplete(raw)) { + TRACE_ERROR("CopyPage: Failed to copy page.\n\r"); + error = NandCommon_ERROR_CANNOTCOPY; + } + } + else { + + // Software copy + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + if (RawNandFlash_ReadPage(raw, sourceBlock, sourcePage, data, spare)) { + + TRACE_ERROR("CopyPage: Failed to read page to copy\n\r"); + error = NandCommon_ERROR_CANNOTREAD; + } + else if (RawNandFlash_WritePage(raw, destBlock, destPage, data, spare)) { + + TRACE_ERROR("CopyPage: Failed to write dest. page\n\r"); + error = NandCommon_ERROR_CANNOTWRITE; + } + } + + return error; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a RawNandFlash instance based on the given model and physical +/// interface. If no model is provided, then the function tries to autodetect +/// it. +/// Returns 0 if initialization is successful; otherwise returns +/// NandCommon_ERROR_UNKNOWNMODEL. +/// \param raw Pointer to a RawNandFlash instance. +/// \param model Pointer to the underlying nand chip model. Can be 0. +/// \param commandAddress Address at which commands are sent. +/// \param addressAddress Address at which addresses are sent. +/// \param dataAddress Address at which data is sent. +/// \param pinChipEnable Pin controlling the CE signal of the NandFlash. +/// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_Initialize( + struct RawNandFlash *raw, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy) +{ + TRACE_DEBUG("RawNandFlash_Initialize()\r\n"); + // Initialize fields + raw->commandAddress = commandAddress; + raw->addressAddress = addressAddress; + raw->dataAddress = dataAddress; + raw->pinChipEnable = pinChipEnable; + raw->pinReadyBusy = pinReadyBusy; + + // Reset + RawNandFlash_Reset(raw); + + // If model is not provided, autodetect it + if (!model) { + + TRACE_DEBUG("No model provided, trying autodetection ...\n\r"); + if (NandFlashModel_Find(nandFlashModelList, + NandFlashModelList_SIZE, + RawNandFlash_ReadId(raw), + &(raw->model))) { + + TRACE_ERROR( + "RawNandFlash_Initialize: Could not autodetect chip.\n\r"); + return NandCommon_ERROR_UNKNOWNMODEL; + } + } + else { + + // Copy provided model + raw->model = *model; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Resets a NandFlash device. +/// \param raw Pointer to a RawNandFlash instance. +//------------------------------------------------------------------------------ +void RawNandFlash_Reset(const struct RawNandFlash *raw) +{ + TRACE_DEBUG("RawNandFlash_Reset()\n\r"); + HSMC4_SendCommand(AT91C_HSMC4_HOSTCMD | // Command. + 0 | // Host read data. + 0 | // Host is disabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_NONE | // No address Cycle. + (COMMAND_RESET << 2), // CMD1 (COMMAND_RESET). + 0, // Dummy address cylce 1,2,3,4. + 0 // Dummy address cylce 0. + ); + while( !HSMC4_isReadyBusy() ); +} + +//------------------------------------------------------------------------------ +/// Reads and returns the identifiers of a NandFlash chip. +/// \param raw Pointer to a RawNandFlash instance. +/// \return id1|(id2<<8)|(id3<<16)|(id4<<24) +//------------------------------------------------------------------------------ +unsigned int RawNandFlash_ReadId(const struct RawNandFlash *raw) +{ + unsigned int chipId; + + TRACE_DEBUG("RawNandFlash_ReadId()\n\r"); + HSMC4_SendCommand(AT91C_HSMC4_HOSTCMD | // Command. + 0 | // Host read Data. + 0 | // Host is disabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_ONE | // One address Cycle. + (COMMAND_READID << 2), // CMD1 (COMMAND_READID). + 0, // Dummy address cylce 1,2,3,4. + 0 // Dummy address cylce 0. + ); + chipId = READ_DATA8(raw); + chipId |= READ_DATA8(raw) << 8; + chipId |= READ_DATA8(raw) << 16; + chipId |= READ_DATA8(raw) << 24; + return chipId; +} + +//------------------------------------------------------------------------------ +/// Erases the specified block of the device, retrying several time if it fails. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_BADBLOCK. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the physical block to erase. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_EraseBlock( + const struct RawNandFlash *raw, + unsigned short block) +{ + #if !defined(OP_BOOTSTRAP_on) + unsigned char numTries = NUMERASETRIES; + + TRACE_DEBUG("RawNandFlash_EraseBlock(B#%d)\n\r", block); + + while (numTries > 0) { + + if (!EraseBlock(raw, block)) { + + return 0; + } + numTries--; + } + + TRACE_ERROR("RawNandFlash_EraseBlock: Failed to erase block after %d tries\n\r", NUMERASETRIES); + return NandCommon_ERROR_BADBLOCK; + #else + return EraseBlock(raw, block); + #endif +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or the spare areas of a page of a NandFlash into the +/// provided buffers. If a buffer pointer is 0, the corresponding area is not +/// read. +/// Returns 0 if the operation has been successful; otherwise returns 1. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the block where the page to read resides. +/// \param page Number of the page to read inside the given block. +/// \param data Buffer where the data area will be stored. +/// \param spare Buffer where the spare area will be stored. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_ReadPage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + volatile unsigned int cntTry = 0; + + unsigned char hasSmallBlocks = NandFlashModel_HasSmallBlocks(MODEL(raw)); + unsigned int pageDataSize = NandFlashModel_GetPageDataSize(MODEL(raw)); + unsigned int pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(raw)); + unsigned int colAddress; + unsigned int rowAddress; + unsigned int addressCycle0; + unsigned int addressCycle1234; + ASSERT(data || spare, "RawNandFlash_ReadPage: At least one area must be read\n\r"); + TRACE_DEBUG("RawNandFlash_ReadPage(B#%d:P#%d)\r\n", block, page); + // Calculate actual address of the page + rowAddress = block * NandFlashModel_GetBlockSizeInPages(MODEL(raw)) + page; + // Start operation + if (data) { + colAddress = 0; + } + else { + // to read spare area in sequential access + colAddress = pageDataSize; + } + + if (spare) { + HSMC4_EnableSpareRead(); + } + else { + HSMC4_DisableSpareRead(); + } + NFC_TranslateAddress(raw, colAddress, rowAddress, &addressCycle0, &addressCycle1234, 1); + // Use either small blocks or large blocks data area read + if (hasSmallBlocks) { + HSMC4_SendCommand (AT91C_HSMC4_HOSTCMD | // Command. + 0 | // Host read data. + AT91C_HSMC4_HOST_EN | // Host is enable. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_FIVE | // Number of address cycle. + 0 | // CMD2 disabled. + 0 | // CMD2. + (COMMAND_READ_A << 2), // CMD1. + addressCycle1234, // Address cylce 1, 2, 3, 4. + addressCycle0 // Address cylce 0. + ); + } + else { + HSMC4_SendCommand (AT91C_HSMC4_HOSTCMD | // Command. + 0 | // Host read data. + AT91C_HSMC4_HOST_EN | // Host is enabled. + AT91C_HSMC4_CSID_1 | // CSID. + AT91C_HSMC4_ACYCLE_HSMC4_ACYCLE_FIVE | // Number of address cycle. + AT91C_HSMC4_VCMD2 | // CMD2 enabled. + (COMMAND_READ_2 << 10)| // CMD2. + (COMMAND_READ_1 << 2), // CMD1. + addressCycle1234, // Address cylce 1, 2, 3, 4. + addressCycle0 // Address cylce 0. + ); + } + // Wait for the nand to be ready + cntTry = 0; + while( !HSMC4_isReadyBusy() && (cntTry++) < 1000000); + cntTry = 0; + while( !HSMC4_TransferComplete() && (cntTry++) < 1000000); + // Read data area if needed + if (data) { + CopyDataFromNfcInternalSram(raw, (unsigned char *) data, 0, pageDataSize); + if (spare) { + CopyDataFromNfcInternalSram(raw, (unsigned char *) spare, pageDataSize, pageSpareSize); + } + } + else { + // Read spare area only + CopyDataFromNfcInternalSram(raw, (unsigned char *) spare, 0, pageSpareSize); + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or the spare area of a page on a NandFlash chip. If one +/// of the buffer pointer is 0, the corresponding area is not written. Retries +/// several time if there is an error. +/// Returns 0 if the write operation is successful; otherwise returns +/// NandCommon_ERROR_BADBLOCK. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the block where the page to write resides. +/// \param page Number of the page to write inside the given block. +/// \param data Buffer containing the data area. +/// \param spare Buffer containing the spare area. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_WritePage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char numTries = NUMWRITETRIES; + + TRACE_DEBUG("RawNandFlash_WritePage(B#%d:P#%d)\r\n", block, page); + + while (numTries > 0) { + + if (!WritePage(raw, block, page, data, spare)) { + + return 0; + } + numTries--; + } + + TRACE_ERROR("RawNandFlash_WritePage: Failed to write page after %d tries\n\r", NUMWRITETRIES); + return NandCommon_ERROR_BADBLOCK; +} + +//------------------------------------------------------------------------------ +/// Copy the data in a page of the NandFlash device to an other page on that +/// same chip. Both pages must have be even or odd; it is not possible to copy +/// and even page to an odd page and vice-versa. Several retries are attempted +/// if errors are encountered. +/// Returns 0 if the operation is successful; otherwise returns +/// NandCommon_ERROR_BADBLOCK indicating that the destination block is bad. +/// \param raw Pointer to a RawNandFlash instance. +/// \param sourceBlock Source block number. +/// \param sourcePage Source page number inside the source block. +/// \param destBlock Destination block number. +/// \param destPage Destination page number inside the destination block. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_CopyPage( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short sourcePage, + unsigned short destBlock, + unsigned short destPage) +{ + unsigned char numTries = NUMCOPYTRIES; + + TRACE_DEBUG("RawNandFlash_CopyPage(B#%d:P#%d -> B#%d:P#%d)\n\r", + sourceBlock, sourcePage, destBlock, destPage); + + while (numTries) { + + if (!CopyPage(raw, sourceBlock, sourcePage, destBlock, destPage)) { + + return 0; + } + numTries--; + } + + TRACE_ERROR("RawNandFlash_CopyPage: Failed to copy page after %d tries\n\r", NUMCOPYTRIES); + return NandCommon_ERROR_BADBLOCK; +} + +//------------------------------------------------------------------------------ +/// Copies the data of one whole block of a NandFlash device to another block. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_BADBLOCK. +/// \param raw Pointer to a RawNandFlash instance. +/// \param sourceBlock Source block number. +/// \param destBlock Destination block number. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_CopyBlock( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short destBlock) +{ + unsigned short numPages = NandFlashModel_GetBlockSizeInPages(MODEL(raw)); + unsigned int i; + + ASSERT(sourceBlock != destBlock, + "RawNandFlash_CopyBlock: Source block must be different from dest block\n\r"); + TRACE_DEBUG("RawNandFlash_CopyBlock(B#%d->B#%d)\n\r", + sourceBlock, destBlock); + + // Copy all pages + for (i=0; i < numPages; i++) { + + if (RawNandFlash_CopyPage(raw, sourceBlock, i, destBlock, i)) { + + TRACE_ERROR( + "RawNandFlash_CopyBlock: Failed to copy page %u\n\r", + i); + return NandCommon_ERROR_BADBLOCK; + } + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Set use DMA flag. +//------------------------------------------------------------------------------ +void RawNandlfash_SetDma(void) +{ + bNfcDMA = 1; +} + +//------------------------------------------------------------------------------ +/// Clear use DMA flag. +//------------------------------------------------------------------------------ +void RawNandlfash_ClearDma(void) +{ + bNfcDMA = 0; +} + +//------------------------------------------------------------------------------ +/// Get use DMA flag. +//------------------------------------------------------------------------------ +unsigned char RawNandlfash_GetDma(void) +{ + return bNfcDMA; +} + +#endif + diff --git a/memories/nandflash/RawNandFlash.c b/memories/nandflash/RawNandFlash.c new file mode 100644 index 0000000..74105ce --- /dev/null +++ b/memories/nandflash/RawNandFlash.c @@ -0,0 +1,792 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "RawNandFlash.h" +#include "NandCommon.h" +#include "NandFlashModelList.h" +#include +#include + +#include + +#if !defined(CHIP_NAND_CTRL) +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Nand flash chip status codes +#define STATUS_READY (1 << 6) +#define STATUS_ERROR (1 << 0) + +/// Nand flash commands +#define COMMAND_READ_1 0x00 +#define COMMAND_READ_2 0x30 +#define COMMAND_COPYBACK_READ_1 0x00 +#define COMMAND_COPYBACK_READ_2 0x35 +#define COMMAND_COPYBACK_PROGRAM_1 0x85 +#define COMMAND_COPYBACK_PROGRAM_2 0x10 +#define COMMAND_RANDOM_OUT 0x05 +#define COMMAND_RANDOM_OUT_2 0xE0 +#define COMMAND_RANDOM_IN 0x85 +#define COMMAND_READID 0x90 +#define COMMAND_WRITE_1 0x80 +#define COMMAND_WRITE_2 0x10 +#define COMMAND_ERASE_1 0x60 +#define COMMAND_ERASE_2 0xD0 +#define COMMAND_STATUS 0x70 +#define COMMAND_RESET 0xFF + + +/// Nand flash commands (small blocks) +#define COMMAND_READ_A 0x00 +#define COMMAND_READ_C 0x50 + +//------------------------------------------------------------------------------ +// Internal macros +//------------------------------------------------------------------------------ +#define ENABLE_CE(raw) PIO_Clear(&(raw->pinChipEnable)) +#define DISABLE_CE(raw) PIO_Set(&(raw->pinChipEnable)) + +#define WRITE_COMMAND(raw, command) \ + {*((volatile unsigned char *) raw->commandAddress) = (unsigned char) command;} +#define WRITE_COMMAND16(raw, command) \ + {*((volatile unsigned short *) raw->commandAddress) = (unsigned short) command;} +#define WRITE_ADDRESS(raw, address) \ + {*((volatile unsigned char *) raw->addressAddress) = (unsigned char) address;} +#define WRITE_ADDRESS16(raw, address) \ + {*((volatile unsigned short *) raw->addressAddress) = (unsigned short) address;} +#define WRITE_DATA8(raw, data) \ + {*((volatile unsigned char *) raw->dataAddress) = (unsigned char) data;} +#define READ_DATA8(raw) \ + (*((volatile unsigned char *) raw->dataAddress)) +#define WRITE_DATA16(raw, data) \ + {*((volatile unsigned short *) raw->dataAddress) = (unsigned short) data;} +#define READ_DATA16(raw) \ + (*((volatile unsigned short *) raw->dataAddress)) + +/// Internal cast macros +#define MODEL(raw) ((struct NandFlashModel *) raw) + +/// Number of tries for erasing a block +#define NUMERASETRIES 2 +/// Number of tries for writing a block +#define NUMWRITETRIES 2 +/// Number of tries for copying a block +#define NUMCOPYTRIES 2 + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Sends the column address to the NandFlash chip. +/// \param raw Pointer to a RawNandFlash instance. +/// \param columnAddress Column address to send. +//------------------------------------------------------------------------------ +static void WriteColumnAddress( + const struct RawNandFlash *raw, + unsigned short columnAddress) +{ + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(raw)); + + /* Check the data bus width of the NandFlash */ + if (NandFlashModel_GetDataBusWidth(MODEL(raw)) == 16) { + /* Div 2 is because we address in word and not in byte */ + columnAddress >>= 1; + } + /* Send single column address byte for small block devices, or two column address bytes for large block devices*/ + while (pageDataSize > 2) { + + if (NandFlashModel_GetDataBusWidth(MODEL(raw)) == 16) { + WRITE_ADDRESS16(raw, columnAddress & 0xFF); + } + else { + WRITE_ADDRESS(raw, columnAddress & 0xFF); + } + pageDataSize >>= 8; + columnAddress >>= 8; + } +} + +//------------------------------------------------------------------------------ +/// Sends the row address to the NandFlash chip. +/// \param raw Pointer to a RawNandFlash instance. +/// \param rowAddress Row address to send. +//------------------------------------------------------------------------------ +static void WriteRowAddress( + const struct RawNandFlash *raw, + unsigned int rowAddress) +{ + unsigned int numPages = NandFlashModel_GetDeviceSizeInPages(MODEL(raw)); + + while (numPages > 0) { + + if (NandFlashModel_GetDataBusWidth(MODEL(raw)) == 16) { + WRITE_ADDRESS16(raw, rowAddress & 0xFF); + } + else { + WRITE_ADDRESS(raw, rowAddress & 0xFF); + } + numPages >>= 8; + rowAddress >>= 8; + } +} + +//------------------------------------------------------------------------------ +/// Waiting for the completion of a page program, erase and random read completion. +/// \param raw Pointer to a RawNandFlash instance. +//------------------------------------------------------------------------------ +static void WaitReady(const struct RawNandFlash *raw) +{ + if (raw->pinReadyBusy.mask) { + while (!PIO_Get(&(raw->pinReadyBusy))); + } + else { + WRITE_COMMAND(raw, COMMAND_STATUS); + while ((READ_DATA8(raw) & STATUS_READY) != STATUS_READY); + } +} + +//------------------------------------------------------------------------------ +/// Return 1 if program or erase operation is completed. +/// and the program or erase operation is completed successfully, otherwise return 0. +/// \param raw Pointer to a RawNandFlash instance. +//------------------------------------------------------------------------------ +static unsigned char IsOperationComplete(const struct RawNandFlash *raw) +{ + unsigned char status; + + WRITE_COMMAND(raw, COMMAND_STATUS); + + status = READ_DATA8(raw); + + if (((status & STATUS_READY) != STATUS_READY) || ((status & STATUS_ERROR) != 0)) { + return 0; + } + return 1; +} + +//------------------------------------------------------------------------------ +/// Sends data to the NandFlash chip from the provided buffer. +/// \param raw Pointer to a RawNandFlash instance. +/// \param buffer Buffer where the data is stored. +/// \param size Number of bytes that will be written +//------------------------------------------------------------------------------ +static void WriteData( + const struct RawNandFlash *raw, + unsigned char *buffer, + unsigned int size) +{ + unsigned int i; + + // Check the data bus width of the NandFlash + if (NandFlashModel_GetDataBusWidth(MODEL(raw)) == 16) { + + unsigned short *buffer16 = (unsigned short *) buffer; + size >>= 1; + + for(i=0; i < size; i++) { + + WRITE_DATA16(raw, buffer16[i]); + } + } + else { + + for(i=0; i < size; i++) { + + WRITE_DATA8(raw, buffer[i]); + } + } +} + +//------------------------------------------------------------------------------ +/// Reads data from the NandFlash chip into the provided buffer. +/// \param nand Pointer to a RawNandFlash instance. +/// \param buffer Buffer where the data will be stored. +/// \param size Number of bytes that will be read +//------------------------------------------------------------------------------ +static void ReadData( + const struct RawNandFlash *raw, + unsigned char *buffer, + unsigned int size) +{ + unsigned int i; + + // Check the chip data bus width + if (NandFlashModel_GetDataBusWidth(MODEL(raw)) == 16) { + + unsigned short *buffer16 = (unsigned short *) buffer; + size >>= 1; + + for (i=0; i < size; i++) { + + buffer16[i] = READ_DATA16(raw); + } + } + else { + + for (i=0; i < size; i++) { + + buffer[i] = READ_DATA8(raw); + } + } +} + +//------------------------------------------------------------------------------ +/// Erases the specified block of the device. Returns 0 if the operation was +/// successful; otherwise returns an error code. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the physical block to erase. +//------------------------------------------------------------------------------ +static unsigned char EraseBlock( + const struct RawNandFlash *raw, + unsigned short block) +{ + unsigned char error = 0; + unsigned int rowAddress; + + TRACE_DEBUG("EraseBlock(%d)\r\n", block); + + // Calculate address used for erase + rowAddress = block * NandFlashModel_GetBlockSizeInPages(MODEL(raw)); + + // Start erase + ENABLE_CE(raw); + WRITE_COMMAND(raw, COMMAND_ERASE_1); + WriteRowAddress(raw, rowAddress); + WRITE_COMMAND(raw, COMMAND_ERASE_2); + + WaitReady(raw); + #if !defined (OP_BOOTSTRAP_on) + if (!IsOperationComplete(raw)) { + TRACE_ERROR( + "EraseBlock: Could not erase block %d.\n\r", + block); + error = NandCommon_ERROR_CANNOTERASE; + } + #endif + + DISABLE_CE(raw); + + return error; +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or the spare area of a page on a NandFlash chip. If one +/// of the buffer pointer is 0, the corresponding area is not written. +/// Returns 0 if the write operation is successful; otherwise returns 1. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the block where the page to write resides. +/// \param page Number of the page to write inside the given block. +/// \param data Buffer containing the data area. +/// \param spare Buffer containing the spare area. +//------------------------------------------------------------------------------ +static unsigned char WritePage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char error = 0; + unsigned int pageDataSize = NandFlashModel_GetPageDataSize(MODEL(raw)); + unsigned int spareDataSize = NandFlashModel_GetPageSpareSize(MODEL(raw)); + unsigned short dummyByte; + unsigned int rowAddress; + + TRACE_DEBUG("WritePage(B#%d:P#%d)\r\n", block, page); + // Calculate physical address of the page + rowAddress = block * NandFlashModel_GetBlockSizeInPages(MODEL(raw)) + page; + + // Start write operation + ENABLE_CE(raw); + + // Write data area if needed + if (data) { + + WRITE_COMMAND(raw, COMMAND_WRITE_1); + WriteColumnAddress(raw, 0); + WriteRowAddress(raw, rowAddress); + WriteData(raw, (unsigned char *) data, pageDataSize); + + // Spare is written here as well since it is more efficient + if (spare) { + + WriteData(raw, (unsigned char *) spare, spareDataSize); + } + else { + // Note: special case when ECC parity generation. + // ECC results are available as soon as the counter reaches the end of the main area. + // But when reach PageSize for an example, it could not generate last ECC_PR, The + // workaround is to receive PageSize+1 word. + ReadData(raw, (unsigned char *) (&dummyByte), 2); + } + WRITE_COMMAND(raw, COMMAND_WRITE_2); + + WaitReady(raw); + if (!IsOperationComplete(raw)) { + TRACE_ERROR("WritePage: Failed writing data area.\n\r"); + error = NandCommon_ERROR_CANNOTWRITE; + } + } + + // Write spare area alone if needed + if (spare && !data) { + + WRITE_COMMAND(raw, COMMAND_WRITE_1); + WriteColumnAddress(raw, pageDataSize); + WriteRowAddress(raw, rowAddress); + WriteData(raw, (unsigned char *) spare, spareDataSize); + WRITE_COMMAND(raw, COMMAND_WRITE_2); + + WaitReady(raw); + if (!IsOperationComplete(raw)) { + TRACE_ERROR("WritePage: Failed writing data area.\n\r"); + error = NandCommon_ERROR_CANNOTWRITE; + } + } + + // Disable chip + DISABLE_CE(raw); + + return error; +} + + +//------------------------------------------------------------------------------ +/// Copies the data in a page of the NandFlash device to an other page on that +/// same chip. Both pages must have be even or odd; it is not possible to copy +/// and even page to an odd page and vice-versa. +/// Returns 0 if the operation is successful; otherwise returns a +/// NandCommon_ERROR code. +/// \param raw Pointer to a RawNandFlash instance. +/// \param sourceBlock Source block number. +/// \param sourcePage Source page number inside the source block. +/// \param destBlock Destination block number. +/// \param destPage Destination page number inside the destination block. +//------------------------------------------------------------------------------ +static unsigned char CopyPage( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short sourcePage, + unsigned short destBlock, + unsigned short destPage) +{ + unsigned short numPages = NandFlashModel_GetBlockSizeInPages(MODEL(raw)); + unsigned int sourceRow = sourceBlock * numPages + sourcePage; + unsigned int destRow = destBlock * numPages + destPage; + unsigned char error = 0; + + ASSERT((sourcePage & 1) == (destPage & 1), + "CopyPage: Source and destination page must have the same parity.\n\r"); + + TRACE_DEBUG("CopyPage(B#%d:P#%d -> B#%d:P#%d)\n\r", + sourceBlock, sourcePage, destBlock, destPage); + + // Use the copy-back facility if available + if (NandFlashModel_SupportsCopyBack(MODEL(raw))) { + + // Start operation + ENABLE_CE(raw); + + // Start copy-back read + WRITE_COMMAND(raw, COMMAND_COPYBACK_READ_1); + WriteColumnAddress(raw, 0); + WriteRowAddress(raw, sourceRow); + WRITE_COMMAND(raw, COMMAND_COPYBACK_READ_2); + WaitReady(raw); + + // Start copy-back write + WRITE_COMMAND(raw, COMMAND_COPYBACK_PROGRAM_1); + WriteColumnAddress(raw, 0); + WriteRowAddress(raw, destRow); + WRITE_COMMAND(raw, COMMAND_COPYBACK_PROGRAM_2); + WaitReady(raw); + + // Check status + if (!IsOperationComplete(raw)) { + TRACE_ERROR("CopyPage: Failed to copy page.\n\r"); + error = NandCommon_ERROR_CANNOTCOPY; + } + + // Finish operation + DISABLE_CE(raw); + } + else { + + // Software copy + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + if (RawNandFlash_ReadPage(raw, sourceBlock, sourcePage, data, spare)) { + + TRACE_ERROR("CopyPage: Failed to read page to copy\n\r"); + error = NandCommon_ERROR_CANNOTREAD; + } + else if (RawNandFlash_WritePage(raw, destBlock, destPage, data, spare)) { + + TRACE_ERROR("CopyPage: Failed to write dest. page\n\r"); + error = NandCommon_ERROR_CANNOTWRITE; + } + } + + return error; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a RawNandFlash instance based on the given model and physical +/// interface. If no model is provided, then the function tries to autodetect +/// it. +/// Returns 0 if initialization is successful; otherwise returns +/// NandCommon_ERROR_UNKNOWNMODEL. +/// \param raw Pointer to a RawNandFlash instance. +/// \param model Pointer to the underlying nand chip model. Can be 0. +/// \param commandAddress Address at which commands are sent. +/// \param addressAddress Address at which addresses are sent. +/// \param dataAddress Address at which data is sent. +/// \param pinChipEnable Pin controlling the CE signal of the NandFlash. +/// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_Initialize( + struct RawNandFlash *raw, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy) +{ + TRACE_DEBUG("RawNandFlash_Initialize()\n\r"); + + // Initialize fields + raw->commandAddress = commandAddress; + raw->addressAddress = addressAddress; + raw->dataAddress = dataAddress; + raw->pinChipEnable = pinChipEnable; + raw->pinReadyBusy = pinReadyBusy; + + // Reset + RawNandFlash_Reset(raw); + + // If model is not provided, autodetect it + if (!model) { + + TRACE_DEBUG("No model provided, trying autodetection ...\n\r"); + if (NandFlashModel_Find(nandFlashModelList, + NandFlashModelList_SIZE, + RawNandFlash_ReadId(raw), + &(raw->model))) { + + TRACE_ERROR( + "RawNandFlash_Initialize: Could not autodetect chip.\n\r"); + return NandCommon_ERROR_UNKNOWNMODEL; + } + } + else { + + // Copy provided model + raw->model = *model; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Resets a NandFlash device. +/// \param raw Pointer to a RawNandFlash instance. +//------------------------------------------------------------------------------ +void RawNandFlash_Reset(const struct RawNandFlash *raw) +{ + TRACE_DEBUG("RawNandFlash_Reset()\n\r"); + + ENABLE_CE(raw); + WRITE_COMMAND16(raw, COMMAND_RESET); + WaitReady(raw); + DISABLE_CE(raw); +} + +//------------------------------------------------------------------------------ +/// Reads and returns the identifiers of a NandFlash chip. +/// \param raw Pointer to a RawNandFlash instance. +/// \return id1|(id2<<8)|(id3<<16)|(id4<<24) +//------------------------------------------------------------------------------ +unsigned int RawNandFlash_ReadId(const struct RawNandFlash *raw) +{ + unsigned int chipId; + + TRACE_DEBUG("RawNandFlash_ReadId()\n\r"); + + ENABLE_CE(raw); + WRITE_COMMAND16(raw, COMMAND_READID); + //WRITE_COMMAND(raw, COMMAND_READID); + WRITE_ADDRESS(raw, 0); + chipId = READ_DATA8(raw); + chipId |= READ_DATA8(raw) << 8; + chipId |= READ_DATA8(raw) << 16; + chipId |= READ_DATA8(raw) << 24; + DISABLE_CE(raw); + + return chipId; +} + +//------------------------------------------------------------------------------ +/// Erases the specified block of the device, retrying several time if it fails. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_BADBLOCK. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the physical block to erase. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_EraseBlock( + const struct RawNandFlash *raw, + unsigned short block) +{ + #if !defined(OP_BOOTSTRAP_on) + unsigned char numTries = NUMERASETRIES; + + TRACE_DEBUG("RawNandFlash_EraseBlock(B#%d)\n\r", block); + + while (numTries > 0) { + + if (!EraseBlock(raw, block)) { + + return 0; + } + numTries--; + } + + TRACE_ERROR("RawNandFlash_EraseBlock: Failed to erase %d after %d tries\n\r", + block, NUMERASETRIES); + return NandCommon_ERROR_BADBLOCK; + #else + return EraseBlock(raw, block); + #endif +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or the spare areas of a page of a NandFlash into the +/// provided buffers. If a buffer pointer is 0, the corresponding area is not +/// read. +/// Returns 0 if the operation has been successful; otherwise returns 1. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the block where the page to read resides. +/// \param page Number of the page to read inside the given block. +/// \param data Buffer where the data area will be stored. +/// \param spare Buffer where the spare area will be stored. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_ReadPage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char hasSmallBlocks = NandFlashModel_HasSmallBlocks(MODEL(raw)); + unsigned int pageDataSize = NandFlashModel_GetPageDataSize(MODEL(raw)); + unsigned int pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(raw)); + unsigned int colAddress; + unsigned int rowAddress; + + ASSERT(data || spare, "RawNandFlash_ReadPage: At least one area must be read\n\r"); + // Shorten the time handling the debug message + TRACE_DEBUG("RdPg(B#%d:P#%d)\r\n", block, page); + //TRACE_DEBUG("RawNandFlash_ReadPage(B#%d:P#%d)\r\n", block, page); + + // Calculate actual address of the page + rowAddress = block * NandFlashModel_GetBlockSizeInPages(MODEL(raw)) + page; + + // Start operation + ENABLE_CE(raw); + + if (data) { + colAddress = 0; + } + else { + // to read spare area in sequential access + colAddress = pageDataSize; + } + + // Use either small blocks or large blocks data area read + if (hasSmallBlocks) { + if(colAddress == 0) { + WRITE_COMMAND(raw, COMMAND_READ_A); + } + else if(colAddress == pageDataSize) { + WRITE_COMMAND(raw, COMMAND_READ_C); + } + WriteColumnAddress(raw, colAddress); + WriteRowAddress(raw, rowAddress); + } + else { + + WRITE_COMMAND(raw, COMMAND_READ_1); + WriteColumnAddress(raw, colAddress); + WriteRowAddress(raw, rowAddress); + WRITE_COMMAND(raw, COMMAND_READ_2); + } + + // Wait for the nand to be ready + WaitReady(raw); + + // Read data area if needed + if (data) { + WRITE_COMMAND(raw, COMMAND_READ_1); + ReadData(raw, (unsigned char *) data, pageDataSize); + + if (spare) { + ReadData(raw, (unsigned char *) spare, pageSpareSize); + } + } + else { + // Read spare area only + WRITE_COMMAND(raw, COMMAND_READ_1); + ReadData(raw, (unsigned char *) spare, pageSpareSize); + } + + // Disable CE + DISABLE_CE(raw); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or the spare area of a page on a NandFlash chip. If one +/// of the buffer pointer is 0, the corresponding area is not written. Retries +/// several time if there is an error. +/// Returns 0 if the write operation is successful; otherwise returns +/// NandCommon_ERROR_BADBLOCK. +/// \param raw Pointer to a RawNandFlash instance. +/// \param block Number of the block where the page to write resides. +/// \param page Number of the page to write inside the given block. +/// \param data Buffer containing the data area. +/// \param spare Buffer containing the spare area. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_WritePage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char numTries = NUMWRITETRIES; + + TRACE_DEBUG("RawNandFlash_WritePage(B#%d:P#%d)\r\n", block, page); + + while (numTries > 0) { + + if (!WritePage(raw, block, page, data, spare)) { + + return 0; + } + numTries--; + } + + TRACE_ERROR("RawNandFlash_WritePage: Failed to write page after %d tries\n\r", NUMWRITETRIES); + return NandCommon_ERROR_BADBLOCK; +} + +//------------------------------------------------------------------------------ +/// Copy the data in a page of the NandFlash device to an other page on that +/// same chip. Both pages must have be even or odd; it is not possible to copy +/// and even page to an odd page and vice-versa. Several retries are attempted +/// if errors are encountered. +/// Returns 0 if the operation is successful; otherwise returns +/// NandCommon_ERROR_BADBLOCK indicating that the destination block is bad. +/// \param raw Pointer to a RawNandFlash instance. +/// \param sourceBlock Source block number. +/// \param sourcePage Source page number inside the source block. +/// \param destBlock Destination block number. +/// \param destPage Destination page number inside the destination block. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_CopyPage( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short sourcePage, + unsigned short destBlock, + unsigned short destPage) +{ + unsigned char numTries = NUMCOPYTRIES; + + TRACE_DEBUG("RawNandFlash_CopyPage(B#%d:P#%d -> B#%d:P#%d)\n\r", + sourceBlock, sourcePage, destBlock, destPage); + + while (numTries) { + + if (!CopyPage(raw, sourceBlock, sourcePage, destBlock, destPage)) { + + return 0; + } + numTries--; + } + + TRACE_ERROR("RawNandFlash_CopyPage: Failed to copy page after %d tries\n\r", NUMCOPYTRIES); + return NandCommon_ERROR_BADBLOCK; +} + +//------------------------------------------------------------------------------ +/// Copies the data of one whole block of a NandFlash device to another block. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_BADBLOCK. +/// \param raw Pointer to a RawNandFlash instance. +/// \param sourceBlock Source block number. +/// \param destBlock Destination block number. +//------------------------------------------------------------------------------ +unsigned char RawNandFlash_CopyBlock( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short destBlock) +{ + unsigned short numPages = NandFlashModel_GetBlockSizeInPages(MODEL(raw)); + unsigned int i; + + ASSERT(sourceBlock != destBlock, + "RawNandFlash_CopyBlock: Source block must be different from dest block\n\r"); + TRACE_DEBUG("RawNandFlash_CopyBlock(B#%d->B#%d)\n\r", + sourceBlock, destBlock); + + // Copy all pages + for (i=0; i < numPages; i++) { + + if (RawNandFlash_CopyPage(raw, sourceBlock, i, destBlock, i)) { + + TRACE_ERROR( + "RawNandFlash_CopyBlock: Failed to copy page %u\n\r", + i); + return NandCommon_ERROR_BADBLOCK; + } + } + + return 0; +} + +#endif diff --git a/memories/nandflash/RawNandFlash.h b/memories/nandflash/RawNandFlash.h new file mode 100644 index 0000000..64a48dd --- /dev/null +++ b/memories/nandflash/RawNandFlash.h @@ -0,0 +1,139 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "RawNandflash" +/// +/// !!!Purpose +/// +/// RawNandflash is a bl Nandflash driver, it directly interacts with hardware's register to +/// operate Nandflash interface, and it is called by upper layer drivers, such as EccNandFlash +/// +/// !!!Usage +/// +/// -# RawNandFlash_Initialize is used to initializes a RawNandFlash instance based on the given +/// model and physical interface. If no model is provided, then the function tries to autodetect +/// it. +/// -# RawNandFlash_Reset is used to reset a Nandflash device. +/// -# RawNandFlash_ReadId is used to read a Nandflash's id. +/// -# RawNandFlash_EraseBlock is used to erase a certain Nandflash device's block. +/// -# RawNandFlash_ReadPage and RawNandFlash_WritePage is used to do read/write operation. +/// -# RawNandFlash_CopyPage is used to issue copypage command to Nandflash device. +/// -# RawNandFlash_CopyBlock calls RawNandFlash_CopyPage to do a Nandflash block copy. +//------------------------------------------------------------------------------ + + +#ifndef RAWNANDFLASH_H +#define RAWNANDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandFlashModel.h" +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Describes a physical NandFlash chip connected to the SAM microcontroller. +//------------------------------------------------------------------------------ +struct RawNandFlash { + + /// Model describing this NandFlash characteristics. + struct NandFlashModel model; + /// Address for sending commands to the NandFlash. + unsigned int commandAddress; + /// Address for sending addresses to the NandFlash + unsigned int addressAddress; + /// Address for sending data to the NandFlash. + unsigned int dataAddress; + /// Pin used to enable the NandFlash chip. + Pin pinChipEnable; + /// Pin used to monitor the ready/busy signal from the NandFlash. + Pin pinReadyBusy; +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char RawNandFlash_Initialize( + struct RawNandFlash *raw, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy); + +extern void RawNandFlash_Reset(const struct RawNandFlash *raw); + +extern unsigned int RawNandFlash_ReadId(const struct RawNandFlash *raw); + +extern unsigned char RawNandFlash_EraseBlock( + const struct RawNandFlash *raw, + unsigned short block); + +extern unsigned char RawNandFlash_ReadPage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char RawNandFlash_WritePage( + const struct RawNandFlash *raw, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char RawNandFlash_CopyPage( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short sourcePage, + unsigned short destBlock, + unsigned short destPage); + +extern unsigned char RawNandFlash_CopyBlock( + const struct RawNandFlash *raw, + unsigned short sourceBlock, + unsigned short destBlock); + +#if defined(CHIP_NAND_CTRL) +extern void RawNandlfash_SetDma(void); +extern void RawNandlfash_ClearDma(void); +extern unsigned char RawNandlfash_GetDma(void); +#endif + +#endif //#ifndef RAWNANDFLASH_H + diff --git a/memories/nandflash/SkipBlockNandFlash.c b/memories/nandflash/SkipBlockNandFlash.c new file mode 100644 index 0000000..a29795f --- /dev/null +++ b/memories/nandflash/SkipBlockNandFlash.c @@ -0,0 +1,444 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "SkipBlockNandFlash.h" +#include "NandSpareScheme.h" +#include "NandFlashModel.h" +#include "RawNandFlash.h" +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +// Casts +#define ECC(skipBlock) ((struct EccNandFlash *) skipBlock) +#define RAW(skipBlock) ((struct RawNandFlash *) skipBlock) +#define MODEL(skipBlock) ((struct NandFlashModel *) skipBlock) + + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Returns BADBLOCK if the given block of a nandflash device is bad; returns +/// GOODBLOCK if the block is good; or returns a NandCommon_ERROR code. +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param block Number of block to check. +//------------------------------------------------------------------------------ +unsigned char SkipBlockNandFlash_CheckBlock( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block) +{ + #if !defined (OP_BOOTSTRAP_on) + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + unsigned char error; + unsigned char badBlockMarker; + const struct NandSpareScheme *scheme; + + + // Retrieve model scheme + scheme = NandFlashModel_GetScheme(MODEL(skipBlock)); + + // Read spare area of first page of block + error = RawNandFlash_ReadPage(RAW(skipBlock), block, 0, 0, spare); + if (error) { + + TRACE_ERROR("CheckBlock: Cannot read page #0 of block #%d\n\r", block); + return error; + } + + NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); + if (badBlockMarker != 0xFF) { + + return BADBLOCK; + } + + // Read spare area of second page of block + error = RawNandFlash_ReadPage(RAW(skipBlock), block, 1, 0, spare); + if (error) { + + TRACE_ERROR("CheckBlock: Cannot read page #1 of block #%d\n\r", block); + return error; + } + + NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); + if (badBlockMarker != 0xFF) { + + return BADBLOCK; + } + #endif + + return GOODBLOCK; +} + + +//------------------------------------------------------------------------------ +/// Initializes a SkipBlockNandFlash instance. Scans the device to retrieve or +/// create block status information. +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param model Pointer to the underlying nand chip model. Can be 0. +/// \param commandAddress Address at which commands are sent. +/// \param addressAddress Address at which addresses are sent. +/// \param dataAddress Address at which data is sent. +/// \param pinChipEnable Pin controlling the CE signal of the NandFlash. +/// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. +//------------------------------------------------------------------------------ +unsigned char SkipBlockNandFlash_Initialize( + struct SkipBlockNandFlash *skipBlock, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy) +{ + unsigned char error; + #if !defined(OP_BOOTSTRAP_on) + unsigned int numBlocks; + unsigned int block; + #endif + + TRACE_DEBUG("SkipBlockNandFlash_Initialize()\n\r"); + + // Initialize SkipBlockNandFlash + #if !defined(OP_BOOTSTRAP_on) + error = EccNandFlash_Initialize(ECC(skipBlock), + model, + commandAddress, + addressAddress, + dataAddress, + pinChipEnable, + pinReadyBusy); + #else + error = RawNandFlash_Initialize(RAW(skipBlock), + model, + commandAddress, + addressAddress, + dataAddress, + pinChipEnable, + pinReadyBusy); + #endif + + #if !defined(OP_BOOTSTRAP_on) + if (error) { + + return error; + } + + // Retrieve model information + numBlocks = NandFlashModel_GetDeviceSizeInBlocks(MODEL(skipBlock)); + + // Initialize block statuses + TRACE_DEBUG("Retrieving bad block information ...\n\r"); + + // Retrieve block status from their first page spare area + for (block = 0; block < numBlocks; block++) { + + // Read spare of first page + error = SkipBlockNandFlash_CheckBlock(skipBlock, block); + + if (error != GOODBLOCK) { + + if (error == BADBLOCK) { + + TRACE_DEBUG("Block #%d is bad\n\r", block); + } + else { + + TRACE_ERROR( + "SkipBlockNandFlash_Initialize: Cannot retrieve info from block #%u\n\r", block); + } + } + } + #endif + + return 0; +} + + +//------------------------------------------------------------------------------ +/// Erases a block of a SkipBlock NandFlash. +/// Returns the RawNandFlash_EraseBlock code or NandCommon_ERROR_WRONGSTATUS. +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param block Number of block to erase. +//------------------------------------------------------------------------------ +unsigned char SkipBlockNandFlash_EraseBlock( + struct SkipBlockNandFlash *skipBlock, + unsigned short block, + unsigned int eraseType) +{ + unsigned char error; + const struct NandSpareScheme *scheme; + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + +// TRACE_INFO("SkipBlockNandFlash_EraseBlock(%d)\n\r", block); + + if (eraseType != SCRUB_ERASE) { + // Check block status + if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { + + TRACE_INFO("SkipBlockNandFlash_EraseBlock: Block is BAD\n\r"); + return NandCommon_ERROR_BADBLOCK; + } + } + + // Erase block + error = RawNandFlash_EraseBlock(RAW(skipBlock), block); + if (error) { + + // Try to mark the block as BAD + TRACE_ERROR("SkipBlockNandFlash_EraseBlock: Cannot erase block, try to mark it BAD\n\r"); + + // Retrieve model scheme + scheme = NandFlashModel_GetScheme(MODEL(skipBlock)); + + memset(spare, 0xFF, NandCommon_MAXPAGESPARESIZE); + NandSpareScheme_WriteBadBlockMarker(scheme, spare, NandBlockStatus_BAD); + return RawNandFlash_WritePage(RAW(skipBlock), block, 0, 0, spare); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or the spare area of a page on a SkipBlock nandflash. If +/// the data pointer is not 0, then the block MUST not be BAD +/// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns +/// EccNandFlash_ReadPage(). +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param block Number of block to read page from. +/// \param page Number of page to read inside the given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char SkipBlockNandFlash_ReadPage( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + #if !defined(OP_BOOTSTRAP_on) + // Check that the block is not BAD if data is requested + if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { + + TRACE_ERROR("SkipBlockNandFlash_ReadPage: Block is BAD.\n\r"); + return NandCommon_ERROR_BADBLOCK; + } + + // Read data with ECC verification + return EccNandFlash_ReadPage(ECC(skipBlock), block, page, data, spare); + #else + return RawNandFlash_ReadPage(RAW(skipBlock), block, page, data, spare); + #endif +} + +//------------------------------------------------------------------------------ +/// Reads the data of a whole block on a SkipBlock nandflash. +/// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns +/// EccNandFlash_ReadPage(). +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param block Number of block to read page from. +/// \param page Number of page to read inside the given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char SkipBlockNandFlash_ReadBlock( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + void *data) +{ + /// Number of pages per block + unsigned int numPagesPerBlock, pageSize; + // Page index + unsigned short i; + // Error returned by SkipBlockNandFlash_WritePage + unsigned char error = 0; + + // Retrieve model information + pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); + numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); + + // Check that the block is not BAD if data is requested + if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { + + TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Block is BAD.\n\r"); + return NandCommon_ERROR_BADBLOCK; + } + + // Read all the pages of the block + for (i = 0; i < numPagesPerBlock; i++) { + error = EccNandFlash_ReadPage(ECC(skipBlock), block, i, data, 0); + if (error) { + + TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Cannot read page %d of block %d.\n\r", i, block); + return error; + } + data = (void *) ((unsigned char *) data + pageSize); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or spare area of a page on a SkipBlock NandFlash. +/// Returns NandCommon_ERROR_BADBLOCK if the page is BAD; otherwise, +/// returns EccNandFlash_WritePage(). +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param block Number of the block to write. +/// \param page Number of the page to write inside the given block. +/// \param data Data area buffer. +/// \param spare Spare area buffer. +//------------------------------------------------------------------------------ +unsigned char SkipBlockNandFlash_WritePage( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + // Check that the block is LIVE + if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { + + TRACE_ERROR("SkipBlockNandFlash_WritePage: Block is BAD.\n\r"); + return NandCommon_ERROR_BADBLOCK; + } + + // Write data with ECC calculation + return EccNandFlash_WritePage(ECC(skipBlock), block, page, data, spare); +} + +//------------------------------------------------------------------------------ +/// Writes the data of a whole block on a SkipBlock nandflash. +/// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns +/// EccNandFlash_ReadPage(). +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param block Number of block to read page from. +/// \param data Data area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char SkipBlockNandFlash_WriteBlock( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + void *data) +{ + // Number of pages per block + unsigned int numPagesPerBlock; + // Page size + unsigned int pageSize; + // Page index + unsigned short i; + // Error returned by SkipBlockNandFlash_WritePage + unsigned char error = 0; + + // Retrieve model information + pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); + numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); + + // Check that the block is LIVE + if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { + + TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Block is BAD.\n\r"); + return NandCommon_ERROR_BADBLOCK; + } + + for (i = 0; i < numPagesPerBlock; i++) { + error = EccNandFlash_WritePage(ECC(skipBlock), block, i, data, 0); + if (error) { + + TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Cannot write page %d of block %d.\n\r", i, block); + return NandCommon_ERROR_CANNOTWRITE; + } + data = (void *) ((unsigned char *) data + pageSize); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes the data of a whole block on a SkipBlock nandflash. +/// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns +/// EccNandFlash_ReadPage(). +/// \param skipBlock Pointer to a SkipBlockNandFlash instance. +/// \param block Number of block to read page from. +/// \param page Number of page to read inside the given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +//unsigned char SkipBlockNandFlash_WriteBlockUnaligned( +// const struct SkipBlockNandFlash *skipBlock, +// unsigned short block, +// void *data, +// void *tmpBuffer, +// ) +//{ +// // Number of pages per block +// unsigned int numPagesPerBlock; +// // Page size +// unsigned int pageSize; +// // Page index +// unsigned short i; +// // Error returned by SkipBlockNandFlash_WritePage +// unsigned char error = 0; +// +// // Retrieve model information +// pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); +// numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); +// +// for (i = 0; i < numPagesPerBlock; i++) { +// error = SkipBlockNandFlash_WritePage(skipBlock, block, i, data, 0); +// if (error == NandCommon_ERROR_BADBLOCK) { +// +// TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Cannot write page %d of block %d.\n\r", i, block); +// return NandCommon_ERROR_BADBLOCK; +// } +// else if (error) { +// +// TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Cannot write page %d of block %d.\n\r", i, block); +// return NandCommon_ERROR_CANNOTWRITE; +// } +// data += pageSize; +// } +// +// return 0; +//} + diff --git a/memories/nandflash/SkipBlockNandFlash.h b/memories/nandflash/SkipBlockNandFlash.h new file mode 100644 index 0000000..26fbd2d --- /dev/null +++ b/memories/nandflash/SkipBlockNandFlash.h @@ -0,0 +1,134 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "SkipBlockNandFlash" +/// +/// !!!Purpose +/// +/// SkipBlockNandFlash layer supplies application a set of interface to operate nandflash, which include +/// initialize, block erase, block write/read, page write/read. This layer is called by upper layer +/// applications, and it will call lower layer drivers, such as EccNandFlash, RawNandFlash. +/// +/// !!!Usage +/// -# SkipBlockNandFlash_Initialize is used to initializes a SkipBlockNandFlash instance. Scans +/// the device to retrieve or create block status information. +/// -# SkipBlockNandFlash_EraseBlock is used to erase a certain block in the device, user can +/// select "check block status before erase" or "erase without check" +/// -# User can use SkipBlockNandFlash_WriteBlock to write a certain block and SkipBlockNandFlash_WritePage +/// to write a certain page. The functions will check the block status before write, if the block +/// is not a good block, the write command will not be issued. +/// -# User can use SkipBlockNandFlash_ReadBlock to read a certain block and SkipBlockNandFlash_ReadPage +/// to read a certain page. The functions will check the block status before read, if the block +/// is not a good block, the read command will not be issued. ECC is also checked after read +/// operation is finished, an error will be reported if ecc check got errors. +//------------------------------------------------------------------------------ + +#ifndef SKIPBLOCKNANDFLASH_H +#define SKIPBLOCKNANDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NandCommon.h" +#include "EccNandFlash.h" + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +#define NandBlockStatus_BAD 0xBA + +// Erase types +/// Check block before erase +#define NORMAL_ERASE 0x00000000 +/// Do NOT check the block status before erasing it +#define SCRUB_ERASE 0x0000EA11 +// Values returned by the CheckBlock() function +#define BADBLOCK 255 +#define GOODBLOCK 254 + + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +struct SkipBlockNandFlash { + struct EccNandFlash ecc; +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char SkipBlockNandFlash_CheckBlock( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block); + +extern unsigned char SkipBlockNandFlash_Initialize( + struct SkipBlockNandFlash *skipBlock, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy); + +extern unsigned char SkipBlockNandFlash_EraseBlock( + struct SkipBlockNandFlash *skipBlock, + unsigned short block, + unsigned int eraseType); + +extern unsigned char SkipBlockNandFlash_ReadPage( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +unsigned char SkipBlockNandFlash_ReadBlock( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + void *data); + +extern unsigned char SkipBlockNandFlash_WritePage( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +unsigned char SkipBlockNandFlash_WriteBlock( + const struct SkipBlockNandFlash *skipBlock, + unsigned short block, + void *data); + +#endif //#ifndef SKIPBLOCKNANDFLASH_H + diff --git a/memories/nandflash/TranslatedNandFlash.c b/memories/nandflash/TranslatedNandFlash.c new file mode 100644 index 0000000..8200d42 --- /dev/null +++ b/memories/nandflash/TranslatedNandFlash.c @@ -0,0 +1,587 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "TranslatedNandFlash.h" +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Casts +#define MAPPED(translated) ((struct MappedNandFlash *) translated) +#define MANAGED(translated) ((struct ManagedNandFlash *) translated) +#define ECC(translated) ((struct EccNandFlash *) translated) +#define RAW(translated) ((struct RawNandFlash *) translated) +#define MODEL(translated) ((struct NandFlashModel *) translated) + +/// Minimum number of blocks that should be kept unallocated +#define MINNUMUNALLOCATEDBLOCKS 32 + +/// Maximum allowed erase count difference +#define MAXERASEDIFFERENCE 5 + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Returns 1 if there are enough free blocks to perform a single block +/// allocation; otherwise returns 0. +/// \param translated Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +static unsigned char BlockCanBeAllocated( + const struct TranslatedNandFlash *translated) +{ + unsigned short count; + + // Count number of free and dirty blocks (unallocated blocks) + count = ManagedNandFlash_CountBlocks(MANAGED(translated), NandBlockStatus_DIRTY) + + ManagedNandFlash_CountBlocks(MANAGED(translated), NandBlockStatus_FREE); + + // Check that count is greater than minimum number of unallocated blocks + if (count > MINNUMUNALLOCATEDBLOCKS) { + + return 1; + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the given page inside the currently written block is clean (has +/// not been written yet); otherwise returns 0. +/// \param translated Pointer to a TranslatedNandFlash instance. +/// \param page Page number. +//------------------------------------------------------------------------------ +static unsigned char PageIsClean( + const struct TranslatedNandFlash *translated, + unsigned short page) +{ + ASSERT(page < NandFlashModel_GetBlockSizeInPages(MODEL(translated)), + "PageIsClean: Page out-of-bounds\n\r"); + + return ((translated->currentBlockPageStatuses[page / 8] >> (page % 8)) & 1) == 0; +} + +//------------------------------------------------------------------------------ +/// Marks the given page as being dirty (i.e. written). +/// \param translated Pointer to a TranslatedNandFlash instance. +/// \param page Page number. +//------------------------------------------------------------------------------ +static void MarkPageDirty( + struct TranslatedNandFlash *translated, + unsigned short page) +{ + ASSERT(page < NandFlashModel_GetBlockSizeInPages(MODEL(translated)), + "PageIsClean: Page out-of-bounds\n\r"); + + translated->currentBlockPageStatuses[page / 8] |= 1 << (page % 8); +} + +//------------------------------------------------------------------------------ +/// Marks all pages as being clean. +/// \param translated Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +static void MarkAllPagesClean(struct TranslatedNandFlash *translated) +{ + memset(translated->currentBlockPageStatuses, 0, + sizeof(translated->currentBlockPageStatuses)); +} + +//------------------------------------------------------------------------------ +/// Allocates the best-fitting physical block for the given logical block. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_NOBLOCKFOUND if +/// there are no more free blocks, or a NandCommon_ERROR code. +/// \param translated Pointer to a TranslatedNandFlash instance. +/// \param block Logical block number. +//------------------------------------------------------------------------------ +static unsigned char AllocateBlock( + struct TranslatedNandFlash *translated, + unsigned short block) +{ + unsigned short freeBlock, liveBlock; + unsigned char error; + signed int eraseDifference; + + TRACE_DEBUG("Allocating a new block\n\r"); + + // Find youngest free block and youngest live block + if (ManagedNandFlash_FindYoungestBlock(MANAGED(translated), + NandBlockStatus_FREE, + &freeBlock)) { + + TRACE_ERROR("AllocateBlock: Could not find a free block\n\r"); + return NandCommon_ERROR_NOBLOCKFOUND; + } + + // If this is the last free block, save the logical mapping in it and + // clean dirty blocks + TRACE_DEBUG("Number of FREE blocks: %d\n\r", + ManagedNandFlash_CountBlocks(MANAGED(translated), NandBlockStatus_FREE)); + if (ManagedNandFlash_CountBlocks(MANAGED(translated), + NandBlockStatus_FREE) == 1) { + + // Save mapping and clean dirty blocks + TRACE_DEBUG("Last FREE block, cleaning up ...\n\r"); + + error = MappedNandFlash_SaveLogicalMapping(MAPPED(translated), freeBlock); + if (error) { + + TRACE_ERROR("AllocateBlock: Failed to save mapping\n\r"); + return error; + } + error = ManagedNandFlash_EraseDirtyBlocks(MANAGED(translated)); + if (error) { + + TRACE_ERROR("AllocatedBlock: Failed to erase dirty blocks\n\r"); + return error; + } + + // Allocate new block + return AllocateBlock(translated, block); + } + + // Find youngest LIVE block to check the erase count difference + if (!ManagedNandFlash_FindYoungestBlock(MANAGED(translated), + NandBlockStatus_LIVE, + &liveBlock)) { + + // Calculate erase count difference + TRACE_DEBUG("Free block erase count = %d\n\r", MANAGED(translated)->blockStatuses[freeBlock].eraseCount); + TRACE_DEBUG("Live block erase count = %d\n\r", MANAGED(translated)->blockStatuses[liveBlock].eraseCount); + eraseDifference = absv(MANAGED(translated)->blockStatuses[freeBlock].eraseCount + - MANAGED(translated)->blockStatuses[liveBlock].eraseCount); + + // Check if it is too big + if (eraseDifference > MAXERASEDIFFERENCE) { + + TRACE_WARNING("Erase difference too big, switching blocks\n\r"); + MappedNandFlash_Map( + MAPPED(translated), + MappedNandFlash_PhysicalToLogical( + MAPPED(translated), + liveBlock), + freeBlock); + ManagedNandFlash_CopyBlock(MANAGED(translated), + liveBlock, + freeBlock); + + // Allocate a new block + return AllocateBlock(translated, block); + } + } + + // Map block + TRACE_DEBUG("Allocating PB#%d for LB#%d\n\r", freeBlock, block); + MappedNandFlash_Map(MAPPED(translated), block, freeBlock); + + return 0; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a TranslatedNandFlash instance. +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xxx code. +/// \param translated Pointer to a TranslatedNandFlash instance. +/// \param model Pointer to the underlying nand chip model. Can be 0. +/// \param commandAddress Address at which commands are sent. +/// \param addressAddress Address at which addresses are sent. +/// \param dataAddress Address at which data is sent. +/// \param pinChipEnable Pin controlling the CE signal of the NandFlash. +/// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. +/// \param baseBlock Basic physical block address of mapped area. +/// \param sizeInBlocks Number of blocks that is mapped. +//------------------------------------------------------------------------------ +unsigned char TranslatedNandFlash_Initialize( + struct TranslatedNandFlash *translated, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy, + unsigned short baseBlock, + unsigned short sizeInBlocks) +{ + translated->currentLogicalBlock = -1; + translated->previousPhysicalBlock = -1; + MarkAllPagesClean(translated); + + // Initialize MappedNandFlash + return MappedNandFlash_Initialize(MAPPED(translated), + model, + commandAddress, + addressAddress, + dataAddress, + pinChipEnable, + pinReadyBusy, + baseBlock, + sizeInBlocks); +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or the spare area of a page on a translated nandflash. +/// If the block is not currently mapped but could be (i.e. there are available +/// physical blocks), then the data/spare is filled with 0xFF. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_NOMOREBLOCKS +/// if no more block can be allocated, or a NandCommon_ERROR code. +/// \param translated Pointer to a TranslatedNandFlash instance. +/// \param block Logical block number. +/// \param page Number of page to read inside logical block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char TranslatedNandFlash_ReadPage( + const struct TranslatedNandFlash *translated, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char error; + + TRACE_INFO("TranslatedNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); + + // If the page to read is in the current block, there is a previous physical + // block and the page is clean -> read the page in the old block since the + // new one does not contain meaningful data + if ((block == translated->currentLogicalBlock) + && (translated->previousPhysicalBlock != -1) + && (PageIsClean(translated, page))) { + + TRACE_DEBUG("Reading page from current block\n\r"); + return ManagedNandFlash_ReadPage(MANAGED(translated), + translated->previousPhysicalBlock, + page, + data, + spare); + } + else { + + // Try to read the page from the logical block + error = MappedNandFlash_ReadPage(MAPPED(translated), block, page, data, spare); + + // Block was not mapped + if (error == NandCommon_ERROR_BLOCKNOTMAPPED) { + + ASSERT(!spare, "Cannot read the spare information of an unmapped block\n\r"); + + // Check if a block can be allocated + if (BlockCanBeAllocated(translated)) { + + // Return 0xFF in buffers with no error + TRACE_DEBUG("Block #%d is not mapped but can be allocated, filling buffer with 0xFF\n\r", block); + if (data) { + + memset(data, 0xFF, NandFlashModel_GetPageDataSize(MODEL(translated))); + } + if (spare) { + + memset(spare, 0xFF, NandFlashModel_GetPageSpareSize(MODEL(translated))); + } + } + else { + + TRACE_ERROR("Block #%d is not mapped and there are no more blocks available\n\r", block); + return NandCommon_ERROR_NOMOREBLOCKS; + } + } + // Error + else if (error) { + + return error; + } + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or spare area of a page on a translated nandflash. +/// Allocates block has needed to keep the wear even between all blocks. +/// \param translated Pointer to a TranslatedNandFlash instance. +/// \param block Logical block number. +/// \param page Number of page to write inside logical block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char TranslatedNandFlash_WritePage( + struct TranslatedNandFlash *translated, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + unsigned char allocate = 1; + unsigned char error; + + TRACE_INFO("TranslatedNandFlash_WritePage(B#%d:P#%d)\n\r", block, page); + + // A new block must be allocated unless: + // 1. the block is not mapped and there are no more blocks to allocate + if (MappedNandFlash_LogicalToPhysical(MAPPED(translated), block) == -1) { + + // Block is not mapped, check if it can be + if (!BlockCanBeAllocated(translated)) { + + TRACE_ERROR("TranslatedNandFlash_WritePage: Not enough free blocks\n\r"); + return NandCommon_ERROR_NOMOREBLOCKS; + } + TRACE_DEBUG("Allocate because block not mapped\n\r"); + } + // or 2. the block to write is the current one and the page to write is + // clean + else if (translated->currentLogicalBlock == block) { + + if (PageIsClean(translated, page)) { + + TRACE_DEBUG("NO allocate because write in current block\n\r"); + allocate = 0; + } + else { + + TRACE_DEBUG("Allocate because page DIRTY in current block\n\r"); + } + } + else { + + TRACE_DEBUG("Allocate because block is mapped and different from current block\n\r"); + } + + // Allocate block if needed + if (allocate) { + + // Flush current block write (if any) and then allocate block + error = TranslatedNandFlash_Flush(translated); + if (error) { + + return error; + } + translated->previousPhysicalBlock = MappedNandFlash_LogicalToPhysical( + MAPPED(translated), + block); + TRACE_DEBUG("Previous physical block is now #%d\n\r", + translated->previousPhysicalBlock); + error = AllocateBlock(translated, block); + if (error) { + + return error; + } + + // Block becomes the current block with all pages clean + translated->currentLogicalBlock = block; + MarkAllPagesClean(translated); + } + + // Start writing page + error = MappedNandFlash_WritePage(MAPPED(translated), + block, + page, + data, + spare); + if (error) { + + return error; + } + + // If write went through, mark page as written + MarkPageDirty(translated, page); + return 0; +} + +//------------------------------------------------------------------------------ +/// Terminates the current write operation by copying all the missing pages from +/// the previous physical block. +/// \param translated Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned char TranslatedNandFlash_Flush(struct TranslatedNandFlash *translated) +{ + unsigned int i; + unsigned char error; + unsigned int currentPhysicalBlock; + + // Check if there is a current block and a previous block + if ((translated->currentLogicalBlock == -1) + || (translated->previousPhysicalBlock == -1)) { + + return 0; + } + + TRACE_INFO("TranslatedNandFlash_Flush(PB#%d -> LB#%d)\n\r", + translated->previousPhysicalBlock, translated->currentLogicalBlock); + + // Copy missing pages in the current block + currentPhysicalBlock = MappedNandFlash_LogicalToPhysical( + MAPPED(translated), + translated->currentLogicalBlock); + + for (i=0; i < NandFlashModel_GetBlockSizeInPages(MODEL(translated)); i++) { + + if (PageIsClean(translated, i)) { + + TRACE_DEBUG("Copying back page #%d of block #%d\n\r", i, + translated->previousPhysicalBlock); + + // Copy page + error = ManagedNandFlash_CopyPage(MANAGED(translated), + translated->previousPhysicalBlock, + i, + currentPhysicalBlock, + i); + if (error) { + + TRACE_ERROR("FinishCurrentWrite: copy page #%d\n\r", i); + return error; + } + } + } + + translated->currentLogicalBlock = -1; + translated->previousPhysicalBlock = -1; + return 0; +} + +//------------------------------------------------------------------------------ +/// Erase all blocks in the tranalated area of nand flash. +/// \param managed Pointer to a TranslatedNandFlash instance. +/// \param level Erase level. +//------------------------------------------------------------------------------ +unsigned char TranslatedNandFlash_EraseAll( + struct TranslatedNandFlash *translated, + unsigned char level) +{ + MappedNandFlash_EraseAll(MAPPED(translated), level); + if (level > NandEraseDIRTY) { + translated->currentLogicalBlock = -1; + translated->previousPhysicalBlock = -1; + MarkAllPagesClean(translated); + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Allocates a free block to save the current logical mapping on it. +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR code. +/// \param translated Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned char TranslatedNandFlash_SaveLogicalMapping( + struct TranslatedNandFlash *translated) +{ + unsigned char error; + unsigned short freeBlock; + + TRACE_INFO("TranslatedNandFlash_SaveLogicalMapping()\n\r"); + + // Save logical mapping in the youngest free block + // Find the youngest block + error = ManagedNandFlash_FindYoungestBlock(MANAGED(translated), + NandBlockStatus_FREE, + &freeBlock); + if (error) { + + TRACE_ERROR("TranNF_SaveLogicalMapping: No free block\n\r"); + return error; + } + + // Check if this is the last free block, in which case dirty blocks are wiped + // prior to saving the mapping + if (ManagedNandFlash_CountBlocks(MANAGED(translated), + NandBlockStatus_FREE) == 1) { + + TranslatedNandFlash_Flush(translated); + error = ManagedNandFlash_EraseDirtyBlocks(MANAGED(translated)); + if (error) { + + TRACE_ERROR("TranNF_Flush: Could not erase dirty blocks\n\r"); + return error; + } + } + + // Save the mapping + error = MappedNandFlash_SaveLogicalMapping(MAPPED(translated), freeBlock); + if (error) { + + TRACE_ERROR("TranNF_Flush: Failed to save mapping in #%d\n\r", + freeBlock); + return error; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns the number of available blocks in a translated nandflash. +/// \param translated Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned short TranslatedNandFlash_GetDeviceSizeInBlocks( + const struct TranslatedNandFlash *translated) +{ + return ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(translated)) + - MINNUMUNALLOCATEDBLOCKS + - ManagedNandFlash_CountBlocks(MANAGED(translated), + NandBlockStatus_BAD) + - 1; // Logical mapping block +} + +//------------------------------------------------------------------------------ +/// Returns the number of available pages in a translated nandflash. +/// \param translated Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned int TranslatedNandFlash_GetDeviceSizeInPages( + const struct TranslatedNandFlash *translated) +{ + return TranslatedNandFlash_GetDeviceSizeInBlocks(translated) + * NandFlashModel_GetBlockSizeInPages(MODEL(translated)); +} + +//------------------------------------------------------------------------------ +/// Returns the number of available data bytes in a translated nandflash. +/// \param translated Pointer to a TranslatedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned long long TranslatedNandFlash_GetDeviceSizeInBytes( + const struct TranslatedNandFlash *translated) +{ + return TranslatedNandFlash_GetDeviceSizeInPages(translated) + * NandFlashModel_GetPageDataSize(MODEL(translated)); +} + diff --git a/memories/nandflash/TranslatedNandFlash.h b/memories/nandflash/TranslatedNandFlash.h new file mode 100644 index 0000000..88bbf38 --- /dev/null +++ b/memories/nandflash/TranslatedNandFlash.h @@ -0,0 +1,108 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "TranslatedNandFlash" +/// +/// !!!Purpose +/// +/// Translate a nandflash physical block to logical block, it will call lower layer such as MappedNandFlash +//------------------------------------------------------------------------------ + +#ifndef TRANSLATEDNANDFLASH_H +#define TRANSLATEDNANDFLASH_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MappedNandFlash.h" + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +struct TranslatedNandFlash { + + struct MappedNandFlash mapped; + signed short currentLogicalBlock; + signed short previousPhysicalBlock; + unsigned char currentBlockPageStatuses[NandCommon_MAXNUMPAGESPERBLOCK / 8]; +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char TranslatedNandFlash_Initialize( + struct TranslatedNandFlash *translated, + const struct NandFlashModel *model, + unsigned int commandAddress, + unsigned int addressAddress, + unsigned int dataAddress, + const Pin pinChipEnable, + const Pin pinReadyBusy, + unsigned short baseBlock, + unsigned short sizeInBlocks); + +extern unsigned char TranslatedNandFlash_ReadPage( + const struct TranslatedNandFlash *translated, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char TranslatedNandFlash_WritePage( + struct TranslatedNandFlash *translated, + unsigned short block, + unsigned short page, + void *data, + void *spare); + +extern unsigned char TranslatedNandFlash_Flush( + struct TranslatedNandFlash *translated); + +extern unsigned char TranslatedNandFlash_EraseAll( + struct TranslatedNandFlash *translated, + unsigned char level); + +extern unsigned char TranslatedNandFlash_SaveLogicalMapping( + struct TranslatedNandFlash *translated); + +extern unsigned short TranslatedNandFlash_GetDeviceSizeInBlocks( + const struct TranslatedNandFlash *translated); + +extern unsigned int TranslatedNandFlash_GetDeviceSizeInPages( + const struct TranslatedNandFlash *translated); + +extern unsigned long long TranslatedNandFlash_GetDeviceSizeInBytes( + const struct TranslatedNandFlash *translated); + +#endif //#ifndef TRANSLATEDNANDFLASH_H + diff --git a/memories/nandflash/nandflash.dir b/memories/nandflash/nandflash.dir new file mode 100644 index 0000000..d20aa6a --- /dev/null +++ b/memories/nandflash/nandflash.dir @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory contains nandflash drivers +/// +/// !!!Description +/// - Nandflash drivers: SkipBlockNandFlash layer, EccNandFlash and NandSpareScheme layer, +/// RawNandflash layer. +/// - Nandflash parameter interface : NandFlashModel. +/// - Supported Nandflash device list : NandFlashModelList. +/// - Nandflash block management drivers : TranslatedNandFlash layer, MappedNandFlash layer, +/// ManagedNandFlash layer. +//------------------------------------------------------------------------------ \ No newline at end of file diff --git a/memories/norflash/NorFlashAmd.c b/memories/norflash/NorFlashAmd.c new file mode 100644 index 0000000..388f9e5 --- /dev/null +++ b/memories/norflash/NorFlashAmd.c @@ -0,0 +1,435 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ +#include "NorFlashCFI.h" +#include "NorFlashCommon.h" +#include +#include + +//------------------------------------------------------------------------------ +// Local defination +//------------------------------------------------------------------------------ + +/// Command for vendor command set CMD_SET_AMD. +#define AMD_CMD_IDOUT 0x00F0 +#define AMD_CMD_CFI 0x0098 +#define AMD_CMD_IDIN 0x0090 +#define AMD_CMD_UNLOCK_1 0x00AA +#define AMD_CMD_UNLOCK_2 0x0055 +#define AMD_CMD_ERASE_SETUP 0x0080 +#define AMD_CMD_ERASE_RESUME 0x0030 +#define AMD_CMD_ERASE_CHIP 0x0010 +#define AMD_CMD_ERASE_SECTOR 0x0030 +#define AMD_CMD_PROGRAM 0x00A0 +#define AMD_CMD_UNLOCK_BYPASS 0x0020 + +// Command offset for vendor command set CMD_SET_AMD +#define AMD_OFFSET_UNLOCK_1 0x05555 +#define AMD_OFFSET_UNLOCK_2 0x0AAAA +/// Query command address. +#define FLASH_ADDRESS_CFI 0x0055 + +/// AMD norflash device Identifier infomation address offset. +#define AMD_MANU_ID 0x00 +#define AMD_DEVIDE_ID 0x01 + +// Data polling mask for vendor command set CMD_SET_AMD +#define AMD_POLLING_DQ7 0x80 +#define AMD_POLLING_DQ6 0x60 +#define AMD_POLLING_DQ5 0x20 +#define AMD_POLLING_DQ3 0x08 + + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// It implements a RESET command. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +/// \param address Dummy data for AMD. +//------------------------------------------------------------------------------ +void amd_Reset(struct NorFlashInfo *pNorFlashInfo, unsigned int address) +{ + unsigned char busWidth; + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_IDOUT); +} + + +//------------------------------------------------------------------------------ +/// Read specified manufactory id or device id. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +/// \param index 0: manufactorid 1: device id. +//------------------------------------------------------------------------------ +unsigned int amd_ReadIdentification( + struct NorFlashInfo *pNorFlashInfo, + unsigned char index) +{ + unsigned int id; + unsigned char busWidth; + unsigned int address; + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + + // The amd_Read identification command sequence is initiated by first + // writing two unlock cycles. + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + + // Followed by a third write cycle that contains the autoselect command. + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_IDIN); + + // The device then enters the autoselect mode. It may read at any address any + // number of times without initiating another autoselect command sequence. + address = NorFlash_GetByteAddressInChip(pNorFlashInfo, index); + ReadRawData(busWidth, address, (unsigned char*)&id); + + // The system must write the exit command to return to the read mode + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_IDOUT); + return id; +} +//------------------------------------------------------------------------------ +/// It implement a program word command. Returns 0 if the operation was +/// successful; otherwise returns an error code. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +/// \param address Start address offset to be wrote. +/// \param data word to be written. +//------------------------------------------------------------------------------ +unsigned char amd_Program( + struct NorFlashInfo *pNorFlashInfo, + unsigned int address, + unsigned int data) +{ + unsigned int pollingData; + unsigned int busAddress; + unsigned char done = 0; + unsigned char busWidth; + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + // The program command sequence is initiated by writing two unlock write cycles. + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + // Followed by the program set-up command. + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_PROGRAM); + + // The program address and data are written next, + // which in turn initiate the Embedded Program algorithm. + busAddress = NorFlash_GetAddressInChip(pNorFlashInfo, address); + WriteRawData(busWidth, busAddress, (unsigned char*)&data); + + // Data polling + do { + ReadRawData(busWidth, busAddress, (unsigned char *)&pollingData); + // Check if the chip program algorithm is completed. + if ((pollingData & AMD_POLLING_DQ7) == (data & AMD_POLLING_DQ7)) { + // Program operation successful. Device in read mode. + done = 1; + } + else { + // check if chip Program algrithm exceeded timing limits + + if (pollingData & AMD_POLLING_DQ5 ) { + + // I/O should be rechecked. + ReadRawData(busWidth, busAddress, (unsigned char *)&pollingData); + + if ((pollingData & AMD_POLLING_DQ7) == (data & AMD_POLLING_DQ7)) { + // Program operation successful. Device in read mode. + done = 1; + } + else { + // Program operation not successful, write reset command. + amd_Reset(pNorFlashInfo, 0); + return NorCommon_ERROR_CANNOTWRITE; + } + } + } + } while (!done); + return 0; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +/// It implements a RESET command. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +/// \param address Dummy data for AMD. +//------------------------------------------------------------------------------ +void AMD_Reset(struct NorFlashInfo *pNorFlashInfo, unsigned int address) +{ + amd_Reset(pNorFlashInfo, address); +} + + +//------------------------------------------------------------------------------ +/// The Read Device Identifier command instructs the device to output manufacturer +/// code. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned int AMD_ReadManufactoryId(struct NorFlashInfo *pNorFlashInfo) +{ + return amd_ReadIdentification(pNorFlashInfo, AMD_MANU_ID); +} + +//------------------------------------------------------------------------------ +/// The Read Device Identifier command instructs the device to output device id. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned int AMD_ReadDeviceID(struct NorFlashInfo *pNorFlashInfo) +{ + return amd_ReadIdentification(pNorFlashInfo, AMD_DEVIDE_ID); +} + +//------------------------------------------------------------------------------ +/// Erases the specified block of the device. Returns 0 if the operation was +/// successful; otherwise returns an error code. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +/// \param address Address offset to be erase. +//------------------------------------------------------------------------------ +unsigned char AMD_EraseSector( + struct NorFlashInfo *pNorFlashInfo, + unsigned int address) +{ + unsigned int pollingData; + unsigned int busAddress; + unsigned char busWidth; + unsigned char done = 0; + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + + //Programming is a six-bus-cycle operation. + // The erase command sequence is initiated by writing two unlock write cycles. + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + // Followed by the program set-up command. + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_ERASE_SETUP); + // Two additional unlock cycles are written. + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth, + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + + // Followed by the address of the sector to be erased, and the sector erase command. + busAddress = NorFlash_GetAddressInChip(pNorFlashInfo,address); + WriteCommand(busWidth, busAddress, AMD_CMD_ERASE_SECTOR); + + // Data polling + do { + ReadRawData(busWidth, busAddress, (unsigned char *)&pollingData); + // Check if the chip erase algorithm is completed. + if ((pollingData & AMD_POLLING_DQ7) == AMD_POLLING_DQ7 ) { + // Erase operation successful. Device in read mode. + done = 1; + } + else { + // check if sector earse algrithm exceeded timing limits + if (pollingData & AMD_POLLING_DQ5 ) { + + // I/O should be rechecked. + ReadRawData(busWidth, busAddress, (unsigned char *)&pollingData); + if ((pollingData & AMD_POLLING_DQ7) == AMD_POLLING_DQ7 ){ + // Erase operation successful. Device in read mode. + done = 1; + } + else { + // Erase operation not successful, write reset command. + amd_Reset(pNorFlashInfo, 0); + return NorCommon_ERROR_CANNOTERASE; + } + } + } + } while (!done); + return 0; +} + +//------------------------------------------------------------------------------ +/// Erases all the block of the device. Returns 0 if the operation was successful; +/// otherwise returns an error code. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned char AMD_EraseChip(struct NorFlashInfo *pNorFlashInfo) +{ + unsigned int pollingData; + unsigned char busWidth; + unsigned int address; + unsigned char done = 0; + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + + //Programming is a six-bus-cycle operation. + // The erase command sequence is initiated by writing two unlock write cycles. + WriteCommand(busWidth , + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth , + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + // Followed by the program set-up command. + WriteCommand(busWidth , + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_ERASE_SETUP); + + // Two additional unlock cycles are written. + WriteCommand(busWidth , + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_UNLOCK_1); + WriteCommand(busWidth , + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_2), + AMD_CMD_UNLOCK_2); + + // Then followed by the chip erase command. + WriteCommand(busWidth , + NorFlash_GetByteAddressInChip(pNorFlashInfo, AMD_OFFSET_UNLOCK_1), + AMD_CMD_ERASE_CHIP); + + address = NorFlash_GetByteAddressInChip(pNorFlashInfo, 0); + // Data polling + do { + ReadRawData(busWidth , address, (unsigned char*)&pollingData); + // Check if the chip erase algorithm is completed. + if ((pollingData & AMD_POLLING_DQ7) == AMD_POLLING_DQ7 ) { + // Erase operation successful. Device in read mode. + done = 1; + } + else { + + // When the time-out period is complete, DQ3 switches from a ¡°0¡± to a ¡°1.¡± + if (pollingData & AMD_POLLING_DQ3 ) { + return NorCommon_ERROR_CANNOTERASE; + } + // check if chip earse algrithm exceeded timing limits + if (pollingData & AMD_POLLING_DQ5 ) { + + // I/O should be rechecked. + ReadRawData(busWidth , address, (unsigned char*)&pollingData); + if ((pollingData & AMD_POLLING_DQ7) == AMD_POLLING_DQ7 ){ + // Erase operation successful. Device in read mode. + done = 1; + } + else { + // Erase operation not successful, write reset command. + amd_Reset(pNorFlashInfo, 0); + return NorCommon_ERROR_CANNOTERASE; + } + } + } + } while (!done); + return 0; +} + + +//------------------------------------------------------------------------------ +/// Sends data to the NorFlashInfo chip from the provided buffer. +/// \param pNorFlashInfo Pointer to an NorFlashInfo instance. +/// \param address Start address offset to be wrote. +/// \param buffer Buffer where the data is stored. +/// \param size Number of bytes that will be written. +//------------------------------------------------------------------------------ +unsigned char AMD_Write_Data( + struct NorFlashInfo *pNorFlashInfo, + unsigned int address, + unsigned char *buffer, + unsigned int size) +{ + unsigned int i; + unsigned char busWidth; + busWidth = pNorFlashInfo->deviceChipWidth; + + if (busWidth == FLASH_CHIP_WIDTH_8BITS ){ + for(i=0; i < size; i++) { + if(amd_Program(pNorFlashInfo, address, buffer[i])) { + return NorCommon_ERROR_CANNOTWRITE; + } + address ++; + } + } + else if( busWidth == FLASH_CHIP_WIDTH_16BITS ){ + unsigned short *buffer16 = (unsigned short *) buffer; + size = (size + 1) >> 1; + for(i=0; i < size; i++) { + if(amd_Program(pNorFlashInfo, address, buffer16[i])){ + return NorCommon_ERROR_CANNOTWRITE; + } + address+= 2; + } + } + else if(busWidth == FLASH_CHIP_WIDTH_32BITS ){ + unsigned int *buffer32 = (unsigned int *) buffer; + size = (size + 3) >> 2; + for(i=0; i < size; i++) { + if(amd_Program(pNorFlashInfo, address, buffer32[i])){ + return NorCommon_ERROR_CANNOTWRITE; + } + address+= 4; + } + } + return 0; +} diff --git a/memories/norflash/NorFlashAmd.h b/memories/norflash/NorFlashAmd.h new file mode 100644 index 0000000..8b08be8 --- /dev/null +++ b/memories/norflash/NorFlashAmd.h @@ -0,0 +1,98 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// The AMD %norflash Low-level driver code implement procedures to program +/// basic operations described AMD-specified command set flash devices. +/// The various commands recognized by the devices are listed in the Commands +/// Tables provided in the corresponding AMD command set compatible flash +/// datasheets. All operation functions are blocked, they wait for the +/// completion of an operation by polling the status register. +/// +/// !!!Usage +/// -# Flash program using AMD_Write_Data(). +/// - The Program command is used to modify the data stored at the +/// specified device address. Programming can only change bits +/// from ¡®1¡¯ to ¡®0¡¯. It may be necessary to erase the block before +/// programming to addresses within it. Programming modifies a single +/// Word at a time using static function amd_Program(). Programming +/// larger amounts of data must be done in one Word at a time by +/// giving a Program command, waiting for the command to complete, +/// giving the next Program command and so on. +/// -# erase a block within the flash using AMD_EraseSector(). +/// - Flash erase is performed on a block basis. An entire block is +/// erased each time an erase command sequence is given. +/// -# erase whole blocks within the flash using AMD_EraseChip(). +/// -# AMD_Reset() function can be issued, between Bus Write cycles +/// before the start of a program or erase operation, to return the +/// device to read mode. +/// -# AMD_ReadDeviceID() is used to retrieve information +/// about the Flash Device type. +/// -# AMD_ReadManufactoryId() is used to retrieve information +/// about the Flash Device Manufactory ID. +//------------------------------------------------------------------------------ + +#ifndef NORFLASHAMD_H +#define NORFLASHAMD_H + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +void AMD_Reset(struct NorFlashInfo *pNorFlashInfo, unsigned int address); + +unsigned int AMD_ReadManufactoryId(struct NorFlashInfo *pNorFlashInfo); + +unsigned int AMD_ReadDeviceID(struct NorFlashInfo *pNorFlashInfo); + +unsigned char AMD_EraseSector( + struct NorFlashInfo *pNorFlashInfo, + unsigned int sectorAddr); + +unsigned char AMD_EraseChip(struct NorFlashInfo *pNorFlashInfo); + +unsigned char AMD_Write_Data( + struct NorFlashInfo *pNorFlashInfo, + unsigned int address, + unsigned char *buffer, + unsigned int size); + +const struct NorFlashOperations amdOperations = { + AMD_Reset, + AMD_Write_Data, + AMD_ReadManufactoryId, + AMD_ReadDeviceID, + AMD_EraseChip, + AMD_EraseSector +}; + +#endif //#ifndef NORFLASHAMD_H diff --git a/memories/norflash/NorFlashApi.c b/memories/norflash/NorFlashApi.c new file mode 100644 index 0000000..33647f9 --- /dev/null +++ b/memories/norflash/NorFlashApi.c @@ -0,0 +1,143 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ +#include "NorFlashApi.h" +#include "NorFlashCommon.h" +#include +#include +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// It will invokes different associate function to implement a RESET command. +/// \param pNorFlash Pointer to a NorFlash instance. +/// \param address Address offset. +//------------------------------------------------------------------------------ +void NORFLASH_Reset(struct NorFlash *pNorFlash, unsigned int address) +{ + ((pNorFlash->pOperations)->_fReset)(&(pNorFlash->norFlashInfo), address); +} + +//------------------------------------------------------------------------------ +/// It will invokes associate function to implement a read manufactory ID command. +/// \param pNorFlash Pointer to a NorFlash instance. +//------------------------------------------------------------------------------ +unsigned int NORFLASH_ReadManufactoryID(struct NorFlash *pNorFlash) +{ + return ((pNorFlash->pOperations)->_fReadManufactoryID)(&(pNorFlash->norFlashInfo)); +} + +//------------------------------------------------------------------------------ +/// It will invokes associate function to implement a read device ID command. +/// ID command. +/// \param pNorFlash Pointer to a NorFlash instance. +//------------------------------------------------------------------------------ +unsigned int NORFLASH_ReadDeviceID(struct NorFlash *pNorFlash) +{ + return ((pNorFlash->pOperations)->_fReadDeviceID)(&(pNorFlash->norFlashInfo)); +} + +//------------------------------------------------------------------------------ +/// Erases the specified block of the device. Returns 0 if the operation was +/// successful; otherwise returns an error code. +/// \param pNorFlash Pointer to a NorFlash instance. +/// \param address Address offset to be erase. +//------------------------------------------------------------------------------ +unsigned char NORFLASH_EraseSector( + struct NorFlash *pNorFlash, + unsigned int address) +{ + return ((pNorFlash->pOperations)->_fEraseSector)(&(pNorFlash->norFlashInfo), address); +} + +//------------------------------------------------------------------------------ +/// Erases all the block of the device. Returns 0 if the operation was successful; +/// otherwise returns an error code. +/// \param pNorFlash Pointer to a NorFlash instance. +//------------------------------------------------------------------------------ +unsigned char NORFLASH_EraseChip( + struct NorFlash *pNorFlash) +{ + return ((pNorFlash->pOperations)->_fEraseChip)(&(pNorFlash->norFlashInfo)); +} + +//------------------------------------------------------------------------------ +/// Sends data to the pNorFlash chip from the provided buffer. +/// \param pNorFlash Pointer to a NorFlash instance. +/// \param address Start address offset to be wrote. +/// \param buffer Buffer where the data is stored. +/// \param size Number of bytes that will be written. +//------------------------------------------------------------------------------ +unsigned char NORFLASH_WriteData( + struct NorFlash *pNorFlash, + unsigned int address, + unsigned char *buffer, + unsigned int size) +{ + return ((pNorFlash->pOperations)->_fWriteData)(&(pNorFlash->norFlashInfo), address, buffer, size); +} + +//------------------------------------------------------------------------------ +/// Reads data from the NandFlash chip into the provided buffer. +/// \param pNorFlash Pointer to a NorFlash instance. +/// \param buffer Buffer where the data will be stored. +/// \param size Number of bytes that will be read. +//------------------------------------------------------------------------------ +unsigned char NORFLASH_ReadData( + struct NorFlash *pNorFlash, + unsigned int address, + unsigned char *buffer, + unsigned int size) +{ + unsigned int busAddress; + unsigned char busWidth; + unsigned int i; + busWidth = NorFlash_GetDataBusWidth(&(pNorFlash->norFlashInfo)); + + busAddress = NorFlash_GetAddressInChip(&(pNorFlash->norFlashInfo), address); + if ((busWidth / 8 ) == FLASH_CHIP_WIDTH_16BITS ){ + size = (size + 1) >> 1; + } + if ((busWidth/8) == FLASH_CHIP_WIDTH_32BITS ){ + size = (size + 3) >> 2; + } + for(i = 0; i < size; i++) { + ReadRawData(busWidth, busAddress, buffer); + buffer+= (busWidth / 8); + busAddress+= (busWidth / 8); + + } + return 0; +} diff --git a/memories/norflash/NorFlashApi.h b/memories/norflash/NorFlashApi.h new file mode 100644 index 0000000..3813078 --- /dev/null +++ b/memories/norflash/NorFlashApi.h @@ -0,0 +1,137 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// The API layer consists of several functions that allow user to do +/// operations with flash in a unified way. As a result, future device changes +/// will not necessarily lead to the code changes in the application environments. +/// In this %norflash library we support AMD and INTEL command set. +/// The API layar code accesses the Flash memory by calling the low-level code, +/// so users do not have to concern themselves with the details of the special +/// command sequences. +/// +/// !!!Usage +/// +/// !!!Usage +/// -# Flash program using NORFLASH_WriteData(). +/// - The Program command is used to modify the data stored at the +/// specified device address. +/// -# erase a block within the flash using NORFLASH_EraseSector(). +/// - Flash erase is performed on a block basis. An entire block is +/// erased each time an erase command sequence is given. +/// -# erase whole blocks within the flash using NORFLASH_EraseChip(). +/// -# NORFLASH_Reset() function can be issued, between Bus Write cycles +/// before the start of a program or erase operation, to return the +/// device to read mode. +/// -# NORFLASH_ReadDeviceID() is used to retrieve information +/// about the Flash Device type. +/// -# NORFLASH_ReadManufactoryID() is used to retrieve information +/// about the Flash Device Manufactory ID. +//------------------------------------------------------------------------------ + +#ifndef NORFLASHAPI_H +#define NORFLASHAPI_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NorFlashCFI.h" + +//------------------------------------------------------------------------------ +// Type +//------------------------------------------------------------------------------ + +/// Reset or exit CFI query mode function. +typedef void (*fReset) (struct NorFlashInfo *, unsigned int ); +/// Write buffer to pNorFlash function. +typedef unsigned char (*fWriteData)(struct NorFlashInfo *, unsigned int , unsigned char *, unsigned int ); +/// Read manufactory function. +typedef unsigned int (*fReadManufactoryID)(struct NorFlashInfo *); +/// Read device ID code function. +typedef unsigned int (*fReadDeviceID)(struct NorFlashInfo *); +/// Full erase chip function. +typedef unsigned char (*fEraseChip) (struct NorFlashInfo *); +/// Erase single sector function. +typedef unsigned char (*fEraseSector)(struct NorFlashInfo *, unsigned int ); + + +struct NorFlashOperations { + /// Reset or exit CFI query mode function. + void (*_fReset)(struct NorFlashInfo *pNorFlashInfo, unsigned int address); + /// Write buffer to norflash function. + unsigned char (*_fWriteData)(struct NorFlashInfo *pNorFlashInfo, + unsigned int address, + unsigned char *buffer, + unsigned int size); + /// Read manufactory function. + unsigned int (*_fReadManufactoryID)(struct NorFlashInfo *pNorFlashInfo); + /// Read device ID code function. + unsigned int (*_fReadDeviceID)(struct NorFlashInfo *pNorFlashInfo); + /// Full erase chip function. + unsigned char (*_fEraseChip) (struct NorFlashInfo *pNorFlashInfo); + /// Erase single sector function. + unsigned char (*_fEraseSector)(struct NorFlashInfo *pNorFlashInfo, unsigned int address); +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void NORFLASH_Reset(struct NorFlash *norFlash, unsigned int address); + +extern unsigned int NORFLASH_ReadManufactoryID(struct NorFlash *norFlash); + +extern unsigned int NORFLASH_ReadDeviceID(struct NorFlash *norFlash); + +extern unsigned char NORFLASH_EraseSector( + struct NorFlash *norFlash, + unsigned int sectorAddr); + +extern unsigned char NORFLASH_EraseChip( + struct NorFlash *norFlash); + +extern unsigned char NORFLASH_WriteData( + struct NorFlash *norFlash, + unsigned int address, + unsigned char *buffer, + unsigned int size); + +extern unsigned char NORFLASH_ReadData( + struct NorFlash *norFlash, + unsigned int address, + unsigned char *buffer, + unsigned int size); + + +#endif //#ifndef NORFLASHAPI_H + diff --git a/memories/norflash/NorFlashCFI.c b/memories/norflash/NorFlashCFI.c new file mode 100644 index 0000000..b812073 --- /dev/null +++ b/memories/norflash/NorFlashCFI.c @@ -0,0 +1,378 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "NorFlashApi.h" +#include "NorFlashAmd.h" +#include "NorFlashIntel.h" +#include "NorFlashCommon.h" +#include +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ +#define DUMP_CFI + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Dump the Common Flash Interface Definition Table. +/// \param pNorFlashCFI Pointer to an NorFlashCFI instance. +//------------------------------------------------------------------------------ +void NorFlash_CFI_DumpConfigruation(struct NorFlashCFI *pNorFlashCFI) +{ + unsigned char i; + + TRACE_DEBUG("Common Flash Interface Definition Table\n\r"); + TRACE_DEBUG("Addr. Data Description \n\r"); + TRACE_DEBUG("0x10 %04Xh Query Unique ASCII string\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.queryUniqueString[0]); + TRACE_DEBUG("0x11 %04Xh \n\r", + pNorFlashCFI->norFlashCfiQueryInfo.queryUniqueString[1]); + TRACE_DEBUG("0x12 %04Xh \n\r", + pNorFlashCFI->norFlashCfiQueryInfo.queryUniqueString[2]); + TRACE_DEBUG("0x13 %04Xh Primary OEM Command Set\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.primaryCode); + TRACE_DEBUG("0x15 %04Xh Address for Primary Extended Table\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.primaryAddr); + TRACE_DEBUG("0x17 %04Xh Alternate OEM Command Set\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.alternateCode); + TRACE_DEBUG("0x19 %04Xh Address for Alternate OEM Extended Table\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.alternateAddr); + TRACE_DEBUG("0x1B %04Xh VCC min write/erase\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.minVcc); + TRACE_DEBUG("0x1C %04Xh VCC max write/erase\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.maxVcc); + TRACE_DEBUG("0x1D %04Xh VPP min voltage\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.minVpp); + TRACE_DEBUG("0x1E %04Xh VPP max voltage\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.maxVpp); + TRACE_DEBUG("0x1F %04Xh Typical timeout per single word write\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.minTimeOutWrite); + TRACE_DEBUG("0x20 %04Xh Typical timeout for Min. size buffer write\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.minTimeOutBuffer); + TRACE_DEBUG("0x21 %04Xh Typical timeout per individual block erase\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.minTimeOutBlockErase); + TRACE_DEBUG("0x22 %04Xh Typical timeout for full chip erase\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.minTimeOutChipErase); + TRACE_DEBUG("0x23 %04Xh Max. timeout for word write\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.maxTimeOutWrite); + TRACE_DEBUG("0x24 %04Xh Max. timeout for buffer write\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.maxTimeOutBuffer); + TRACE_DEBUG("0x25 %04Xh Max. timeout per individual block erase\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.maxTimeOutBlockErase); + TRACE_DEBUG("0x26 %04Xh Max. timeout for full chip erase\n\r", + pNorFlashCFI->norFlashCfiQueryInfo.maxTimeOutChipErase); + + TRACE_DEBUG("0x27 %04Xh Device Size = 2N byte\n\r", + pNorFlashCFI->norFlashCfiDeviceGeometry.deviceSize); + TRACE_DEBUG("0x28 %04Xh Flash Device Interface description\n\r", + pNorFlashCFI->norFlashCfiDeviceGeometry.deviceInterface); + TRACE_DEBUG("0x2A %04Xh Max. number of byte in multi-byte write\n\r", + pNorFlashCFI->norFlashCfiDeviceGeometry.numMultiWrite); + TRACE_DEBUG("0x2C %04Xh Number of Erase Block Regions within device\n\r", + pNorFlashCFI->norFlashCfiDeviceGeometry.numEraseRegion); + for(i = 0; i < pNorFlashCFI->norFlashCfiDeviceGeometry.numEraseRegion; i++) { + TRACE_DEBUG("0x%2X %04Xh Number of Erase Blocks of identical size within region %x \n\r", + 0x2D + i * 4, pNorFlashCFI->norFlashCfiDeviceGeometry.eraseRegionInfo[i].Y, i ); + TRACE_DEBUG("0x%2X %04Xh (z) times 256 bytes within region %x \n\r", + 0x2E + i * 4, pNorFlashCFI->norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z, i ); + } +} + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Returns the numbers of block in all Norflash regions. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetDeviceNumOfBlocks( + struct NorFlashInfo *pNorFlashInfo) +{ + unsigned char i; + unsigned int blocks = 0; + unsigned short numBlockRegion; + + numBlockRegion = pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.numEraseRegion; + + for (i = 0; i < numBlockRegion; i++) { + blocks += (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i]).Y + 1; + } + return blocks; +} + +//------------------------------------------------------------------------------ +/// Returns the minimun block size in all Norflash regions. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetDeviceMinBlockSize( + struct NorFlashInfo *pNorFlashInfo) +{ + unsigned char i; + unsigned short numBlockRegion; + unsigned long size ; + numBlockRegion = pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.numEraseRegion; + + size = (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[0].Z) * 256; + + for (i = 1; i < numBlockRegion; i++) { + if (size > (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z) * 256) { + size = (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z) * 256 ; + } + } + return size; +} + +//------------------------------------------------------------------------------ +/// Returns the maximun block size in all Norflash regions. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetDeviceMaxBlockSize( + struct NorFlashInfo *pNorFlashInfo) +{ + unsigned char i; + unsigned short numBlockRegion; + unsigned long size ; + numBlockRegion = pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.numEraseRegion; + + size = (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[0].Z) * 256; + + for (i = 1; i < numBlockRegion; i++) { + if (size < (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z) * 256) { + size = (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z) * 256 ; + } + } + return size; +} + +//------------------------------------------------------------------------------ +/// Returns the block size in giving block number. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +/// \param sector Sector number. +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetDeviceBlockSize( + struct NorFlashInfo *pNorFlashInfo, + unsigned int sector) +{ + unsigned short i; + unsigned short j; + unsigned short numBlockRegion,numBlockPerRegion; + unsigned int block = 0; + + numBlockRegion = pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.numEraseRegion; + for (i = 0; i < numBlockRegion; i++) { + numBlockPerRegion = (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i]).Y + 1; + for (j = 0; j < numBlockPerRegion; j++) { + if (block == sector) { + return (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z) * 256 ; + } + block++; + } + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns secort number on specified memory offset. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +/// \param memoryOffset Memory offset. +//------------------------------------------------------------------------------ +unsigned short NorFlash_GetDeviceSectorInRegion( + struct NorFlashInfo *pNorFlashInfo, + unsigned int memoryOffset) +{ + unsigned short numBlockRegion,numBlockPerRegion; + unsigned short sectorId = 0; + unsigned int size = 0; + unsigned char done = 0; + unsigned short i , j; + + numBlockRegion = pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.numEraseRegion; + + for (i = 0; i < numBlockRegion; i++) { + numBlockPerRegion = (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i]).Y + 1; + for (j = 0; j < numBlockPerRegion; j++) { + size+= (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z) * 256 ; + if(size > memoryOffset) { + done = 1; + break; + } + sectorId++; + } + if (done) break; + } + + return sectorId; +} + +//------------------------------------------------------------------------------ +/// Returns start address of specified sector number. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +/// \param sector Sector number. +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetDeviceSectorAddress( + struct NorFlashInfo *pNorFlashInfo, + unsigned int sector) +{ + unsigned short numBlockRegion,numBlockPerRegion; + unsigned short sectorId = 0; + unsigned int address = 0; + unsigned char done = 0; + unsigned short i , j; + + numBlockRegion = pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.numEraseRegion; + for (i = 0; i < numBlockRegion; i++) { + numBlockPerRegion = (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i]).Y + 1; + for (j = 0; j < numBlockPerRegion; j++) { + if (sector == sectorId) { + done = 1; + break; + } + address+= (pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.eraseRegionInfo[i].Z) * 256 ; + sectorId++; + } + if (done) break; + } + + return address; +} + +//------------------------------------------------------------------------------ +/// Convert address to byte addressing. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +/// \Param offset Address offset +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetByteAddress( + struct NorFlashInfo *pNorFlashInfo, unsigned int offset) +{ + return (offset * pNorFlashInfo-> deviceChipWidth); +} + +//------------------------------------------------------------------------------ +/// Convert address to byte addressing and return the address in chip. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +/// \Param offset Address offset +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetByteAddressInChip( + struct NorFlashInfo *pNorFlashInfo, unsigned int offset) +{ + return (pNorFlashInfo->baseAddress + (offset * pNorFlashInfo-> deviceChipWidth)); +} + + +//------------------------------------------------------------------------------ +/// Returns the address in chip. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +/// \Param offset Address offset +//------------------------------------------------------------------------------ +unsigned int NorFlash_GetAddressInChip(struct NorFlashInfo *pNorFlashInfo, unsigned int offset) +{ + return (pNorFlashInfo->baseAddress + offset); +} + +//------------------------------------------------------------------------------ +/// Returns bus width in bits of giving device. +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned char NorFlash_GetDataBusWidth( struct NorFlashInfo *pNorFlashInfo) +{ + return (pNorFlashInfo->deviceChipWidth * 8); +} + +//------------------------------------------------------------------------------ +/// Returns the size of the whole device in bytes +/// \param pNorFlashInfo Pointer to a NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned long NorFlash_GetDeviceSizeInBytes( + struct NorFlashInfo *pNorFlashInfo) +{ + return ((unsigned long) 2 << ((pNorFlashInfo->cfiDescription.norFlashCfiDeviceGeometry.deviceSize) - 1)); +} + +//------------------------------------------------------------------------------ +/// Looks for query struct in Norflash common flash interface. +/// If found, the model variable is filled with the correct values. +/// This function returns 0 if a matching model has been found; otherwise it +/// returns NorCommon_ERROR_UNKNOWNMODEL. +//------------------------------------------------------------------------------ +unsigned char NorFlash_CFI_Detect( + struct NorFlash *pNorFlash, + unsigned char hardwareBusWidth) +{ + + unsigned char i; + unsigned char *pCfi = (unsigned char*)(&(pNorFlash->norFlashInfo.cfiDescription)); + unsigned int address; + + pNorFlash->norFlashInfo.cfiCompatible = 0; + pNorFlash->norFlashInfo.deviceChipWidth = hardwareBusWidth; + address = CFI_QUERY_OFFSET; + for(i = 0; i< sizeof(struct NorFlashInfo) ; i++){ + WriteCommand(8, NorFlash_GetByteAddressInChip(&(pNorFlash->norFlashInfo), CFI_QUERY_ADDRESS), CFI_QUERY_COMMAND); + ReadRawData(8, NorFlash_GetByteAddressInChip(&(pNorFlash->norFlashInfo), address), pCfi); + address++; + pCfi++; + + } + // Check the query-unique ASCII string "QRY" + if( (pNorFlash->norFlashInfo.cfiDescription.norFlashCfiQueryInfo.queryUniqueString[0] != 'Q' ) + || (pNorFlash->norFlashInfo.cfiDescription.norFlashCfiQueryInfo.queryUniqueString[1] != 'R') + || (pNorFlash->norFlashInfo.cfiDescription.norFlashCfiQueryInfo.queryUniqueString[2] != 'Y') ) { + return NorCommon_ERROR_UNKNOWNMODEL; + } + +#ifdef DUMP_CFI + NorFlash_CFI_DumpConfigruation(&(pNorFlash->norFlashInfo.cfiDescription)); +#endif + + if (pNorFlash->norFlashInfo.cfiDescription.norFlashCfiQueryInfo.primaryCode == CMD_SET_AMD) { + pNorFlash->pOperations = &amdOperations; + } + else if ((pNorFlash->norFlashInfo.cfiDescription.norFlashCfiQueryInfo.primaryCode == CMD_SET_INTEL_EXT) + || (pNorFlash->norFlashInfo.cfiDescription.norFlashCfiQueryInfo.primaryCode == CMD_SET_INTEL)) { + pNorFlash->pOperations = &intelOperations; + } + else { + return NorCommon_ERROR_UNKNOWNMODEL; + } + + pNorFlash->norFlashInfo.cfiCompatible = 1; + NORFLASH_Reset(pNorFlash, 0); + return 0; +} + diff --git a/memories/norflash/NorFlashCFI.h b/memories/norflash/NorFlashCFI.h new file mode 100644 index 0000000..217608b --- /dev/null +++ b/memories/norflash/NorFlashCFI.h @@ -0,0 +1,230 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef NORFLASHCFI_H +#define NORFLASHCFI_H + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +#define CFI_MAX_ERASE_REGION 4 + +/// Common flash interface query command. +#define CFI_QUERY_COMMAND 0x98 +#define CFI_QUERY_ADDRESS 0x55 +#define CFI_QUERY_OFFSET 0x10 + +//------------------------------------------------------------------------------ +// Exported defination +//------------------------------------------------------------------------------ +/// Vendor command set control interface ID code . +#define CMD_SET_NULL 0x0000 +#define CMD_SET_INTEL_EXT 0x0001 +#define CMD_SET_AMD 0x0002 +#define CMD_SET_INTEL 0x0003 +#define CMD_SET_AMD_EXT 0x0004 +#define CMD_SET_MISUBISHI 0x0100 +#define CMD_SET_MISUBISHI_EXT 0x0101 +#define CMD_SET_SST 0x0102 + + +/// Indicates the maximum region for norflash device. +#define NORFLASH_MAXNUMRIGONS 4 +/// Indicates the NorFlash uses an 8-bit address bus. +#define FLASH_CHIP_WIDTH_8BITS 0x01 +/// Indicates the NorFlash uses an 16-bit address bus. +#define FLASH_CHIP_WIDTH_16BITS 0x02 +/// Indicates the NorFlash uses an 32-bit address bus. +#define FLASH_CHIP_WIDTH_32BITS 0x04 +/// Indicates the NorFlash uses an 64-bit address bus. +#define FLASH_CHIP_WIDTH_64BITS 0x08 + +//------------------------------------------------------------------------------ +// Local Type +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Describes a Norflash CFI query system interface information. +//------------------------------------------------------------------------------ +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +struct NorFlashCfiQueryInfo { + + /// Query Unique String "QRY". + unsigned char queryUniqueString[3]; + /// Primary vendor command set and control interface ID . + unsigned short primaryCode; + /// Address for primary extended query table. + unsigned short primaryAddr; + /// Alternate vendor command set and control interface ID . + unsigned short alternateCode; + /// Address for alternate extended query table. + unsigned short alternateAddr; + /// Vcc logic supply minimum write/erase voltage. + unsigned char minVcc; + /// Vcc logic supply maximum write/erase voltage. + unsigned char maxVcc; + /// Vpp logic supply minimum write/erase voltage. + unsigned char minVpp; + /// Vpp logic supply maximum write/erase voltage. + unsigned char maxVpp; + /// Timeout per single write (2< +#include + +//------------------------------------------------------------------------------ +// Internal macros +//------------------------------------------------------------------------------ + +#define WRITE_COMMAND8(commandAddress, command) \ + {*(volatile unsigned char *)(commandAddress) = (unsigned char) command;} +#define WRITE_COMMAND16(commandAddress, command) \ + {*(volatile unsigned short *)(commandAddress) = (unsigned short) command;} +#define WRITE_COMMAND32(commandAddress, command) \ + {*(volatile unsigned int *)(commandAddress) = (unsigned int) command;} + +#define WRITE_DATA8(dataAddress, data) \ + {(*((volatile unsigned char *) dataAddress)) = (unsigned char) data;} +#define WRITE_DATA16(dataAddress, data) \ + {(*((volatile unsigned short *) dataAddress)) = (unsigned short) data;} +#define WRITE_DATA32(dataAddress, data) \ + {(*((volatile unsigned int *) dataAddress)) = (unsigned int) data;} + +#define READ_DATA8(dataAddress) \ + (*((volatile unsigned char *) dataAddress)) +#define READ_DATA16(dataAddress) \ + (*((volatile unsigned short *) dataAddress)) +#define READ_DATA32(dataAddress) \ + (*((volatile unsigned int *) dataAddress)) + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Write a command to address. +/// \param busWidth Bus width in device. +/// \param commandAddress Command address offset. +/// \param command Command to be send. +//------------------------------------------------------------------------------ +unsigned char WriteCommand( + unsigned char busWidth, + unsigned int commandAddress, + unsigned int command) +{ + if (busWidth == 8 ){ + WRITE_COMMAND8(commandAddress, command); + } + else if( busWidth == 16 ){ + WRITE_COMMAND16(commandAddress, command); + } + else if(busWidth == 32 ){ + WRITE_COMMAND32(commandAddress, command); + } + return 0; +} + + +//------------------------------------------------------------------------------ +/// Reads data from the NorFlash chip into the provided buffer. +/// \param busWidth Bus width in device. +/// \param address Address of data. +/// \param buffer Buffer where the data will be stored. +//------------------------------------------------------------------------------ +void ReadRawData( + unsigned char busWidth, + unsigned int address, + unsigned char *buffer) +{ + if (busWidth == 8 ){ + *buffer = READ_DATA8(address); + } + else if( busWidth == 16 ){ + + unsigned short *buffer16 = (unsigned short *) buffer; + *buffer16 = READ_DATA16(address); + } + else if(busWidth == 32 ){ + unsigned int *buffer32 = (unsigned int *) buffer; + *buffer32 = READ_DATA32(address); + } +} + +//------------------------------------------------------------------------------ +/// Writes data to the NorFlash chip from the provided buffer. +/// \param busWidth Bus width in device. +/// \param address Address of data. +/// \param buffer Buffer where the data will be stored. +//------------------------------------------------------------------------------ + +void WriteRawData( + unsigned char busWidth, + unsigned int address, + unsigned char *buffer) + +{ + if (busWidth == 8 ){ + WRITE_DATA8(address, *buffer); + } + else if( busWidth == 16 ){ + unsigned short *buffer16 = (unsigned short *) buffer; + WRITE_DATA16(address, *buffer16); + } + else if(busWidth == 32 ){ + unsigned int *buffer32 = (unsigned int *) buffer; + WRITE_DATA32(address, *buffer32); + } +} + diff --git a/memories/norflash/NorFlashCommon.h b/memories/norflash/NorFlashCommon.h new file mode 100644 index 0000000..deee05e --- /dev/null +++ b/memories/norflash/NorFlashCommon.h @@ -0,0 +1,101 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !Purpose +/// +/// The Hardware Adaptation Layer manages the hardware functions of the %NORFLASH +/// %Flash. It implements a low level driver to manage the hardware functionality +/// of %NORFLASH. +/// +/// !Usage +/// +/// -# WriteRawData() is used to write data to the %NorFlash device. +/// -# ReadRawData() is used to read data from the %NorFlash device. +/// -# The specified address for read/write opertion is an linear byte +/// address of targer application byte address space represented by +/// WORD((8-bit, 16-bit, 32-bit). The start (base) address of the +/// flash memory in this address space is defined in board.h file +/// as BOARD_NORFLASH_ADDR and users might need to change it. +/// -# WriteCommand() is used to write a command to specified command +/// address. +/// +/// ! NorFlash Error Codes +/// +/// It provides detailed error codes to describe the various errors +/// that may occur during the operation. Some functions return an error +/// number directly as the function value. These functions return a value +/// of zero (NorCommon_ERROR_NONE) to indicate a success. +//------------------------------------------------------------------------------ + + +#ifndef NORFLASHCOMMON_H +#define NORFLASHCOMMON_H + +//------------------------------------------------------------------------------ +// Defination +//------------------------------------------------------------------------------ +/// The function completed successfully. +#define NorCommon_ERROR_NONE 0 + +/// Can not detect common flash infterface. +#define NorCommon_ERROR_UNKNOWNMODEL 1 + +/// A read operation cannot be carried out. +#define NorCommon_ERROR_CANNOTREAD 2 + +/// A write operation cannot be carried out. +#define NorCommon_ERROR_CANNOTWRITE 3 + +/// A erase operation cannot be carried out. +#define NorCommon_ERROR_CANNOTERASE 4 + +/// A locked operation cannot be carried out. +#define NorCommon_ERROR_PROTECT 5 + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +extern unsigned char WriteCommand( + unsigned char portWidth, + unsigned int commandAddress, + unsigned int command); + +extern void ReadRawData( + unsigned char portWidth, + unsigned int dataAddress, + unsigned char *buffer); + +extern void WriteRawData( + unsigned char portWidth, + unsigned int dataAddress, + unsigned char *buffer); + +#endif //#ifndef NORFLASHCOMMON_H diff --git a/memories/norflash/NorFlashIntel.c b/memories/norflash/NorFlashIntel.c new file mode 100644 index 0000000..15c1ea3 --- /dev/null +++ b/memories/norflash/NorFlashIntel.c @@ -0,0 +1,416 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ +#include "NorFlashCFI.h" +#include "NorFlashCommon.h" +#include +#include + +//------------------------------------------------------------------------------ +// Local defination +//------------------------------------------------------------------------------ + +/// Command for vendor command set CMD_SET_INTEL. Device commands are written +/// to the Command User Interface (CUI) to control all flash memory device operations. +#define INTEL_CMD_IDIN 0x0090 +#define INTEL_CMD_BLOCK_ERASE_1 0x0020 +#define INTEL_CMD_BLOCK_ERASE_2 0x00D0 +#define INTEL_CMD_READ_STATUS 0x0070 +#define INTEL_CMD_CLEAR_STATUS 0x0050 +#define INTEL_CMD_BLOCK_LOCKSTART 0x0060 +#define INTEL_CMD_BLOCK_LOCK 0x0001 +#define INTEL_CMD_BLOCK_UNLOCK 0x00D0 +#define INTEL_CMD_BLOCK_LOCKDOWN 0x002F +#define INTEL_CMD_PROGRAM_WORD 0x0010 +#define INTEL_CMD_RESET 0x00FF + + +/// Intel norflash status resgister +#define INTEL_STATUS_DWS 0x80 +#define INTEL_STATUS_ESS 0x40 +#define INTEL_STATUS_ES 0x20 +#define INTEL_STATUS_PS 0x10 +#define INTEL_STATUS_VPPS 0x08 +#define INTEL_STATUS_PSS 0x04 +#define INTEL_STATUS_BLS 0x02 +#define INTEL_STATUS_BWS 0x01 + +/// Intel norflash device Identifier infomation address offset. +#define INTEL_MANU_ID 0x00 +#define INTEL_DEVIDE_ID 0x01 +#define INTEL_LOCKSTATUS 0x02 + +/// Intel norflash device lock status. +#define INTEL_LOCKSTATUS_LOCKED 0x01 +#define INTEL_LOCKSTATUS_LOCKDOWNED 0x02 + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// It implements a RESET command. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +//------------------------------------------------------------------------------ +void intel_Reset(struct NorFlashInfo *pNorFlashInfo, unsigned int address) +{ + unsigned int busAddress; + unsigned int busWidth; + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + busAddress = NorFlash_GetAddressInChip(pNorFlashInfo, address); + WriteCommand(busWidth, busAddress, INTEL_CMD_RESET); +} + + +//------------------------------------------------------------------------------ +/// The Read Device Identifier command instructs the device to output manufacturer +/// code, device identifier code, block-lock status, protection register data, +/// or configuration register data by giving offset. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +/// \param offset 0: Identifier address offset. +//------------------------------------------------------------------------------ +unsigned int intel_ReadIdentification( + struct NorFlashInfo *pNorFlashInfo, + unsigned int offset) +{ + unsigned int data; + unsigned char busWidth; + unsigned int address; + + // Issue Read Array Command - just in case that the flash is not in Read Array mode + intel_Reset(pNorFlashInfo, 0); + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + address = NorFlash_GetAddressInChip(pNorFlashInfo, offset); + // Issue the Read Device Identifier command at specified address. + WriteCommand(busWidth, address, INTEL_CMD_IDIN); + ReadRawData(busWidth, address, (unsigned char*)&data); + + intel_Reset(pNorFlashInfo, 0); + return data; +} + + +//------------------------------------------------------------------------------ +/// Return the status register value. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned char intel_ReadStatus(struct NorFlashInfo *pNorFlashInfo, unsigned int address) +{ + unsigned int status; + unsigned char busWidth; + unsigned int budAddress; + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + + // Issue the Read Status Register command at any address. + budAddress = NorFlash_GetAddressInChip(pNorFlashInfo, address), + WriteCommand(busWidth, budAddress, INTEL_CMD_READ_STATUS); + ReadRawData(busWidth, budAddress, (unsigned char*)&status); + return status; +} + +//------------------------------------------------------------------------------ +/// Clear the status register. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +//------------------------------------------------------------------------------ +void intel_ClearStatus(struct NorFlashInfo *pNorFlashInfo) +{ + unsigned char busWidth; + unsigned int address; + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + + // Issue the Clear Status Register command at any address + address = NorFlash_GetAddressInChip(pNorFlashInfo, 0), + WriteCommand(busWidth, address, INTEL_CMD_CLEAR_STATUS); +} + +//------------------------------------------------------------------------------ +/// Unlocks the specified block of the device. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +/// \param address Address in sector. +//------------------------------------------------------------------------------ +void intel_UnlockSector(struct NorFlashInfo *pNorFlashInfo, unsigned int address) +{ + unsigned int busAddress; + unsigned char busWidth; + // Issue Read Array Command - just in case that the flash is not in Read Array mode + intel_Reset(pNorFlashInfo, 0); + // Clear the status register first. + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + busAddress = NorFlash_GetAddressInChip(pNorFlashInfo,address); + + WriteCommand(busWidth, busAddress, INTEL_CMD_BLOCK_LOCKSTART); + WriteCommand(busWidth, busAddress, INTEL_CMD_BLOCK_UNLOCK); + intel_Reset(pNorFlashInfo, 0); +} + +//------------------------------------------------------------------------------ +/// The Read Device Identifier command instructs the device to output block-lock +/// status. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +/// \param address 0: Address in sector/block. +//------------------------------------------------------------------------------ +unsigned int intel_GetBlockLockStatus(struct NorFlashInfo *pNorFlashInfo, unsigned int address) +{ + return intel_ReadIdentification(pNorFlashInfo, (address + NorFlash_GetByteAddress(pNorFlashInfo ,INTEL_LOCKSTATUS))); +} + +//------------------------------------------------------------------------------ +/// It implement a program word command. Returns 0 if the operation was +/// successful; otherwise returns an error code. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +/// \param address Start address offset to be wrote. +/// \param data word to be written. +//------------------------------------------------------------------------------ +unsigned char intel_Program( + struct NorFlashInfo *pNorFlashInfo, + unsigned int address, + unsigned int data + ) +{ + unsigned int status; + unsigned int datain; + volatile unsigned int busAddress; + unsigned char done = 0; + unsigned char busWidth; + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + // Issue Read Array Command - just in case that the flash is not in Read Array mode + intel_Reset(pNorFlashInfo, address); + + busAddress = NorFlash_GetAddressInChip(pNorFlashInfo, address); + /* + // Check if the data already have been erased. + ReadRawData(busWidth, busAddress, (unsigned char*)&datain); + if((datain & data)!= data) { + return NorCommon_ERROR_CANNOTWRITE; + } + */ + // Word programming operations are initiated by writing the Word Program Setup command to the device. + WriteCommand(busWidth, busAddress, INTEL_CMD_PROGRAM_WORD); + // This is followed by a second write to the device with the address and data to be programmed. + WriteRawData(busWidth, busAddress, (unsigned char*)&data); + + // Status register polling + do { + status = intel_ReadStatus(pNorFlashInfo,address); + // Check if the device is ready. + if ((status & INTEL_STATUS_DWS) == INTEL_STATUS_DWS ) { + // check if VPP within acceptable limits during program or erase operation. + if ((status & INTEL_STATUS_VPPS) == INTEL_STATUS_VPPS ) { + return NorCommon_ERROR_CANNOTWRITE; + } + // Check if the erase block operation is completed. + if ((status & INTEL_STATUS_PS) == INTEL_STATUS_PS ) { + return NorCommon_ERROR_CANNOTWRITE; + } + // check if Block locked during program or erase, operation aborted. + else if ((status & INTEL_STATUS_BLS) == INTEL_STATUS_BLS ) { + return NorCommon_ERROR_CANNOTWRITE; + } + else { + done = 1; + } + } + } while (!done); + + intel_ClearStatus(pNorFlashInfo); + intel_Reset(pNorFlashInfo, address); + return 0; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// It implements a RESET command. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +//------------------------------------------------------------------------------ +void INTEL_Reset(struct NorFlashInfo *pNorFlashInfo, unsigned int address) +{ + intel_Reset(pNorFlashInfo, address); +} + +//------------------------------------------------------------------------------ +/// The Read Device Identifier command instructs the device to output manufacturer +/// code. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned int INTEL_ReadManufactoryId(struct NorFlashInfo *pNorFlashInfo) +{ + return intel_ReadIdentification(pNorFlashInfo, INTEL_MANU_ID); +} + +//------------------------------------------------------------------------------ +/// The Read Device Identifier command instructs the device to output device id. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +//------------------------------------------------------------------------------ +unsigned int INTEL_ReadDeviceID(struct NorFlashInfo *pNorFlashInfo) +{ + return intel_ReadIdentification(pNorFlashInfo, INTEL_DEVIDE_ID); +} + +//------------------------------------------------------------------------------ +/// Erases the specified block of the device. Returns 0 if the operation was +/// successful; otherwise returns an error code. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +/// \param address Address offset to be erase. +//------------------------------------------------------------------------------ +unsigned char INTEL_EraseSector( + struct NorFlashInfo *pNorFlashInfo, + unsigned int address) +{ + unsigned int status; + unsigned int busAddress; + unsigned char busWidth; + unsigned char done = 0; + + busWidth = NorFlash_GetDataBusWidth(pNorFlashInfo); + // Issue Read Array Command - just in case that the flash is not in Read Array mode + intel_Reset(pNorFlashInfo, address); + + // Check the lock status is locked. + status = intel_GetBlockLockStatus(pNorFlashInfo, address); + if(( status & INTEL_LOCKSTATUS_LOCKED ) == INTEL_LOCKSTATUS_LOCKED){ + intel_UnlockSector(pNorFlashInfo, address); + } + // Clear the status register first. + intel_ClearStatus(pNorFlashInfo); + busAddress = NorFlash_GetAddressInChip(pNorFlashInfo,address); + // Block erase operations are initiated by writing the Block Erase Setup command to the address of the block to be erased. + WriteCommand(busWidth, busAddress, INTEL_CMD_BLOCK_ERASE_1); + // Next, the Block Erase Confirm command is written to the address of the block to be erased. + WriteCommand(busWidth, busAddress, INTEL_CMD_BLOCK_ERASE_2); + // Status register polling + do { + status = intel_ReadStatus(pNorFlashInfo,address); + // Check if the device is ready. + if ((status & INTEL_STATUS_DWS) == INTEL_STATUS_DWS ) { + // check if VPP within acceptable limits during program or erase operation. + if ((status & INTEL_STATUS_VPPS) == INTEL_STATUS_VPPS ) { + intel_Reset(pNorFlashInfo, 0); + return NorCommon_ERROR_CANNOTWRITE; + } + // Check if the erase block operation is completed. + if ((status & INTEL_STATUS_PS) == INTEL_STATUS_PS ) { + intel_Reset(pNorFlashInfo, 0); + return NorCommon_ERROR_CANNOTWRITE; + } + // Check if the erase block operation is completed. + if ((status & INTEL_STATUS_ES) == INTEL_STATUS_ES ) { + intel_Reset(pNorFlashInfo, 0); + return NorCommon_ERROR_CANNOTWRITE; + } + + // check if Block locked during program or erase, operation aborted. + else if ((status & INTEL_STATUS_BLS) == INTEL_STATUS_BLS ) { + intel_Reset(pNorFlashInfo, 0); + return NorCommon_ERROR_CANNOTWRITE; + } + else { + done = 1; + } + } + } while (!done); + intel_Reset(pNorFlashInfo, address); + return 0; +} + +//------------------------------------------------------------------------------ +/// Erases all the block of the device. Returns 0 if the operation was successful; +/// otherwise returns an error code. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +//------------------------------------------------------------------------------ + +unsigned char INTEL_EraseChip(struct NorFlashInfo *pNorFlashInfo) +{ + // Interl flash have no independent Chip-erase command. + unsigned int i; + unsigned int sectors; + sectors = NorFlash_GetDeviceNumOfBlocks(pNorFlashInfo); + for (i = 0; i < sectors; i++) { + if (INTEL_EraseSector(pNorFlashInfo, NorFlash_GetDeviceSectorAddress(pNorFlashInfo, i))) { + return NorCommon_ERROR_CANNOTERASE; + } + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Sends data to the struct NorFlashInfo chip from the provided buffer. +/// \param pNorFlashInfo Pointer to an struct NorFlashInfo instance. +/// \param address Start address offset to be wrote. +/// \param buffer Buffer where the data is stored. +/// \param size Number of bytes that will be written. +//------------------------------------------------------------------------------ +unsigned char INTEL_Write_Data( + struct NorFlashInfo *pNorFlashInfo, + unsigned int address, + unsigned char *buffer, + unsigned int size) +{ + unsigned int i; + unsigned char busWidth; + + busWidth = pNorFlashInfo->deviceChipWidth; + if (busWidth == FLASH_CHIP_WIDTH_8BITS ){ + for(i=0; i < size; i++) { + if(intel_Program(pNorFlashInfo, address, buffer[i])) { + return NorCommon_ERROR_CANNOTWRITE; + } + address ++; + } + } + else if( busWidth == FLASH_CHIP_WIDTH_16BITS ){ + unsigned short *buffer16 = (unsigned short *) buffer; + size = (size + 1) >> 1; + for(i=0; i < size; i++) { + + if(intel_Program(pNorFlashInfo, address, buffer16[i])){ + return NorCommon_ERROR_CANNOTWRITE; + } + address+= 2; + } + } + else if(busWidth == FLASH_CHIP_WIDTH_32BITS ){ + unsigned int *buffer32 = (unsigned int *) buffer; + size = (size + 3) >> 2; + for(i=0; i < size; i++) { + if(intel_Program(pNorFlashInfo, address, buffer32[i])){ + return NorCommon_ERROR_CANNOTWRITE; + } + address+= 4; + } + } + return 0; +} diff --git a/memories/norflash/NorFlashIntel.h b/memories/norflash/NorFlashIntel.h new file mode 100644 index 0000000..8934015 --- /dev/null +++ b/memories/norflash/NorFlashIntel.h @@ -0,0 +1,99 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// The INTERL %norflash Low-level driver code implement procedures to program +/// basic operations described INTEL-specified command set flash devices. +/// The various commands recognized by the devices are listed in the Commands +/// Tables provided in the corresponding INTEL command set compatible flash +/// datasheets. All operation functions are blocked, they wait for the +/// completion of an operation by polling the status register. +/// +/// !!!Usage +/// -# Flash program using INTEL_Write_Data(). +/// - The Program command is used to modify the data stored at the +/// specified device address. Programming can only change bits +/// from ¡®1¡¯ to ¡®0¡¯. It may be necessary to erase the block before +/// programming to addresses within it. Programming modifies a single +/// Word at a time using static function intel_Program(). Programming +/// larger amounts of data must be done in one Word at a time by +/// giving a Program command, waiting for the command to complete, +/// giving the next Program command and so on. +/// -# erase a block within the flash using INTEL_EraseSector(). +/// - Flash erase is performed on a block basis. An entire block is +/// erased each time an erase command sequence is given. +/// -# erase whole blocks within the flash using INTEL_EraseChip(). +/// -# INTEL_Reset() function can be issued, between Bus Write cycles +/// before the start of a program or erase operation, to return the +/// device to read mode. +/// -# INTEL_ReadDeviceID() is used to retrieve information +/// about the Flash Device type. +/// -# INTEL_ReadManufactoryId() is used to retrieve information +/// about the Flash Device Manufactory ID. +//------------------------------------------------------------------------------ +#ifndef NORFLASHINTEL_H +#define NORFLASHINTEL_H + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +void INTEL_Reset(struct NorFlashInfo *pNorFlashInfo, unsigned int address); + +unsigned int INTEL_ReadManufactoryId(struct NorFlashInfo *NorFlashInfo); + +unsigned int INTEL_ReadDeviceID(struct NorFlashInfo *pNorFlashInfo); + +unsigned char INTEL_EraseSector( + struct NorFlashInfo *NorFlashInfo, + unsigned int sectorAddr); + +unsigned char INTEL_EraseChip(struct NorFlashInfo *NorFlashInfo); + +unsigned char INTEL_Write_Data( + struct NorFlashInfo *NorFlashInfo, + unsigned int address, + unsigned char *buffer, + unsigned int size); + +const struct NorFlashOperations intelOperations = { + INTEL_Reset, + INTEL_Write_Data, + INTEL_ReadManufactoryId, + INTEL_ReadDeviceID, + INTEL_EraseChip, + INTEL_EraseSector + +}; + +#endif //#ifndef NORFLASHINTEL_H + diff --git a/memories/norflash/norflash.dir b/memories/norflash/norflash.dir new file mode 100644 index 0000000..efb54cd --- /dev/null +++ b/memories/norflash/norflash.dir @@ -0,0 +1,66 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// !!!Purpose +/// +/// This directory provides a library of re-usable code to build %Norflash +/// applications with Atmel AT91 microcontrollers. +/// +/// !!!Contents +/// The %norflash driver interface is implemented to support four layers of +/// abstractions such CFI level, API level, low-level flash device driver and +/// Hardware-specific bus operations. Users can focus on writing the high-level +/// code required for their particular applications. +/// +/// - CFI interface : Common Flash Memory Interface(CFI) allow in-system or +/// programmer reading of flash device characteristics. It +/// provides basic fuction NorFlash_CFI_Detect() to detect %flash +/// chip by CFI, it also includes some general functions to get +/// %flash device informations like memory size, byte and word +/// configuration, block configurations and so on. +/// - API layer: API layer consists of several functions that allow user to +/// do operations with flash in a unified way. As a result, +/// future device changes will not necessarily lead to the code +/// changes in the application environments. In this %norflash +/// library we support AMD and INTEL command set. +/// - Low-level driver: It implement procedures to program basic operations +/// described in the datasheets for flash devices. All low-level +/// functions invkes the related supported program algorithm +/// (AMD/Inter) which is auto detect by CFI before. The low-level +/// code takes care of issuing the correct sequences of write/read +/// operations for each command. +/// - Hardware-specific bus operations: The low-level code requires hardware- +/// specific read/write Bus operations to communicate with the +/// %norflash devices. The implementation of these operations is +/// hardware-platform dependent as it depends on the microprocessor +/// on the location of the memory in the microprocessor's address +/// space. +//------------------------------------------------------------------------------ diff --git a/memories/sdmmc/sdmmc.dir b/memories/sdmmc/sdmmc.dir new file mode 100644 index 0000000..177fe80 --- /dev/null +++ b/memories/sdmmc/sdmmc.dir @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory contains sdmmc drivers +//------------------------------------------------------------------------------ \ No newline at end of file diff --git a/memories/sdmmc/sdmmc_mci.c b/memories/sdmmc/sdmmc_mci.c new file mode 100644 index 0000000..3202945 --- /dev/null +++ b/memories/sdmmc/sdmmc_mci.c @@ -0,0 +1,4048 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "sdmmc_mci.h" + +#include +#include + +#include + +#include + +//------------------------------------------------------------------------------ +// Global variables +//------------------------------------------------------------------------------ + +#if defined(MCI2_INTERFACE) +unsigned char gSdmmcAutoHsEnable = 1; +#else +unsigned char gSdmmcAutoHsEnable = 0; +#endif + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +//#define SINGLE_READ +//#define SINGLE_WRITE + +// Timeout wait loop +#define TO_LOOP 0x10000 + +// SD card operation states +#define SD_STATE_IDLE 0 +#define SD_STATE_INIT 1 +#define SD_STATE_READY 2 +#define SD_STATE_READ 0x10 +#define SD_STATE_RD_RDY 0x11 +#define SD_STATE_RD_BSY 0x12 +#define SD_STATE_WRITE 0x20 +#define SD_STATE_WR_RDY 0x21 +#define SD_STATE_WR_BSY 0x22 +#define SD_STATE_BOOT 0x30 + +// Delay between sending MMC commands +#define MMC_DELAY 0x4FF + +#define SD_ADDRESS(pSd, address) \ + ( ((pSd)->totalSize == 0xFFFFFFFF) ? \ + (address):((address) << SD_BLOCK_SIZE_BIT) ) + +//----------------------------------------------------------------------------- +/// MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS +/// R1 is the low order byte; R2 is the next highest byte, when present. +//----------------------------------------------------------------------------- +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +// R1 bit 7 is always zero +#define R2_SPI_CARD_LOCKED (1 << 0) +#define R2_SPI_WP_ERASE_SKIP (1 << 1) +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 2) +#define R2_SPI_CC_ERROR (1 << 3) +#define R2_SPI_CARD_ECC_ERROR (1 << 4) +#define R2_SPI_WP_VIOLATION (1 << 5) +#define R2_SPI_ERASE_PARAM (1 << 6) +#define R2_SPI_OUT_OF_RANGE (1 << 7) +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + +// Status register constants +#define STATUS_APP_CMD (1UL << 5) +#define STATUS_SWITCH_ERROR (1UL << 7) +#define STATUS_READY_FOR_DATA (1UL << 8) +#define STATUS_IDLE (0UL << 9) +#define STATUS_READY (1UL << 9) +#define STATUS_IDENT (2UL << 9) +#define STATUS_STBY (3UL << 9) +#define STATUS_TRAN (4UL << 9) +#define STATUS_DATA (5UL << 9) +#define STATUS_RCV (6UL << 9) +#define STATUS_PRG (7UL << 9) +#define STATUS_DIS (8UL << 9) +#define STATUS_STATE (0xFUL << 9) +#define STATUS_ERASE_RESET (1UL << 13) +#define STATUS_WP_ERASE_SKIP (1UL << 15) +#define STATUS_CIDCSD_OVERWRITE (1UL << 16) +#define STATUS_OVERRUN (1UL << 17) +#define STATUS_UNERRUN (1UL << 18) +#define STATUS_ERROR (1UL << 19) +#define STATUS_CC_ERROR (1UL << 20) +#define STATUS_CARD_ECC_FAILED (1UL << 21) +#define STATUS_ILLEGAL_COMMAND (1UL << 22) +#define STATUS_COM_CRC_ERROR (1UL << 23) +#define STATUS_UN_LOCK_FAILED (1UL << 24) +#define STATUS_CARD_IS_LOCKED (1UL << 25) +#define STATUS_WP_VIOLATION (1UL << 26) +#define STATUS_ERASE_PARAM (1UL << 27) +#define STATUS_ERASE_SEQ_ERROR (1UL << 28) +#define STATUS_BLOCK_LEN_ERROR (1UL << 29) +#define STATUS_ADDRESS_MISALIGN (1UL << 30) +#define STATUS_ADDR_OUT_OR_RANGE (1UL << 31) + +#define STATUS_STOP ( STATUS_CARD_IS_LOCKED \ + | STATUS_COM_CRC_ERROR \ + | STATUS_ILLEGAL_COMMAND \ + | STATUS_CC_ERROR \ + | STATUS_ERROR \ + | STATUS_STATE \ + | STATUS_READY_FOR_DATA ) + +#define STATUS_WRITE ( STATUS_ADDR_OUT_OR_RANGE \ + | STATUS_ADDRESS_MISALIGN \ + | STATUS_BLOCK_LEN_ERROR \ + | STATUS_WP_VIOLATION \ + | STATUS_CARD_IS_LOCKED \ + | STATUS_COM_CRC_ERROR \ + | STATUS_ILLEGAL_COMMAND \ + | STATUS_CC_ERROR \ + | STATUS_ERROR \ + | STATUS_ERASE_RESET \ + | STATUS_STATE \ + | STATUS_READY_FOR_DATA ) + +#define STATUS_READ ( STATUS_ADDR_OUT_OR_RANGE \ + | STATUS_ADDRESS_MISALIGN \ + | STATUS_BLOCK_LEN_ERROR \ + | STATUS_CARD_IS_LOCKED \ + | STATUS_COM_CRC_ERROR \ + | STATUS_ILLEGAL_COMMAND \ + | STATUS_CARD_ECC_FAILED \ + | STATUS_CC_ERROR \ + | STATUS_ERROR \ + | STATUS_ERASE_RESET \ + | STATUS_STATE \ + | STATUS_READY_FOR_DATA ) + +#define STATUS_SD_SWITCH ( STATUS_ADDR_OUT_OR_RANGE \ + | STATUS_CARD_IS_LOCKED \ + | STATUS_COM_CRC_ERROR \ + | STATUS_ILLEGAL_COMMAND \ + | STATUS_CARD_ECC_FAILED \ + | STATUS_CC_ERROR \ + | STATUS_ERROR \ + | STATUS_UNERRUN \ + | STATUS_OVERRUN \ + | STATUS_STATE) + +#define STATUS_MMC_SWITCH ( STATUS_CARD_IS_LOCKED \ + | STATUS_COM_CRC_ERROR \ + | STATUS_ILLEGAL_COMMAND \ + | STATUS_CC_ERROR \ + | STATUS_ERROR \ + | STATUS_ERASE_RESET \ + | STATUS_STATE \ + | STATUS_READY_FOR_DATA \ + | STATUS_SWITCH_ERROR ) + +// | (0x3UL << 12) /* IO_CURRENT_STATE */ +#define STATUS_SDIO_CMD52 ( (1UL << 15) /* COM_CRC_ERROR */ \ + | (1UL << 14) /* ILLEGAL_COMMAND */ \ + | (1UL << 11) /* ERRIR */ \ + | (1UL << 9) /* FUNCTION_NUMBER */ \ + | (1UL << 8) /* OUT_OF_RANGE */) + +//----------------------------------------------------------------------------- +/// OCR Register +//----------------------------------------------------------------------------- +#define AT91C_VDD_16_17 (1UL << 4) +#define AT91C_VDD_17_18 (1UL << 5) +#define AT91C_VDD_18_19 (1UL << 6) +#define AT91C_VDD_19_20 (1UL << 7) +#define AT91C_VDD_20_21 (1UL << 8) +#define AT91C_VDD_21_22 (1UL << 9) +#define AT91C_VDD_22_23 (1UL << 10) +#define AT91C_VDD_23_24 (1UL << 11) +#define AT91C_VDD_24_25 (1UL << 12) +#define AT91C_VDD_25_26 (1UL << 13) +#define AT91C_VDD_26_27 (1UL << 14) +#define AT91C_VDD_27_28 (1UL << 15) +#define AT91C_VDD_28_29 (1UL << 16) +#define AT91C_VDD_29_30 (1UL << 17) +#define AT91C_VDD_30_31 (1UL << 18) +#define AT91C_VDD_31_32 (1UL << 19) +#define AT91C_VDD_32_33 (1UL << 20) +#define AT91C_VDD_33_34 (1UL << 21) +#define AT91C_VDD_34_35 (1UL << 22) +#define AT91C_VDD_35_36 (1UL << 23) +#define AT91C_SDIO_MP (1UL << 27) +#define AT91C_SDIO_NF (7UL << 28) +#define AT91C_MMC_OCR_BIT2930 (3UL << 29) +#define AT91C_CARD_POWER_UP_BUSY (1UL << 31) + +#define AT91C_MMC_HOST_VOLTAGE_RANGE (AT91C_VDD_27_28 +\ + AT91C_VDD_28_29 +\ + AT91C_VDD_29_30 +\ + AT91C_VDD_30_31 +\ + AT91C_VDD_31_32 +\ + AT91C_VDD_32_33) + +// MMC OCR response for Bit 29, 30 +#define AT91C_MMC_NORM_DENSITY (0x0UL << 29) +#define AT91C_MMC_HIGH_DENSITY (0x2UL << 29) + +#define AT91C_CCS (1 << 30) + +// MCI_CMD Register Value +#define AT91C_POWER_ON_INIT (0 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_INIT \ + | AT91C_MCI_OPDCMD) + +//----------------------------------------------------------------------------- +/// eMMC CMD6 +//----------------------------------------------------------------------------- +#define AT91C_EMMC_CMD6ARG_ACCESS_BITS (0x3UL << 24) +#define AT91C_EMMC_CMD6ARG_ACCESS_SHIFT (24) +// change command sets +#define AT91C_EMMC_CMD6ARG_ACCESS_CMDSETS (0x0UL << 24) +// set bits in the value field +#define AT91C_EMMC_CMD6ARG_ACCESS_SETBITS (0x1UL << 24) +// clear bits in the value field +#define AT91C_EMMC_CMD6ARG_ACCESS_CLRBITS (0x2UL << 24) +// the value field is written into the pointed byte +#define AT91C_EMMC_CMD6ARG_ACCESS_WRBYTES (0x3UL << 24) +#define AT91C_EMMC_CMD6ARG_INDEX_BITS (0xffUL << 16) +#define AT91C_EMMC_CMD6ARG_INDEX_SHIFT (16) +#define AT91C_EMMC_CMD6ARG_VALUE_BITS (0xffUL << 8) +#define AT91C_EMMC_CMD6ARG_VALUE_SHIFT (8) +#define AT91C_EMMC_CMD6ARG_CMDSET_BITS (0x7UL << 0) +#define AT91C_EMMC_CMD6ARG_CMDSET_SHIFT (0) + +//----------------------------------------------------------------------------- +// Command Classes +//----------------------------------------------------------------------------- +// +// Class 0, 2, 4, 5, 7 and 8 are mandatory and shall be supported by +// all SD Memory Cards. +// +// Basic Commands (class 0) +// +// Cmd0 MCI + SPI +#define AT91C_GO_IDLE_STATE_CMD (0 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE ) +// Cmd1 SPI +#define AT91C_MMC_SEND_OP_COND_CMD (1 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_OPDCMD) +// Cmd2 MCI +#define AT91C_ALL_SEND_CID_CMD (2 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_OPDCMD \ + | AT91C_MCI_RSPTYP_136 ) +// Cmd3 MCI +#define AT91C_SET_RELATIVE_ADDR_CMD (3 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_OPDCMD \ + | AT91C_MCI_MAXLAT ) +// Cmd4 MCI +#define AT91C_SET_DSR_CMD (4 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_NO \ + | AT91C_MCI_MAXLAT ) +// Cmd5 MCI +#define AT91C_IO_SEND_OP_COND_CMD (5 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_OPDCMD ) +// Cmd6 SD/MMC +#if defined(MCI2_INTERFACE) +#define AT91C_MMC_SWITCH_CMD (6 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_R1B \ + | AT91C_MCI_MAXLAT ) +#else +#define AT91C_MMC_SWITCH_CMD (6 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +#endif +#define AT91C_SD_SWITCH_CMD (6 | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_TRTYP_BLOCK \ + | AT91C_MCI_TRDIR_READ \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +// cmd7 MCI +#define AT91C_SEL_DESEL_CARD_CMD (7 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +#define AT91C_SEL_CARD_CMD (7 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +#define AT91C_DESEL_CARD_CMD (7 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_NO \ + | AT91C_MCI_MAXLAT ) +// Cmd8 MCI + SPI +#define AT91C_SEND_IF_COND (8 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +// Cmd9 MCI + SPI +#define AT91C_SEND_CSD_CMD (9 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_136 \ + | AT91C_MCI_MAXLAT ) +// Cmd10 MCI + SPI +#define AT91C_SEND_CID_CMD (10 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_136 \ + | AT91C_MCI_MAXLAT ) +// Cmd12 MCI + SPI +#if defined(MCI2_INTERFACE) +#define AT91C_STOP_TRANSMISSION_CMD (12 | AT91C_MCI_TRCMD_STOP \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_R1B \ + | AT91C_MCI_MAXLAT ) +#else +#define AT91C_STOP_TRANSMISSION_CMD (12 | AT91C_MCI_TRCMD_STOP \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +#endif +// Cmd13 MCI + SPI +#define AT91C_SEND_STATUS_CMD (13 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +// Cmd15 MCI +#define AT91C_GO_INACTIVE_STATE_CMD (15 | AT91C_MCI_RSPTYP_NO ) + +// Cmd58 SPI +#define AT91C_READ_OCR_CMD (58 | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_MAXLAT ) +// Cmd59 SPI +#define AT91C_CRC_ON_OFF_CMD (59 | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_MAXLAT ) + +//*------------------------------------------------ +//* Class 2 commands: Block oriented Read commands +//*------------------------------------------------ + +// Cmd8 for MMC +#define AT91C_SEND_EXT_CSD_CMD (8 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_OPDCMD_PUSHPULL \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_TRTYP_BLOCK \ + | AT91C_MCI_TRDIR \ + | AT91C_MCI_MAXLAT) + +// Cmd16 +#define AT91C_SET_BLOCKLEN_CMD (16 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) +// Cmd17 +#define AT91C_READ_SINGLE_BLOCK_CMD (17 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_TRTYP_BLOCK \ + | AT91C_MCI_TRDIR \ + | AT91C_MCI_MAXLAT) +// Cmd18 +#define AT91C_READ_MULTIPLE_BLOCK_CMD (18 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_TRTYP_MULTIPLE \ + | AT91C_MCI_TRDIR \ + | AT91C_MCI_MAXLAT) + +//*------------------------------------------------ +//* Class 4 commands: Block oriented write commands +//*------------------------------------------------ +// Cmd24 +#define AT91C_WRITE_BLOCK_CMD (24 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | (AT91C_MCI_TRTYP_BLOCK \ + & ~(AT91C_MCI_TRDIR)) \ + | AT91C_MCI_MAXLAT) +// Cmd25 +#define AT91C_WRITE_MULTIPLE_BLOCK_CMD (25 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | (AT91C_MCI_TRTYP_MULTIPLE \ + & ~(AT91C_MCI_TRDIR)) \ + | AT91C_MCI_MAXLAT) +// Cmd27 +#define AT91C_PROGRAM_CSD_CMD (27 | AT91C_MCI_RSPTYP_48 ) + +//*---------------------------------------- +//* Class 5 commands: Erase commands +//*---------------------------------------- +// Cmd32 +//#define AT91C_TAG_SECTOR_START_CMD (32 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT) +// Cmd33 +//#define AT91C_TAG_SECTOR_END_CMD (33 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT) +// Cmd38 +//#define AT91C_ERASE_CMD (38 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT ) + +//*---------------------------------------- +//* Class 7 commands: Lock commands +//*---------------------------------------- +// Cmd42 +//#define AT91C_LOCK_UNLOCK (42 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT) // not tested + +//*----------------------------------------------- +// Class 8 commands: Application specific commands +//*----------------------------------------------- +// Cmd55 +#define AT91C_APP_CMD (55 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_MAXLAT) +// cmd 56 +//#define AT91C_GEN_CMD (56 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT) // not tested +// ACMD6 +#define AT91C_SD_SET_BUS_WIDTH_CMD (6 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_MAXLAT) +// ACMD13 +#define AT91C_SD_STATUS_CMD (13 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_TRTYP_BLOCK \ + | AT91C_MCI_TRDIR_READ \ + | AT91C_MCI_MAXLAT) +// ACMD22 +//#define AT91C_SD_SEND_NUM_WR_BLOCKS_CMD (22 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT) +// ACMD23 +//#define AT91C_SD_SET_WR_BLK_ERASE_COUNT_CMD (23 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT) +// ACMD41 +#define AT91C_SD_APP_OP_COND_CMD (41 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_NO ) +// ACMD42 +//#define AT91C_SD_SET_CLR_CARD_DETECT_CMD (42 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO | AT91C_MCI_MAXLAT) +// ACMD51 +#define AT91C_SD_SEND_SCR_CMD (51 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_TRDIR_READ \ + | AT91C_MCI_TRTYP_BLOCK \ + | AT91C_MCI_MAXLAT) + +// SDIO commands +// CMD5, R4 +#define AT91C_SDIO_SEND_OP_COND (5 | AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_OPDCMD) + +// CMD52, R5 +#define AT91C_SDIO_IO_RW_DIRECT (52| AT91C_MCI_TRCMD_NO \ + | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_MAXLAT ) + +// CMD53, R5 +#define AT91C_SDIO_IO_RW_EXTENDED (53 | AT91C_MCI_SPCMD_NONE \ + | AT91C_MCI_RSPTYP_48 \ + | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_MAXLAT) + +#ifdef AT91C_MCI_SPCMD_BOOTREQ +// BOOTREQ +#define AT91C_BOOTREQ (AT91C_MCI_SPCMD_BOOTREQ \ + | AT91C_MCI_TRDIR_READ \ + | AT91C_MCI_TRCMD_START \ + | AT91C_MCI_MAXLAT) +// BOOTEND +#define AT91C_BOOTEND (AT91C_MCI_SPCMD_BOOTEND \ + | AT91C_MCI_OPDCMD_PUSHPULL) +#endif +// Optional commands +#define SD_ACMD6_SUPPORT (1UL << 0) +#define SD_ACMD13_SUPPORT (1UL << 1) +#define SD_ACMD41_SUPPORT (1UL << 2) +#define SD_ACMD51_SUPPORT (1UL << 3) + +#define SD_CMD16_SUPPORT (1UL << 8) + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// DecodeR1 +/// \param R1 +//------------------------------------------------------------------------------ +void DecodeR1(unsigned char R1) +{ + if( (R1 & R1_SPI_IDLE)==R1_SPI_IDLE) { + TRACE_DEBUG("R1_SPI_IDLE\n\r"); + } + if( (R1 & R1_SPI_ERASE_RESET)==R1_SPI_ERASE_RESET) { + TRACE_DEBUG("R1_SPI_ERASE_RESET\n\r"); + } + if( (R1 & R1_SPI_ILLEGAL_COMMAND)==R1_SPI_ILLEGAL_COMMAND) { + TRACE_DEBUG("R1_SPI_ILLEGAL_COMMAND\n\r"); + } + if( (R1 & R1_SPI_COM_CRC)==R1_SPI_COM_CRC) { + TRACE_DEBUG("R1_SPI_COM_CRC\n\r"); + } + if( (R1 & R1_SPI_ERASE_SEQ)==R1_SPI_ERASE_SEQ) { + TRACE_DEBUG("R1_SPI_ERASE_SEQ\n\r"); + } + if( (R1 & R1_SPI_ADDRESS)==R1_SPI_ADDRESS) { + TRACE_DEBUG("R1_SPI_ADDRESS\n\r"); + } + if( (R1 & R1_SPI_PARAMETER)==R1_SPI_PARAMETER) { + TRACE_DEBUG("R1_SPI_PARAMETER\n\r"); + } +} + +//------------------------------------------------------------------------------ +/// DecodeR2 +/// \param R2 +//------------------------------------------------------------------------------ +void DecodeR2(unsigned char R2) +{ + if( (R2 & R2_SPI_CARD_LOCKED)==R2_SPI_CARD_LOCKED) { + TRACE_DEBUG("R2_SPI_CARD_LOCKED\n\r"); + } + if( (R2 & R2_SPI_WP_ERASE_SKIP)==R2_SPI_WP_ERASE_SKIP) { + TRACE_DEBUG("R2_SPI_WP_ERASE_SKIP/R2_SPI_LOCK_UNLOCK_FAIL\n\r"); + } + if( (R2 & R2_SPI_ERROR)==R2_SPI_ERROR) { + TRACE_DEBUG("R2_SPI_ERROR\n\r"); + } + if( (R2 & R2_SPI_CC_ERROR)==R2_SPI_CC_ERROR) { + TRACE_DEBUG("R2_SPI_CC_ERROR\n\r"); + } + if( (R2 & R2_SPI_CARD_ECC_ERROR)==R2_SPI_CARD_ECC_ERROR) { + TRACE_DEBUG("R2_SPI_CARD_ECC_ERROR\n\r"); + } + if( (R2 & R2_SPI_WP_VIOLATION)==R2_SPI_WP_VIOLATION) { + TRACE_DEBUG("R2_SPI_WP_VIOLATION\n\r"); + } + if( (R2 & R2_SPI_ERASE_PARAM)==R2_SPI_ERASE_PARAM) { + TRACE_DEBUG("R2_SPI_ERASE_PARAM\n\r"); + } + if( (R2 & R2_SPI_OUT_OF_RANGE)==R2_SPI_OUT_OF_RANGE) { + TRACE_DEBUG("R2_SPI_OUT_OF_RANGE/R2_SPI_CSD_OVERWRITE\n\r"); + } +} + +//------------------------------------------------------------------------------ +/// Get Trans Speed Value (Kbit/s) +/// \param tranSpeed The TRAN_SPEED value from SD(IO)/MMC enum information. +/// \param unitList Transfer rate units (Kbit/s), 4 units listed. +/// \param nbUnits Transfer rate units list size. +/// \param codeList Time value codes list, 16 codes listed. +//------------------------------------------------------------------------------ +static unsigned int MmcGetTranSpeed(unsigned int tranSpeed, + const unsigned int* unitList, unsigned int nbUnits, + const unsigned int* codeList) +{ + unsigned int unit, value; + unit = tranSpeed & 0x7; + if (unit < nbUnits) unit = unitList[unit]; + else return 0; + value = (tranSpeed >> 3) & 0xF; + if (value < 16) value = codeList[value]; + else return 0; + return (unit * value); +} + +//------------------------------------------------------------------------------ +/// Get Trans Speed Value +/// \param pSd +//------------------------------------------------------------------------------ +void GetTransSpeedValue(SdCard *pSd) +{ + // CSD register, TRANS_SPEED bit + const unsigned int units[4] = {10, 100, 1000, 10000 }; // *Kbit/s + const unsigned int values_emmc[16] = {0, 10, 12, 13, 15, 20, + 26, 30, 35, 40, 45, 52, + 55, 60, 70, 80}; + const unsigned int values_sdmmc[16] = {0, 10, 12, 13, 15, 20, + 25, 30, 35, 40, 45, 50, + 55, 60, 70, 80}; + #if 0 + unsigned int unit, value; + unit = (SD_CSD_TRAN_SPEED(pSd) & 0x7); + if(unit < 4) unit = units[unit]; + else return; + value = (SD_CSD_TRAN_SPEED(pSd) >> 3) & 0xF; + if (value < 16) { + if (pSd->cardType >= CARD_MMC && SD_CID_BGA(pSd) == 1) { + value = values_emmc[value]; + } + else + value = values_sdmmc[value]; + } + else return; + pSd->transSpeed = (unit * value); + #else + pSd->transSpeed = MmcGetTranSpeed(SD_CSD_TRAN_SPEED(pSd), + units, 4, + (pSd->cardType >= CARD_MMC + && SD_CID_BGA(pSd) == 1) ? + values_emmc : values_sdmmc); + #endif + if (pSd->cardType >= CARD_MMC && SD_EXTCSD_HS_TIMING(pSd)) { + pSd->transSpeed *= 2; + } + TRACE_WARNING_WP("-I- SD/MMC TRANS SPEED %d KBit/s\r\n", pSd->transSpeed); + pSd->transSpeed *= 1000; +} + +#if 1 +//------------------------------------------------------------------------------ +/// Reset the SdCmd +//------------------------------------------------------------------------------ +static void ResetCommand(SdCmd *pCommand) +{ + #if 0 + unsigned char* p = (unsigned char*)pCommand; + unsigned int l = sizeof(SdCmd); + while(l --) *p++ = 0; + #else + pCommand->cmd = 0; + pCommand->arg = 0; + pCommand->pData = 0; + pCommand->blockSize = 0; + pCommand->nbBlock = 0; + pCommand->pResp = 0; + pCommand->callback = 0; + pCommand->pArg = 0; + pCommand->resType = 0; + pCommand->dataTran = 0; + pCommand->tranType = 0; + pCommand->isRead = 0; + pCommand->status = 0; + #endif +} +#else +// GNU halt on memset now +# define ResetCommand(pCommand) memset(pCommand, 0, sizeof(SdCmd)) +#endif + +//------------------------------------------------------------------------------ +/// Delay some loop +//------------------------------------------------------------------------------ +static void Delay(volatile unsigned int loop) +{ + for(;loop > 0; loop --); +} + +//------------------------------------------------------------------------------ +/// Sends the current SD card driver command to the card. +/// Returns 0 if successful; Otherwise, returns the transfer status code or +/// SD_ERROR_DRIVER if there was a problem with the SD transfer. +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char SendCommand(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + SdDriver *pSdDriver = pSd->pSdDriver; + unsigned char error; + + // Send command + error = MCI_SendCommand((Mci *)pSdDriver, (MciCmd *)pCommand); + if (error) { + TRACE_ERROR("MCI SendCommand: Failed to send command (%d)\n\r", error); + return SD_ERROR_DRIVER; + } + + // Wait for command to complete (if no callback defined) + if (pCommand->callback == 0) { + while (!MCI_IsTxComplete((Mci *)pSdDriver)); + } + + // Check for using fifo to transfer data + #if !defined(MCI_DMA_ENABLE) && defined(MCI2_INTERFACE) + if (pCommand->dataTran && pCommand->nbBlock) { + MCI_FifoTransfer((Mci *)pSdDriver, (MciCmd *)pCommand); + } + #endif + + return pCommand->status; +} + +//------------------------------------------------------------------------------ +/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp +/// up time. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char Pon(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_POWER_ON_INIT; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + return error; +} + +#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ) +//------------------------------------------------------------------------------ +/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp +/// up time, CMD keeps low so that the device run in boot mode. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char PonBoot(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_POWER_ON_INIT; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + return error; +} +#endif + +//------------------------------------------------------------------------------ +/// Resets all cards to idle state +/// \param pSd Pointer to a SdCard driver instance. +/// \param arg Argument used. +/// \return the command transfer result (see SendCommand). +//------------------------------------------------------------------------------ +static unsigned char Cmd0(SdCard *pSd, unsigned int arg) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + + TRACE_DEBUG("Cmd0()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_GO_IDLE_STATE_CMD; + pCommand->arg = arg; + pCommand->pResp = &response; + + // send command + error = SendCommand(pSd); + return error; +} + +//------------------------------------------------------------------------------ +/// MMC send operation condition command. +/// Sends host capacity support information and activates the card's +/// initialization process. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +/// \param hdSupport Indicate whether the host support high density MMC. +/// \param pHdSupport Indicate whether the card is a high density MMC. +//------------------------------------------------------------------------------ +static unsigned char Cmd1(SdCard *pSd, + unsigned char hdSupport, + unsigned char *pHdSupport) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd1()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_MMC_SEND_OP_COND_CMD; + pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; + if(hdSupport) { + pCommand->arg |= AT91C_MMC_HIGH_DENSITY; + } + else { + pCommand->arg |= AT91C_MMC_NORM_DENSITY; + } + pCommand->resType = 3; + pCommand->pResp = &response; + + // send command + *pHdSupport = 0; + error = SendCommand(pSd); + if (error) { + return error; + } + if ((response & AT91C_CARD_POWER_UP_BUSY) == AT91C_CARD_POWER_UP_BUSY) { if((response & AT91C_MMC_OCR_BIT2930) == AT91C_MMC_HIGH_DENSITY) { + *pHdSupport = 1; + } + return 0; + } + else { + return SD_ERROR_DRIVER; + } +} + +//------------------------------------------------------------------------------ +/// Asks any card to send the CID numbers +/// on the CMD line (any card that is +/// connected to the host will respond) +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param pCid Buffer for storing the CID numbers. +//------------------------------------------------------------------------------ +static unsigned char Cmd2(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + + TRACE_DEBUG("Cmd2()\n\r"); + ResetCommand(pCommand); + // Fill the command information + pCommand->cmd = AT91C_ALL_SEND_CID_CMD; + pCommand->resType = 2; + pCommand->pResp = pSd->cid; + + // Send the command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Ask the card to publish a new relative address (RCA) +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd3(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int cardAddress; + unsigned char error; + + TRACE_DEBUG("Cmd3()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SET_RELATIVE_ADDR_CMD; + + // Assign relative address to MMC card + if ((pSd->cardType == CARD_MMC) || (pSd->cardType == CARD_MMCHD)) { + pCommand->arg = (0x1 << 16); + } + pCommand->resType = 1; + pCommand->pResp = &cardAddress; + + // Send command + error = SendCommand(pSd); + if (error) { + return error; + } + + // Save card address in driver + if ( (pSd->cardType == CARD_SD) + || (pSd->cardType == CARD_SDHC)) { + pSd->cardAddress = (cardAddress >> 16) & 0xFFFF; + } + else if (pSd->cardType >= CARD_SDIO) { + pSd->cardAddress = (cardAddress >> 16) & 0xFFFF; + } + else { + // Default MMC RCA is 0x0001 + pSd->cardAddress = 1; + } + + return 0; +} + +#if MCI_SDIO_ENABLE +//------------------------------------------------------------------------------ +/// SDIO send operation condition command. +/// Sends host capacity support information and activates the card's +/// initialization process. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +/// \param pIo Pointer to data send as well as response buffer. +//------------------------------------------------------------------------------ +static unsigned char Cmd5(SdCard *pSd, + unsigned int *pIo) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + + TRACE_DEBUG("Cmd5()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_IO_SEND_OP_COND_CMD; + pCommand->arg = *pIo; + pCommand->resType = 4; + pCommand->pResp = pIo; + + // send command + error = SendCommand(pSd); + return error; +} +#endif + +//------------------------------------------------------------------------------ +/// Command toggles a card between the +/// stand-by and transfer states or between +/// the programming and disconnect states. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param address Relative Card Address (0 deselects all). +//------------------------------------------------------------------------------ +static unsigned char Cmd7(SdCard *pSd, unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + + TRACE_DEBUG("Cmd7()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SEL_DESEL_CARD_CMD; + pCommand->arg = address << 16; + pCommand->resType = 1; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Switches the mode of operation of the selected card (SD/MMC) or +/// modifies the EXT_CSD registers (for MMC only). +/// CMD6 is valid under the "trans" state. +/// \return The command transfer result (see SendCommand). +/// \param pSd Pointer to a SD/MMC card driver instance. +/// \param pSwitchArg Pointer to a MmcCmd6Arg instance. +/// \param pStatus Pointer to where the 512bit status is returned. +/// \param pResp Pointer to where the response is returned. +//------------------------------------------------------------------------------ +static unsigned char Cmd6(SdCard *pSd, + const void * pSwitchArg, + unsigned int * pStatus, + unsigned int * pResp) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + SdCmd6Arg * pSdSwitch; + MmcCmd6Arg * pMmcSwitch; + + SANITY_CHECK(pSd); + SANITY_CHECK(pSwitchArg); + + TRACE_DEBUG("CMD6()\n\r"); + + ResetCommand(pCommand); + + if (pSd->cardType >= CARD_MMC) { + pMmcSwitch = (MmcCmd6Arg*)pSwitchArg; + // R1b response + pCommand->cmd = AT91C_MMC_SWITCH_CMD; + pCommand->resType = 1; + pCommand->arg = (pMmcSwitch->access << 24) + | (pMmcSwitch->index << 16) + | (pMmcSwitch->value << 8) + | (pMmcSwitch->cmdSet << 0); + } + else if (pSd->cardType >= CARD_SD) { + + pSdSwitch = (SdCmd6Arg*)pSwitchArg; + // R1 response & 512 bits of status on DAT + pCommand->cmd = AT91C_SD_SWITCH_CMD; + pCommand->resType = 1; + pCommand->arg = (pSdSwitch->mode << 31) + | (pSdSwitch->reserved << 30) + | (pSdSwitch->reserveFG6 << 20) + | (pSdSwitch->reserveFG5 << 16) + | (pSdSwitch->reserveFG4 << 12) + | (pSdSwitch->reserveFG3 << 8) + | (pSdSwitch->command << 4) + | (pSdSwitch->accessMode << 0); + if (pStatus) { + pCommand->blockSize = 512 / 8; + pCommand->nbBlock = 1; + pCommand->pData = (unsigned char*)pStatus; + + pCommand->dataTran = 1; + pCommand->isRead = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + } + } + pCommand->pResp = &response; + + TRACE_INFO("CMD6(%d) arg 0x%X\n\r", pSd->cardType, pCommand->arg); + + error = SendCommand(pSd); + + if (error) + return error; + else if (pResp) + *pResp = response; + + return 0; +} + +//------------------------------------------------------------------------------ +/// SD: Sends SD Memory Card interface condition, which includes host supply +/// voltage information and asks the card whether card supports voltage. +/// Should be performed at initialization time to detect the card type. +/// MMC: SEND_EXT_CSD, to get EXT_CSD register as a block of data. +/// Valid under "trans" state. +/// \param pSd Pointer to a SD card driver instance. +/// \param sdCmd For SD Memory Card interface condition +/// \param arg Expected supply voltage(SD) or 512 byte buffer pointer (MMC). +/// \return 0 if successful; +/// otherwise returns SD_ERROR_NORESPONSE if the card did not answer +/// the command, or SD_ERROR_DRIVER. +//------------------------------------------------------------------------------ +static unsigned char Cmd8(SdCard *pSd, + unsigned char sdCmd, + void* arg) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + unsigned char supplyVoltage = (unsigned char)((unsigned int)arg); + + TRACE_DEBUG("Cmd8()\n\r"); + ResetCommand(pCommand); + + if (sdCmd) { + + // Fill command information + pCommand->cmd = AT91C_SEND_IF_COND; + pCommand->arg = (supplyVoltage << 8) | (0xAA); + pCommand->resType = 7; + + TRACE_DEBUG("supplyVoltage: 0x%x\n\r", supplyVoltage); + } + else { + + pCommand->cmd = AT91C_SEND_EXT_CSD_CMD; + pCommand->resType = 1; + + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = 512 / SD_BLOCK_SIZE; + pCommand->pData = arg; + + pCommand->dataTran = 1; + pCommand->isRead = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + } + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + + if (sdCmd) { + + // Check result + if (error == MCI_STATUS_NORESPONSE) { + + return SD_ERROR_NORESPONSE; + } + // SD_R7 + // Bit 0 - 7: check pattern (echo-back) + // Bit 8 -11: voltage accepted + else if (!error && + ((response & 0x00000FFF) == ((supplyVoltage << 8) | 0xAA))) { + return 0; + } + else { + return SD_ERROR_DRIVER; + } + } + + return error; +} + +//------------------------------------------------------------------------------ +/// Addressed card sends its card-specific +/// data (CSD) on the CMD line. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd9(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + + TRACE_DEBUG("Cmd9()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SEND_CSD_CMD; + pCommand->arg = pSd->cardAddress << 16; + pCommand->resType = 2; + pCommand->pResp = pSd->csd; + + // Send command + error = SendCommand(pSd); + return error; +} + +//------------------------------------------------------------------------------ +/// Forces the card to stop transmission +/// \param pSd Pointer to a SD card driver instance. +/// \param stopRead Stop reading stream/writing stream. +/// \param pStatus Pointer to a status variable. +//------------------------------------------------------------------------------ +static unsigned char Cmd12(SdCard *pSd, + unsigned char stopRead, + unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd12()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_STOP_TRANSMISSION_CMD; + pCommand->isRead = stopRead; + pCommand->tranType = MCI_STOP_TRANSFER; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + if (pStatus) *pStatus = response; + return error; +} + +//------------------------------------------------------------------------------ +/// Addressed card sends its status register. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param pStatus Pointer to a status variable. +//------------------------------------------------------------------------------ +static unsigned char Cmd13(SdCard *pSd, unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + + TRACE_DEBUG("Cmd13()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SEND_STATUS_CMD; + pCommand->arg = pSd->cardAddress << 16; + pCommand->resType = 1; + pCommand->pResp = pStatus; + + // Send command + error = SendCommand(pSd); + + return error; +} + +//------------------------------------------------------------------------------ +/// In the case of a Standard Capacity SD Memory Card, this command sets the +/// block length (in bytes) for all following block commands +/// (read, write, lock). +/// Default block length is fixed to 512 Bytes. +/// Set length is valid for memory access commands only if partial block read +/// operation are allowed in CSD. +/// In the case of a High Capacity SD Memory Card, block length set by CMD16 +/// command does not affect the memory read and write commands. Always 512 +/// Bytes fixed block length is used. This command is effective for LOCK_UNLOCK +/// command. In both cases, if block length is set larger than 512Bytes, the +/// card sets the BLOCK_LEN_ERROR bit. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockLength Block length in bytes. +//------------------------------------------------------------------------------ +static unsigned char Cmd16(SdCard *pSd, unsigned short blockLength) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd16()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SET_BLOCKLEN_CMD; + pCommand->arg = blockLength; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + + return error; +} + +#ifdef SINGLE_READ +static unsigned char Cmd17(SdCard *pSd, + unsigned char *pData, + unsigned int address, + unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd17()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_READ_SINGLE_BLOCK_CMD; + pCommand->arg = address; + pCommand->resType = 1; + pCommand->pResp = &response; + + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = 1; + pCommand->pData = pData; + + pCommand->dataTran = 1; + pCommand->isRead = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + + // Send command + error = SendCommand(pSd); + + if (pStatus) *pStatus = response; + return error; +} +#endif + +//------------------------------------------------------------------------------ +/// Continously transfers datablocks from card to host until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the DW aligned buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char Cmd18(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address, + unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd18()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_READ_MULTIPLE_BLOCK_CMD; + pCommand->arg = address; + pCommand->resType = 1; + pCommand->pResp = &response; + + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = pData; + + pCommand->dataTran = 1; + pCommand->isRead = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + + // Send command + error = SendCommand(pSd); + + if (pStatus) *pStatus = response; + + return error; +} +#ifdef SINGLE_WRITE +static unsigned char Cmd24(SdCard *pSd, + unsigned char *pData, + unsigned int address, + unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd24()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_WRITE_BLOCK_CMD; + pCommand->arg = address; + pCommand->resType = 1; + pCommand->pResp = &response; + + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = 1; + pCommand->pData = pData; + + pCommand->dataTran = 1; + pCommand->isRead = 0; + pCommand->tranType = MCI_NEW_TRANSFER; + + // Send command + error = SendCommand(pSd); + + if (pStatus) *pStatus = response; + return error; +} +#endif +//------------------------------------------------------------------------------ +/// Write block command +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the DW aligned buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char Cmd25(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address, + unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd25()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_WRITE_MULTIPLE_BLOCK_CMD; + pCommand->arg = address; + pCommand->resType = 1; + pCommand->pResp = &response; + + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = pData; + + pCommand->dataTran = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + + // Send command + error = SendCommand(pSd); + if (pStatus) *pStatus = response; + + return error; +} + +#if MCI_SDIO_ENABLE +//------------------------------------------------------------------------------ +/// SDIO R/W Byte Direct, response R5 +/// \param pSd Pointer to SdCard instance. +/// \param func Number of function. +/// \param rw The direction of IO operation, 1 for write. +/// \param raw Read after write +/// \param addr The register address to access. +/// \param pIoData Pointer to fill written data and response. +//------------------------------------------------------------------------------ +static unsigned char Cmd52(SdCard *pSd, + unsigned char func, + unsigned char rw, + unsigned char raw, + unsigned int addr, + unsigned int *pIoData) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + unsigned char byte = 0; + + TRACE_DEBUG("Cmd52()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SDIO_IO_RW_DIRECT; + // - argument + if (pIoData) byte = *pIoData; + pCommand->arg = byte + | (addr << 9) // register address 25: 9 + | (raw << 27) // ReadAfterWrite 27 + | (func << 28) // FunctionNumber 30:28 + | (rw << 31); // R/W 31 + pCommand->resType = 5; + pCommand->pResp = &response; + // Send command + error = SendCommand(pSd); + if (pIoData) *pIoData = response; + return error; +} + +//------------------------------------------------------------------------------ +/// SDIO R/W Extended, response R5 +/// \param pSd Pointer to SdCard instance. +/// \param func Number of function. +/// \param rw The direction of IO operation, 1 for write. +/// \param blockMode R/O on a block basis +/// \param incAddress R/W to incrementing address (1) or fixed address (0) +/// \param addr The register address to access. +/// \param pIoData Pointer to fill written data and response. +/// \param size Data size base on bytes or blocks depending on blockMode +/// \param pResp Pointer to response buffer +//------------------------------------------------------------------------------ +static unsigned char Cmd53(SdCard *pSd, + unsigned char func, + unsigned char rw, + unsigned char blockMode, + unsigned char incAddress, + unsigned int addr, + unsigned char *pIoData, + unsigned short size, + unsigned int *pResp) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + + TRACE_DEBUG("Cmd53()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SDIO_IO_RW_EXTENDED; + // - argument + pCommand->arg = size + | (addr << 9) // register address 25: 9 + | (incAddress << 26) // OP Code 26 + | (blockMode << 27) // ReadAfterWrite 27 + | (func << 28) // FunctionNumber 30:28 + | (rw << 31); // R/W 31 + pCommand->resType = 5; + pCommand->pResp = pResp; + + // - Write... + if (rw) {} + else { + pCommand->cmd |= AT91C_MCI_TRDIR_READ; + pCommand->isRead = 1; + } + // - Block... + if (blockMode) { + pCommand->cmd |= AT91C_MCI_TRTYP_SDIO_BLOCK + | AT91C_MCI_TRCMD_START; + pCommand->blockSize = SD_BLOCK_SIZE; + } + else { + pCommand->cmd |= AT91C_MCI_TRTYP_SDIO_BYTE + | AT91C_MCI_TRCMD_START; + pCommand->blockSize = 1; + } + pCommand->nbBlock = size; + pCommand->pData = pIoData; + pCommand->dataTran = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + + // Send command + error = SendCommand(pSd); + return error; +} +#endif + +//------------------------------------------------------------------------------ +/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply +/// ramp up time. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd55(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd55()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_APP_CMD; + pCommand->arg = (pSd->cardAddress << 16); + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + + return error; +} + +/* +//------------------------------------------------------------------------------ +/// SPI Mode, Reads the OCR register of a card +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param pOcr OCR value of the card +//------------------------------------------------------------------------------ +static unsigned char Cmd58(SdCard *pSd, unsigned int *pOcr) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response[2]; + + TRACE_DEBUG("Cmd58()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_READ_OCR_CMD; + pCommand->resType = 3; + pCommand->pResp = &response[0]; + + // Send command + error = SendCommand(pSd); + return error; +} + +//------------------------------------------------------------------------------ +/// SPI Mode, Set CRC option of a card +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param option CRC option, 1 to turn on, 0 to trun off +//------------------------------------------------------------------------------ +static unsigned char Cmd59(SdCard *pSd, unsigned char option) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd59()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_CRC_ON_OFF_CMD; + pCommand->arg = (option & 0x1); + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + + return error; +} +*/ + +//------------------------------------------------------------------------------ +/// Defines the data bus width (00=1bit or 10=4 bits bus) to be used for data +/// transfer. +/// The allowed data bus widths are given in SCR register. +/// \param pSd Pointer to a SD card driver instance. +/// \param busWidth Bus width in bits. +/// \return the command transfer result (see SendCommand). +//------------------------------------------------------------------------------ +static unsigned char Acmd6(SdCard *pSd, unsigned char busWidth) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Acmd6()\n\r"); + + error = Cmd55(pSd); + if (error) { + TRACE_ERROR("Acmd6.Cmd55: %d\n\r", error); + return error; + } + + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SD_SET_BUS_WIDTH_CMD; + pCommand->arg = (busWidth == 4) ? SD_STAT_DATA_BUS_WIDTH_4BIT : + SD_STAT_DATA_BUS_WIDTH_1BIT; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// The SD Status contains status bits that are related to the SD memory Card +/// proprietary features and may be used for future application-specific usage. +/// Can be sent to a card only in 'tran_state'. +//------------------------------------------------------------------------------ +static unsigned char Acmd13(SdCard *pSd, unsigned int * pSdSTAT) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response[1]; + + TRACE_DEBUG("Acmd13()\n\r"); + + error = Cmd55(pSd); + if (error) { + TRACE_ERROR("Acmd13.Cmd55: %d\n\r", error); + return error; + } + + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SD_STATUS_CMD; + pCommand->resType = 1; + pCommand->pResp = response; + + pCommand->blockSize = 512 / 8; + pCommand->nbBlock = 1; + pCommand->pData = (unsigned char*)pSdSTAT; + + pCommand->dataTran = 1; + pCommand->isRead = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + + // Send command + error = SendCommand(pSd); + + return error; +} + +//------------------------------------------------------------------------------ +/// Asks to all cards to send their operations conditions. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param hcs Shall be true if Host support High capacity. +/// \param pCCS Set the pointed flag to 1 if hcs != 0 and SD OCR CCS flag is set. +//------------------------------------------------------------------------------ +static unsigned char Acmd41(SdCard *pSd, unsigned char hcs, unsigned char *pCCS) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + do { + error = Cmd55(pSd); + if (error) { + return error; + } + + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SD_APP_OP_COND_CMD; + pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; + if (hcs) { + pCommand->arg |= AT91C_CCS; + } + + pCommand->resType = 3; + pCommand->pResp = &response; + + // Send command + TRACE_DEBUG("Acmd41()\n\r"); + error = SendCommand(pSd); + if (error) { + return error; + } + *pCCS = ((response & AT91C_CCS) != 0); + } + while ((response & AT91C_CARD_POWER_UP_BUSY) != AT91C_CARD_POWER_UP_BUSY); + + return 0; +} + +//------------------------------------------------------------------------------ +/// SD Card Configuration Register (SCR) provides information on the SD Memory +/// Card's special features that were configured into the given card. The size +/// of SCR register is 64 bits. +//------------------------------------------------------------------------------ +static unsigned char Acmd51(SdCard *pSd, unsigned int * pSCR) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response[1]; + + TRACE_DEBUG("Acmd51()\n\r"); + + error = Cmd55(pSd); + if (error) { + TRACE_ERROR("Acmd51.Cmd55: %d\n\r", error); + return error; + } + + ResetCommand(pCommand); + // Fill command information + pCommand->cmd = AT91C_SD_SEND_SCR_CMD; + pCommand->resType = 1; + pCommand->pResp = response; + + pCommand->blockSize = 64 / 8; + pCommand->nbBlock = 1; + pCommand->pData = (unsigned char*)pSCR; + + pCommand->dataTran = 1; + pCommand->isRead = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + + // Send command + error = SendCommand(pSd); + + //if (!error) Int2MsbFirstStream((unsigned char*)pSCR, 8 / 4); + + return error; +} + +#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ) +//------------------------------------------------------------------------------ +/// Terminate boot stream. +/// \param pSd Pointer to SdCard instance. +//------------------------------------------------------------------------------ +static unsigned char BootEnd(SdCard *pSd) +{ + SdCmd * pCommand = &(pSd->command); + + TRACE_DEBUG("BootEnd()\n\r"); + ResetCommand(pCommand); + + // Send boot end + pCommand->cmd = AT91C_BOOTEND; + + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// In boot operation mode, the processor can read boot data from the slave, +/// by keeping the CMD line low after power-on before issuing CMD1. +/// BootEnd() must be invoked after the boot request done. +/// \param pSd Pointer to SdCard instance. +/// \param pBuffer The buffer holding received data. +/// \param length The buffer length. +//------------------------------------------------------------------------------ +static unsigned char BootREQ(SdCard *pSd, + unsigned char* pBuffer, + unsigned int nbBlocks, + unsigned char ackEnable) +{ + SdCmd * pCommand = &(pSd->command); + unsigned char error; + + TRACE_DEBUG("BootREQ()\n\r"); + ResetCommand(pCommand); + + // Send boot request + pCommand->cmd = ackEnable ? (AT91C_BOOTREQ | AT91C_MCI_BOOTACK) + : AT91C_BOOTREQ; + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlocks; + pCommand->pData = pBuffer; + pCommand->isRead = 1; + pCommand->tranType = MCI_NEW_TRANSFER; + + error = SendCommand(pSd); + if (error) { + TRACE_ERROR("BootOperation.BootReq: %d\n\r", error); + return error; + } + return error; +} +#endif + +//------------------------------------------------------------------------------ +/// Continue to transfer datablocks from card to host until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char ContinuousRead(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + SdCallback pCb, + void *pArg) +{ + SdCmd *pCommand = &(pSd->command); + + TRACE_DEBUG("Read()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = pData; + + pCommand->dataTran = 1; + pCommand->tranType = MCI_CONTINUE_TRANSFER; + pCommand->isRead = 1; + + pCommand->callback = pCb; + pCommand->pArg = pArg; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Continue to transfer datablocks from host to card until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +//------------------------------------------------------------------------------ +static unsigned char ContinuousWrite(SdCard *pSd, + unsigned short nbBlock, + const unsigned char *pData, + SdCallback pCb, + void *pArg) +{ + SdCmd *pCommand = &(pSd->command); + + TRACE_DEBUG("Write()\n\r"); + ResetCommand(pCommand); + // Fill command information + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = (unsigned char*)pData; + + pCommand->dataTran = 1; + pCommand->tranType = MCI_CONTINUE_TRANSFER; + + pCommand->callback = pCb; + pCommand->pArg = pArg; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Try SW Reset several times (CMD0 with ARG 0) +/// \param pSd Pointer to a SD card driver instance. +/// \param retry Retry times. +/// \return 0 or MCI error code. +//------------------------------------------------------------------------------ +static unsigned char SwReset(SdCard *pSd, unsigned int retry) +{ + unsigned int i; + unsigned char error = 0; + + for (i = 0; i < retry; i ++) { + error = Cmd0(pSd, 0); + if (error != MCI_STATUS_NORESPONSE) + break; + } + return error; +} +/* +//------------------------------------------------------------------------------ +/// Re-init card to trans state. +//------------------------------------------------------------------------------ +static unsigned char ReInit(SdCard *pSd) +{ + #if 0 + unsigned char error; + error = SwReset(pSd, 1); + if (error) { + TRACE_ERROR("ReInit.Cmd0: %d\n\r", error); + return error; + } + error = Cmd1(pSd); + if (error) { + TRACE_ERROR("ReInit.Cmd1: %d\n\r", error); + return error; + } + error = Cmd2(pSd); + if (error) { + TRACE_ERROR("ReInit.Cmd2: %d\n\r", error); + return error; + } + error = Cmd3(pSd); + if (error) { + TRACE_ERROR("ReInit.Cmd3: %d\n\r", error); + return error; + } + error = Cmd7(pSd, pSd->cardAddress); + if (error) { + TRACE_ERROR("ReInit.Cmd7: %d\n\r", error); + return error; + } + #endif + return 0; +} +*/ +//------------------------------------------------------------------------------ +/// Move SD card to transfer state. +//------------------------------------------------------------------------------ +static unsigned char MoveToTranState(SdCard * pSd) +{ + unsigned char error; + unsigned int status; + + // Quit transfer state + if((pSd->state == SD_STATE_READ) + || (pSd->state == SD_STATE_WRITE)) { + + error = Cmd12(pSd, + (pSd->state == SD_STATE_READ), + &status); + if (error) { + TRACE_ERROR("MvToTran.Cmd12: %d\n\r", error); + return error; + } + } + + // Put device into tran state + error = Cmd13(pSd, &status); + if (error) { + TRACE_ERROR("MvToTran.Cmd13: %d\n\r", error); + return error; + } + if ((status & STATUS_STATE) == STATUS_STBY) { + error = Cmd7(pSd, pSd->cardAddress); + if (error) { + TRACE_ERROR("MvToTran.Cmd7: %d\n\r", error); + return error; + } + } + + return 0; +} + +#if defined(SINGLE_READ) || defined(SINGLE_WRITE) +static unsigned char PerformSingleTransfer(SdCard *pSd, + unsigned int address, + unsigned char *pData, + unsigned char isRead) +{ + unsigned int status; + unsigned char error = 0; + + /* Reset transfer state if previous in multi- mode */ + if( (pSd->state == SD_STATE_READ) + || (pSd->state == SD_STATE_WRITE)) { + /* Stop transfer */ + error = Cmd12(pSd, (pSd->state == SD_STATE_READ), &status); + if (error) { + TRACE_ERROR("SingleTx.Cmd12: st%x, er%d\n\r", pSd->state, error); + } + pSd->state = SD_STATE_READY; + pSd->preBlock = 0xFFFFFFFF; + } + +#ifdef SINGLE_READ + if(isRead) { + // Wait for card to be ready for data transfers + do { + error = Cmd13(pSd, &status); + if (error) { + TRACE_ERROR("SingleTx.RD.Cmd13: %d\n\r", error); + return error; + } + if( ((status & STATUS_STATE) == STATUS_IDLE) + ||((status & STATUS_STATE) == STATUS_READY) + ||((status & STATUS_STATE) == STATUS_IDENT)) { + TRACE_ERROR("SingleTx.mode\n\r"); + return SD_ERROR_NOT_INITIALIZED; + } + // If the card is in sending data state or in receivce data state + if ( ((status & STATUS_STATE) == STATUS_RCV) + ||((status & STATUS_STATE) == STATUS_DATA) ){ + + TRACE_DEBUG("SingleTx.state = 0x%X\n\r", (status & STATUS_STATE) >> 9); + } + } + while ( ((status & STATUS_READY_FOR_DATA) == 0) + || ((status & STATUS_STATE) != STATUS_TRAN) ); + ASSERT((status & STATUS_STATE) == STATUS_TRAN, + "SD Card can't be configured in transfer state 0x%X\n\r", + (status & STATUS_STATE)>>9); + // Read data + // Move to Sending data state + error = Cmd17(pSd, pData, SD_ADDRESS(pSd,address), &status); + if (error) { + TRACE_ERROR("SingleTx.Cmd17: %d\n\r", error); + return error; + } + if (status & ~(STATUS_READY_FOR_DATA | STATUS_STATE)) { + TRACE_ERROR("CMD17.stat: %x\n\r", + status & ~(STATUS_READY_FOR_DATA | STATUS_STATE)); + return SD_ERROR_DRIVER; + } + return error; + } +#endif +#ifdef SINGLE_WRITE + // Write + { + // Wait for card to be ready for data transfers + do { + error = Cmd13(pSd, &status); + if (error) { + TRACE_ERROR("SingleTx.WR.Cmd13: %d\n\r", error); + return error; + } + } + while ((status & STATUS_READY_FOR_DATA) == 0); + // Move to Sending data state + error = Cmd24(pSd, pData, SD_ADDRESS(pSd,address), &status); + if (error) { + TRACE_DEBUG("SingleTx.Cmd25: %d\n\r", error); + return error; + } + if (status & (STATUS_WRITE & ~(STATUS_READY_FOR_DATA | STATUS_STATE))) { + TRACE_ERROR("CMD24(0x%x).stat: %x\n\r", + SD_ADDRESS(pSd,address), + status & (STATUS_WRITE + & ~(STATUS_READY_FOR_DATA | STATUS_STATE))); + return SD_ERROR_DRIVER; + } + } + return error; +#endif +} +#endif + +//------------------------------------------------------------------------------ +/// Move SD card to transfer state. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the transfer command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to transfer. +/// \param nbBlocks Number of blocks to be transfer, 0 for infinite transfer. +/// \param pData Data buffer whose size is at least the block size. +/// \param isRead 1 for read data and 0 for write data. +//------------------------------------------------------------------------------ +static unsigned char MoveToTransferState(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData, + unsigned char isRead) +{ + unsigned int status; + unsigned char error; + + if( (pSd->state == SD_STATE_READ) + || (pSd->state == SD_STATE_WRITE)) { +#if 1//!defined(MCI2_INTERFACE) + if (pSd->state == SD_STATE_WRITE) { + DBGU_PutChar(0); + DBGU_PutChar(0); + DBGU_PutChar(0); + DBGU_PutChar(0); + } +#endif + // RW MULTI with length + error = Cmd12(pSd, + (pSd->state == SD_STATE_READ), + &status); + if (error) { + TRACE_ERROR("MTTranState.Cmd12: st%x, er%d\n\r", pSd->state, error); + return error; + } +#if !defined(MCI2_INTERFACE) + // RW MULTI Infinite + if (pSd->state == SD_STATE_WRITE) { + while(MCI_CheckBusy((Mci *)pSd->pSdDriver) != 0); + } +#endif + } + + if(isRead) { + // Wait for card to be ready for data transfers + do { + error = Cmd13(pSd, &status); + if (error) { + TRACE_ERROR("MTTranState.RD.Cmd13: %d\n\r", error); + return error; + } + if( ((status & STATUS_STATE) == STATUS_IDLE) + ||((status & STATUS_STATE) == STATUS_READY) + ||((status & STATUS_STATE) == STATUS_IDENT)) { + TRACE_ERROR("Pb Card Identification mode\n\r"); + return SD_ERROR_NOT_INITIALIZED; + } + // If the card is in sending data state or in receivce data state + if ( ((status & STATUS_STATE) == STATUS_RCV) + ||((status & STATUS_STATE) == STATUS_DATA) ){ + + TRACE_DEBUG("state = 0x%X\n\r", (status & STATUS_STATE) >> 9); + } + } + while ( ((status & STATUS_READY_FOR_DATA) == 0) + || ((status & STATUS_STATE) != STATUS_TRAN) ); + ASSERT((status & STATUS_STATE) == STATUS_TRAN, + "SD Card can't be configured in transfer state 0x%X\n\r", + (status & STATUS_STATE)>>9); + // Read data + // Move to Sending data state + error = Cmd18(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address), &status); + if (error) { + TRACE_ERROR("MTTranState.Cmd18: %d\n\r", error); + return error; + } + if (status & ~(STATUS_READY_FOR_DATA | STATUS_STATE)) { + TRACE_ERROR("CMD18.stat: %x\n\r", + status & ~(STATUS_READY_FOR_DATA | STATUS_STATE)); + return SD_ERROR_DRIVER; + } + } + else { + // Wait for card to be ready for data transfers + do { + error = Cmd13(pSd, &status); + if (error) { + TRACE_ERROR("MoveToTransferState.WR.Cmd13: %d\n\r", error); + return error; + } + } + while ((status & STATUS_READY_FOR_DATA) == 0); + // Move to Sending data state + error = Cmd25(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address), &status); + if (error) { + TRACE_DEBUG("MoveToTransferState.Cmd25: %d\n\r", error); + return error; + } + if (status & (STATUS_WRITE & ~(STATUS_READY_FOR_DATA | STATUS_STATE))) { + TRACE_ERROR("CMD25(0x%x, %d).stat: %x\n\r", + SD_ADDRESS(pSd,address), nbBlocks, + status & (STATUS_WRITE + & ~(STATUS_READY_FOR_DATA | STATUS_STATE))); + return SD_ERROR_DRIVER; + } + } + + if (!error) pSd->preBlock = address + (nbBlocks-1); + return error; +} + +//------------------------------------------------------------------------------ +/// Switch the bus width of card +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Switch the HS mode of card +/// \param pSd Pointer to SdCard instance. +/// \param hsEnable 1 to enable, 0 to disable. +//------------------------------------------------------------------------------ +static unsigned char SdMmcSwitchHsMode(SdCard *pSd, unsigned char hsEnable) +{ + unsigned int status; + unsigned char error = SD_ERROR_DRIVER; + if (pSd->mode == hsEnable) + return 0; + if (pSd->cardType >= CARD_MMC) { + MmcCmd6Arg cmd6Arg; + cmd6Arg.access = 0x3; + cmd6Arg.index = SD_EXTCSD_HS_TIMING_INDEX; + cmd6Arg.value = hsEnable ? SD_EXTCSD_HS_TIMING_ENABLE + : SD_EXTCSD_HS_TIMING_DISABLE; + cmd6Arg.cmdSet = 0; + error = Cmd6(pSd, &cmd6Arg, 0, &status); + if (error) { + TRACE_ERROR("MMC SwitchHs.Cmd6: %d\n\r", error); + } + else if (status & STATUS_SWITCH_ERROR) { + TRACE_WARNING("MMC HS SW Fail\n\r"); + error = SD_ERROR_DRIVER; + } + else { + TRACE_WARNING_WP("-I- MMC HS %d\n\r", hsEnable); + pSd->mode = hsEnable; + } + } + else if (pSd->cardType >= CARD_SD) { + SdCmd6Arg cmd6Arg; + unsigned int switchStatus[512/32]; + cmd6Arg.mode = 1; + cmd6Arg.reserved = 0; + cmd6Arg.reserveFG6 = 0xF; + cmd6Arg.reserveFG5 = 0xF; + cmd6Arg.reserveFG4 = 0xF; + cmd6Arg.reserveFG3 = 0xF; + cmd6Arg.command = 0; + cmd6Arg.accessMode = 1; + error = Cmd6(pSd, + &cmd6Arg, + switchStatus, + &status); + #if 0 + unsigned int i; + printf("SD Switch status:"); + for(i = 0; i < 512 / 8; i ++) { + if ((i % 8) == 0) printf("\n\r[%3d]", i); + printf(" %02x", ((char*)switchStatus)[i]); + } + printf("\n\r"); + printf(" _FG1_INFO %x\n\r", + SD_SW_STAT_FUN_GRP1_INFO(switchStatus)); + printf(" _FG1_RC %x\n\r", + SD_SW_STAT_FUN_GRP1_RC(switchStatus)); + printf(" _FG1_BUSY %x\n\r", + SD_SW_STAT_FUN_GRP1_BUSY(switchStatus)); + printf(" _FG1_DS_V %x\n\r", + SD_SW_STAT_DATA_STRUCT_VER(switchStatus)); + #endif + if (error) { + TRACE_ERROR("SD SwitchHs.Cmd6: %d\n\r", error); + } + else if (status & STATUS_SWITCH_ERROR) { + TRACE_WARNING("SD HS SW Fail\n\r"); + error = SD_ERROR_DRIVER; + } + else if (SD_SW_STAT_FUN_GRP1_RC(switchStatus) + == SD_SW_STAT_FUN_GRP_RC_ERROR) { + TRACE_ERROR_WP("-I- SD HS Not Supported\n\r"); + error = SD_ERROR_DRIVER; + } + else if (SD_SW_STAT_FUN_GRP1_BUSY(switchStatus)) { + TRACE_WARNING("SD HS Busy\n\r"); + error = SD_ERROR_DRIVER; + } + else { + TRACE_WARNING_WP("-I- SD HS %d\n\r", hsEnable); + pSd->mode = hsEnable; + } + } + + return error; +} + +#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ) +//------------------------------------------------------------------------------ +/// Process a list of SWITCH command +/// \param pSd Pointer to SdCard instance. +/// \param pArgList Argument list. +/// \param listSize Number of arguments listed. +/// \return 0, or error code and argument index. +//------------------------------------------------------------------------------ +static unsigned short MmcSwitchSettings(SdCard *pSd, + const MmcCmd6Arg * pArgList, + unsigned int listSize, + unsigned int * pErrSta) +{ + unsigned int i, status; + unsigned char error; + + SANITY_CHECK(pSd); + SANITY_CHECK(pArgList); + + for (i = 0; i < listSize; i ++) { + error = Cmd6(pSd, &pArgList[i], 0, &status); + if (pErrSta) *pErrSta = status; + if (error) { + return (error | (i << 8)); + } + if (status & ~(STATUS_STATE | STATUS_READY_FOR_DATA)) { + TRACE_WARNING("Error in SWITCH.%d, 0x%x\n\r", + pArgList[i].index, status); + } + else { + TRACE_INFO("SWITCH.%d: 0x%x\n\r", + pArgList[i].index, status); + } + } + return 0; +} +#endif + +//------------------------------------------------------------------------------ +/// Switch card state between STBY and TRAN +/// \param pSd Pointer to a SD card driver instance. +/// \param address Card address to TRAN, 0 to STBY +/// \param check Whether to check the state +//------------------------------------------------------------------------------ +static unsigned char MmcSelectCard(SdCard *pSd, + unsigned short address, + unsigned char check) +{ + unsigned char error; + unsigned int status; + unsigned int targetState = address ? STATUS_TRAN : STATUS_STBY; + unsigned int srcState = address ? STATUS_STBY : STATUS_TRAN; + if (pSd->cardType == CARD_SDIO) check = 0; + + // At this stage the Initialization and identification process is achieved + // The SD card is supposed to be in Stand-by State + while(check) { + error = Cmd13(pSd, &status); + if (error) { + TRACE_ERROR("MmcSelectCard.Cmd13 (%d)\n\r", error); + return error; + } + if ((status & STATUS_READY_FOR_DATA)) { + unsigned int currState = status & STATUS_STATE; + if (currState == targetState) return 0; + if (currState != srcState) { + TRACE_ERROR("MmcSelectCard, wrong state %x\n\r", currState); + return SD_ERROR_DRIVER; + } + break; + } + } + + // witch to TRAN mode to Select the current SD/MMC + // so that SD ACMD6 can process or EXT_CSD can read. + error = Cmd7(pSd, address); + if (error == SD_ERROR_NOT_INITIALIZED && address == 0) {} + else if (error) { + TRACE_ERROR("MmcSelectCard.Cmd7 (%d)\n\r", error); + } + + return error; +} + +//------------------------------------------------------------------------------ +/// Get EXT_CSD information +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char MmcGetExtInformation(SdCard *pSd) +{ + unsigned char error; + unsigned int i; + + // CSD 1.2 or Higher version + if(SD_CSD_STRUCTURE(pSd) >= 2) { + + /* Clear EXT_CSD data */ + for (i = 0;i < 512/4; i ++) pSd->extData[i] = 0; + error = Cmd8(pSd, 0, pSd->extData); + if (error) { + TRACE_ERROR("MmcGetExt.Cmd8: %d\n\r", error); + } + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Get SCR and SD Status information +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char SdGetExtInformation(SdCard *pSd) +{ + unsigned char error; + + // Reset data (64 + 512 bits, 8 + 64 bytes) + // memset(pSd->extData, 0x00, 512); + + // SD Status + if (pSd->optCmdBitMap & SD_ACMD13_SUPPORT) { + error = Acmd13(pSd, &pSd->extData[SD_EXT_OFFSET_SD_STAT]); + if (error) { + TRACE_ERROR("SdGetExt.Acmd13: %d\n\r", error); + pSd->optCmdBitMap &= ~SD_ACMD13_SUPPORT; + } + } + + // SD SCR + error = Acmd51(pSd, &pSd->extData[SD_EXT_OFFSET_SD_SCR]); + if (error) { + TRACE_ERROR("SdGetExt.Acmd51: %d\n\r", error); + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Update SD/MMC information. +/// Update CSD for card speed switch. +/// Update ExtDATA for any card function switch. +/// \param pSd Pointer to a SD card driver instance. +/// \return error code when update CSD error. +//------------------------------------------------------------------------------ +static unsigned char SdMmcUpdateInformation(SdCard *pSd, + unsigned char csd, + unsigned char extData) +{ + unsigned char error; + + // Update CSD for new TRAN_SPEED value + if (csd) { + MmcSelectCard(pSd, 0, 1); + Delay(800); + error = Cmd9(pSd); + if (error ) { + TRACE_ERROR("SdMmcUpdateInfo.Cmd9 (%d)\n\r", error); + return error; + } + error = MmcSelectCard(pSd, pSd->cardAddress, 1); + } + if (pSd->cardType >= CARD_MMC) MmcGetExtInformation(pSd); + else if (pSd->cardType >= CARD_SD) SdGetExtInformation(pSd); + GetTransSpeedValue(pSd); + + return 0; +} + +#if MCI_SDIO_ENABLE +//------------------------------------------------------------------------------ +/// Find ManfID, Func0 tuple. +//------------------------------------------------------------------------------ +static unsigned char SdioFindTuples(SdCard * pSd, + unsigned int address, unsigned int size, + unsigned int *pAddrManfID, + unsigned int *pAddrFunc0) +{ + unsigned char error, tmp[3]; + unsigned int addr = address; + unsigned char nbFound = 0; + for (;;) { + error = SDIO_ReadDirect(pSd, 0, addr, tmp, 3); + if (error) + return error; + // ManfID + if (tmp[0] == CISTPL_MANFID) { + if (pAddrManfID) *pAddrManfID = addr; + nbFound ++; + } + // Func0 + if (tmp[0] == CISTPL_FUNCE && tmp[2] == 0x00) { + if (pAddrFunc0) *pAddrFunc0 = addr; + nbFound ++; + } + // END + if (tmp[0] == CISTPL_END) break; + + // All found + if (nbFound >= 2) break; + // Not tuple? + if (tmp[1] == 0) break; + + // Next address + addr += (tmp[1] + 2); + if (addr > (address + size)) + break; + } + return 0; +} +#endif + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +#if MCI_SDIO_ENABLE +//------------------------------------------------------------------------------ +/// Read at least one byte from SDIO card, using RW_DIRECT command. +/// \param pSd Pointer to SdCard instance. +/// \param funNb Function number. +/// \param address First byte address of data in SDIO card. +/// \param pBytes Pointer to data buffer. +/// \param size Buffer size. +//------------------------------------------------------------------------------ +unsigned char SDIO_ReadDirect(SdCard *pSd, + unsigned char funNb, + unsigned int address, + unsigned char *pBytes, + unsigned int size) +{ + unsigned char error; + unsigned int status; + if (pSd->cardType < CARD_SDIO) { + return SD_ERROR_NOT_SUPPORT; + } + if (size == 0) + return SD_ERROR_DRIVER; + + while(size --) { + status = 0; + error = Cmd52(pSd, funNb, 0, 0, address ++, &status); + if (pBytes) *pBytes ++ = (unsigned char)status; + if (error) { + TRACE_ERROR("SDIO_ReadDirect.Cmd52: %d, %x\n\r", error, status); + return SD_ERROR_DRIVER; + } + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Write one byte to SDIO card, using RW_DIRECT command. +/// \param pSd Pointer to SdCard instance. +/// \param funNb Function number. +/// \param address First byte address of data in SDIO card. +/// \param pBytes Pointer to data buffer. +/// \param size Buffer size. +//------------------------------------------------------------------------------ +unsigned char SDIO_WriteDirect(SdCard *pSd, + unsigned char funNb, + unsigned int address, + unsigned char byte) +{ + if (pSd->cardType < CARD_SDIO) { + return SD_ERROR_NOT_SUPPORT; + } + unsigned char error; + unsigned int status; + status = byte; + error = Cmd52(pSd, funNb, 1, 0, address, &status); + if (error) { + TRACE_ERROR("SDIO_ReadDirect.Cmd52: %d, %x\n\r", error, status); + return SD_ERROR_DRIVER; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Read byte by byte from SDIO card, using RW_EXT command. +/// \param pSd Pointer to SdCard instance. +/// \param funNb Function number. +/// \param address First byte address of data in SDIO card. +/// \param isFixedAddr Address not increased. +/// \param pBytes Pointer to data buffer. +/// \param size Buffer size. +//------------------------------------------------------------------------------ +unsigned char SDIO_ReadBytes(SdCard *pSd, + unsigned char funNb, + unsigned int address, + unsigned char isFixedAddr, + unsigned char *pBytes, + unsigned int size) +{ + unsigned char error; + unsigned int status; + if (pSd->cardType < CARD_SDIO) { + return SD_ERROR_NOT_SUPPORT; + } + + if (size == 0) + return SD_ERROR_DRIVER; + + error = Cmd53(pSd, funNb, + 0, 0, !isFixedAddr, + address, pBytes, size, &status); + if (error || (status & STATUS_SDIO_CMD52)) { + TRACE_ERROR("SDIO_ReadBytes.Cmd53: %d, %x\n\r", error, status); + return SD_ERROR_DRIVER; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Write byte by byte to SDIO card, using RW_EXT command. +/// \param pSd Pointer to SdCard instance. +/// \param funNb Function number. +/// \param address First byte address of data in SDIO card. +/// \param isFixedAddr Address not increased. +/// \param pBytes Pointer to data buffer. +/// \param size Buffer size. +//------------------------------------------------------------------------------ +unsigned char SDIO_WriteBytes(SdCard *pSd, + unsigned char funNb, + unsigned int address, + unsigned char isFixedAddr, + unsigned char *pBytes, + unsigned int size) +{ + unsigned char error; + unsigned int status; + if (pSd->cardType < CARD_SDIO) { + return SD_ERROR_NOT_SUPPORT; + } + if (size == 0) + return SD_ERROR_DRIVER; + + error = Cmd53(pSd, funNb, + 1, 0, !isFixedAddr, + address, pBytes, size, &status); + if (error || (status & STATUS_SDIO_CMD52)) { + TRACE_ERROR("SDIO_ReadBytes.Cmd53: %d, %x\n\r", error, status); + return SD_ERROR_DRIVER; + } + + return 0; +} +#endif + +//------------------------------------------------------------------------------ +/// Read Block of data in a buffer pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to read. +/// \param pData Data buffer whose size is at least the block size, it can +/// be 1,2 or 4-bytes aligned when used with DMA. +/// \param length Number of blocks to be read. +/// \param pCallback Pointer to callback function that invoked when read done. +/// 0 to start a blocked read. +/// \param pArgs Pointer to callback function arguments. +//------------------------------------------------------------------------------ +unsigned char SD_Read(SdCard *pSd, + unsigned int address, + void *pData, + unsigned short length, + SdCallback pCallback, + void *pArgs) +{ + unsigned char error; + + // If callback is defined, performe none blocked reading + if (pCallback) { + if (MCI_IsTxComplete((Mci *)pSd) == 0) { + return SD_ERROR_BUSY; + } + } + + if ( pSd->state != SD_STATE_READ + || pSd->preBlock + 1 != address ) { + // Start infinite block reading + error = MoveToTransferState(pSd, address, 0, 0, 1); + } + else error = 0; + if (!error) { + pSd->state = SD_STATE_READ; + pSd->preBlock = address + (length - 1); + error = ContinuousRead(pSd, + length, + pData, + pCallback, pArgs); + } + TRACE_DEBUG("SDrd(%u,%u):%u\n\r", address, length, error); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Write Block of data in a buffer pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to read. +/// \param pData Data buffer whose size is at least the block size, it can +/// be 1,2 or 4-bytes aligned when used with DMA. +/// \param length Number of blocks to be read. +/// \param pCallback Pointer to callback function that invoked when read done. +/// 0 to start a blocked read. +/// \param pArgs Pointer to callback function arguments. +//------------------------------------------------------------------------------ +unsigned char SD_Write(SdCard *pSd, + unsigned int address, + void *pData, + unsigned short length, + SdCallback pCallback, + void *pArgs) +{ + unsigned char error; + // If callback is defined, performe none blocked writing + if (pCallback) { + if (MCI_IsTxComplete((Mci *)pSd) == 0) { + return SD_ERROR_BUSY; + } + } + if ( pSd->state != SD_STATE_WRITE + || pSd->preBlock + 1 != address ) { + // Start infinite block writing + error = MoveToTransferState(pSd, address, 0, 0, 0); + } + else error = 0; + if (!error) { + pSd->state = SD_STATE_WRITE; + error = ContinuousWrite(pSd, + length, + pData, + pCallback, pArgs); + pSd->preBlock = address + (length - 1); + } + TRACE_DEBUG("SDwr(%u,%u):%u\n\r", address, length, error); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Read Block of data in a buffer pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to read. +/// \param nbBlocks Number of blocks to be read. +/// \param pData Data buffer whose size is at least the block size, it can +/// be 1,2 or 4-bytes aligned when used with DMA. +//------------------------------------------------------------------------------ +unsigned char SD_ReadBlock(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData) +{ + unsigned char error = 0; + + SANITY_CHECK(pSd); + SANITY_CHECK(pData); + SANITY_CHECK(nbBlocks); + + TRACE_DEBUG("ReadBlk(%d,%d)\n\r", address, nbBlocks); +#if defined(SINGLE_READ) + while(nbBlocks --) { + error = PerformSingleTransfer(pSd, address, pData, 1); + if (error) + break; + // SDHC + if (pSd->totalSize == 0xFFFFFFFF) { + address += 1; + pData = &pData[512]; + } + else { + address += 1; + pData = &pData[512]; + } + } + return error; +#endif +#if !defined(MCI2_INTERFACE) + #if !defined(AT91C_MCI_RDPROOF) + error = MoveToTransferState(pSd, address, nbBlocks, pData, 1); + pSd->state = SD_STATE_READ; + #else + if((pSd->state == SD_STATE_READ) + && ((pSd->preBlock + 1) == address)) { + + #if defined(at91rm9200) + error = Cmd12(pSd, 0); + if (error) { + return error; + } + #else + TRACE_DEBUG("SD_ReadBlock:ContinuousRead\n\r"); + error = ContinuousRead(pSd, + nbBlocks, + pData, + 0, 0); + pSd->preBlock = address + (nbBlocks-1); + #endif + } + else { + error = MoveToTransferState(pSd, address, nbBlocks, pData, 1); + pSd->state = SD_STATE_READ; + } + #endif +#else + if ( pSd->state != SD_STATE_READ + || pSd->preBlock + 1 != address ) { + // Start infinite block reading + error = MoveToTransferState(pSd, address, 0, 0, 1); + } + if (!error) { + pSd->state = SD_STATE_READ; + error = ContinuousRead(pSd, + nbBlocks, + pData, + 0, 0); + if (!error) pSd->preBlock = address + (nbBlocks - 1); + } +#endif + return error; +} + +//------------------------------------------------------------------------------ +/// Write Block of data pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of block to write. +/// \param nbBlocks Number of blocks to be read +/// \param pData Data buffer whose size is at least the block size, it can +/// be 1,2 or 4-bytes aligned when used with DMA. +//------------------------------------------------------------------------------ +unsigned char SD_WriteBlock(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + const unsigned char *pData) +{ + unsigned char error = 0; + + SANITY_CHECK(pSd); + SANITY_CHECK(pData); + SANITY_CHECK(nbBlocks); + + TRACE_DEBUG("WriteBlk(%d,%d)\n\r", address, nbBlocks); + +#if defined(SINGLE_WRITE) + unsigned char *pB = (unsigned char*)pData; + while(nbBlocks --) { + error = PerformSingleTransfer(pSd, address, pB, 0); + if (error) + break; + // SDHC + if (pSd->totalSize == 0xFFFFFFFF) { + address += 1; + pB = &pB[512]; + } + else { + address += 1; + pB = &pB[512]; + } + } + return error; +#endif +#if !defined(MCI2_INTERFACE) + #if !defined(AT91C_MCI_WRPROOF) + error = MoveToTransferState(pSd, address, nbBlocks, + (unsigned char *)pData, 0); + pSd->state = SD_STATE_WRITE; + #else + if((pSd->state == SD_STATE_WRITE) + && ((pSd->preBlock + 1) == address)) { + + TRACE_DEBUG("SD_WriteBlock:ContinuousWrite\n\r"); + error = ContinuousWrite(pSd, + nbBlocks, + pData, + 0, 0); + pSd->preBlock = address + (nbBlocks-1); + } + else { + + //TRACE_FATAL("SD_WriteBlock:MoveToTransferState\n\r"); + error = MoveToTransferState(pSd, address, nbBlocks, + (unsigned char *)pData, 0); + pSd->state = SD_STATE_WRITE; + } + #endif +#else + if ( pSd->state != SD_STATE_WRITE + || pSd->preBlock + 1 != address ) { + // Start infinite block writing + error = MoveToTransferState(pSd, address, 0, 0, 0); + } + if (!error) { + pSd->state = SD_STATE_WRITE; + error = ContinuousWrite(pSd, + nbBlocks, + pData, + 0, 0); + if (!error) pSd->preBlock = address + (nbBlocks - 1); + } +#endif + + return error; +} + +//------------------------------------------------------------------------------ +/// Run the SDcard SD/MMC/SDIO Mode initialization sequence. +/// This function resets both IO and memory controller, runs the initialisation +/// procedure and the identification process. Then it leaves the card in ready +/// state. The following command must check the card type and continue to put +/// the card into tran(for memory card) or cmd(for io card) state for data +/// exchange. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char SdMmcIdentify(SdCard *pSd) +{ + unsigned char mem = 0, io = 0, f8 = 0, mp = 1, ccs = 0; + unsigned char error = 0; +#if MCI_SDIO_ENABLE + unsigned int status; +#endif + + // Reset HC to default HS and BusMode + MCI_EnableHsMode(pSd->pSdDriver, 0); + MCI_SetBusWidth(pSd->pSdDriver, MCI_SDCBUS_1BIT); + +#if MCI_SDIO_ENABLE + // Reset SDIO + // CMD52, write 1 to RES bit in the CCCR (bit 3 of register 6) + status = (0x1 << 3); + error = Cmd52(pSd, 0, 1, 0, 6, &status); + if (!error && ((status & STATUS_SDIO_CMD52) == 0)) {} + else if (error == MCI_STATUS_NORESPONSE) {} + else { + TRACE_DEBUG("SdMmcIdentify.Cmd52 fail: %d, %x\n\r", error, status); + } +#endif + + // Reset MEM + error = SwReset(pSd, 1); + if (error) { + TRACE_DEBUG("SdMmcIdentify.SwReset: %d\n\r", error) + } + + // CMD8 is newly added in the Physical Layer Specification Version 2.00 to + // support multiple voltage ranges and used to check whether the card + // supports supplied voltage. The version 2.00 host shall issue CMD8 and + // verify voltage before card initialization. + // The host that does not support CMD8 shall supply high voltage range... + error = Cmd8(pSd, 1, (void*)1); + if (error == 0) { + f8 = 1; + } + else if (error != SD_ERROR_NORESPONSE) { + TRACE_ERROR("SdMmcIdentify.Cmd8: %d\n\r", error); + return SD_ERROR_DRIVER; + } + else { + // Delay after "no response" + Delay(800); + } + +#if MCI_SDIO_ENABLE + // CMD5 is added for SDIO OCR check + status = 0; + error = Cmd5(pSd, &status); + if (error) { + TRACE_WARNING("SdMmcIdentify.Cmd5: %d\n\r", error); + } + // SDIO or SD COMBO: FN > 0 + else if ((status & AT91C_SDIO_NF) > 0) { + // Set New Voltage + unsigned int cmd5Retries = 10000; + do { + status &= AT91C_MMC_HOST_VOLTAGE_RANGE; + error = Cmd5(pSd, &status); + if (status & AT91C_CARD_POWER_UP_BUSY) + break; + } while(!error && cmd5Retries --); + if (error) { + TRACE_ERROR("SdMmcIdentify.Cmd5 V: %d\n\r", error); + return SD_ERROR_DRIVER; + } + TRACE_INFO("SDIO\n\r"); + io = 1; + // SDIO only? + if ((status & AT91C_SDIO_MP) == 0) mp = 0; + } +#endif + // SD or MMC or COMBO: mp is 1 + if (mp) { + // Try SD memory initialize + error = Acmd41(pSd, f8, &ccs); + if (error) { + unsigned int cmd1Retries = 10000; + TRACE_DEBUG("SdMmcIdentify.Acmd41: %d, try MMC\n\r", error); + + // Try MMC initialize + error = SwReset(pSd, 10); + if (error) { + TRACE_ERROR("SdMmcIdentify.Mmc.SwReset: %d\n\r", error); + return SD_ERROR_DRIVER; + } + // - Set Voltage + do { + error = Cmd1(pSd, 1, &ccs); + } + while ((error) && (cmd1Retries-- > 0)); + if (error) { + TRACE_ERROR("SdMmcIdentify.Cmd1: %d\n\r", error); + return SD_ERROR_DRIVER; + } + else if (ccs) { + pSd->cardType = CARD_MMCHD; + } + else { + pSd->cardType = CARD_MMC; + } + + // MMC Identified OK + return 0; + } + else if (ccs) { + TRACE_INFO("SDHC MEM\n\r"); + } + else { + TRACE_INFO("SD MEM\n\r"); + } + mem = 1; + } + + // SD(IO)+MEM ? + if (!mem) { + // SDIO only + if (io) { + pSd->cardType = CARD_SDIO; + return 0; + } + } + // SD COMBO, continue with memory initialize + else if (io) { + if (ccs) pSd->cardType = CARD_SDHCCOMBO; + else pSd->cardType = CARD_SDCOMBO; + } + // SD(HC), continue with memory initialize + else { + if (ccs) pSd->cardType = CARD_SDHC; + else pSd->cardType = CARD_SD; + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Run the SDcard SD Mode enumeration sequence. This function runs after the +/// initialisation procedure and the identification process. It sets the +/// SD card in transfer state to set the block length and the bus width. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char SdMmcEnum(SdCard *pSd) +{ + unsigned char mem = 0, io = 0; + unsigned int status; + unsigned short error; + unsigned char isHsSupport = 0; + unsigned char updateInformation = 0; + + if (pSd->cardType & CARD_TYPE_bmSDMMC) mem = 1; + if (pSd->cardType & CARD_TYPE_bmSDIO) io = 1; + + // For MEM + // The host then issues the command ALL_SEND_CID (CMD2) to the card to get + // its unique card identification (CID) number. + // Card that is unidentified (i.e. which is in Ready State) sends its CID + // number as the response (on the CMD line). + if (mem) { + error = Cmd2(pSd); + if (error) { + TRACE_ERROR("SdMmcEnum.Cmd2: %d\n\r", error); + return SD_ERROR_DRIVER; + } + } + + // For SDIO & MEM + // Thereafter, the host issues CMD3 (SEND_RELATIVE_ADDR) asks the + // card to publish a new relative card address (RCA), which is shorter than + // CID and which is used to address the card in the future data transfer + // mode. Once the RCA is received the card state changes to the Stand-by + // State. At this point, if the host wants to assign another RCA number, it + // can ask the card to publish a new number by sending another CMD3 command + // to the card. The last published RCA is the actual RCA number of the card. + error = Cmd3(pSd); + if (error) { + TRACE_ERROR("SdMmcInit.Cmd3 %d\n\r", error); + return SD_ERROR_DRIVER; + } + + // For MEM + // SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register), + // e.g. block length, card storage capacity, etc... + if (mem) { + error = Cmd9(pSd); + if (error) { + TRACE_ERROR("SdMmcInit.Cmd9 %d\n\r", error); + return SD_ERROR_DRIVER; + } + } + + // For SDIO & MEM + // Now select the card, to TRAN state + error = MmcSelectCard(pSd, pSd->cardAddress, 0); + if (error) { + TRACE_ERROR("SdMmcInit.Sel %d\n\r", error); + return SD_ERROR_DRIVER; + } + // SDIO only card, enumeration done + if (!mem && io) { + // Default tranSpeed: 25MHz + pSd->transSpeed = 25000000; + return 0; + } + + // For MEM cards or combo + // If the card support EXT_CSD, read it! + TRACE_INFO("Card Type %d, CSD_STRUCTURE %d\n\r", + pSd->cardType, SD_CSD_STRUCTURE(pSd)); + + // Get extended information of the card + SdMmcUpdateInformation(pSd, 0, 0); + + // Advanced settings for HD & HS card + if (pSd->cardType >= CARD_MMC){ + + MmcCmd6Arg cmd6Arg; + + // MMC4 or later + if (SD_CSD_SPEC_VERS(pSd) >= 4) { + + unsigned char busWidth, widthMode; + + // Calculate MMC busWidth (limited by slot information) + switch (pSd->pSdDriver->mciMode & AT91C_MCI_SCDBUS) { + #if defined(AT91C_MCI_SCDBUS_8BITS) + case AT91C_MCI_SCDBUS_8BITS: + busWidth = 8; + widthMode = MCI_SDCBUS_8BIT; + break; + #endif + + #if defined(AT91C_MCI_SCDBUS_4BITS) + case AT91C_MCI_SCDBUS_4BITS: + busWidth = 4; + widthMode = MCI_SDCBUS_4BIT; + break; + #endif + + default: + busWidth = 1; + widthMode = MCI_SDCBUS_1BIT; + } + + // Switch to max bus width (4 now) + cmd6Arg.access = 0x1; + cmd6Arg.index = SD_EXTCSD_BUS_WIDTH_INDEX; + cmd6Arg.value = SD_EXTCSD_BUS_WIDTH_4BIT; + cmd6Arg.cmdSet = 0; + error = Cmd6(pSd, &cmd6Arg, 0, &status); + if (!error) { + + TRACE_WARNING_WP("-I- MMC %d-BIT BUS\n\r", busWidth); + if (status + & (STATUS_MMC_SWITCH + & ~(STATUS_STATE | STATUS_READY_FOR_DATA))) { + printf("-E- Status %x\n\r", status); + } + else { + MCI_SetBusWidth(pSd->pSdDriver, widthMode); + updateInformation = 1; + } + } + else { + TRACE_WARNING("MMC %d-BIT not supported\n\r", busWidth) + } + } + + // CARD_TYPE 3 + if (SD_CSD_STRUCTURE(pSd) >= 2 + && (SD_EXTCSD_CARD_TYPE(pSd) & 0x2)) { + + #if !defined(OP_BOOTSTRAP_MCI_on) + // Switch to HS mode + if (gSdmmcAutoHsEnable) { + cmd6Arg.access = 0x3; + cmd6Arg.index = SD_EXTCSD_HS_TIMING_INDEX; + cmd6Arg.value = SD_EXTCSD_HS_TIMING_ENABLE; + cmd6Arg.cmdSet = 0; + error = Cmd6(pSd, &cmd6Arg, 0, &status); + if (error + || (status + & (STATUS_MMC_SWITCH + & ~(STATUS_STATE | STATUS_READY_FOR_DATA)))) { + TRACE_WARNING("MMC HS Fail, st %x\n\r", status); + } + else { + MCI_EnableHsMode(pSd->pSdDriver, 1); + TRACE_WARNING_WP("-I- MMC HS Enabled\n\r"); + isHsSupport = 1; + updateInformation = 1; + } + } + #endif // end of OP_BOOTSTRAP_MCI_on + } + } + else if (pSd->cardType >= CARD_SD) { + #if 1 + // Switch to 4-bits bus width + // (All SD Card shall support 1-bit, 4 bitswidth) + error = Acmd6(pSd, 4); + TRACE_WARNING_WP("-I- SD 4-BITS BUS\n\r"); + if (error) { + TRACE_ERROR("SdMmcInit.12 (%d)\n\r", error); + return error; + } + MCI_SetBusWidth(pSd->pSdDriver, MCI_SDCBUS_4BIT); + + #if !defined(OP_BOOTSTRAP_MCI_on) + // SD Spec V1.10 or higher, switch to high-speed mode + if (gSdmmcAutoHsEnable) { + if (SD_SCR_SD_SPEC(pSd) >= SD_SCR_SD_SPEC_1_10) { + SdCmd6Arg cmd6Arg; + unsigned int switchStatus[512/32]; + cmd6Arg.mode = 1; + cmd6Arg.reserved = 0; + cmd6Arg.reserveFG6 = 0xF; + cmd6Arg.reserveFG5 = 0xF; + cmd6Arg.reserveFG4 = 0xF; + cmd6Arg.reserveFG3 = 0xF; + cmd6Arg.command = 0; + cmd6Arg.accessMode = 1; + error = Cmd6(pSd, + &cmd6Arg, + switchStatus, + &status); + #if 0 + unsigned int i; + printf("SD Switch status:"); + for(i = 0; i < 512 / 8; i ++) { + if ((i % 8) == 0) printf("\n\r[%3d]", i); + printf(" %02x", ((char*)switchStatus)[i]); + } + printf("\n\r"); + printf(" _FG1_INFO %x\n\r", + SD_SW_STAT_FUN_GRP1_INFO(switchStatus)); + printf(" _FG1_RC %x\n\r", + SD_SW_STAT_FUN_GRP1_RC(switchStatus)); + printf(" _FG1_BUSY %x\n\r", + SD_SW_STAT_FUN_GRP1_BUSY(switchStatus)); + printf(" _FG1_DS_V %x\n\r", + SD_SW_STAT_DATA_STRUCT_VER(switchStatus)); + #endif + if (error || (status & STATUS_SWITCH_ERROR)) { + TRACE_WARNING("SD HS Fail\n\r"); + } + else if (SD_SW_STAT_FUN_GRP1_RC(switchStatus) + == SD_SW_STAT_FUN_GRP_RC_ERROR) { + TRACE_ERROR_WP("-I- SD HS Not Supported\n\r"); + } + else if (SD_SW_STAT_FUN_GRP1_BUSY(switchStatus)) { + TRACE_WARNING("SD HS Busy\n\r") + } + else { + MCI_EnableHsMode(pSd->pSdDriver, 1); + TRACE_WARNING_WP("-I- SD HS Enable\n\r"); + isHsSupport = 1; + } + } + } + #endif + // Update + updateInformation = 1; + #endif + } + + if (updateInformation) { + + SdMmcUpdateInformation(pSd, isHsSupport, 1); + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Run the SDcard initialization sequence. This function runs the +/// initialisation procedure and the identification process, then it sets the +/// SD card in transfer state to set the block length and the bus width. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to SD driver already initialized. +//------------------------------------------------------------------------------ +unsigned char SD_Init(SdCard *pSd, SdDriver *pSdDriver) +{ + unsigned char error; + + //TRACE_DEBUG("SD_Init()\n\r"); + + // Initialize SdCard structure + pSd->pSdDriver = pSdDriver; + pSd->cardAddress = 0; + pSd->preBlock = 0xffffffff; + pSd->state = SD_STATE_INIT; + pSd->cardType = CARD_UNKNOWN; + pSd->optCmdBitMap = 0xFFFFFFFF; + pSd->mode = 0; + ResetCommand(&pSd->command); + + // Initialization delay: The maximum of 1 msec, 74 clock cycles and supply + // ramp up time. Supply ramp up time provides the time that the power is + // built up to the operating level (the bus master supply voltage) and the + // time to wait until the SD card can accept the first command. + + // Power On Init Special Command + //TRACE_DEBUG("Pon()\n\r"); + error = Pon(pSd); + if (error) { + TRACE_ERROR("SD_Init.1 (%d)\n\r", error); + return error; + } + // After power-on or CMD0, all cards?CMD lines are in input mode, waiting + // for start bit of the next command. + // The cards are initialized with a default relative card address + // (RCA=0x0000) and with a default driver stage register setting + // (lowest speed, highest driving current capability). + error = SdMmcIdentify(pSd); + if (error) { + TRACE_ERROR("SD_Init.Identify\n\r"); + return error; + } + error = SdMmcEnum(pSd); + if (error) { + TRACE_ERROR("SD_Init.Enum\n\r"); + return error; + } + + // In the case of a Standard Capacity SD Memory Card, this command sets the + // block length (in bytes) for all following block commands + // (read, write, lock). + // Default block length is fixed to 512 Bytes. + // Set length is valid for memory access commands only if partial block read + // operation are allowed in CSD. + // In the case of a High Capacity SD Memory Card, block length set by CMD16 + // command does not affect the memory read and write commands. Always 512 + // Bytes fixed block length is used. This command is effective for + // LOCK_UNLOCK command. + // In both cases, if block length is set larger than 512Bytes, the card sets + // the BLOCK_LEN_ERROR bit. + if (pSd->cardType == CARD_SD) { + error = Cmd16(pSd, SD_BLOCK_SIZE); + if (error) { + pSd->optCmdBitMap &= ~SD_CMD16_SUPPORT; + TRACE_INFO("SD_Init.Cmd16 (%d)\n\r", error); + TRACE_INFO("Fail to set BLK_LEN, default is 512\n\r"); + } + } + + // Reset status for R/W + pSd->state = SD_STATE_READY; + + // If SDIO Card + if (pSd->cardType == CARD_SDIO) { + pSd->blockNr = 0; + pSd->totalSize = 0; + } + // If MMC Card & get size from EXT_CSD + else if (pSd->cardType >= CARD_MMC && SD_CSD_C_SIZE(pSd) == 0xFFF) { + pSd->blockNr = SD_EXTCSD_BLOCKNR(pSd); + // Block number less than 0x100000000/512 + if (pSd->blockNr > 0x800000) + pSd->totalSize = 0xFFFFFFFF; + else + pSd->totalSize = SD_EXTCSD_TOTAL_SIZE(pSd); + } + // If SD CSD v2.0 + else if(pSd->cardType >= CARD_SD + && pSd->cardType < CARD_MMC + && SD_CSD_STRUCTURE(pSd) >= 1) { + pSd->blockNr = SD_CSD_BLOCKNR_HC(pSd); + pSd->totalSize = 0xFFFFFFFF; + } + // Normal card + else { + pSd->totalSize = SD_CSD_TOTAL_SIZE(pSd); + pSd->blockNr = SD_CSD_BLOCKNR(pSd); + } + + if (pSd->cardType == CARD_UNKNOWN) { + return SD_ERROR_NOT_INITIALIZED; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Stop the SDcard. This function stops all SD operations. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to MCI driver already initialized. +//------------------------------------------------------------------------------ +unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver) +{ + unsigned char error; + SdCmd *pCommand = &(pSd->command); + + if (pSd == 0 || pSdDriver == 0) + return 0; + + if(pCommand->tranType == MCI_CONTINUE_TRANSFER) + { + TRACE_DEBUG("SD_StopTransmission()\n\r"); + + error = Cmd12(pSd, (pSd->state != SD_STATE_WRITE), 0); + if(error) { + return error; + } + } + + MCI_Close((Mci *)pSdDriver); + return 0; +} + +//------------------------------------------------------------------------------ +/// Switch the SD/MMC card to High-Speed mode. +/// pSd->transSpeed will change to new speed limit. +/// Invoke MCI_SetSpeed() and MCI_EnableHsMode() to change MCI timing after. +/// For SD/MMC card, the speed mode will not change back until another init. +/// \param pSd Pointer to a SD card driver instance. +/// \param hsMode 1 to enable HS mode, 0 to disable +/// 0xFF to return current mode. +/// \return current mode is hsMode is 0xFF; +/// error code if hsMode is 0 or 1. +//------------------------------------------------------------------------------ +unsigned char SD_HighSpeedMode(SdCard *pSd, + unsigned char hsMode) +{ + unsigned char error = 0; + + if (hsMode == 0xFF) + return pSd->mode; + if (hsMode == 0) { + TRACE_WARNING("Can not switch, do re-init to disable HS mode\n\r"); + return SD_ERROR_DRIVER; + } + + // Quit transfer state + error = MoveToTranState(pSd); + if (error) { + TRACE_ERROR("SD_HighSpeedMode.Tran: %d\n\r", error); + return error; + } + + if (pSd->mode != hsMode) { + error = SdMmcSwitchHsMode(pSd, hsMode); + if (error == 0) + error = SdMmcUpdateInformation(pSd, 1, 1); + } + // Reset state for data R/W + pSd->state = SD_STATE_READY; + + return error; +} + +unsigned char SD_BusWidth(SdCard *pSd, + unsigned char busWidth) +{ + return 0; +} + +#if defined(MCI2_INTERFACE) && defined(AT91C_MCI_SPCMD_BOOTREQ) +//------------------------------------------------------------------------------ +/// Read Block of data in a buffer pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to read. +/// \param nbBlocks Number of blocks to be read. +/// \param pData Data buffer whose size is at least the block size. +//------------------------------------------------------------------------------ +unsigned char MMC_BootRead(SdCard *pSd, + unsigned int nbBlocks, + unsigned char *pData) +{ + unsigned char error; + unsigned char bootAck = 0; + unsigned char busWidth = MCI_SDCBUS_4BIT; + + SANITY_CHECK(pSd); + + if (pSd->state != SD_STATE_BOOT) + return SD_ERROR_DRIVER; + + #if 0 + switch(SD_EXTCSD_BOOT_BUS_WIDTH(pSd)) { + case SD_EXTCSD_BOOT_BUS_1BIT: + busWidth = MCI_SDCBUS_1BIT; + break; + case SD_EXTCSD_BOOT_BUS_8BIT: + busWidth = MCI_SDCBUS_8BIT; + break; + } + + if (SD_EXTCSD_BOOT_CONFIG(pSd) & SD_EXTCSD_BOOT_PARTITION_ACK) + bootAck = 1; + #endif + + MCI_SetBusWidth((Mci*)pSd->pSdDriver, busWidth); + error = BootREQ(pSd, pData, nbBlocks, bootAck); + pSd->state = SD_STATE_BOOT; + + return error; +} + +//------------------------------------------------------------------------------ +/// In boot operation mode, the master can read boot data from the slave. +/// By keeping CMD line low after power-on +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +unsigned char MMC_BootInit(SdCard *pSd) +{ + unsigned char error = 0; + + SANITY_CHECK(pSd); + + error = PonBoot(pSd); + + if (!error) { + + //error = BootREQ(pSd, 0, 0, 0); + + if (!error) + pSd->state = SD_STATE_BOOT; + else { + TRACE_ERROR("MMC_BootInit.BootREQ: %d\n\r", error); + } + } + else { + TRACE_ERROR("MMC_BootInit.PonBoot: %d\n\r", error); + } + + return error; +} + +//------------------------------------------------------------------------------ +/// In boot operation mode, the master can read boot data from the slave. +/// By sending CMD0 with argument + 0xFFFFFFFA +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +unsigned char MMC_BootStart(SdCard *pSd) +{ + unsigned char error; + + SANITY_CHECK(pSd); + + if (pSd->state == SD_STATE_BOOT) + return 0; + + if (pSd->cardType >= CARD_MMC + && SD_CSD_STRUCTURE(pSd) >= 2 + && SD_CID_BGA(pSd) == 1 + && SD_EXTCSD_BOOT_INFO(pSd) == 1) {} + else + return SD_ERROR_NOT_SUPPORT; + + error = Cmd0(pSd, 0xFFFFFFFA); + pSd->state = SD_STATE_BOOT; + + return 0; +} + +//------------------------------------------------------------------------------ +/// Terminate the boot operation mode +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +unsigned char MMC_BootStop(SdCard *pSd) +{ + unsigned char error; + + SANITY_CHECK(pSd); + + if (pSd->state != SD_STATE_BOOT) + return 0; + + error = BootEnd(pSd); + + if(!error) + pSd->state = SD_STATE_IDLE; + + return error; +} + +//------------------------------------------------------------------------------ +/// Setup Boot Settings +/// \param resetBus Wether bus width is reset to 1-bit after quit boot mode. +/// \param busWidth The bus width in boot operation. +/// \param bootPart The partition used for boot operation. +/// \param accPart The partition to access with normal read/write. +/// \param bootAck Enable boot acknoledge. +/// \return 0 or error code. +//------------------------------------------------------------------------------ +unsigned char MMC_SetupBootMode(SdCard *pSd, + unsigned char resetBus, + unsigned char busWidth, + unsigned char bootPart, + unsigned char accPart, + unsigned char bootAck) +{ + unsigned int status; + unsigned short error; + const MmcCmd6Arg bootArgs[] = { + // BOOT_CONFIG + {3, 179, (bootAck << 6)|(bootPart << 3)|(accPart << 0), 0}, + // BOOT_BUS_WIDTH + {3, 177, (busWidth << 2)|(resetBus << 0), 0} + }; + + SANITY_CHECK(pSd); + if ( pSd->cardType >= CARD_MMC + && SD_CSD_STRUCTURE(pSd) >= 2 + && SD_CID_CBS(pSd) == 1) {} + else return SD_ERROR_NOT_SUPPORT; + //if (MMC_GetBootSizeKB(pSd) == 0) return SD_ERROR_NOT_SUPPORT; + + // Quit transfer state + error = MoveToTranState(pSd); + if (error) { + TRACE_ERROR("MMC_SetupBootMode.Tran: %d\n\r", error); + return error; + } + + // Setup all boot informations + error = MmcSwitchSettings(pSd, + bootArgs, + sizeof(bootArgs)/sizeof(MmcCmd6Arg), + &status); + if (error) { + TRACE_ERROR("MMC_SetupBootMode.Cmd6: 0x%x, %x\n\r", error, status); + return (unsigned char)error; + } + + // Update the EXT_CSD + #if 1 + error = Cmd8(pSd, 0, pSd->extData); + if (error) { + TRACE_ERROR("MMC_SetupBootMode.Cmd8 (%d)\n\r", error); + } + + #if 0 + if ( SD_EXTCSD_BOOT_BUS_WIDTH(pSd) != bootArgs[0].value + || SD_EXTCSD_BOOT_CONFIG(pSd) != bootArgs[1].value ) { + + TRACE_ERROR("MMC_SetupBootMode: ExtCSD not changed\n\r"); + + #if 1 + Cmd13(pSd, &status); + TRACE_INFO(" CARD status: 0x%x\n\r", status); + #endif + return SD_ERROR_DRIVER; + } + #endif + #endif + + // Reset state for data R/W + pSd->state = SD_STATE_READY; + + return 0; +} + +//------------------------------------------------------------------------------ +/// \return 0 or error code. +//------------------------------------------------------------------------------ +unsigned char MMC_StopBootMode() +{ + return 0; +} +#endif + + +//------------------------------------------------------------------------------ +/// \return size of the card in KB +//------------------------------------------------------------------------------ +unsigned int MMC_GetTotalSizeKB(SdCard *pSd) +{ + SANITY_CHECK(pSd); + + if (pSd->totalSize == 0xFFFFFFFF) { + + return pSd->blockNr / 2; + } + + return pSd->totalSize / 1024; +} + + +//------------------------------------------------------------------------------ +/// \return size of boot area if the card support boot operation. +//------------------------------------------------------------------------------ +unsigned int MMC_GetBootSizeKB(SdCard *pSd) +{ + SANITY_CHECK(pSd); + if ( pSd->cardType >= CARD_MMC + && SD_CSD_STRUCTURE(pSd) >= 2) { + + return SD_EXTCSD_BOOT_SIZE_MULTI(pSd) * 128; + } + return 0; +} + +#if MCI_SDIO_ENABLE +//------------------------------------------------------------------------------ +/// Display the content of the CCCR +//------------------------------------------------------------------------------ +void SDIO_DisplayCardInformation(SdCard *pSd) +{ + unsigned int tmp = 0, addrCIS = 0, addrManfID = 0, addrFunc0 = 0; + unsigned char* p = (unsigned char*)&tmp; + unsigned char buf[8]; + + switch(pSd->cardType) { + case CARD_SDIO: + TRACE_INFO("** SDIO ONLY card\n\r"); + break; + case CARD_SDCOMBO: + case CARD_SDHCCOMBO: + TRACE_INFO("** SDIO Combo card\n\r"); + break; + default: + TRACE_INFO("** NO SDIO, CCCR not supported\n\r"); + return; + } + // CCCR + TRACE_INFO("====== CCCR ======\n\r"); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CCCR_REG, p, 1); + TRACE_INFO(".SDIO %02X\n\r", (tmp & SDIO_SDIO) >> 4); + TRACE_INFO(".CCCR %02X\n\r", (tmp & SDIO_CCCR) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_SD_REV_REG, p, 1); + TRACE_INFO(".SD %02X\n\r", (tmp & SDIO_SD) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IOE_REG, p, 1); + TRACE_INFO(".IOE %02X\n\r", (tmp & SDIO_IOE) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IOR_REG, p, 1); + TRACE_INFO(".IOR %02X\n\r", (tmp & SDIO_IOR) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IEN_REG, p, 1); + TRACE_INFO(".IEN %02X\n\r", (tmp & SDIO_IEN) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_INT_REG, p, 1); + TRACE_INFO(".INT %02X\n\r", (tmp & SDIO_INT) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_BUS_CTRL_REG, p, 1); + TRACE_INFO(".CD %x\n\r", (tmp & SDIO_CD) >> 7); + TRACE_INFO(".SCSI %x\n\r", (tmp & SDIO_SCSI) >> 6); + TRACE_INFO(".ECSI %x\n\r", (tmp & SDIO_ECSI) >> 5); + TRACE_INFO(".BUS_WIDTH %x\n\r", (tmp & SDIO_BUSWIDTH) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CAP_REG, p, 1); + TRACE_INFO(".4BLS %x\n\r", (tmp & SDIO_4BLS) >> 7); + TRACE_INFO(".LSC %x\n\r", (tmp & SDIO_LSC) >> 6); + TRACE_INFO(".E4MI %x\n\r", (tmp & SDIO_E4MI) >> 5); + TRACE_INFO(".S4MI %x\n\r", (tmp & SDIO_S4MI) >> 4); + TRACE_INFO(".SBS %x\n\r", (tmp & SDIO_SBS) >> 3); + TRACE_INFO(".SRW %x\n\r", (tmp & SDIO_SRW) >> 2); + TRACE_INFO(".SMB %x\n\r", (tmp & SDIO_SMB) >> 1); + TRACE_INFO(".SDC %x\n\r", (tmp & SDIO_SDC) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CIS_PTR_REG, p, 3); + TRACE_INFO(".CIS_PTR %06X\n\r", tmp); + addrCIS = tmp; tmp = 0; + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_BUS_SUSP_REG, p, 1); + TRACE_INFO(".BR %x\n\r", (tmp & SDIO_BR) >> 1); + TRACE_INFO(".BS %x\n\r", (tmp & SDIO_BS) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_FUN_SEL_REG, p, 1); + TRACE_INFO(".DF %x\n\r", (tmp & SDIO_DF) >> 7); + TRACE_INFO(".FS %x\n\r", (tmp & SDIO_FS) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_EXEC_REG, p, 1); + TRACE_INFO(".EX %x\n\r", (tmp & SDIO_EX)); + TRACE_INFO(".EXM %x\n\r", (tmp & SDIO_EXM) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_READY_REG, p, 1); + TRACE_INFO(".RF %x\n\r", (tmp & SDIO_RF)); + TRACE_INFO(".RFM %x\n\r", (tmp & SDIO_RFM) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_FN0_BLKSIZ_REG, p, 2); + TRACE_INFO(".FN0_SIZE %d(%04X)\n\r", tmp, tmp); + tmp = 0; + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_POWER_REG, p, 1); + TRACE_INFO(".EMPC %x\n\r", (tmp & SDIO_EMPC) >> 1); + TRACE_INFO(".SMPC %x\n\r", (tmp & SDIO_SMPC) >> 0); + SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_HS_REG, p, 1); + TRACE_INFO(".EHS %x\n\r", (tmp & SDIO_EHS) >> 1); + TRACE_INFO(".SHS %x\n\r", (tmp & SDIO_SHS) >> 0); + // Metaformat + SdioFindTuples(pSd, addrCIS, 128, &addrManfID, &addrFunc0); + if (addrManfID != 0) { + SDIO_ReadDirect(pSd, SDIO_CIA, addrManfID, buf, 6); + TRACE_INFO("==== CISTPL_MANFID ====\n\r"); + TRACE_INFO("._MANF %04X\n\r", buf[2] + (buf[3] << 8)); + TRACE_INFO("._CARD %04X\n\r", buf[4] + (buf[5] << 8)); + } + if (addrFunc0 != 0) { + SDIO_ReadDirect(pSd, SDIO_CIA, addrFunc0, buf, 6); + TRACE_INFO("== CISTPL_FUNCE Fun0 ==\n\r"); + TRACE_INFO("._FN0_BLK_SIZE %d(0x%04X)\n\r", + buf[3] + (buf[4] << 8), buf[3] + (buf[4] << 8)); + TRACE_INFO("._MAX_TRAN_SPEED %02X\n\r", buf[5]); + } +} +#endif + +//------------------------------------------------------------------------------ +/// Display the content of the CID register +/// \param pCid Pointer to the Cid register value +//------------------------------------------------------------------------------ +void SD_DisplayRegisterCID(SdCard *pSd) +{ + // CID for memory card only + if (pSd->cardType == CARD_UNKNOWN || pSd->cardType >= CARD_SDIO) + return; + + TRACE_INFO("======= CID =======\n\r"); + #if 0 + TRACE_INFO(" .Card/BGA %X\n\r", SD_CID_BGA(pSd)); + #else + TRACE_INFO("CID MID Manufacturer ID %02X\n\r", + SD_CID_MID(pSd)); + + TRACE_INFO("CID OID OEM/Application ID %c%c\n\r", + (char)SD_CID_OID_BYTE_1(pSd), + (char)SD_CID_OID_BYTE_0(pSd)); + + TRACE_INFO("CID PNM Product revision %c%c%c%c%c\n\r", + (char)SD_CID_PNM_BYTE_4(pSd), + (char)SD_CID_PNM_BYTE_3(pSd), + (char)SD_CID_PNM_BYTE_2(pSd), + (char)SD_CID_PNM_BYTE_1(pSd), + (char)SD_CID_PNM_BYTE_0(pSd)); + + TRACE_INFO("CID PRV Product serial number %02X%04X\n\r", + SD_CID_PRV_2(pSd), + SD_CID_PRV_1(pSd)); + + TRACE_INFO("CID MDT Manufacturing date %04d/%02d\n\r", + (unsigned short)SD_CID_MDT_YEAR(pSd), + (unsigned char)SD_CID_MDT_MONTH(pSd)); + + TRACE_INFO("CID CRC checksum %02X\n\r", + SD_CID_CRC(pSd)); + #endif +} + +//------------------------------------------------------------------------------ +/// Display the content of the CSD register +/// \param pSd +//------------------------------------------------------------------------------ +void SD_DisplayRegisterCSD(SdCard *pSd) +{ + // CID for memory card only + if (pSd->cardType == CARD_UNKNOWN || pSd->cardType >= CARD_SDIO) + return; + + TRACE_INFO("======== CSD ========"); + #if 0 + { + unsigned int i; + unsigned char *p = (unsigned char *)pSd->csd; + for(i = 0; i < 128 / 8; i++) { + if ((i % 16) == 0) TRACE_INFO_WP("\n\r [%3d]:", i); + TRACE_INFO_WP(" %2x", p[i]); + } + TRACE_INFO_WP("\n\r"); + TRACE_INFO("------------------------\n\r"); + } + #else + TRACE_INFO_WP("\n\r"); + #endif + TRACE_INFO(" .CSD_STRUCTURE 0x%x\r\n", SD_CSD_STRUCTURE(pSd)); + TRACE_INFO(" .SPEC_VERS 0x%x\r\n", SD_CSD_SPEC_VERS(pSd)); + TRACE_INFO(" .TAAC 0x%X\r\n", SD_CSD_TAAC(pSd) ); + TRACE_INFO(" .NSAC 0x%X\r\n", SD_CSD_NSAC(pSd) ); + TRACE_INFO(" .TRAN_SPEED 0x%X\r\n", SD_CSD_TRAN_SPEED(pSd) ); + TRACE_INFO(" .CCC 0x%X\r\n", SD_CSD_CCC(pSd) ); + TRACE_INFO(" .READ_BL_LEN 0x%X\r\n", SD_CSD_READ_BL_LEN(pSd) ); + TRACE_INFO(" .READ_BL_PARTIAL 0x%X\r\n", SD_CSD_READ_BL_PARTIAL(pSd) ); + TRACE_INFO(" .WRITE_BLK_MISALIGN 0x%X\r\n", SD_CSD_WRITE_BLK_MISALIGN(pSd)); + TRACE_INFO(" .READ_BLK_MISALIGN 0x%X\r\n", SD_CSD_READ_BLK_MISALIGN(pSd) ); + TRACE_INFO(" .DSR_IMP 0x%X\r\n", SD_CSD_DSR_IMP(pSd) ); + TRACE_INFO(" .C_SIZE 0x%X\r\n", SD_CSD_C_SIZE(pSd) ); + TRACE_INFO(" .C_SIZE_HC 0x%X\r\n", SD_CSD_C_SIZE_HC(pSd) ); + TRACE_INFO(" .VDD_R_CURR_MIN 0x%X\r\n", SD_CSD_VDD_R_CURR_MIN(pSd) ); + TRACE_INFO(" .VDD_R_CURR_MAX 0x%X\r\n", SD_CSD_VDD_R_CURR_MAX(pSd) ); + TRACE_INFO(" .VDD_W_CURR_MIN 0x%X\r\n", SD_CSD_VDD_W_CURR_MIN(pSd) ); + TRACE_INFO(" .VDD_W_CURR_MAX 0x%X\r\n", SD_CSD_VDD_W_CURR_MAX(pSd) ); + TRACE_INFO(" .C_SIZE_MULT 0x%X\r\n", SD_CSD_C_SIZE_MULT(pSd) ); + TRACE_INFO(" .ERASE_BLK_EN 0x%X\r\n", SD_CSD_ERASE_BLK_EN(pSd) ); + TRACE_INFO(" .SECTOR_SIZE 0x%X\r\n", SD_CSD_SECTOR_SIZE(pSd) ); + TRACE_INFO(" .WP_GRP_SIZE 0x%X\r\n", SD_CSD_WP_GRP_SIZE(pSd) ); + TRACE_INFO(" .WP_GRP_ENABLE 0x%X\r\n", SD_CSD_WP_GRP_ENABLE(pSd) ); + TRACE_INFO(" .R2W_FACTOR 0x%X\r\n", SD_CSD_R2W_FACTOR(pSd) ); + TRACE_INFO(" .WRITE_BL_LEN 0x%X\r\n", SD_CSD_WRITE_BL_LEN(pSd) ); + TRACE_INFO(" .WRITE_BL_PARTIAL 0x%X\r\n", SD_CSD_WRITE_BL_PARTIAL(pSd) ); + TRACE_INFO(" .FILE_FORMAT_GRP 0x%X\r\n", SD_CSD_FILE_FORMAT_GRP(pSd) ); + TRACE_INFO(" .COPY 0x%X\r\n", SD_CSD_COPY(pSd) ); + TRACE_INFO(" .PERM_WRITE_PROTECT 0x%X\r\n", SD_CSD_PERM_WRITE_PROTECT(pSd)); + TRACE_INFO(" .TMP_WRITE_PROTECT 0x%X\r\n", SD_CSD_TMP_WRITE_PROTECT(pSd) ); + TRACE_INFO(" .FILE_FORMAT 0x%X\r\n", SD_CSD_FILE_FORMAT(pSd) ); + TRACE_INFO(" .ECC 0x%X\r\n", SD_CSD_ECC(pSd) ); + TRACE_INFO(" .CRC 0x%X\r\n", SD_CSD_CRC(pSd) ); + TRACE_INFO(" .MULT 0x%X\r\n", SD_CSD_MULT(pSd) ); + TRACE_INFO(" .BLOCKNR 0x%X\r\n", SD_CSD_BLOCKNR(pSd) ); + TRACE_INFO(" .BLOCKNR_HC 0x%X\r\n", SD_CSD_BLOCKNR_HC(pSd) ); + TRACE_INFO(" .BLOCK_LEN 0x%X\r\n", SD_CSD_BLOCK_LEN(pSd) ); + TRACE_INFO(" .TOTAL_SIZE 0x%X\r\n", SD_CSD_TOTAL_SIZE(pSd) ); + TRACE_INFO(" .TOTAL_SIZE_HC 0x%X\r\n", SD_CSD_TOTAL_SIZE_HC(pSd) ); + TRACE_INFO(" -SD_TOTAL_SIZE 0x%X\r\n", SD_TOTAL_SIZE(pSd) ); + TRACE_INFO(" -SD_TOTAL_BLOCK 0x%X\r\n", SD_TOTAL_BLOCK(pSd) ); +} + +//------------------------------------------------------------------------------ +/// Display the content of the EXT_CSD register +/// \param pSd +//------------------------------------------------------------------------------ +void SD_DisplayRegisterECSD(SdCard *pSd) +{ + if (pSd->cardType >= CARD_MMC && pSd->cardType <= CARD_MMCHD + && SD_CSD_STRUCTURE(pSd) >= 2) {} + else { + TRACE_INFO("** EXT_CSD NOT SUPPORTED\n\r"); + return; + } + TRACE_INFO("======= EXT_CSD ======="); + #if 0 + { + unsigned int i; + unsigned char *p = (unsigned char *)pSd->extData; + for(i = 0; i < 512; i++) { + if ((i % 16) == 0) TRACE_INFO_WP("\n\r [%3d]:", i); + TRACE_INFO_WP(" %2x", p[i]); + } + TRACE_INFO_WP("\n\r"); + TRACE_INFO("------------------------\n\r"); + } + #else + TRACE_INFO_WP("\n\r"); + #endif + TRACE_INFO(" .S_CMD_SET : 0x%X\n\r", + SD_EXTCSD_S_CMD_SET(pSd)); + TRACE_INFO(" .BOOT_INFO : 0x%X\n\r", + SD_EXTCSD_BOOT_INFO(pSd)); + TRACE_INFO(" .BOOT_SIZE_MULTI : 0x%X\n\r", + SD_EXTCSD_BOOT_SIZE_MULTI(pSd)); + TRACE_INFO(" .ACC_SIZE : 0x%X\n\r", + SD_EXTCSD_ACC_SIZE(pSd)); + TRACE_INFO(" .HC_ERASE_GRP_SIZE : 0x%X\n\r", + SD_EXTCSD_HC_ERASE_GRP_SIZE(pSd)); + TRACE_INFO(" .ERASE_TIMEOUT_MULT : 0x%X\n\r", + SD_EXTCSD_ERASE_TIMEOUT_MULT(pSd)); + TRACE_INFO(" .REL_WR_SEC_C : 0x%X\n\r", + SD_EXTCSD_REL_WR_SEC_C(pSd)); + TRACE_INFO(" .HC_WP_GRP_SIZE : 0x%X\n\r", + SD_EXTCSD_HC_WP_GRP_SIZE(pSd)); + TRACE_INFO(" .S_C_VCC : 0x%X\n\r", + SD_EXTCSD_S_C_VCC(pSd)); + TRACE_INFO(" .S_C_VCCQ : 0x%X\n\r", + SD_EXTCSD_S_C_VCCQ(pSd)); + TRACE_INFO(" .S_A_TIMEOUT : 0x%X\n\r", + SD_EXTCSD_S_A_TIMEOUT(pSd)); + TRACE_INFO(" .SEC_COUNT : 0x%X\n\r", + SD_EXTCSD_SEC_COUNT(pSd)); + TRACE_INFO(" .MIN_PERF_W_8_52 : 0x%X\n\r", + SD_EXTCSD_MIN_PERF_W_8_52(pSd)); + TRACE_INFO(" .MIN_PERF_R_8_52 : 0x%X\n\r", + SD_EXTCSD_MIN_PERF_R_8_52(pSd)); + TRACE_INFO(" .MIN_PERF_W_8_26_4_52 : 0x%X\n\r", + SD_EXTCSD_MIN_PERF_W_8_26_4_52(pSd)); + TRACE_INFO(" .MIN_PERF_R_8_26_4_52 : 0x%X\n\r", + SD_EXTCSD_MIN_PERF_R_8_26_4_52(pSd)); + TRACE_INFO(" .MIN_PERF_W_4_26 : 0x%X\n\r", + SD_EXTCSD_MIN_PERF_W_4_26(pSd)); + TRACE_INFO(" .MIN_PERF_R_4_26 : 0x%X\n\r", + SD_EXTCSD_MIN_PERF_R_4_26(pSd)); + TRACE_INFO(" .PWR_CL_26_360 : 0x%X\n\r", + SD_EXTCSD_PWR_CL_26_360(pSd)); + TRACE_INFO(" .PWR_CL_52_360 : 0x%X\n\r", + SD_EXTCSD_PWR_CL_52_360(pSd)); + TRACE_INFO(" .PWR_CL_26_195 : 0x%X\n\r", + SD_EXTCSD_PWR_CL_26_195(pSd)); + TRACE_INFO(" .PWR_CL_52_195 : 0x%X\n\r", + SD_EXTCSD_PWR_CL_52_195(pSd)); + TRACE_INFO(" .CARD_TYPE : 0x%X\n\r", + SD_EXTCSD_CARD_TYPE(pSd)); + TRACE_INFO(" .CSD_STRUCTURE : 0x%X\n\r", + SD_EXTCSD_CSD_STRUCTURE(pSd)); + TRACE_INFO(" .EXT_CSD_REV : 0x%X\n\r", + SD_EXTCSD_EXT_CSD_REV(pSd)); + TRACE_INFO(" .CMD_SET : 0x%X\n\r", + SD_EXTCSD_CMD_SET(pSd)); + TRACE_INFO(" .CMD_SET_REV : 0x%X\n\r", + SD_EXTCSD_CMD_SET_REV(pSd)); + TRACE_INFO(" .POWER_CLASS : 0x%X\n\r", + SD_EXTCSD_POWER_CLASS(pSd)); + TRACE_INFO(" .HS_TIMING : 0x%X\n\r", + SD_EXTCSD_HS_TIMING(pSd)); + TRACE_INFO(" .BUS_WIDTH : 0x%X\n\r", + SD_EXTCSD_BUS_WIDTH(pSd)); + TRACE_INFO(" .ERASED_MEM_CONT : 0x%X\n\r", + SD_EXTCSD_ERASED_MEM_CONT(pSd)); + TRACE_INFO(" .BOOT_CONFIG : 0x%X\n\r", + SD_EXTCSD_BOOT_CONFIG(pSd)); + TRACE_INFO(" .BOOT_BUS_WIDTH : 0x%X\n\r", + SD_EXTCSD_BOOT_BUS_WIDTH(pSd)); + TRACE_INFO(" .ERASE_GROUP_DEF : 0x%X\n\r", + SD_EXTCSD_ERASE_GROUP_DEF(pSd)); +} + +//------------------------------------------------------------------------------ +/// Display the content of the SCR register +/// \param pSd Pointer to SdCard instance. +//------------------------------------------------------------------------------ +void SD_DisplayRegisterSCR(SdCard *pSd) +{ + if (pSd->cardType >= CARD_SD && pSd->cardType <= CARD_SDHC) {} + else { + TRACE_INFO("** SCR NOT Supported!\n\r"); + return; + } + TRACE_INFO("========== SCR =========="); + #if 0 + { + unsigned int i; + unsigned char *p = (unsigned char*)pSd->extData; + //TRACE_INFO_WP("\n\r"); + //TRACE_INFO("DATA @ 0x%X", (unsigned int)p); + for(i = 0; i < 16; i ++) { + if ((i % 8) == 0) TRACE_INFO_WP("\n\r [%3d]:", i); + TRACE_INFO_WP(" %02x", p[i]); + } + TRACE_INFO_WP("\n\r"); + TRACE_INFO("------------------------\n\r"); + } + #else + TRACE_INFO_WP("\n\r"); + #endif + + TRACE_INFO(" .SCR_STRUCTURE :0x%X\n\r", + SD_SCR_SCR_STRUCTURE(pSd)); + TRACE_INFO(" .SD_SPEC :0x%X\n\r", + SD_SCR_SD_SPEC(pSd)); + TRACE_INFO(" .DATA_STAT_AFTER_ERASE :0x%X\n\r", + SD_SCR_DATA_STAT_AFTER_ERASE(pSd)); + TRACE_INFO(" .SD_SECURITY :0x%X\n\r", + SD_SCR_SD_SECURITY(pSd)); + TRACE_INFO(" .SD_BUS_WIDTHS :0x%X\n\r", + SD_SCR_SD_BUS_WIDTHS(pSd)); +} + +//------------------------------------------------------------------------------ +/// Display the content of the SD Status +/// \param pSd Pointer to SdCard instance. +//------------------------------------------------------------------------------ +void SD_DisplaySdStatus(SdCard *pSd) +{ + if ( pSd->cardType >= CARD_SD + && pSd->cardType <= CARD_SDHC + && (pSd->optCmdBitMap & SD_ACMD13_SUPPORT) ) {} + else { + TRACE_INFO("** SD Status NOT Supported!\n\r"); + return; + } + TRACE_INFO("=========== STAT ============"); + #if 0 + { + unsigned int i; + unsigned char *p = (unsigned char*)pSd->extData; + //TRACE_INFO_WP("\n\r"); + //TRACE_INFO("DATA @ 0x%X", (unsigned int)p); + for(i = 0; i < 72; i ++) { + if ((i % 8) == 0) TRACE_INFO_WP("\n\r [%3d]:", i); + TRACE_INFO_WP(" %02x", p[i]); + } + TRACE_INFO_WP("\n\r"); + TRACE_INFO("------------------------\n\r"); + } + #else + TRACE_INFO_WP("\n\r"); + #endif + + TRACE_INFO(" .DAT_BUS_WIDTH :0x%X\n\r", + SD_STAT_DAT_BUS_WIDTH(pSd)); + TRACE_INFO(" .SECURED_MODE :0x%X\n\r", + SD_STAT_SECURED_MODE(pSd)); + TRACE_INFO(" .SD_CARD_TYPE :0x%X\n\r", + SD_STAT_SD_CARD_TYPE(pSd)); + TRACE_INFO(" .SIZE_OF_PROTECTED_AREA :0x%X\n\r", + SD_STAT_SIZE_OF_PROTECTED_AREA(pSd)); + TRACE_INFO(" .SPEED_CLASS :0x%X\n\r", + SD_STAT_SPEED_CLASS(pSd)); + TRACE_INFO(" .PERFORMANCE_MOVE :0x%X\n\r", + SD_STAT_PERFORMANCE_MOVE(pSd)); + TRACE_INFO(" .AU_SIZE :0x%X\n\r", + SD_STAT_AU_SIZE(pSd)); + TRACE_INFO(" .ERASE_SIZE :0x%X\n\r", + SD_STAT_ERASE_SIZE(pSd)); + TRACE_INFO(" .ERASE_TIMEOUT :0x%X\n\r", + SD_STAT_ERASE_TIMEOUT(pSd)); + TRACE_INFO(" .ERASE_OFFSET :0x%X\n\r", + SD_STAT_ERASE_OFFSET(pSd)); +} diff --git a/memories/sdmmc/sdmmc_mci.h b/memories/sdmmc/sdmmc_mci.h new file mode 100644 index 0000000..0e1bb62 --- /dev/null +++ b/memories/sdmmc/sdmmc_mci.h @@ -0,0 +1,1038 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "sdcard sd mode initialization and identification" +/// +/// !Purpose +/// +/// sdcard sd mode initialization and identification sequence +/// +/// !Description +/// - Host sends Cmd0 to do card reset, card is in "idle state". +/// - Host sends Cmd8 and checks the response of the card, only sdcard supports physical +/// layer version 2.00 will response correctly to Cmd8, and this command is mandatory to be +/// sent before ACmd41 for sdcard which support physical layer version 2.00, to enable new +/// functions or to validate a dual-voltage card. +/// - refer to "If Cmd8 response ok" branch for the initialize of sdcard 2.0. +/// - refer to "If Cmd8 response fail" branch for the initialize of sdcard 1.x, mmc card, sdcard2.0 +/// with invalid voltage. +/// - If Cmd8 response ok +/// - Host sends ACmd41* with argument "HCS" equal to "1". +/// - If the response to ACmd41 failed, it means the card does not match the voltage +/// desired by the host, the card will be put into inactive state, initialize ends. +/// - If the response with "CCS" equal to "1", the card is a version 2.0 high capacity sdcard, +/// refer to "Card Initialize" for the succeeding initialize sequence. +/// - If the response with "CCS" equal to "0", the card is a version 2.0 standard capacity sdcard. +/// refer to "Card Initialize" for the succeeding initialize sequence. +/// - If Cmd8 response fail +/// - Host sends ACmd41* argument "HCS" equal to "0". +/// - If the response to ACmd41 ok, the card is a version 1.x sdcard, refer to "Card Initialize" for +/// the succeeding initialize sequence. +/// - If the response to ACmd41 fails +/// - Host sends Cmd0 to reset card. +/// - Host sends Cmd1 with argument "hdSupport" equal to "1" to card. +/// - If card has response to Cmd1, the card is a MMC card, refer to "Card Initialize" for the +/// succeeding initialize sequence. Furthermore, if the response with bit[30:29] equal to +/// "00" or "11", the card is a High Density MMC, else the card is a standard MMC. +/// - If card has no response to Cmd1, the card is either an unknown card or a card does +/// not match host's voltage, the initialize ends. +/// - Card Initialize +/// - Host sends Cmd2 to get the its unique card identification number (CID). +/// - Host sends Cmd3 to ask the card to publish a new relative card address (RCA), once the +/// RCA is received the card state changes to the "stand-by state". +/// - Host sends Cmd9 to get the Card Specific Data (CSD). +/// - At this stage, the initialization and identification process is over, the following steps are done +/// for the sdcard's succeeding operation. +/// - Host sends Cmd13 to obtain the card status, make sure the card is "ready-for-data". +/// - Host sends Cmd7 to transit card in "transfer state". +/// - If card is a sdcard*, hosts send ACmd6 to set bus to 4-wire mode. +/// - If card is a mmc card, the bus is set as 1-wire mode. +/// +/// \note Send Cmd55 before send ACmd41. \endnote +/// \note sdcard include ver 1.x sdcard, ver2.0 standard capacity sdcard, ver2.0 high capacity sdcard \endnote +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard sd mode write" +/// +/// !Purpose +/// +/// sdcard sd mode write process +/// +/// !Description +/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send +/// Cmd12 to stop the current operation and to transit sdcard to "stand-by state". +/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". +/// - Host sends Cmd25 to do multiple blocks write, the address here is different between high capacity +/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's +/// address is equal to block number times 512. +/// +/// !Write Optimization +/// - To optimize the write multiple blocks, try to keep the sdcard in the "rcv state" as long as possible. +/// after send WRITE_MULTIPLE_BLOCK command, set the block number in the MCI_BLKR as 0, each time a +/// new write start, do not re-send the WRITE_MULTIPLE_BLOCK command, just re-configure the PDC. In +/// this case, host does not have to send STOP_TRANSMISSION to transfer the sdcard's state and the +/// performance is optimized. +/// - MoveToTransferState will check the state of the sdcard, and send STOP_TRANSMISSION if need +/// to transfer state. Normally this function is called between the state tranfer. +/// - ContinuousWrite will be called if WRITE_MULTIPLE_BLOCK already been sent and this function +/// will not re-send the write command, but will re-configure PDC accordingly. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard sd mode read" +/// +/// !Purpose +/// +/// sdcard sd mode read process +/// +/// !Description +/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send +/// Cmd12 to stop the current operation and to transit sdcard to "stand-by state". +/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". +/// - Host sends Cmd18 to do multiple blocks read, the address here is different between high capacity +/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's +/// address is equal to block number times 512. +/// +/// !Read Optimization +/// - To optimize the read multiple blocks, try to keep the sdcard in the "data state" as long as possible. +/// after send READ_MULTIPLE_BLOCK command, set the block number in the MCI_BLKR as 0, each time a +/// new read start, do not re-send the READ_MULTIPLE_BLOCK command, just re-configure the PDC. In this +/// case, host does not have to send STOP_TRANSMISSION to transfer the sdcard's state and the +/// performance is optimized. +/// - MoveToTransferState will check the state of the sdcard, and send STOP_TRANSMISSION if need +/// to transfer state. Normally this function is called between the state tranfer. +/// - ContinuousRead will be called if READ_MULTIPLE_BLOCK already been sent and this function +/// will not re-send the read command, but will re-configure PDC accordingly. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdhc" +/// +/// !Purpose +/// +/// highlight of sdhc +/// +/// !Sdhc initialization and identification +/// +/// - Refer to page "sdcard sd mode initialization and identification" for the initialization and identification +/// sequence of a sdhc. +/// +/// !Functional difference between sdhc and standard capacity sdcard +/// +/// - Command argument is different: +/// - Sdhc uses block address format in memory access commands*, block length is fixed to 512 bytes. +/// - Standard capacity sdcard uses byte address format in memory access commands, block length +/// is defined in Cmd16. +/// - Partial access and misalign access are disabled in sdhc as the block address is used. +/// - Sdhc does not support write-protected commands (Cmd28, Cmd29, Cmd30). +/// +/// \note Memory access commands means block read commands (CMD17, CMD18), block write commands +/// (CMD24, CMD25), and block erase commands (CMD32, CMD33). +/// +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdmmc_mci" +/// +/// !Purpose +/// +/// Implementation for sdcard sd mode physical layer driver. Supply a set of sdcard sd mode's +/// interface. +/// +/// !Usage +/// +/// -# SD_Init: Run the SDcard initialization sequence +/// -# SD_MCI_Init : Run the SDcard SD Mode initialization sequence +/// -# SD_Stop: Stop the SDcard by sending Cmd12 +/// -# SD_ReadBlock : Read blocks of data +/// -# SD_WriteBlock : Write blocks of data +/// -# Cmd0 : Resets all cards to idle state +/// -# Cmd1 : MMC send operation condition command +/// -# Cmd2 : Asks any card to send the CID numbers on the CMD line +/// -# Cmd3 : Ask the card to publish a new relative address +/// -# Cmd7 : Command toggles a card between the stand-by and transfer states or between +/// the programming and disconnect states +/// -# Cmd8 : Sends SD Memory Card interface condition, which includes host supply voltage +/// information and asks the card whether card supports voltage +/// -# Cmd9 : Addressed card sends its card-specific data (CSD) on the CMD line +/// -# Cmd12 : Forces the card to stop transmission +/// -# Cmd13 : Addressed card sends its status register +/// -# Cmd16 : Set block length +/// -# Cmd18 : Read multiple blocks +/// -# Cmd25 : Write multiple blocks +/// -# Cmd55 : App command, should be sent before application specific command +/// -# Acmd6 : Defines the data bus width +/// -# Acmd41 : Asks to all cards to send their operations conditions +/// -# CmdEMMC8 : Sends eMMC EXT_CSD command +/// -# CmdEMMC6 : Switches the mode of operation of the selected card or modifies the +/// EXT_CSD registers +//------------------------------------------------------------------------------ + +#ifndef SDMMC_MCI_H +#define SDMMC_MCI_H + +//------------------------------------------------------------------------------ +// Header +//------------------------------------------------------------------------------ + +#include + +#if !defined(MCI2_INTERFACE) +#include +#else +#include +#endif + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + + +/// There was an error with the SD driver. +#define SD_ERROR_DRIVER 1 +/// The SD card did not answer the command. +#define SD_ERROR_NORESPONSE 2 +/// The SD card did not answer the command. +#define SD_ERROR_NOT_INITIALIZED 3 +/// The SD card is busy +#define SD_ERROR_BUSY 4 +/// The operation is not supported +#define SD_ERROR_NOT_SUPPORT 5 + +/// Card types +#define CARD_TYPE_bmHC (1 << 0) /// Bit for High-Capacity(Density) +#define CARD_TYPE_bmSDMMC (0x3 << 1) /// Bit mask for SD/MMC +#define CARD_TYPE_bmSD (0x1 << 1) /// Bit for SD +#define CARD_TYPE_bmMMC (0x2 << 1) /// Bit for MMC +#define CARD_TYPE_bmSDIO (1 << 3) /// Bit for SDIO + +/// Card can not identified +#define CARD_UNKNOWN (0) +/// SD Card (0x2) +#define CARD_SD (CARD_TYPE_bmSD) +/// SD High Capacity Card (0x3) +#define CARD_SDHC (CARD_TYPE_bmSD|CARD_TYPE_bmHC) +/// MMC Card (0x4) +#define CARD_MMC (CARD_TYPE_bmMMC) +/// MMC High-Density Card (0x5) +#define CARD_MMCHD (CARD_TYPE_bmMMC|CARD_TYPE_bmHC) +/// SDIO only card (0x8) +#define CARD_SDIO (CARD_TYPE_bmSDIO) +/// SDIO Combo, with SD embedded (0xA) +#define CARD_SDCOMBO (CARD_TYPE_bmSDIO|CARD_SD) +/// SDIO Combo, with SDHC embedded (0xB) +#define CARD_SDHCCOMBO (CARD_TYPE_bmSDIO|CARD_SDHC) + +/// SD card block size in bytes. +#define SD_BLOCK_SIZE 512 +/// SD card block size binary shift value +#define SD_BLOCK_SIZE_BIT 9 + +//- MMC Card Command Types +/// Broadcast commands (bc), no response +#define MMC_CCT_BC 0 +/// Broadcase commands with response (bcr) +#define MMC_CCT_BCR 1 +/// Addressed commands (ac), no data transfer on DAT lines +#define MMC_CCT_AC 2 +/// Addressed data transfer commands (adtc), data transfer on DAT lines +#define MMC_CCT_ADTC 3 + +//- MMC Card Command Classes (CCC) +/// Class 0: basic +#define MMC_CCC_BASIC 0 +/// Class 1: stream read +#define MMC_CCC_STREAM_READ 1 +/// Class 2: block read +#define MMC_CCC_BLOCK_READ 2 +/// Class 3: stream write +#define MMC_CCC_STREAM_WRITE 3 +/// Class 4: block write +#define MMC_CCC_BLOCK_WRITE 4 +/// Class 5: erase +#define MMC_CCC_ERASE 5 +/// Class 6: write protection +#define MMC_CCC_WRITE_PROTECTION 6 +/// Class 7: lock card +#define MMC_CCC_LOCK_CARD 7 +/// Class 8: application specific +#define MMC_CCC_APP_SPEC 8 +/// Class 9: I/O mode +#define MMC_CCC_IO_MODE 9 + +//- MMC/SD Card Command Response Type +/// R1 (normal response command), 48bits +#define MMC_RESP_R1 1 +#define SD_RESP_R1 MMC_RESP_R1 +/// R1b: busy signal transmitted on the data line DAT0 +#define MMC_RESP_R1b 1 +#define SD_RESP_R1b MMC_RESP_R1b +/// R2: 136bits, CID, CSD register +#define MMC_RESP_R2 2 +#define SD_RESP_R2 MMC_RESP_R2 +/// R3: 48bits, OCR +#define MMC_RESP_R3 3 +#define SD_RESP_R3 MMC_RESP_R3 +/// R4 (Fast I/O), 48bits +#define MMC_RESP_R4 4 +/// R5 (Interrupt request), 48bits +#define MMC_RESP_R5 5 +/// R6 (Published RCA response), 48bits +#define SD_RESP_R6 6 +/// R7 (Card interface condition), 48bits +#define SD_RESP_R7 7 + +//- MMC Card CMD6 access mode +#define MMC_SWITCH_CMDSET 0 +#define MMC_SWITCH_SETBITS 1 +#define MMC_SWITCH_CLRBITS 2 +#define MMC_SWITCH_WRITE 3 + +//-MMC Boot partition enable +/// Boot partition 1 enabled for boot +#define MMC_BOOT_PART_1 1 +/// Boot partition 2 enabled for boot +#define MMC_BOOT_PART_2 2 +/// User area enabled for boot +#define MMC_BOOT_PART_USER 7 + +//-MMC Boot partition access +/// R/W boot partition 1 +#define MMC_BOOT_ACC_PART1 1 +/// R/W boot partition 2 +#define MMC_BOOT_ACC_PART2 2 + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +// CID register access macros (128 bits, 4 * 32 bits). +#define SD_CID(pSd, bitfield, bits) ( (pSd->cid[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) +#define SD_CID_MID(pSd) SD_CID(pSd, 120, 8) ///< Manufacturer ID +#define SD_CID_BGA(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) +#define SD_CID_CBS(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) +#define SD_CID_OID_BYTE_1(pSd) SD_CID(pSd, 112, 8) ///< OEM/Application ID byte 1 +#define SD_CID_OID_BYTE_0(pSd) SD_CID(pSd, 104, 8) ///< OEM/Application ID byte 0 +#define SD_CID_PNM_BYTE_4(pSd) SD_CID(pSd, 96, 8) ///< Product revision byte 4 +#define SD_CID_PNM_BYTE_3(pSd) SD_CID(pSd, 88, 8) ///< Product revision byte 3 +#define SD_CID_PNM_BYTE_2(pSd) SD_CID(pSd, 80, 8) ///< Product revision byte 2 +#define SD_CID_PNM_BYTE_1(pSd) SD_CID(pSd, 72, 8) ///< Product revision byte 1 +#define SD_CID_PNM_BYTE_0(pSd) SD_CID(pSd, 64, 8) ///< Product revision byte 0 +#define SD_CID_PRV_1(pSd) SD_CID(pSd, 24, 8) ///< Product serial number 1 +#define SD_CID_PRV_2(pSd) SD_CID(pSd, 32,24) ///< Product serial number 2 +#define SD_CID_MDT_YEAR(pSd) (SD_CID(pSd, 12, 8))+2000///< Manufacturing date year +#define SD_CID_MDT_MONTH(pSd) SD_CID(pSd, 8, 4) ///< Manufacturing date month +#define SD_CID_CRC(pSd) SD_CID(pSd, 1, 7) ///< CRC7 checksum + +// CSD register access macros (128 bits, 4 * 32 bits). +#define SD_CSD(pSd, bitfield, bits) ((((pSd)->csd)[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) +#define SD_CSD_STRUCTURE(pSd) SD_CSD(pSd, 126, 2) ///< CSD structure 00b Version 1.0 01b version 2.0 High Cap +#define SD_CSD_SPEC_VERS(pSd) SD_CSD(pSd, 122, 4) ///< System Specification Version Number +#define SD_CSD_TAAC(pSd) SD_CSD(pSd, 112, 8) ///< Data read-access-time-1 +#define SD_CSD_NSAC(pSd) SD_CSD(pSd, 104, 8) ///< Data read access-time-2 in CLK cycles +#define SD_CSD_TRAN_SPEED(pSd) SD_CSD(pSd, 96, 8) ///< Max. data transfer rate +#define SD_CSD_CCC(pSd) SD_CSD(pSd, 84, 12) ///< Card command class +#define SD_CSD_READ_BL_LEN(pSd) SD_CSD(pSd, 80, 4) ///< Max. read data block length +#define SD_CSD_READ_BL_PARTIAL(pSd) SD_CSD(pSd, 79, 1) ///< Bartial blocks for read allowed +#define SD_CSD_WRITE_BLK_MISALIGN(pSd) SD_CSD(pSd, 78, 1) ///< Write block misalignment +#define SD_CSD_READ_BLK_MISALIGN(pSd) SD_CSD(pSd, 77, 1) ///< Read block misalignment +#define SD_CSD_DSR_IMP(pSd) SD_CSD(pSd, 76, 1) ///< DSP implemented +#define SD_CSD_C_SIZE(pSd) ((SD_CSD(pSd, 72, 2) << 10) + \ + (SD_CSD(pSd, 64, 8) << 2) + \ + SD_CSD(pSd, 62, 2)) ///< Device size +#define SD_CSD_C_SIZE_HC(pSd) ((SD_CSD(pSd, 64, 6) << 16) + \ + (SD_CSD(pSd, 56, 8) << 8) + \ + SD_CSD(pSd, 48, 8)) ///< Device size v2.0 High Capacity +#define SD_CSD_VDD_R_CURR_MIN(pSd) SD_CSD(pSd, 59, 3) ///< Max. read current @VDD min +#define SD_CSD_VDD_R_CURR_MAX(pSd) SD_CSD(pSd, 56, 3) ///< Max. read current @VDD max +#define SD_CSD_VDD_W_CURR_MIN(pSd) SD_CSD(pSd, 53, 3) ///< Max. write current @VDD min +#define SD_CSD_VDD_W_CURR_MAX(pSd) SD_CSD(pSd, 50, 3) ///< Max. write current @VDD max +#define SD_CSD_C_SIZE_MULT(pSd) SD_CSD(pSd, 47, 3) ///< Device size multiplier +#define SD_CSD_ERASE_BLK_EN(pSd) SD_CSD(pSd, 46, 1) ///< Erase single block enable +#define MMC_CSD_ERASE_BLK_EN(pSd) SD_CSD(pSd, 46, 1) ///< Erase single block enable +#define MMC_CSD_ERASE_GRP_SIZE(pSd) SD_CSD(pSd, 42, 4) ///< Erase group size +#define SD_CSD_ERASE_GRP_MULT(pSd) SD_CSD(pSd, 37, 4) ///< Erase group size multiplier +#define SD_CSD_SECTOR_SIZE(pSd) ((SD_CSD(pSd, 40, 6) << 1) + SD_CSD(pSd, 39, 1)) ///< Erase sector size +#define SD_CSD_WP_GRP_SIZE(pSd) SD_CSD(pSd, 32, 7) ///< Write protect group size +#define SD_CSD_WP_GRP_ENABLE(pSd) SD_CSD(pSd, 31, 1) ///< write protect group enable +#define SD_CSD_R2W_FACTOR(pSd) SD_CSD(pSd, 26, 3) ///< Write speed factor +#define SD_CSD_WRITE_BL_LEN(pSd) ((SD_CSD(pSd, 24, 2) << 2) + SD_CSD(pSd, 22, 2)) ///< Max write block length +#define SD_CSD_WRITE_BL_PARTIAL(pSd) SD_CSD(pSd, 21, 1) ///< Partial blocks for write allowed +#define SD_CSD_CONTENT_PROT_APP(pSd) SD_CSD(pSd, 16, 1) ///< File format group +#define SD_CSD_FILE_FORMAT_GRP(pSd) SD_CSD(pSd, 15, 1) ///< File format group +#define SD_CSD_COPY(pSd) SD_CSD(pSd, 14, 1) ///< Copy flag (OTP) +#define SD_CSD_PERM_WRITE_PROTECT(pSd) SD_CSD(pSd, 13, 1) ///< Permanent write protect +#define SD_CSD_TMP_WRITE_PROTECT(pSd) SD_CSD(pSd, 12, 1) ///< Temporary write protection +#define SD_CSD_FILE_FORMAT(pSd) SD_CSD(pSd, 10, 2) ///< File format +#define SD_CSD_ECC(pSd) SD_CSD(pSd, 8, 2) ///< CRC +#define SD_CSD_CRC(pSd) SD_CSD(pSd, 1, 7) ///< CRC +#define SD_CSD_MULT(pSd) (1 << (SD_CSD_C_SIZE_MULT(pSd) + 2)) +#define SD_CSD_BLOCKNR(pSd) ((SD_CSD_C_SIZE(pSd) + 1) * SD_CSD_MULT(pSd)) +#define SD_CSD_BLOCKNR_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 1024) +#define SD_CSD_BLOCK_LEN(pSd) (1 << SD_CSD_READ_BL_LEN(pSd)) +#define SD_CSD_TOTAL_SIZE(pSd) (SD_CSD_BLOCKNR(pSd) * SD_CSD_BLOCK_LEN(pSd)) +#define SD_CSD_TOTAL_SIZE_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 512* 1024) +#define SD_TOTAL_SIZE(pSd) ((pSd)->totalSize) +#define SD_TOTAL_BLOCK(pSd) ((pSd)->blockNr) + +// SCR register access macros (64 bits, 2 * 32 bits, 8 * 8 bits). +#define SD_EXT_OFFSET_SD_SCR 0 // DW +#define SD_SCR(pSd, bitfield, bits) \ + ( ((char*)(pSd)->extData)[7 - ((bitfield)/8)] >> ((bitfield)%8) \ + & ((1 << (bits)) - 1) \ + ) +#define SD_SCR_SCR_STRUCTURE(pSd) SD_SCR(pSd, 60, 4) +#define SD_SCR_SCR_STRUCTURE_1_0 0 +#define SD_SCR_SD_SPEC(pSd) SD_SCR(pSd, 56, 4) +#define SD_SCR_SD_SPEC_1_0_01 0 +#define SD_SCR_SD_SPEC_1_10 1 +#define SD_SCR_SD_SPEC_2_00 2 +#define SD_SCR_DATA_STAT_AFTER_ERASE(pSd) SD_SCR(pSd, 55, 1) +#define SD_SCR_SD_SECURITY(pSd) SD_SCR(pSd, 52, 3) +#define SD_SCR_SD_SECURITY_NO 0 +#define SD_SCR_SD_SECURITY_NOTUSED 1 +#define SD_SCR_SD_SECURITY_1_01 2 +#define SD_SCR_SD_SECURITY_2_00 3 +#define SD_SCR_SD_BUS_WIDTHS(pSd) SD_SCR(pSd, 48, 4) +#define SD_SCR_SD_BUS_WIDTH_1BITS (1 << 0) +#define SD_SCR_SD_BUS_WIDTH_4BITS (1 << 2) + +// SD Status access macros (512 bits, 16 * 32 bits, 64 * 8 bits). +#define SD_EXT_OFFSET_SD_STAT 2 // DW +#define SD_STAT(pSd, bitfield, bits) \ + ( ((char*)&(pSd)->extData[2])[63 - ((bitfield)/8)] >> ((bitfield)%8) \ + & ((1 << (bits)) - 1) \ + ) +/// Bus width, 00: default, 10:4-bit +#define SD_STAT_DAT_BUS_WIDTH(pSd) SD_STAT(pSd, 510, 2) +#define SD_STAT_DATA_BUS_WIDTH_1BIT 0x0 +#define SD_STAT_DATA_BUS_WIDTH_4BIT 0x2 +/// Secured Mode +#define SD_STAT_SECURED_MODE(pSd) SD_STAT(pSd, 509, 1) +/// SD Memory Cards as defined in 1.01~2.00 +#define SD_STAT_SD_CARD_TYPE(pSd) (SD_STAT(pSd, 480, 8) \ + + (SD_STAT(pSd, 488, 8) << 8) ) +/// STD: ThisSize*Multi*BlockLen, HC: Size in bytes +#define SD_STAT_SIZE_OF_PROTECTED_AREA(pSd) (SD_STAT(pSd, 448, 8) \ + + (SD_STAT(pSd, 456, 8) << 8) \ + + (SD_STAT(pSd, 464, 8) << 16) \ + + (SD_STAT(pSd, 472, 8) << 24) ) +/// Speed Class, value can be calculated by Pw/2 +#define SD_STAT_SPEED_CLASS(pSd) SD_STAT(pSd, 440, 8) +#define SD_STAT_SPEED_CLASS_0 0 +#define SD_STAT_SPEED_CLASS_2 1 // >= 2MB/s +#define SD_STAT_SPEED_CLASS_4 2 // >= 4MB/s +#define SD_STAT_SPEED_CLASS_6 3 // >= 6MB/s +/// 8-bit, by 1MB/s step. +#define SD_STAT_PERFORMANCE_MOVE(pSd) SD_STAT(pSd, 432, 8) +/// AU Size, in power of 2 from 16KB +#define SD_STAT_AU_SIZE(pSd) SD_STAT(pSd, 428, 4) +#define SD_STAT_AU_SIZE_16K 1 +#define SD_STAT_AU_SIZE_32K 2 +#define SD_STAT_AU_SIZE_64K 3 +#define SD_STAT_AU_SIZE_128K 4 +#define SD_STAT_AU_SIZE_256K 5 +#define SD_STAT_AU_SIZE_512K 6 +#define SD_STAT_AU_SIZE_1M 7 +#define SD_STAT_AU_SIZE_2M 8 +#define SD_STAT_AU_SIZE_4M 9 +/// 16-bit, number of AUs erased. +#define SD_STAT_ERASE_SIZE(pSd) (SD_STAT(pSd, 408, 8) \ + + (SD_STAT(pSd, 416, 8) << 8) ) +#define SD_STAT_ERASE_TIMEOUT(pSd) SD_STAT(pSd, 402, 6) +#define SD_STAT_ERASE_OFFSET(pSd) SD_STAT(pSd, 400, 2) + +// SD Switch Status access macros (512 bits, 16 * 32 bits, 64 * 8 bits). +#define SD_SW_STAT(p, bitfield, bits) \ + ( ((char*)(p))[63 - ((bitfield)/8)] >> ((bitfield)%8) \ + & ((1 << (bits)) - 1) \ + ) +#define SD_SW_STAT_MAX_CURRENT_CONSUMPTION(p) ( SD_SW_STAT(p, 496, 8) \ + + (SD_SW_STAT(p, 504, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP6_INFO(p) ( SD_SW_STAT(p, 480, 8) \ + + (SD_SW_STAT(p, 488, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP5_INFO(p) ( SD_SW_STAT(p, 464, 8) \ + + (SD_SW_STAT(p, 472, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP4_INFO(p) ( SD_SW_STAT(p, 448, 8) \ + + (SD_SW_STAT(p, 456, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP3_INFO(p) ( SD_SW_STAT(p, 432, 8) \ + + (SD_SW_STAT(p, 440, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP2_INFO(p) ( SD_SW_STAT(p, 416, 8) \ + + (SD_SW_STAT(p, 424, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP1_INFO(p) ( SD_SW_STAT(p, 400, 8) \ + + (SD_SW_STAT(p, 408, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP6_RC(p) SD_SW_STAT(p, 396, 4) +#define SD_SW_STAT_FUN_GRP5_RC(p) SD_SW_STAT(p, 392, 4) +#define SD_SW_STAT_FUN_GRP4_RC(p) SD_SW_STAT(p, 388, 4) +#define SD_SW_STAT_FUN_GRP3_RC(p) SD_SW_STAT(p, 384, 4) +#define SD_SW_STAT_FUN_GRP2_RC(p) SD_SW_STAT(p, 380, 4) +#define SD_SW_STAT_FUN_GRP1_RC(p) SD_SW_STAT(p, 376, 4) +#define SD_SW_STAT_FUN_GRP_RC_ERROR 0xF +#define SD_SW_STAT_DATA_STRUCT_VER(p) SD_SW_STAT(p, 368, 8) +#define SD_SW_STAT_FUN_GRP6_BUSY(p) ( SD_SW_STAT(p, 352, 8) \ + + (SD_SW_STAT(p, 360, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP5_BUSY(p) ( SD_SW_STAT(p, 336, 8) \ + + (SD_SW_STAT(p, 344, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP4_BUSY(p) ( SD_SW_STAT(p, 320, 8) \ + + (SD_SW_STAT(p, 328, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP3_BUSY(p) ( SD_SW_STAT(p, 304, 8) \ + + (SD_SW_STAT(p, 312, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP2_BUSY(p) ( SD_SW_STAT(p, 288, 8) \ + + (SD_SW_STAT(p, 296, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP1_BUSY(p) ( SD_SW_STAT(p, 272, 8) \ + + (SD_SW_STAT(p, 280, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP_FUN_BUSY(funNdx) (1 << (funNdx)) + +// EXT_CSD register definition. +#define SD_EXTCSD_S_CMD_SET_INDEX 504 // Below belongs to Properties Segment +#define SD_EXTCSD_BOOT_INFO_INDEX 228 +#define SD_EXTCSD_BOOT_SIZE_MULTI_INDEX 226 +#define SD_EXTCSD_ACC_SIZE_INDEX 225 +#define SD_EXTCSD_HC_ERASE_GRP_SIZE_INDEX 224 +#define SD_EXTCSD_ERASE_TIMEOUT_MULT_INDEX 223 +#define SD_EXTCSD_REL_WR_SEC_C_INDEX 222 +#define SD_EXTCSD_HC_WP_GRP_SIZE_INDEX 221 +#define SD_EXTCSD_S_C_VCC_INDEX 220 +#define SD_EXTCSD_S_C_VCCQ_INDEX 219 +#define SD_EXTCSD_S_A_TIMEOUT_INDEX 217 +#define SD_EXTCSD_SEC_COUNT_INDEX 212 +#define SD_EXTCSD_MIN_PERF_W_8_52_INDEX 210 +#define SD_EXTCSD_MIN_PERF_R_8_52_INDEX 209 +#define SD_EXTCSD_MIN_PERF_W_8_26_4_52_INDEX 208 +#define SD_EXTCSD_MIN_PERF_R_8_26_4_52_INDEX 207 +#define SD_EXTCSD_MIN_PERF_W_4_26_INDEX 206 +#define SD_EXTCSD_MIN_PERF_R_4_26_INDEX 205 +#define SD_EXTCSD_PWR_CL_26_360_INDEX 203 +#define SD_EXTCSD_PWR_CL_52_360_INDEX 202 +#define SD_EXTCSD_PWR_CL_26_195_INDEX 201 +#define SD_EXTCSD_PWR_CL_52_195_INDEX 200 +#define SD_EXTCSD_CARD_TYPE_INDEX 196 +#define SD_EXTCSD_CSD_STRUCTURE_INDEX 194 +#define SD_EXTCSD_EXT_CSD_REV_INDEX 192 + +#define SD_EXTCSD_CMD_SET_INDEX 191 //Below belongs to Mode Segment +#define SD_EXTCSD_CMD_SET_REV_INDEX 189 +#define SD_EXTCSD_POWER_CLASS_INDEX 187 +#define SD_EXTCSD_HS_TIMING_INDEX 185 +#define SD_EXTCSD_BUS_WIDTH_INDEX 183 +#define SD_EXTCSD_ERASED_MEM_CONT_INDEX 181 +#define SD_EXTCSD_BOOT_CONFIG_INDEX 179 +#define SD_EXTCSD_BOOT_BUS_WIDTH_INDEX 177 +#define SD_EXTCSD_ERASE_GROUP_DEF_INDEX 175 + +// Ext_CSD register access marco +#define MMC_EXTCSD_U8(pSd, bytefield) \ + ( ((unsigned char*)((pSd)->extData))[(bytefield)] ) +#define MMC_EXTCSD_U16(pSd, bytefield) \ + ( (((unsigned char*)((pSd)->extData))[(bytefield) ] << 0) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 1] << 8) ) +#define MMC_EXTCSD_U32(pSd, bytefield) \ + ( (((unsigned char*)((pSd)->extData))[(bytefield) ] << 0) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 1] << 8) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 2] << 16) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 24] << 24) ) +#define MMC_EXTCSD(pSd) ((unsigned char*)((pSd)->extData)) +#define SD_EXTCSD_S_CMD_SET(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_CMD_SET_INDEX]) // Supported Command Sets +#define SD_EXTCSD_BOOT_INFO(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_INFO_INDEX]) // Boot information +#define SD_EXTCSD_BOOT_SIZE_MULTI(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_SIZE_MULTI_INDEX]) // Boot partition size +#define SD_EXTCSD_ACC_SIZE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ACC_SIZE_INDEX]) // Access size +#define SD_EXTCSD_HC_ERASE_GRP_SIZE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_HC_ERASE_GRP_SIZE_INDEX]) // High-capacity erase unit size +#define SD_EXTCSD_ERASE_TIMEOUT_MULT(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ERASE_TIMEOUT_MULT_INDEX]) // High-capacity erase timeout +#define SD_EXTCSD_REL_WR_SEC_C(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_REL_WR_SEC_C_INDEX]) // Reliable write sector count +#define SD_EXTCSD_HC_WP_GRP_SIZE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_HC_WP_GRP_SIZE_INDEX]) // High-capacity write protect group size +#define SD_EXTCSD_S_C_VCC(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_C_VCC_INDEX]) // Sleep current(VCC) +#define SD_EXTCSD_S_C_VCCQ(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_C_VCCQ_INDEX]) // Sleep current(VCCQ) +#define SD_EXTCSD_S_A_TIMEOUT(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_A_TIMEOUT_INDEX]) // Sleep/awake timeout +#define SD_EXTCSD_SEC_COUNT(pSd) ((MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX]) + \ + (MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX+1] << 8 ) + \ + (MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX+2] << 16 ) + \ + (MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX+3] << 24 )) //Sector Count +#define SD_EXTCSD_MIN_PERF_W_8_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_W_8_52_INDEX]) // Minimum Write Performance for 8bit at 52MHz +#define SD_EXTCSD_MIN_PERF_R_8_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_R_8_52_INDEX]) // Minimum Read Performance for 8bit at 52MHz +#define SD_EXTCSD_MIN_PERF_W_8_26_4_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_W_8_26_4_52_INDEX]) // Minimum Write Performance for 8bit at 26MHz, for 4bit at 52MHz +#define SD_EXTCSD_MIN_PERF_R_8_26_4_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_R_8_26_4_52_INDEX]) // Minimum Read Performance for 8bit at 26MHz, for 4bit at 52MHz +#define SD_EXTCSD_MIN_PERF_W_4_26(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_W_4_26_INDEX]) // Minimum Write Performance for 4bit at 26MHz +#define SD_EXTCSD_MIN_PERF_R_4_26(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_R_4_26_INDEX]) // Minimum Read Performance for 4bit at 26MHz +#define SD_EXTCSD_PWR_CL_26_360(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_26_360_INDEX]) // Power class for 26MHz at 3.6v +#define SD_EXTCSD_PWR_CL_52_360(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_52_360_INDEX]) // Power class for 52MHz at 3.6v +#define SD_EXTCSD_PWR_CL_26_195(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_26_195_INDEX]) // Power class for 26MHz at 1.95v +#define SD_EXTCSD_PWR_CL_52_195(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_52_195_INDEX]) // Power class for 52MHz at 1.95v +#define SD_EXTCSD_CARD_TYPE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CARD_TYPE_INDEX]) // Card type +#define SD_EXTCSD_CSD_STRUCTURE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CSD_STRUCTURE_INDEX]) // CSD structure version +#define SD_EXTCSD_EXT_CSD_REV(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_EXT_CSD_REV_INDEX]) // Extended CSD structure version +#define SD_EXTCSD_CMD_SET(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CMD_SET_INDEX]) // Command set +#define SD_EXTCSD_CMD_SET_REV(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CMD_SET_REV_INDEX]) // Command set revision +#define SD_EXTCSD_POWER_CLASS(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_POWER_CLASS_INDEX]) // Power class +#define SD_EXTCSD_HS_TIMING(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_HS_TIMING_INDEX]) // High-speed interface timing +#define SD_EXTCSD_BUS_WIDTH(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BUS_WIDTH_INDEX]) // Bus width mode +#define SD_EXTCSD_ERASED_MEM_CONT(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ERASED_MEM_CONT_INDEX]) // Erased memory content +#define SD_EXTCSD_BOOT_CONFIG(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_CONFIG_INDEX]) // Boot configuration +#define SD_EXTCSD_BOOT_BUS_WIDTH(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_BUS_WIDTH_INDEX]) // Boot bus width +#define SD_EXTCSD_ERASE_GROUP_DEF(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ERASE_GROUP_DEF_INDEX]) // High-density erase group definition + +// EXTCSD total size and block number +#define SD_EXTCSD_TOTAL_SIZE(pSd) (SD_EXTCSD_SEC_COUNT(pSd)*512) +#define SD_EXTCSD_BLOCKNR(pSd) (SD_EXTCSD_SEC_COUNT(pSd)) + +// Bus width Byte +#define SD_EXTCSD_BUS_WIDTH_1BIT (0x0UL) +#define SD_EXTCSD_BUS_WIDTH_4BIT (0x1UL) +#define SD_EXTCSD_BUS_WIDTH_8BIT (0x2UL) + +// High speed mode +#define SD_EXTCSD_HS_TIMING_ENABLE (0x1UL) +#define SD_EXTCSD_HS_TIMING_DISABLE (0x0UL) + +// Boot config +#define SD_EXTCSD_BOOT_PARTITION_ACCESS (0x7UL) // boot partition access +#define SD_EXTCSD_BOOT_PART_NO_ACCESS (0x0UL) +#define SD_EXTCSD_BOOT_PART_RW_PART1 (0x1UL) +#define SD_EXTCSD_BOOT_PART_RW_PART2 (0x2UL) +#define SD_EXTCSD_BOOT_PARTITION_ENABLE (0x7UL << 3) // boot partition enable +#define SD_EXTCSD_BOOT_PART_DISABLE (0x0UL << 3) +#define SD_EXTCSD_BOOT_PART_ENABLE_PART1 (0x1UL << 3) +#define SD_EXTCSD_BOOT_PART_ENABLE_PART2 (0x2UL << 3) +#define SD_EXTCSD_BOOT_PART_ENABLE_USER (0x7UL << 3) +#define SD_EXTCSD_BOOT_PARTITION_ACK (0x1UL << 7) // boot acknowledge +#define SD_EXTCSD_BOOT_PART_NOACK (0x0UL << 7) +#define SD_EXTCSD_BOOT_PART_ACK (0x1UL << 7) + +// Boot bus width +#define SD_EXTCSD_BOOT_BUS_WIDTH_BIT (0x3UL) // boot bus width +#define SD_EXTCSD_BOOT_BUS_1BIT (0x0UL) +#define SD_EXTCSD_BOOT_BUS_4BIT (0x1UL) +#define SD_EXTCSD_BOOT_BUS_8BIT (0x2UL) +#define SD_EXTCSD_RESET_BOOT_BUS_WIDTH_BIT (0x1UL << 2) // boot bus width +#define SD_EXTCSD_RESET_BOOT_BUS (0x0UL << 2) +#define SD_EXTCSD_RETAIN_BOOT_BUS (0x1UL << 2) + +// Mode Switch Arguments for CMD6 +#define MMC_CMD6_ARG_ACCESS +#define MMC_CMD6_ARG_INDEX +#define MMC_CMD6_ARG_VALUE +#define MMC_CMD6_ARG_CMDSET + +// SDIO functions +#define SDIO_CIA 0 /// SDIO Function 0 (CIA) +#define SDIO_FN0 0 /// SDIO Function 0 +#define SDIO_FN1 1 /// SDIO Function 1 +#define SDIO_FN2 2 /// SDIO Function 2 +#define SDIO_FN3 3 /// SDIO Function 3 +#define SDIO_FN4 4 /// SDIO Function 4 +#define SDIO_FN5 5 /// SDIO Function 5 +#define SDIO_FN6 6 /// SDIO Function 6 +#define SDIO_FN7 7 /// SDIO Function 7 + +// SDIO Card Common Control Registers (CCCR) +#define SDIO_CCCR_REG 0x00 /// CCCR/SDIO revision (RO) +#define SDIO_CCCR (0xFUL << 0)/// CCCR Format Version number +#define SDIO_CCCR_1_00 (0x0UL << 0)/// CCCR/FBR Version 1.00 +#define SDIO_CCCR_1_10 (0x1UL << 0)/// CCCR/FBR Version 1.10 +#define SDIO_CCCR_1_20 (0x2UL << 0)/// CCCR/FBR Version 1.20 +#define SDIO_SDIO (0xFUL << 4)/// SDIO Specification +#define SDIO_SDIO_1_00 (0x0UL << 4)/// SDIO Specification 1.00 +#define SDIO_SDIO_1_10 (0x1UL << 4)/// SDIO Specification 1.10 +#define SDIO_SDIO_1_20 (0x2UL << 4)/// SDIO Specification 1.20(unreleased) +#define SDIO_SDIO_2_00 (0x3UL << 4)/// SDIO Specification Version 2.00 +#define SDIO_SD_REV_REG 0x01 /// SD Specification Revision (RO) +#define SDIO_SD (0xFUL << 0)/// SD Physical Specification +#define SDIO_SD_1_01 (0x0UL << 0)/// SD 1.01 (Mar 2000) +#define SDIO_SD_1_10 (0x1UL << 0)/// SD 1.10 (Oct 2004) +#define SDIO_SD_2_00 (0x2UL << 0)/// SD 2.00 (May 2006) +#define SDIO_IOE_REG 0x02 /// I/O Enable (R/W) +#define SDIO_IOE 0xFEUL /// Enable/Disable Function +#define SDIO_IOE_FN1 (0x1UL << 1)/// Function 1 Enable/Disable +#define SDIO_IOE_FN2 (0x1UL << 2)/// Function 2 Enable/Disable +#define SDIO_IOE_FN3 (0x1UL << 3)/// Function 3 Enable/Disable +#define SDIO_IOE_FN4 (0x1UL << 4)/// Function 4 Enable/Disable +#define SDIO_IOE_FN5 (0x1UL << 5)/// Function 5 Enable/Disable +#define SDIO_IOE_FN6 (0x1UL << 6)/// Function 6 Enable/Disable +#define SDIO_IOE_FN7 (0x1UL << 7)/// Function 7 Enable/Disable +#define SDIO_IOR_REG 0x03 /// I/O Ready (RO) +#define SDIO_IOR 0xFEUL /// I/O Function Ready +#define SDIO_IOR_FN1 (0x1UL << 1)/// Function 1 ready +#define SDIO_IOR_FN2 (0x1UL << 2)/// Function 2 ready +#define SDIO_IOR_FN3 (0x1UL << 3)/// Function 3 ready +#define SDIO_IOR_FN4 (0x1UL << 4)/// Function 4 ready +#define SDIO_IOR_FN5 (0x1UL << 5)/// Function 5 ready +#define SDIO_IOR_FN6 (0x1UL << 6)/// Function 6 ready +#define SDIO_IOR_FN7 (0x1UL << 7)/// Function 7 ready +#define SDIO_IEN_REG 0x04 /// Int Enable +#define SDIO_IENM 0x01UL /// Int Enable Master (R/W) +#define SDIO_IEN 0xFEUL /// Int Enable for function (R/W) +#define SDIO_IEN_FN1 (0x1UL << 1)/// Function 1 Int Enable +#define SDIO_IEN_FN2 (0x1UL << 2)/// Function 2 Int Enable +#define SDIO_IEN_FN3 (0x1UL << 3)/// Function 3 Int Enable +#define SDIO_IEN_FN4 (0x1UL << 4)/// Function 4 Int Enable +#define SDIO_IEN_FN5 (0x1UL << 5)/// Function 5 Int Enable +#define SDIO_IEN_FN6 (0x1UL << 6)/// Function 6 Int Enable +#define SDIO_IEN_FN7 (0x1UL << 7)/// Function 7 Int Enable +#define SDIO_INT_REG 0x05 /// Int Pending +#define SDIO_INT 0xFE /// Int Pending for functions (RO) +#define SDIO_INT_FN1 (0x1UL << 1)/// Function 1 Int pending +#define SDIO_INT_FN2 (0x1UL << 2)/// Function 2 Int pending +#define SDIO_INT_FN3 (0x1UL << 3)/// Function 3 Int pending +#define SDIO_INT_FN4 (0x1UL << 4)/// Function 4 Int pending +#define SDIO_INT_FN5 (0x1UL << 5)/// Function 5 Int pending +#define SDIO_INT_FN6 (0x1UL << 6)/// Function 6 Int pending +#define SDIO_INT_FN7 (0x1UL << 7)/// Function 7 Int pending +#define SDIO_IOA_REG 0x06 /// I/O Abort +#define SDIO_AS (0x7UL << 0)/// Abort Select In Order (WO) +#define SDIO_AS_FN1 (0x1UL << 0)/// Abort function 1 IO +#define SDIO_AS_FN2 (0x2UL << 0)/// Abort function 2 IO +#define SDIO_AS_FN3 (0x3UL << 0)/// Abort function 3 IO +#define SDIO_AS_FN4 (0x4UL << 0)/// Abort function 4 IO +#define SDIO_AS_FN5 (0x5UL << 0)/// Abort function 5 IO +#define SDIO_AS_FN6 (0x6UL << 0)/// Abort function 6 IO +#define SDIO_AS_FN7 (0x7UL << 0)/// Abort function 7 IO +#define SDIO_RES (0x1UL << 3)/// IO CARD RESET (WO) +#define SDIO_BUS_CTRL_REG 0x07 /// Bus Interface Control +#define SDIO_BUSWIDTH (0x3UL << 0)/// Data bus width (R/W) +#define SDIO_BUSWIDTH_1B (0x0UL << 0)/// 1-bit data bus +#define SDIO_BUSWIDTH_4B (0x2UL << 0)/// 4-bit data bus +#define SDIO_ECSI (0x1UL << 5)/// Enable Continuous SPI interrupt (R/W) +#define SDIO_SCSI (0x1UL << 6)/// Support Continuous SPI interrupt (RO) +#define SDIO_CD (0x1UL << 7)/// Connect(0)/Disconnect(1) pull-up on CD/DAT[3] (R/W) +#define SDIO_CAP_REG 0x08 /// Card Capability +#define SDIO_SDC (0x1UL << 0)/// Support Direct Commands during data transfer (RO) +#define SDIO_SMB (0x1UL << 1)/// Support Multi-Block (RO) +#define SDIO_SRW (0x1UL << 2)/// Support Read Wait (RO) +#define SDIO_SBS (0x1UL << 3)/// Support Suspend/Resume (RO) +#define SDIO_S4MI (0x1UL << 4)/// Support interrupt between blocks of data in 4-bit SD mode (RO) +#define SDIO_E4MI (0x1UL << 5)/// Enable interrupt between blocks of data in 4-bit SD mode (R/W) +#define SDIO_LSC (0x1UL << 6)/// Low-Speed Card (RO) +#define SDIO_4BLS (0x1UL << 7)/// 4-bit support for Low-Speed Card (RO) +#define SDIO_CIS_PTR_REG 0x09 /// Pointer to CIS (3B, LSB first) +#define SDIO_BUS_SUSP_REG 0x0C /// Bus Suspend +#define SDIO_BS (0x1UL << 0)/// Bus Status (transfer on DAT[x] lines) (RO) +#define SDIO_BR (0x1UL << 1)/// Bus Release Request/Status (R/W) +#define SDIO_FUN_SEL_REG 0x0D /// Function select +#define SDIO_DF (0x1UL << 7)/// Resume Data Flag (RO) +#define SDIO_FS (0xFUL << 0)/// Select Function (R/W) +#define SDIO_FS_CIA (0x0UL << 0)/// Select CIA (function 0) +#define SDIO_FS_FN1 (0x1UL << 0)/// Select Function 1 +#define SDIO_FS_FN2 (0x2UL << 0)/// Select Function 2 +#define SDIO_FS_FN3 (0x3UL << 0)/// Select Function 3 +#define SDIO_FS_FN4 (0x4UL << 0)/// Select Function 4 +#define SDIO_FS_FN5 (0x5UL << 0)/// Select Function 5 +#define SDIO_FS_FN6 (0x6UL << 0)/// Select Function 6 +#define SDIO_FS_FN7 (0x7UL << 0)/// Select Function 7 +#define SDIO_FS_MEM (0x8UL << 0)/// Select memory in combo card +#define SDIO_EXEC_REG 0x0E /// Exec Flags (RO) +#define SDIO_EXM (0x1UL << 0)/// Executing status of memory +#define SDIO_EX (0xFEUL) /// Executing status of functions +#define SDIO_EX_FN1 (0x1UL << 1)/// Executing status of function 1 +#define SDIO_EX_FN2 (0x1UL << 2)/// Executing status of function 2 +#define SDIO_EX_FN3 (0x1UL << 3)/// Executing status of function 3 +#define SDIO_EX_FN4 (0x1UL << 4)/// Executing status of function 4 +#define SDIO_EX_FN5 (0x1UL << 5)/// Executing status of function 5 +#define SDIO_EX_FN6 (0x1UL << 6)/// Executing status of function 6 +#define SDIO_EX_FN7 (0x1UL << 7)/// Executing status of function 7 +#define SDIO_READY_REG 0x0F /// Ready Flags (RO) +#define SDIO_RFM (0x1UL << 0)/// Ready Flag for memory +#define SDIO_RF (0xFEUL) /// Ready Flag for functions +#define SDIO_RF_FN1 (0x1UL << 1)/// Ready Flag for function 1 +#define SDIO_RF_FN2 (0x1UL << 2)/// Ready Flag for function 2 +#define SDIO_RF_FN3 (0x1UL << 3)/// Ready Flag for function 3 +#define SDIO_RF_FN4 (0x1UL << 4)/// Ready Flag for function 4 +#define SDIO_RF_FN5 (0x1UL << 5)/// Ready Flag for function 5 +#define SDIO_RF_FN6 (0x1UL << 6)/// Ready Flag for function 6 +#define SDIO_RF_FN7 (0x1UL << 7)/// Ready Flag for function 7 +#define SDIO_FN0_BLKSIZ_REG 0x10 /// FN0 Block Size (2B, LSB first) (R/W) +#define SDIO_POWER_REG 0x12 /// Power Control +#define SDIO_SMPC (0x1UL << 0)/// Support Master Power Control (RO) +#define SDIO_EMPC (0x1UL << 1)/// Enable Master Power Control (R/W) +#define SDIO_HS_REG 0x13 /// High-Speed +#define SDIO_SHS (0x1UL << 0)/// Support High-Speed (RO) +#define SDIO_EHS (0x1UL << 1)/// Enable High-Speed (R/W) + +// SDIO Function Basic Registers (FBR) +#define SDIO_FBR_ADDR(fn, x) (0x100*(fn) + (x)) +#define SDIO_FBR_CSA_IF 0x0 /// CSA and function interface code (RO) +#define SDIO_IFC (0xFUL << 0)/// Standard SDIO Fun Interface Code +#define SDIO_IFC_NO_IF (0x0UL << 0)/// No SDIO standard interface +#define SDIO_IFC_UART (0x1UL << 0)/// UART +#define SDIO_IFC_TA_BT (0x2UL << 0)/// Type-A Bluetooth +#define SDIO_IFC_TB_BT (0x3UL << 0)/// Type-B Bluetooth +#define SDIO_IFC_GPS (0x4UL << 0)/// GPS +#define SDIO_IFC_CAMERA (0x5UL << 0)/// Camera +#define SDIO_IFC_PHS (0x6UL << 0)/// PHS +#define SDIO_IFC_WLAN (0x7UL << 0)/// WLAN +#define SDIO_IFC_ATA (0x8UL << 0)/// Embedded SDIO-ATA +#define SDIO_IFC_EXT (0xFUL << 0)/// Check EXT interface code +#define SDIO_SCSA (0x1UL << 6)/// Function supports Code Storage Area (CSA) +#define SDIO_FBR_CSA (0x1UL << 7)/// Function CSA enable +#define SDIO_FBR_EXT_IF 0x1 /// Extended function interface code (RO) +#define SDIO_FBR_PWR 0x2 /// function power control +#define SDIO_SPS (0x1UL << 0)/// function support power selection (RO) +#define SDIO_EPS (0x1UL << 1)/// Low Current Mode/High Current Mode (R/W) +#define SDIO_FBR_CIS_PTR 0x9 /// Address pointer to function CIS (3B, LSB first) (RO) +#define SDIO_FBR_CSA_PTR 0xC /// Address pointer to CSA (3B, LSB first) (R/W) +#define SDIO_FBR_CSA_DATA 0xF /// Read/Write fifo to CSA (R/W) +#define SDIO_FBR_BLK_SIZ 0x10 /// Block size (2B, LSB first) (R/W) + +// SDIO Card Metaformat +#define CISTPL_NULL 0x00 /// Null tuple (PCMCIA 3.1.9) +#define CISTPL_DEVICE 0x01 /// Device tuple (PCMCIA 3.2.2) +#define CISTPL_CHECKSUM 0x10 /// Checksum control (PCMCIA 3.1.1) +#define CISTPL_VERS_1 0x15 /// Level 1 version (PCMCIA 3.2.10) +#define CISTPL_ALTSTR 0x16 /// Alternate Language String (PCMCIA 3.2.1) +#define CISTPL_MANFID 0x20 /// Manufacturer Identification String (PCMCIA 3.2.9) +#define CISTPL_FUNCID 0x21 /// Function Identification (PCMCIA 3.2.7) +#define CISTPL_FUNCE 0x22 /// Function Extensions (PCMCIA 3.2.6) +#define CISTPL_SDIO_STD 0x91 /// Additional information for SDIO (PCMCIA 6.1.2) +#define CISTPL_SDIO_EXT 0x92 /// Reserved for future SDIO (PCMCIA 6.1.3) +#define CISTPL_END 0xFF /// The End-of-chain Tuple (PCMCIA 3.1.2) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +typedef MciCallback SdCallback; +typedef MciCmd SdCmd; +typedef Mci SdDriver; + +//------------------------------------------------------------------------------ +/// Sdcard driver structure. It holds the current command being processed and +/// the SD card address. +//------------------------------------------------------------------------------ +typedef struct _SdCard { + + /// Pointer to the underlying MCI driver. + SdDriver *pSdDriver; + /// Current MCI command being processed. + SdCmd command; + /// Card IDentification (CID register) + unsigned int cid[4]; + /// Card-specific data (CSD register) + unsigned int csd[4]; + /// SD SCR(64 bit), Status(512 bit) or MMC EXT_CSD(512 bytes) register + unsigned int extData[512 / 4]; + /// Previous access block number. + unsigned int preBlock; + /// Card total size + unsigned int totalSize; + /// Card block number + unsigned int blockNr; + /// Card option command support list + unsigned int optCmdBitMap; + /// Card CSD TRANS_SPEED + unsigned int transSpeed; + /// SD card current address. + unsigned short cardAddress; + /// Card type + unsigned char cardType; + /// Card access mode + unsigned char mode; + /// State after sd command complete + unsigned char state; +} SdCard; + +typedef struct _MmcCmd6Arg { + + /// Access + unsigned char access; + /// Index + unsigned char index; + /// Value + unsigned char value; + /// Cmd Set + unsigned char cmdSet; +} MmcCmd6Arg; + +typedef struct _SdCmd6Arg { + unsigned int accessMode:4, /// [ 3: 0] function group 1, access mode + command:4, /// [ 7: 4] function group 2, command system + reserveFG3:4, /// [11: 8] function group 3, 0xF or 0x0 + reserveFG4:4, /// [15:12] function group 4, 0xF or 0x0 + reserveFG5:4, /// [19:16] function group 5, 0xF or 0x0 + reserveFG6:4, /// [23:20] function group 6, 0xF or 0x0 + reserved:7, /// [30:24] reserved 0 + mode:1; /// [31 ] Mode, 0: Check, 1: Switch +} SdCmd6Arg; + +/// General header struct for Tuple +typedef struct _CisTplHeader { + unsigned char tpl_code; /// Tuple code + unsigned char tpl_link; /// Link to next tuple +} CisTplHeader; + +/// +typedef struct _CisTpl_ManfID { + unsigned char tpl_code; /// CISTPL_MANFID (20H) + unsigned char tpl_link; /// Link to next tuple (at least 4) + unsigned char tplmid_manf[2]; /// SDIO Card manufacturer code + unsigned char tplmid_card[2]; /// manufacturer information (Part Nb, Rev.) +} CisTpl_ManfID; + +/// Function Identification Tuple +typedef struct _CisTpl_FuncID { + unsigned char tpl_code; /// CISTPL_FUNCID (0x21) + unsigned char tpl_link; /// Link to next tuple (0x02) + unsigned char tplfid_function; /// Card function code (0x0C) + unsigned char tplfid_sysinit; /// System initialization bit mask (0x00) +} CisTpl_FuncID; + +/// Function Extenstion Tuple +typedef struct _CisTpl_FuncE { + unsigned char tpl_code; /// CISTPL_FUNCE (0x22) + unsigned char tpl_link; /// Link to next tuple + unsigned char tplfe_type; /// Type of extended data + unsigned char tplfe_data[1]; /// Function information +} CisTpl_FuncE; + +/// Tuple for Function 0 (common) +typedef struct _CisTpl_Func0 { + unsigned char tpl_code; /// CISTPL_FUNCE (0x22) + unsigned char tpl_link; /// Link to next tuple (0x04) + unsigned char tplfe_type; /// Type of extended data (0x00) + unsigned char tplfe_fn0_blk_size[2];/// Max block size and byte count + unsigned char tple_max_tran_speed; /// Indicates the max transfer rate +} CisTpl_Func0; + +/// Tuple for Function 1-7 +typedef struct _CisTpl_FuncN { + unsigned char tpl_code; /// CISTPL_FUNCE (0x22) + unsigned char tpl_link; /// Link to next tuple (0x2A) + unsigned char tplfe_type; /// Type of extended data (0x01) + unsigned char tplfe_function_info;/// Bits significant information + unsigned char tplfe_std_io_rev; /// Version level of App Spec. for STD SDIO + unsigned char tplfe_card_psn[4];/// Product Serial Number + unsigned char tplfe_csa_size[4];/// Size of the CSA space + unsigned char tplfe_csa_property;/// Flags identifying properties of CSA + unsigned char tplfe_max_blk_size[2];/// Max block size and byte count + unsigned char tplfe_ocr[4]; /// OCR value (SD Spec.) + unsigned char tplfe_op_min_pwr; /// Min current in mA + unsigned char tplfe_op_avg_pwr; /// Avg current in mA + unsigned char tplfe_op_max_pwr; /// Max(peak) current in mA + unsigned char tplfe_sb_min_pwr; /// Min standby current in mA + unsigned char tplfe_sb_avg_pwr; /// Avg standby current in mA + unsigned char tplfe_sb_max_pwr; /// Max standby current in mA + unsigned char tplfe_min_bw[2]; /// Min data transfer bandwidth, in KB/s + unsigned char tplfe_opt_bw[2]; /// Optimum data transfer bandwidth, in KB/s + unsigned char tplfe_enable_timeout_val[2];/// Time out IOEx->IORx, in 10ms + unsigned char tplfe_sp_avg_pwr_3_3_v[2];/// same as tplfe_op_avg_pwr + unsigned char tplfe_sp_max_pwr_3_3_v[2];/// same as tplfe_op_max_pwr + unsigned char tplfe_hg_avg_pwr_3_3_v[2];/// Higher Current Mode avg current in mA + unsigned char tplfe_hg_max_pwr_3_3_v[2];/// Higher Current Mode peak current in mA + unsigned char tplfe_lp_avg_pwr_3_3_v[2];/// Lower Current Mode avg current in mA + unsigned char tplfe_lp_max_pwr_3_3_v[2];/// Lower Current Mode avg current in mA +} CisTpl_FuncN; + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +extern unsigned char SD_Init(SdCard *pSd, + SdDriver *pSdDriver); + +extern unsigned char SDIO_ReadDirect(SdCard * pSd, + unsigned char funNb, + unsigned int address, + unsigned char * pBytes, + unsigned int size); +extern unsigned char SDIO_WriteDirect(SdCard * pSd, + unsigned char funNb, + unsigned int address, + unsigned char byte); +extern unsigned char SDIO_ReadBytes(SdCard * pSd, + unsigned char funNb, + unsigned int address, + unsigned char isFixedAddr, + unsigned char * pBytes, + unsigned int size); +extern unsigned char SDIO_WriteBytes(SdCard * pSd, + unsigned char funNb, + unsigned int address, + unsigned char isFixedAddr, + unsigned char * pBytes, + unsigned int size); + +extern unsigned char SD_Read(SdCard *pSd, + unsigned int address, + void *pData, + unsigned short length, + SdCallback pCallback, + void *pArgs); + +extern unsigned char SD_Write(SdCard *pSd, + unsigned int address, + void *pData, + unsigned short length, + SdCallback pCallback, + void *pArgs); + +extern unsigned char SD_ReadBlock( + SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData); + +extern unsigned char SD_WriteBlock( + SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + const unsigned char *pData); + +extern unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver); + +extern unsigned char SD_HighSpeedMode(SdCard *pSd, + unsigned char cardHsMode); + +extern unsigned char MMC_SetupBootMode(SdCard * pSd, + unsigned char resetBus, + unsigned char busWidth, + unsigned char bootPart, + unsigned char accPart, + unsigned char bootAck); + +extern unsigned char MMC_BootInit(SdCard * pSd); +extern unsigned char MMC_BootRead(SdCard * pSd, + unsigned int nbBlocks, + unsigned char * pData); +extern unsigned char MMC_BootStop(SdCard * pSd); + +extern unsigned int MMC_GetTotalSizeKB(SdCard *pSd); + +extern void SDIO_DisplayCardInformation(SdCard *pSd); + +extern void SD_DisplayRegisterCID(SdCard *pSd); + +extern void SD_DisplayRegisterCSD(SdCard *pSd); + +extern void SD_DisplayRegisterECSD(SdCard * pSd); + +extern void SD_DisplayRegisterSCR(SdCard * pSd); + +extern void SD_DisplaySdStatus(SdCard * pSd); + +#endif //#ifndef SDCARD_H + diff --git a/memories/sdmmc/sdmmc_spi.c b/memories/sdmmc/sdmmc_spi.c new file mode 100644 index 0000000..73426a1 --- /dev/null +++ b/memories/sdmmc/sdmmc_spi.c @@ -0,0 +1,1453 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "sdmmc_spi.h" +#include "sdspi.h" +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +// SD card operation states +#define SD_STATE_STBY 0 +#define SD_STATE_DATA 1 +#define SD_STATE_RCV 2 + +// Card type +#define UNKNOWN_CARD 0 +#define CARD_SD 1 +#define CARD_SDHC 2 +#define CARD_MMC 3 + +// Delay between sending MMC commands +#define MMC_DELAY 0x4FF + +//#define SD_ADDRESS(pSd, address) (((pSd)->cardType == CARD_SDHC) ? \ +// (address):((address) << SD_BLOCK_SIZE_BIT)) +#define SD_ADDRESS(pSd, address) \ + ( ((pSd)->totalSize == 0xFFFFFFFF) ? \ + (address) : ((address) << SD_BLOCK_SIZE_BIT) ) + +//----------------------------------------------------------------------------- +/// MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS +/// R1 is the low order byte; R2 is the next highest byte, when present. +//----------------------------------------------------------------------------- +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +// R1 bit 7 is always zero +#define R2_SPI_CARD_LOCKED (1 << 0) +#define R2_SPI_WP_ERASE_SKIP (1 << 1) +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 2) +#define R2_SPI_CC_ERROR (1 << 3) +#define R2_SPI_CARD_ECC_ERROR (1 << 4) +#define R2_SPI_WP_VIOLATION (1 << 5) +#define R2_SPI_ERASE_PARAM (1 << 6) +#define R2_SPI_OUT_OF_RANGE (1 << 7) +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + +// Status register constants +#define STATUS_READY_FOR_DATA (1 << 8) +#define STATUS_IDLE (0 << 9) +#define STATUS_READY (1 << 9) +#define STATUS_IDENT (2 << 9) +#define STATUS_STBY (3 << 9) +#define STATUS_TRAN (4 << 9) +#define STATUS_DATA (5 << 9) +#define STATUS_RCV (6 << 9) +#define STATUS_PRG (7 << 9) +#define STATUS_DIS (8 << 9) +#define STATUS_STATE (0xF << 9) + +//----------------------------------------------------------------------------- +/// OCR Register +//----------------------------------------------------------------------------- +#define AT91C_VDD_16_17 (1 << 4) +#define AT91C_VDD_17_18 (1 << 5) +#define AT91C_VDD_18_19 (1 << 6) +#define AT91C_VDD_19_20 (1 << 7) +#define AT91C_VDD_20_21 (1 << 8) +#define AT91C_VDD_21_22 (1 << 9) +#define AT91C_VDD_22_23 (1 << 10) +#define AT91C_VDD_23_24 (1 << 11) +#define AT91C_VDD_24_25 (1 << 12) +#define AT91C_VDD_25_26 (1 << 13) +#define AT91C_VDD_26_27 (1 << 14) +#define AT91C_VDD_27_28 (1 << 15) +#define AT91C_VDD_28_29 (1 << 16) +#define AT91C_VDD_29_30 (1 << 17) +#define AT91C_VDD_30_31 (1 << 18) +#define AT91C_VDD_31_32 (1 << 19) +#define AT91C_VDD_32_33 (1 << 20) +#define AT91C_VDD_33_34 (1 << 21) +#define AT91C_VDD_34_35 (1 << 22) +#define AT91C_VDD_35_36 (1 << 23) +#define AT91C_CARD_POWER_UP_BUSY (1 << 31) + +#define AT91C_MMC_HOST_VOLTAGE_RANGE (AT91C_VDD_27_28 +\ + AT91C_VDD_28_29 +\ + AT91C_VDD_29_30 +\ + AT91C_VDD_30_31 +\ + AT91C_VDD_31_32 +\ + AT91C_VDD_32_33) +#define AT91C_CCS (1 << 30) + +// SPI_CMD Register Value +#define AT91C_POWER_ON_INIT (0) + +//----------------------------------------------------------------------------- +// Command Classes +//----------------------------------------------------------------------------- +// +// Class 0, 2, 4, 5, 7 and 8 are mandatory and shall be supported by all SD Memory Cards. +// Basic Commands (class 0) +// +// Cmd0 MCI + SPI +#define AT91C_GO_IDLE_STATE_CMD (0) +// Cmd1 SPI +#define AT91C_MMC_SEND_OP_COND_CMD (1) +// Cmd2 MCI +#define AT91C_ALL_SEND_CID_CMD (2) +// Cmd3 MCI +#define AT91C_SET_RELATIVE_ADDR_CMD (3) +// Cmd4 MCI +//#define AT91C_SET_DSR_CMD (4) +// cmd7 MCI +#define AT91C_SEL_DESEL_CARD_CMD (7) +// Cmd8 MCI + SPI +#define AT91C_SEND_IF_COND (8) +// Cmd9 MCI + SPI +#define AT91C_SEND_CSD_CMD (9) +// Cmd10 MCI + SPI +#define AT91C_SEND_CID_CMD (10) +// Cmd12 MCI + SPI +#define AT91C_STOP_TRANSMISSION_CMD (12) +// Cmd13 MCI + SPI +#define AT91C_SEND_STATUS_CMD (13) +// Cmd15 MCI +//#define AT91C_GO_INACTIVE_STATE_CMD (15) +// Cmd58 SPI +#define AT91C_READ_OCR_CMD (58) +// Cmd59 SPI +#define AT91C_CRC_ON_OFF_CMD (59) +//#define AT91C_MMC_ALL_SEND_CID_CMD (2) +//#define AT91C_MMC_SET_RELATIVE_ADDR_CMD (3) +//#define AT91C_MMC_READ_DAT_UNTIL_STOP_CMD (11) +//#define AT91C_STOP_TRANSMISSION_SYNC_CMD (12) + +//*------------------------------------------------ +//* Class 2 commands: Block oriented Read commands +//*------------------------------------------------ +// Cmd16 +#define AT91C_SET_BLOCKLEN_CMD (16) +// Cmd17 +#define AT91C_READ_SINGLE_BLOCK_CMD (17) +// Cmd18 +#define AT91C_READ_MULTIPLE_BLOCK_CMD (18) + +//*------------------------------------------------ +//* Class 4 commands: Block oriented write commands +//*------------------------------------------------ +// Cmd24 +#define AT91C_WRITE_BLOCK_CMD (24) +// Cmd25 +#define AT91C_WRITE_MULTIPLE_BLOCK_CMD (25) +// Cmd27 +//#define AT91C_PROGRAM_CSD_CMD (27) + +//*---------------------------------------- +//* Class 5 commands: Erase commands +//*---------------------------------------- +// Cmd32 +//#define AT91C_TAG_SECTOR_START_CMD (32) +// Cmd33 +//#define AT91C_TAG_SECTOR_END_CMD (33) +// Cmd38 +//#define AT91C_ERASE_CMD (38) + +//*---------------------------------------- +//* Class 7 commands: Lock commands +//*---------------------------------------- +// Cmd42 +//#define AT91C_LOCK_UNLOCK (42) + +//*----------------------------------------------- +// Class 8 commands: Application specific commands +//*----------------------------------------------- +// Cmd55 +#define AT91C_APP_CMD (55) +// cmd 56 +//#define AT91C_GEN_CMD (56) +// ACMD6 +#define AT91C_SDCARD_SET_BUS_WIDTH_CMD (6) +// ACMD13 +//#define AT91C_SDCARD_STATUS_CMD (13) +// ACMD22 +//#define AT91C_SDCARD_SEND_NUM_WR_BLOCKS_CMD (22) +// ACMD23 +//#define AT91C_SDCARD_SET_WR_BLK_ERASE_COUNT_CMD (23) +// ACMD41 +#define AT91C_SDCARD_APP_OP_COND_CMD (41) +// ACMD42 +//#define AT91C_SDCARD_SET_CLR_CARD_DETECT_CMD (42) +// ACMD51 +#define AT91C_SDCARD_SEND_SCR_CMD (51) + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Delay some loop +//------------------------------------------------------------------------------ +static void Delay(volatile unsigned int loop) +{ + for(;loop > 0; loop --); +} + +//------------------------------------------------------------------------------ +/// Sends the current SD card driver command to the card. +/// Returns 0 if successful; Otherwise, returns the transfer status code or +/// SD_ERROR_DRIVER if there was a problem with the SD transfer. +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char SendCommand(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + SdDriver *pSdDriver = pSd->pSdDriver; + unsigned char error; + unsigned int i; + + // Send command + SDSPI_NCS((SdSpi *)pSdDriver); + + error = SDSPI_SendCommand((SdSpi *)pSdDriver, (SdSpiCmd *)pCommand); + if (error) { + TRACE_ERROR("SendCmd%d,%d (%d)\n\r", + pCommand->cmd & 0x3F, pCommand->conTrans, + error); + return SD_ERROR_DRIVER; + } + + // Wait for command to complete + while (!SDSPI_IsTxComplete((SdSpiCmd *)pCommand)); + + // Delay between sending commands. + Delay(MMC_DELAY); + + return pCommand->status; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void DecodeR1(unsigned char R1) +{ + if( (R1 & R1_SPI_IDLE)==R1_SPI_IDLE) { + TRACE_DEBUG("R1_SPI_IDLE\n\r"); + } + if( (R1 & R1_SPI_ERASE_RESET)==R1_SPI_ERASE_RESET) { + TRACE_DEBUG("R1_SPI_ERASE_RESET\n\r"); + } + if( (R1 & R1_SPI_ILLEGAL_COMMAND)==R1_SPI_ILLEGAL_COMMAND) { + TRACE_DEBUG("R1_SPI_ILLEGAL_COMMAND\n\r"); + } + if( (R1 & R1_SPI_COM_CRC)==R1_SPI_COM_CRC) { + TRACE_DEBUG("R1_SPI_COM_CRC\n\r"); + } + if( (R1 & R1_SPI_ERASE_SEQ)==R1_SPI_ERASE_SEQ) { + TRACE_DEBUG("R1_SPI_ERASE_SEQ\n\r"); + } + if( (R1 & R1_SPI_ADDRESS)==R1_SPI_ADDRESS) { + TRACE_DEBUG("R1_SPI_ADDRESS\n\r"); + } + if( (R1 & R1_SPI_PARAMETER)==R1_SPI_PARAMETER) { + TRACE_DEBUG("R1_SPI_PARAMETER\n\r"); + } +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void DecodeR2(unsigned char R2) +{ + if( (R2 & R2_SPI_CARD_LOCKED)==R2_SPI_CARD_LOCKED) { + TRACE_DEBUG("R2_SPI_CARD_LOCKED\n\r"); + } + if( (R2 & R2_SPI_WP_ERASE_SKIP)==R2_SPI_WP_ERASE_SKIP) { + TRACE_DEBUG("R2_SPI_WP_ERASE_SKIP/R2_SPI_LOCK_UNLOCK_FAIL\n\r"); + } + if( (R2 & R2_SPI_ERROR)==R2_SPI_ERROR) { + TRACE_DEBUG("R2_SPI_ERROR\n\r"); + } + if( (R2 & R2_SPI_CC_ERROR)==R2_SPI_CC_ERROR) { + TRACE_DEBUG("R2_SPI_CC_ERROR\n\r"); + } + if( (R2 & R2_SPI_CARD_ECC_ERROR)==R2_SPI_CARD_ECC_ERROR) { + TRACE_DEBUG("R2_SPI_CARD_ECC_ERROR\n\r"); + } + if( (R2 & R2_SPI_WP_VIOLATION)==R2_SPI_WP_VIOLATION) { + TRACE_DEBUG("R2_SPI_WP_VIOLATION\n\r"); + } + if( (R2 & R2_SPI_ERASE_PARAM)==R2_SPI_ERASE_PARAM) { + TRACE_DEBUG("R2_SPI_ERASE_PARAM\n\r"); + } + if( (R2 & R2_SPI_OUT_OF_RANGE)==R2_SPI_OUT_OF_RANGE) { + TRACE_DEBUG("R2_SPI_OUT_OF_RANGE/R2_SPI_CSD_OVERWRITE\n\r"); + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 1. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R1(unsigned char *pResp) +{ + DecodeR1(*pResp); + + if((*pResp & 0x7E) !=0) { + // An error occured + return SD_ERROR_NORESPONSE; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 1b. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R1b(unsigned char *pResp) +{ + // A zero value indicates card is busy. + // A non-zero value indicates the card is ready for the next command. + if( (*pResp) == 0 ) { + TRACE_INFO("Card is busy\n\r"); + return SD_ERROR_BUSY; + } + + DecodeR1(*(pResp+1)); + if(((*(pResp+1)) & 0x7E) !=0) { + // An error occured + return SD_ERROR_NORESPONSE; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 2. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R2(unsigned char *pResp) +{ + DecodeR1(*pResp); + DecodeR2(*(pResp+1)); + + if((( *pResp & 0x7e ) != 0) && (*(pResp+1) != 0)) { + return SD_ERROR_NORESPONSE; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 3. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R3(unsigned char *pResp, unsigned int *ocr) +{ + // *pResp: bit 32-39: R1 + if(( *pResp & 0x7e ) != 0) { + return SD_ERROR_NORESPONSE; + } + else { + // bit 0-31: OCR + *ocr = ((*(pResp+1) << 24) \ + |(*(pResp+2) << 16) \ + |(*(pResp+3) << 8) \ + | *(pResp+4)); + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 7. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R7(unsigned char *pResp, unsigned char *isSdhc) +{ + *isSdhc = 0; + + if(( *pResp & 0x7e ) != 0) { + return SD_ERROR_NORESPONSE; + } + else { + // *(pResp+4): bit 0- 7: check pattern + // *(pResp+3): bit 8-11: voltage accepted: 0x01: 2.7-3.6V + if ((*(pResp+3) == 0x1) && (*(pResp+4) == 0xAA)) { + *isSdhc = 1; + } + else { + *isSdhc = 0; + } + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp +/// up time. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char Pon(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + + TRACE_DEBUG("Pon()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_POWER_ON_INIT; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + if( error == 0 ) { + error = SD_SPI_R1((unsigned char *)&response); + } + return error; +} + +//------------------------------------------------------------------------------ +/// Resets all cards to idle state +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd0(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + + TRACE_DEBUG("Cmd0()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_GO_IDLE_STATE_CMD; + pCommand->resType = 1; + pCommand->pResp = &response; + + // send command + error = SendCommand(pSd); + if (error == 0) + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// MMC send operation condition command. +/// Sends host capacity support information and activates the card's +/// initialization process. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd1(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd1()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_MMC_SEND_OP_COND_CMD; + pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; + pCommand->resType = 1; + pCommand->pResp = &response; + + // send command + error = SendCommand(pSd); + if (error) { + return error; + } + + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Sends SD Memory Card interface +/// condition, which includes host supply +/// voltage information and asks the card +/// whether card supports voltage. +/// Returns 0 if successful; otherwise returns SD_ERROR_NORESPONSE if the card did +/// not answer the command, or SD_ERROR_DRIVER. +/// \param pSd Pointer to a SD card driver instance. +/// \param supplyVoltage Expected supply voltage. +//------------------------------------------------------------------------------ +static unsigned char Cmd8(SdCard *pSd, unsigned char supplyVoltage) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response[2]; + unsigned char error; + unsigned char isSdhc; + + TRACE_DEBUG("Cmd8()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SEND_IF_COND; + pCommand->arg = (supplyVoltage << 8) | (0xAA); + pCommand->resType = 7; + pCommand->pResp = &response[0]; + + TRACE_DEBUG("supplyVoltage: 0x%x\n\r", supplyVoltage); + + // Send command + error = SendCommand(pSd); + + TRACE_DEBUG("SD_R7[0]: 0x%x\n\r", response[0]); + TRACE_DEBUG("SD_R7[1]: 0x%x\n\r", response[1]); + error = SD_SPI_R7((unsigned char *)&response, &isSdhc); + if( error == SD_ERROR_NORESPONSE ) { + TRACE_DEBUG("Cmd8 R7 error:%d \n\r", error); + return error; + } + else { + if(isSdhc == 1) { + TRACE_DEBUG("Cmd8 Ver 2.00 isSdhc:%d\n\r", isSdhc); + return 0; + } + else { + TRACE_DEBUG("Cmd8 Ver 1.X isSdhc:%d\n\r", isSdhc); + return error; + } + } +} + +//------------------------------------------------------------------------------ +/// Addressed card sends its card-specific +/// data (CSD) on the CMD line. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd9(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response = 0; + unsigned char csdData[16]; + unsigned int i; + + TRACE_DEBUG("Cmd9()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SEND_CSD_CMD; + pCommand->resType = 1; + pCommand->blockSize = 16; + pCommand->pData = csdData; + pCommand->isRead = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + + // In SPI mode, reading CSD is the same as reading data. + for (i = 0; i < 4; i++) { + pSd->csd[i] = csdData[i*4] << 24 | + csdData[i*4+1] << 16 | + csdData[i*4+2] << 8 | + csdData[i*4+3]; + } + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Forces the card to stop transmission +/// \param pSd Pointer to a SD card driver instance. +/// \param pStatus Pointer to a status variable. +//------------------------------------------------------------------------------ +static unsigned char Cmd12(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd12()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_STOP_TRANSMISSION_CMD; + pCommand->conTrans = SPI_NEW_TRANSFER; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + + //TRACE_DEBUG("cmd12 resp 0x%X\n\r",response); + error = SD_SPI_R1b((unsigned char *)&response); + if (error == SD_ERROR_BUSY) { + while (SDSPI_WaitDataBusy((SdSpi *)pSd->pSdDriver) == 1); + error = 0; + } + return error; +} + +//------------------------------------------------------------------------------ +/// Addressed card sends its status register. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param pStatus Pointer to a status variable. +//------------------------------------------------------------------------------ +static unsigned char Cmd13(SdCard *pSd, unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + + TRACE_DEBUG("Cmd13()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SEND_STATUS_CMD; + pCommand->resType = 2; + pCommand->pResp = pStatus; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R2((unsigned char *)pStatus); + return error; +} + +//------------------------------------------------------------------------------ +/// In the case of a Standard Capacity SD Memory Card, this command sets the +/// block length (in bytes) for all following block commands (read, write, lock). +/// Default block length is fixed to 512 Bytes. +/// Set length is valid for memory access commands only if partial block read +/// operation are allowed in CSD. +/// In the case of a High Capacity SD Memory Card, block length set by CMD16 +/// command does not affect the memory read and write commands. Always 512 +/// Bytes fixed block length is used. This command is effective for LOCK_UNLOCK command. +/// In both cases, if block length is set larger than 512Bytes, the card sets the +/// BLOCK_LEN_ERROR bit. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockLength Block length in bytes. +//------------------------------------------------------------------------------ +static unsigned char Cmd16(SdCard *pSd, unsigned short blockLength) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd16()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SET_BLOCKLEN_CMD; + pCommand->arg = blockLength; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Continously transfers datablocks from card to host until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char Cmd18(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + //TRACE_DEBUG("Cmd18()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_READ_MULTIPLE_BLOCK_CMD; + pCommand->arg = address; + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = pData; + pCommand->isRead = 1; + pCommand->conTrans = SPI_NEW_TRANSFER; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Write block command +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char Cmd25(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd25()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_WRITE_MULTIPLE_BLOCK_CMD; + pCommand->arg = address; + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = (unsigned char *) pData; + pCommand->conTrans = SPI_NEW_TRANSFER; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + + if (!error) error = SD_SPI_R1((unsigned char *)&response); + return error; +} + + +//------------------------------------------------------------------------------ +/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply +/// ramp up time. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd55(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd55()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_APP_CMD; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// SPI Mode, Reads the OCR register of a card +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param pOcr OCR value of the card +//------------------------------------------------------------------------------ +static unsigned char Cmd58(SdCard *pSd, unsigned int *pOcr) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response[2]; + + TRACE_DEBUG("Cmd58()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_READ_OCR_CMD; + pCommand->resType = 3; + pCommand->pResp = &response[0]; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R3((unsigned char *)&response, pOcr); + return error; +} + +//------------------------------------------------------------------------------ +/// SPI Mode, Set CRC option of a card +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param option CRC option, 1 to turn on, 0 to trun off +//------------------------------------------------------------------------------ +static unsigned char Cmd59(SdCard *pSd, unsigned char option) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd59()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_CRC_ON_OFF_CMD; + pCommand->arg = (option & 0x1); + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Asks to all cards to send their operations conditions. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param hcs Shall be true if Host support High capacity. +/// \param pCCS Set the pointed flag to 1 if hcs != 0 and SD OCR CCS flag is set. +//------------------------------------------------------------------------------ +static unsigned char Acmd41(SdCard *pSd, unsigned char hcs, unsigned char *pCCS) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + do { + error = Cmd55(pSd); + if (error) { + return error; + } + + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SDCARD_APP_OP_COND_CMD; + pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; + if (hcs) { + pCommand->arg |= AT91C_CCS; + } + + pCommand->resType = 1; + pCommand->pResp = &response; + + // Send command + TRACE_DEBUG("Acmd41()\n\r"); + error = SendCommand(pSd); + if (error) { + return error; + } + error = SD_SPI_R1((unsigned char *)&response); + if (error) { + return error; + } + // continue if in idle mode + if ((response & 0x1) != 0) { // R1_SPI_IDLE + continue; + } + *pCCS = ((response & AT91C_CCS) != 0); + return 0; + } + while ((response & AT91C_CARD_POWER_UP_BUSY) != AT91C_CARD_POWER_UP_BUSY); + + return 0; +} + + +//------------------------------------------------------------------------------ +/// Continue to transfer datablocks from card to host until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char ContinuousRead(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + + TRACE_DEBUG("ContinuousRD(%d)\n\r", address); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = pData; + pCommand->isRead = 1; + pCommand->conTrans = SPI_CONTINUE_TRANSFER; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Continue to transfer datablocks from host to card until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char ContinuousWrite(SdCard *pSd, + unsigned short nbBlock, + const unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + + TRACE_DEBUG("ContinuousWR(%d)\n\r", address); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = (unsigned char *) pData; + pCommand->isRead = 0; + pCommand->conTrans = SPI_CONTINUE_TRANSFER; + pCommand->pResp = &response; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Move SD card to transfer state. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the transfer command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to transfer. +/// \param nbBlocks Number of blocks to be transfer. +/// \param pData Data buffer whose size is at least the block size. +/// \param isRead 1 for read data and 0 for write data. +//------------------------------------------------------------------------------ +static unsigned char MoveToTransferState(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData, + unsigned char isRead) +{ + unsigned int status; + unsigned char error; + SdDriver *pSdDriver = pSd->pSdDriver; + + if((pSd->state == SD_STATE_DATA) + || (pSd->state == SD_STATE_RCV)) { + + // SD SPI mode uses stop transmission token to stop multiple block write. + if ((pSd->state == SD_STATE_RCV) ) { + SDSPI_StopTranToken((SdSpi *)pSdDriver); + SDSPI_Wait((SdSpi *)pSdDriver, 1); + while (SDSPI_WaitDataBusy((SdSpi *)pSdDriver) == 1); + pSd->state = SD_STATE_STBY; + } + else { + error = Cmd12(pSd); + if (error) { + return error; + } + } + } + pSd->preBlock = address + (nbBlocks-1); + + if(isRead) { + TRACE_DEBUG("Read\n\r"); + // Wait for card to be ready for data transfers + do { + //TRACE_DEBUG("state = 0x%X\n\r", (status & STATUS_STATE) >> 9); + error = Cmd13(pSd, &status); + if (error) { + TRACE_DEBUG("Pb MTTS cmd13\n\r"); + return error; + } + break; + } + while (((status & STATUS_READY_FOR_DATA) == 0) || + ((status & STATUS_STATE) != STATUS_TRAN)); + // Read data + // Move to Sending data state + error = Cmd18(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + if (error) { + return error; + } + + pSd->state = SD_STATE_DATA; + } + else { + TRACE_DEBUG("Write\n\r"); + // Wait for card to be ready for data transfers + do { + error = Cmd13(pSd, &status); + if (error) { + TRACE_DEBUG("error cmd 13\n\r"); + return error; + } + break; + } + while ((status & STATUS_READY_FOR_DATA) == 0); + + // Move to Sending data state + error = Cmd25(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + if (error) { + TRACE_DEBUG("error cmd 25\n\r"); + return error; + } + + pSd->state = SD_STATE_RCV; + } + + return error; +} + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Read Block of data in a buffer pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to read. +/// \param nbBlocks Number of blocks to be read. +/// \param pData Data buffer whose size is at least the block size. +//------------------------------------------------------------------------------ +unsigned char SD_ReadBlock(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData) +{ + unsigned char error; + + SANITY_CHECK(pSd); + SANITY_CHECK(pData); + SANITY_CHECK(nbBlocks); +#if 1 + error = MoveToTransferState(pSd, address, nbBlocks, pData, 1); +#else + if((pSd->state == SD_STATE_DATA) + && ((pSd->preBlock + 1) == address)) { + +#if defined(at91rm9200) + error = Cmd12(pSd); + if (error) { + return error; + } +#else + error = ContinuousRead(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + pSd->preBlock = address + (nbBlocks-1); +#endif + } + else { + error = MoveToTransferState(pSd, address, nbBlocks, pData, 1); + } +#endif + return error; +} + +//------------------------------------------------------------------------------ +/// Write Block of data pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of block to write. +/// \param nbBlocks Number of blocks to be read +/// \param pData Pointer to a 512 bytes buffer to be transfered +//------------------------------------------------------------------------------ +unsigned char SD_WriteBlock(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + const unsigned char *pData) +{ + unsigned char error; + + SANITY_CHECK(pSd); + SANITY_CHECK(pData); + SANITY_CHECK(nbBlocks); + + if((pSd->state == SD_STATE_RCV) + && ((pSd->preBlock + 1) == address)) { + + error = ContinuousWrite(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + pSd->preBlock = address + (nbBlocks-1); + } + else { + + //TRACE_FATAL("SD_WriteBlock:MoveToTransferState\n\r"); + error = MoveToTransferState(pSd, address, nbBlocks, + (unsigned char *)pData, 0); + } + return error; +} + + +//------------------------------------------------------------------------------ +/// Run the SDcard SPI Mode initialization sequence. This function runs the +/// initialisation procedure and the identification process, then it sets the SD +/// card in transfer state to set the block length. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to SD driver already initialized. +//------------------------------------------------------------------------------ +unsigned char SD_SPI_Init(SdCard *pSd, SdDriver *pSpi) +{ + unsigned char isCCSet; + unsigned char error; + unsigned char cmd8Retries = 2; + unsigned char cmd1Retries = 1; + unsigned int pOCR; + + // The command GO_IDLE_STATE (CMD0) is the software reset command and sets card into Idle State + // regardless of the current card state. + error = Cmd0(pSd); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + + // CMD8 is newly added in the Physical Layer Specification Version 2.00 to support multiple voltage + // ranges and used to check whether the card supports supplied voltage. The version 2.00 host shall + // issue CMD8 and verify voltage before card initialization. + // The host that does not support CMD8 shall supply high voltage range... + do { + error = Cmd8(pSd, 1); + } + while ((error == SD_ERROR_NORESPONSE) && (cmd8Retries-- > 0)); + + if (error == SD_ERROR_NORESPONSE) { + // No response : Ver2.00 or later SD Memory Card(voltage mismatch) + // or Ver1.X SD Memory Card + // or not SD Memory Card + + TRACE_DEBUG("No response to Cmd8\n\r"); + + // CMD58 ? ! + error = Cmd58(pSd, &pOCR); + if (error) { + TRACE_ERROR("Error during initialization (%d), 8\n\r", error); + return error; + } + + // ACMD41 is a synchronization command used to negotiate the operation voltage range and to poll the + // cards until they are out of their power-up sequence. + error = Acmd41(pSd, 0, &isCCSet); + if (error) { + // Acmd41 failed : MMC card or unknown card + error = Cmd0(pSd); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + do { + error = Cmd1(pSd); + } + while ((error) && (cmd1Retries-- > 0)); + + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + else { + pSd->cardType = CARD_MMC; + } + } + else { + if(isCCSet == 0) { + TRACE_DEBUG("CARD SD\n\r"); + pSd->cardType = CARD_SD; + } + } + } + else if (!error) { + error = Cmd58(pSd, &pOCR); + if (error) { + TRACE_ERROR("Error during initialization (%d), 8\n\r", error); + return error; + } + + // Valid response : Ver2.00 or later SD Memory Card + error = Acmd41(pSd, 1, &isCCSet); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + error = Cmd58(pSd, &pOCR); + if (error) { + TRACE_ERROR("Error during initialization (%d), 8\n\r", error); + return error; + } + if (isCCSet) { + TRACE_DEBUG("CARD SDHC\n\r"); + pSd->cardType = CARD_SDHC; + } + else { + TRACE_DEBUG("CARD SD\n\r"); + pSd->cardType = CARD_SD; + } + } + else { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + + if (pSd->cardType != CARD_MMC) { + // The host issues CRC_ON_OFF (CMD59) to set data CRC on/off + // The host can turn the CRC option on and off using the CRC_ON_OFF command (CMD59). + // Host should enable CRC verification before issuing ACMD41. + error = Cmd59(pSd,0); // turn crc option OFF + if (error) { + + TRACE_ERROR("Error during initialization (%d)\n\r, 59", error); + return error; + } + } + + // The host issues SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register), + // e.g. block length, card storage capacity, etc... + error = Cmd9(pSd); + if (error) { + + TRACE_ERROR("Error during initialization (%d), 9\n\r", error); + return error; + } + return 0; +} +// + +//------------------------------------------------------------------------------ +/// Run the SDcard initialization sequence. This function runs the initialisation +/// procedure and the identification process, then it sets the SD card in transfer +/// state to set the block length and the bus width. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to SD driver already initialized. +/// \param mode Select SD or SPI access mode +//------------------------------------------------------------------------------ +unsigned char SD_Init(SdCard *pSd, SdDriver *pSdDriver) +{ + unsigned char error; + + //TRACE_DEBUG("SD_Init()\n\r"); + + // Initialize SdCard structure + pSd->pSdDriver = pSdDriver; + pSd->cardAddress = 0; + pSd->preBlock = 0xffffffff; + pSd->state = SD_STATE_STBY; + pSd->cardType = UNKNOWN_CARD; + memset(&(pSd->command), 0, sizeof(SdCmd)); + + // Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp up time + // ‘Supply ramp up time?provides the time that the power is built up to the operating level (the bus + // master supply voltage) and the time to wait until the SD card can accept the first command + + // Power On Init Special Command + //TRACE_DEBUG("Pon()\n\r"); + error = Pon(pSd); + if (error) { + TRACE_ERROR("SD_Init.Pon: (%d)\n\r", error); + return error; + } + + // After power-on or CMD0, all cards?CMD lines are in input mode, waiting for start bit of the next command. + // The cards are initialized with a default relative card address (RCA=0x0000) and with a default + // driver stage register setting (lowest speed, highest driving current capability). + + error = SD_SPI_Init(pSd, pSdDriver); + if (error) { + TRACE_ERROR("SD_Init._Init (%d)\n\r", error); + return error; + } + + // In the case of a Standard Capacity SD Memory Card, this command sets the + // block length (in bytes) for all following block commands (read, write, lock). + // Default block length is fixed to 512 Bytes. + // Set length is valid for memory access commands only if partial block read + // operation are allowed in CSD. + // In the case of a High Capacity SD Memory Card, block length set by CMD16 + // command does not affect the memory read and write commands. Always 512 + // Bytes fixed block length is used. This command is effective for LOCK_UNLOCK command. + // In both cases, if block length is set larger than 512Bytes, the card sets the + // BLOCK_LEN_ERROR bit. + if (pSd->cardType == CARD_SD) { + error = Cmd16(pSd, SD_BLOCK_SIZE); + if (error) { + TRACE_ERROR("SD_Init.Cmd16: (%d)\n\r", error); + return error; + } + } + + // If MMC Card & get size from EXT_CSD + if (pSd->cardType >= CARD_MMC && SD_CSD_C_SIZE(pSd) == 0xFFF) { +#if 0 + pSd->blockNr = SD_EXTCSD_BLOCKNR(pSd); + // Block number less than 0x100000000/512 + if (pSd->blockNr > 0x800000) + pSd->totalSize = 0xFFFFFFFF; + else + pSd->totalSize = SD_EXTCSD_TOTAL_SIZE(pSd); +#endif + } + // If SD CSD v2.0 + else if(pSd->cardType >= CARD_SD && SD_CSD_STRUCTURE(pSd) >= 1) { + pSd->blockNr = SD_CSD_BLOCKNR_HC(pSd); + pSd->totalSize = 0xFFFFFFFF; + } + // Normal card + else { + pSd->totalSize = SD_CSD_TOTAL_SIZE(pSd); + pSd->blockNr = SD_CSD_BLOCKNR(pSd); + } + + if (pSd->cardType == UNKNOWN_CARD) { + return SD_ERROR_NOT_INITIALIZED; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Stop the SDcard. This function stops all SD operations. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to MCI driver already initialized. +//------------------------------------------------------------------------------ +unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver) +{ + unsigned char error; + SdCmd *pCommand = &(pSd->command); + + SANITY_CHECK(pSd); + SANITY_CHECK(pSdDriver); + + if(pCommand->conTrans == SPI_CONTINUE_TRANSFER) + { + TRACE_DEBUG("SD_StopTransmission()\n\r"); + + error = Cmd12(pSd); + if(error) { + return error; + } + } + + SDSPI_Close((SdSpi *)pSdDriver); + return 0; +} + + + +//------------------------------------------------------------------------------ +/// Display the content of the CSD register +/// \param pSd +//------------------------------------------------------------------------------ +void SD_DisplayRegisterCSD(SdCard *pSd) +{ + TRACE_INFO("======== CSD ========"); + #if 0 + { + unsigned int i; + unsigned char *p = (unsigned char *)pSd->csd; + for(i = 0; i < 128 / 8; i++) { + if ((i % 16) == 0) TRACE_INFO_WP("\n\r [%3d]:", i); + TRACE_INFO_WP(" %2x", p[i]); + } + TRACE_INFO_WP("\n\r"); + TRACE_INFO("------------------------\n\r"); + } + #else + TRACE_INFO_WP("\n\r"); + #endif + TRACE_INFO(" .CSD_STRUCTURE 0x%x\r\n", SD_CSD_STRUCTURE(pSd)); + TRACE_INFO(" .SPEC_VERS 0x%x\r\n", SD_CSD_SPEC_VERS(pSd)); + TRACE_INFO(" .TAAC 0x%X\r\n", SD_CSD_TAAC(pSd) ); + TRACE_INFO(" .NSAC 0x%X\r\n", SD_CSD_NSAC(pSd) ); + TRACE_INFO(" .TRAN_SPEED 0x%X\r\n", SD_CSD_TRAN_SPEED(pSd) ); + TRACE_INFO(" .CCC 0x%X\r\n", SD_CSD_CCC(pSd) ); + TRACE_INFO(" .READ_BL_LEN 0x%X\r\n", SD_CSD_READ_BL_LEN(pSd) ); + TRACE_INFO(" .READ_BL_PARTIAL 0x%X\r\n", SD_CSD_READ_BL_PARTIAL(pSd) ); + TRACE_INFO(" .WRITE_BLK_MISALIGN 0x%X\r\n", SD_CSD_WRITE_BLK_MISALIGN(pSd)); + TRACE_INFO(" .READ_BLK_MISALIGN 0x%X\r\n", SD_CSD_READ_BLK_MISALIGN(pSd) ); + TRACE_INFO(" .DSR_IMP 0x%X\r\n", SD_CSD_DSR_IMP(pSd) ); + TRACE_INFO(" .C_SIZE 0x%X\r\n", SD_CSD_C_SIZE(pSd) ); + TRACE_INFO(" .C_SIZE_HC 0x%X\r\n", SD_CSD_C_SIZE_HC(pSd) ); + TRACE_INFO(" .VDD_R_CURR_MIN 0x%X\r\n", SD_CSD_VDD_R_CURR_MIN(pSd) ); + TRACE_INFO(" .VDD_R_CURR_MAX 0x%X\r\n", SD_CSD_VDD_R_CURR_MAX(pSd) ); + TRACE_INFO(" .VDD_W_CURR_MIN 0x%X\r\n", SD_CSD_VDD_W_CURR_MIN(pSd) ); + TRACE_INFO(" .VDD_W_CURR_MAX 0x%X\r\n", SD_CSD_VDD_W_CURR_MAX(pSd) ); + TRACE_INFO(" .C_SIZE_MULT 0x%X\r\n", SD_CSD_C_SIZE_MULT(pSd) ); + TRACE_INFO(" .ERASE_BLK_EN 0x%X\r\n", SD_CSD_ERASE_BLK_EN(pSd) ); + TRACE_INFO(" .SECTOR_SIZE 0x%X\r\n", SD_CSD_SECTOR_SIZE(pSd) ); + TRACE_INFO(" .WP_GRP_SIZE 0x%X\r\n", SD_CSD_WP_GRP_SIZE(pSd) ); + TRACE_INFO(" .WP_GRP_ENABLE 0x%X\r\n", SD_CSD_WP_GRP_ENABLE(pSd) ); + TRACE_INFO(" .R2W_FACTOR 0x%X\r\n", SD_CSD_R2W_FACTOR(pSd) ); + TRACE_INFO(" .WRITE_BL_LEN 0x%X\r\n", SD_CSD_WRITE_BL_LEN(pSd) ); + TRACE_INFO(" .WRITE_BL_PARTIAL 0x%X\r\n", SD_CSD_WRITE_BL_PARTIAL(pSd) ); + TRACE_INFO(" .FILE_FORMAT_GRP 0x%X\r\n", SD_CSD_FILE_FORMAT_GRP(pSd) ); + TRACE_INFO(" .COPY 0x%X\r\n", SD_CSD_COPY(pSd) ); + TRACE_INFO(" .PERM_WRITE_PROTECT 0x%X\r\n", SD_CSD_PERM_WRITE_PROTECT(pSd)); + TRACE_INFO(" .TMP_WRITE_PROTECT 0x%X\r\n", SD_CSD_TMP_WRITE_PROTECT(pSd) ); + TRACE_INFO(" .FILE_FORMAT 0x%X\r\n", SD_CSD_FILE_FORMAT(pSd) ); + TRACE_INFO(" .ECC 0x%X\r\n", SD_CSD_ECC(pSd) ); + TRACE_INFO(" .CRC 0x%X\r\n", SD_CSD_CRC(pSd) ); + TRACE_INFO(" .MULT 0x%X\r\n", SD_CSD_MULT(pSd) ); + TRACE_INFO(" .BLOCKNR 0x%X\r\n", SD_CSD_BLOCKNR(pSd) ); + TRACE_INFO(" .BLOCKNR_HC 0x%X\r\n", SD_CSD_BLOCKNR_HC(pSd) ); + TRACE_INFO(" .BLOCK_LEN 0x%X\r\n", SD_CSD_BLOCK_LEN(pSd) ); + TRACE_INFO(" .TOTAL_SIZE 0x%X\r\n", SD_CSD_TOTAL_SIZE(pSd) ); + TRACE_INFO(" .TOTAL_SIZE_HC 0x%X\r\n", SD_CSD_TOTAL_SIZE_HC(pSd) ); + TRACE_INFO(" -SD_TOTAL_SIZE 0x%X\r\n", SD_TOTAL_SIZE(pSd) ); + TRACE_INFO(" -SD_TOTAL_BLOCK 0x%X\r\n", SD_TOTAL_BLOCK(pSd) ); +} + diff --git a/memories/sdmmc/sdmmc_spi.h b/memories/sdmmc/sdmmc_spi.h new file mode 100644 index 0000000..3df15cd --- /dev/null +++ b/memories/sdmmc/sdmmc_spi.h @@ -0,0 +1,720 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "sdmmc_spi" +/// +/// !Purpose +/// +/// Implementation for sdcard spi mode physical layer driver. Supply a set of sdcard spi mode's +/// interface. +/// +/// !Usage +/// +/// -# SD_Init: Run the SDcard initialization sequence +/// -# SD_SPI_Init : Run the SDcard SPI Mode initialization sequence +/// -# SD_Stop: Stop the SDcard by sending Cmd12 +/// -# SD_ReadBlock : Read blocks of data +/// -# SD_WriteBlock : Write blocks of data +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard spi mode initialization and identification" +/// +/// !Purpose +/// +/// sdcard spi mode initialization and identification sequence +/// +/// !Description +/// - Host sends Cmd0 to do card reset, card is in "idle state". +/// - Host sends Cmd8 and checks the response of the card, only sdcard supports physical +/// layer version 2.00 will response correctly to Cmd8, and this command is mandatory to be +/// sent before ACmd41 for sdcard which support physical layer version 2.00, to enable new +/// functions or to validate a dual-voltage card. +/// - refer to "If Cmd8 response ok" branch for the initialize of sdcard 2.0. +/// - refer to "If Cmd8 response fail" branch for the initialize of sdcard 1.x, mmc card, sdcard2.0 +/// with invalid voltage. +/// - If Cmd8 response ok +/// - Host sends Cmd58 to read OCR register to validate the voltage. +/// - If the response to Cmd58 is fail, initialize ends, the card is put into inactive state. +/// - If the response to Cmd58 is ok, continue to the next step. +/// - Host sends ACmd41* with argument "HCS" equal to "1". +/// - If the response to ACmd41 failed, it means the card does not match the voltage +/// desired by the host, the card will be put into inactive state, initialize ends. +/// - If the response with "CCS" equal to "1", the card is a version 2.0 high capacity sdcard, +/// refer to "Card Initialize" for the succeeding initialize sequence. +/// - If the response with "CCS" equal to "0", the card is a version 2.0 standard capacity sdcard. +/// refer to "Card Initialize" for the succeeding initialize sequence. +/// - If Cmd8 response fail +/// - Host sends Cmd58 to read OCR register to validate the voltage. +/// - If the response to Cmd58 is fail, initialize ends, the card is put into inactive state. +/// - If the response to Cmd58 is ok, continue to the next step. +/// - Host sends ACmd41* argument "HCS" equal to "0". +/// - If the response to ACmd41 ok, the card is a version 1.x sdcard, refer to "Card Initialize" for +/// the succeeding initialize sequence. +/// - If the response to ACmd41 fails +/// - Host sends Cmd0 to reset card. +/// - Host sends Cmd1 to card. +/// - If card has response to Cmd1, the card is a MMC card, refer to "Card Initialize" for the +/// succeeding initialize sequence. +/// - If card has no response to Cmd1, the card is either an unknown card or a card does +/// not match host's voltage, the initialize ends. +/// - Card Initialize +/// - At this stage, the initialization and identification process is over, the following steps are done +/// for the sdcard's succeeding operation. +/// - Host sends Cmd59 to turn sdcard's CRC option off. +/// - Host sends Cmd9 to get the Card Specific Data (CSD). +/// +/// \note Send Cmd55 before send ACmd41. \endnote +/// \note sdcard include ver 1.x sdcard, ver2.0 standard capacity sdcard, ver2.0 high capacity sdcard \endnote +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard spi mode write" +/// +/// !Purpose +/// +/// sdcard spi mode write process +/// +/// !Description +/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send +/// Cmd12 or use stop transmission token to stop multiple block write, and to transit sdcard to +/// "stand-by state". +/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". +/// - Host sends Cmd25 to do multiple blocks write, the address here is different between high capacity +/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's +/// address is equal to block number times 512. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard spi mode read" +/// +/// !Purpose +/// +/// sdcard spi mode read process +/// +/// !Description +/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send +/// Cmd12 or use stop transmission token to stop multiple block read and to transit sdcard to +/// "stand-by state". +/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". +/// - Host sends Cmd18 to do multiple blocks read, the address here is different between high capacity +/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's +/// address is equal to block number times 512. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdhc" +/// +/// !Purpose +/// +/// highlight of sdhc +/// +/// !Sdhc initialization and identification +/// +/// - Refer to page "sdcard spi mode initialization and identification" for the initialization and identification +/// sequence of a sdhc. +/// +/// !Functional difference between sdhc and standard capacity sdcard +/// +/// - Command argument is different: +/// - Sdhc uses block address format in memory access commands*, block length is fixed to 512 bytes. +/// - Standard capacity sdcard uses byte address format in memory access commands, block length +/// is defined in Cmd16. +/// - Partial access and misalign access are disabled in sdhc as the block address is used. +/// - Sdhc does not support write-protected commands (Cmd28, Cmd29, Cmd30). +/// +/// \note Memory access commands means block read commands (CMD17, CMD18), block write commands +/// (CMD24, CMD25), and block erase commands (CMD32, CMD33). +/// +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdmmc_spi" +/// +/// !Purpose +/// +/// Implementation for sdcard sd mode physical layer driver. Supply a set of sdcard sd mode's +/// interface. +/// +/// !Usage +/// +/// -# SD_Init: Run the SDcard initialization sequence +/// -# SD_MCI_Init : Run the SDcard SD Mode initialization sequence +/// -# SD_Stop: Stop the SDcard by sending Cmd12 +/// -# SD_ReadBlock : Read blocks of data +/// -# SD_WriteBlock : Write blocks of data +/// -# Cmd0 : Resets all cards to idle state +/// -# Cmd1 : MMC send operation condition command +/// -# Cmd2 : Asks any card to send the CID numbers on the CMD line +/// -# Cmd3 : Ask the card to publish a new relative address +/// -# Cmd7 : Command toggles a card between the stand-by and transfer states or between +/// the programming and disconnect states +/// -# Cmd8 : Sends SD Memory Card interface condition, which includes host supply voltage +/// information and asks the card whether card supports voltage +/// -# Cmd9 : Addressed card sends its card-specific data (CSD) on the CMD line +/// -# Cmd12 : Forces the card to stop transmission +/// -# Cmd13 : Addressed card sends its status register +/// -# Cmd16 : Set block length +/// -# Cmd18 : Read multiple blocks +/// -# Cmd25 : Write multiple blocks +/// -# Cmd55 : App command, should be sent before application specific command +/// -# Acmd6 : Defines the data bus width +/// -# Acmd41 : Asks to all cards to send their operations conditions +/// -# CmdEMMC8 : Sends eMMC EXT_CSD command +/// -# CmdEMMC6 : Switches the mode of operation of the selected card or modifies the +/// EXT_CSD registers +//------------------------------------------------------------------------------ + +#ifndef SDCARD_H +#define SDCARD_H + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + + +/// There was an error with the SD driver. +#define SD_ERROR_DRIVER 1 +/// The SD card did not answer the command. +#define SD_ERROR_NORESPONSE 2 +/// The SD card did not answer the command. +#define SD_ERROR_NOT_INITIALIZED 3 +/// The SD card is busy +#define SD_ERROR_BUSY 4 +/// The operation is not supported +#define SD_ERROR_NOT_SUPPORT 5 + +/// SD card block size in bytes. +#define SD_BLOCK_SIZE 512 +/// SD card block size binary shift value +#define SD_BLOCK_SIZE_BIT 9 + +//- MMC Card Command Types +/// Broadcast commands (bc), no response +#define MMC_CCT_BC 0 +/// Broadcase commands with response (bcr) +#define MMC_CCT_BCR 1 +/// Addressed commands (ac), no data transfer on DAT lines +#define MMC_CCT_AC 2 +/// Addressed data transfer commands (adtc), data transfer on DAT lines +#define MMC_CCT_ADTC 3 + +//- MMC Card Command Classes (CCC) +/// Class 0: basic +#define MMC_CCC_BASIC 0 +/// Class 1: stream read +#define MMC_CCC_STREAM_READ 1 +/// Class 2: block read +#define MMC_CCC_BLOCK_READ 2 +/// Class 3: stream write +#define MMC_CCC_STREAM_WRITE 3 +/// Class 4: block write +#define MMC_CCC_BLOCK_WRITE 4 +/// Class 5: erase +#define MMC_CCC_ERASE 5 +/// Class 6: write protection +#define MMC_CCC_WRITE_PROTECTION 6 +/// Class 7: lock card +#define MMC_CCC_LOCK_CARD 7 +/// Class 8: application specific +#define MMC_CCC_APP_SPEC 8 +/// Class 9: I/O mode +#define MMC_CCC_IO_MODE 9 + +//- MMC/SD Card Command Response Type +/// R1 (normal response command), 48bits +#define MMC_RESP_R1 1 +#define SD_RESP_R1 MMC_RESP_R1 +/// R1b: busy signal transmitted on the data line DAT0 +#define MMC_RESP_R1b 1 +#define SD_RESP_R1b MMC_RESP_R1b +/// R2: 136bits, CID, CSD register +#define MMC_RESP_R2 2 +#define SD_RESP_R2 MMC_RESP_R2 +/// R3: 48bits, OCR +#define MMC_RESP_R3 3 +#define SD_RESP_R3 MMC_RESP_R3 +/// R4 (Fast I/O), 48bits +#define MMC_RESP_R4 4 +/// R5 (Interrupt request), 48bits +#define MMC_RESP_R5 5 +/// R6 (Published RCA response), 48bits +#define SD_RESP_R6 6 +/// R7 (Card interface condition), 48bits +#define SD_RESP_R7 7 + +//- MMC Card CMD6 access mode +#define MMC_SWITCH_CMDSET 0 +#define MMC_SWITCH_SETBITS 1 +#define MMC_SWITCH_CLRBITS 2 +#define MMC_SWITCH_WRITE 3 + +//-MMC Boot partition enable +/// Boot partition 1 enabled for boot +#define MMC_BOOT_PART_1 1 +/// Boot partition 2 enabled for boot +#define MMC_BOOT_PART_2 2 +/// User area enabled for boot +#define MMC_BOOT_PART_USER 7 + +//-MMC Boot partition access +/// R/W boot partition 1 +#define MMC_BOOT_ACC_PART1 1 +/// R/W boot partition 2 +#define MMC_BOOT_ACC_PART2 2 + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +// CID register access macros (128 bits, 4 * 32 bits). +#define SD_CID(pSd, bitfield, bits) ( (pSd->cid[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) +#define SD_CID_MID(pSd) SD_CID(pSd, 120, 8) ///< Manufacturer ID +#define SD_CID_BGA(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) +#define SD_CID_CBS(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) +#define SD_CID_OID_BYTE_1(pSd) SD_CID(pSd, 112, 8) ///< OEM/Application ID byte 1 +#define SD_CID_OID_BYTE_0(pSd) SD_CID(pSd, 104, 8) ///< OEM/Application ID byte 0 +#define SD_CID_PNM_BYTE_4(pSd) SD_CID(pSd, 96, 8) ///< Product revision byte 4 +#define SD_CID_PNM_BYTE_3(pSd) SD_CID(pSd, 88, 8) ///< Product revision byte 3 +#define SD_CID_PNM_BYTE_2(pSd) SD_CID(pSd, 80, 8) ///< Product revision byte 2 +#define SD_CID_PNM_BYTE_1(pSd) SD_CID(pSd, 72, 8) ///< Product revision byte 1 +#define SD_CID_PNM_BYTE_0(pSd) SD_CID(pSd, 64, 8) ///< Product revision byte 0 +#define SD_CID_PRV_1(pSd) SD_CID(pSd, 24, 8) ///< Product serial number 1 +#define SD_CID_PRV_2(pSd) SD_CID(pSd, 32,24) ///< Product serial number 2 +#define SD_CID_MDT_YEAR(pSd) (SD_CID(pSd, 12, 8))+2000///< Manufacturing date year +#define SD_CID_MDT_MONTH(pSd) SD_CID(pSd, 8, 4) ///< Manufacturing date month +#define SD_CID_CRC(pSd) SD_CID(pSd, 1, 7) ///< CRC7 checksum + +// CSD register access macros (128 bits, 4 * 32 bits). +#define SD_CSD(pSd, bitfield, bits) ((((pSd)->csd)[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) +#define SD_CSD_STRUCTURE(pSd) SD_CSD(pSd, 126, 2) ///< CSD structure 00b Version 1.0 01b version 2.0 High Cap +#define SD_CSD_SPEC_VERS(pSd) SD_CSD(pSd, 122, 4) ///< System Specification Version Number +#define SD_CSD_TAAC(pSd) SD_CSD(pSd, 112, 8) ///< Data read-access-time-1 +#define SD_CSD_NSAC(pSd) SD_CSD(pSd, 104, 8) ///< Data read access-time-2 in CLK cycles +#define SD_CSD_TRAN_SPEED(pSd) SD_CSD(pSd, 96, 8) ///< Max. data transfer rate +#define SD_CSD_CCC(pSd) SD_CSD(pSd, 84, 12) ///< Card command class +#define SD_CSD_READ_BL_LEN(pSd) SD_CSD(pSd, 80, 4) ///< Max. read data block length +#define SD_CSD_READ_BL_PARTIAL(pSd) SD_CSD(pSd, 79, 1) ///< Bartial blocks for read allowed +#define SD_CSD_WRITE_BLK_MISALIGN(pSd) SD_CSD(pSd, 78, 1) ///< Write block misalignment +#define SD_CSD_READ_BLK_MISALIGN(pSd) SD_CSD(pSd, 77, 1) ///< Read block misalignment +#define SD_CSD_DSR_IMP(pSd) SD_CSD(pSd, 76, 1) ///< DSP implemented +#define SD_CSD_C_SIZE(pSd) ((SD_CSD(pSd, 72, 2) << 10) + \ + (SD_CSD(pSd, 64, 8) << 2) + \ + SD_CSD(pSd, 62, 2)) ///< Device size +#define SD_CSD_C_SIZE_HC(pSd) ((SD_CSD(pSd, 64, 6) << 16) + \ + (SD_CSD(pSd, 56, 8) << 8) + \ + SD_CSD(pSd, 48, 8)) ///< Device size v2.0 High Capacity +#define SD_CSD_VDD_R_CURR_MIN(pSd) SD_CSD(pSd, 59, 3) ///< Max. read current @VDD min +#define SD_CSD_VDD_R_CURR_MAX(pSd) SD_CSD(pSd, 56, 3) ///< Max. read current @VDD max +#define SD_CSD_VDD_W_CURR_MIN(pSd) SD_CSD(pSd, 53, 3) ///< Max. write current @VDD min +#define SD_CSD_VDD_W_CURR_MAX(pSd) SD_CSD(pSd, 50, 3) ///< Max. write current @VDD max +#define SD_CSD_C_SIZE_MULT(pSd) SD_CSD(pSd, 47, 3) ///< Device size multiplier +#define SD_CSD_ERASE_BLK_EN(pSd) SD_CSD(pSd, 46, 1) ///< Erase single block enable +#define MMC_CSD_ERASE_BLK_EN(pSd) SD_CSD(pSd, 46, 1) ///< Erase single block enable +#define MMC_CSD_ERASE_GRP_SIZE(pSd) SD_CSD(pSd, 42, 4) ///< Erase group size +#define SD_CSD_ERASE_GRP_MULT(pSd) SD_CSD(pSd, 37, 4) ///< Erase group size multiplier +#define SD_CSD_SECTOR_SIZE(pSd) ((SD_CSD(pSd, 40, 6) << 1) + SD_CSD(pSd, 39, 1)) ///< Erase sector size +#define SD_CSD_WP_GRP_SIZE(pSd) SD_CSD(pSd, 32, 7) ///< Write protect group size +#define SD_CSD_WP_GRP_ENABLE(pSd) SD_CSD(pSd, 31, 1) ///< write protect group enable +#define SD_CSD_R2W_FACTOR(pSd) SD_CSD(pSd, 26, 3) ///< Write speed factor +#define SD_CSD_WRITE_BL_LEN(pSd) ((SD_CSD(pSd, 24, 2) << 2) + SD_CSD(pSd, 22, 2)) ///< Max write block length +#define SD_CSD_WRITE_BL_PARTIAL(pSd) SD_CSD(pSd, 21, 1) ///< Partial blocks for write allowed +#define SD_CSD_CONTENT_PROT_APP(pSd) SD_CSD(pSd, 16, 1) ///< File format group +#define SD_CSD_FILE_FORMAT_GRP(pSd) SD_CSD(pSd, 15, 1) ///< File format group +#define SD_CSD_COPY(pSd) SD_CSD(pSd, 14, 1) ///< Copy flag (OTP) +#define SD_CSD_PERM_WRITE_PROTECT(pSd) SD_CSD(pSd, 13, 1) ///< Permanent write protect +#define SD_CSD_TMP_WRITE_PROTECT(pSd) SD_CSD(pSd, 12, 1) ///< Temporary write protection +#define SD_CSD_FILE_FORMAT(pSd) SD_CSD(pSd, 10, 2) ///< File format +#define SD_CSD_ECC(pSd) SD_CSD(pSd, 8, 2) ///< CRC +#define SD_CSD_CRC(pSd) SD_CSD(pSd, 1, 7) ///< CRC +#define SD_CSD_MULT(pSd) (1 << (SD_CSD_C_SIZE_MULT(pSd) + 2)) +#define SD_CSD_BLOCKNR(pSd) ((SD_CSD_C_SIZE(pSd) + 1) * SD_CSD_MULT(pSd)) +#define SD_CSD_BLOCKNR_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 1024) +#define SD_CSD_BLOCK_LEN(pSd) (1 << SD_CSD_READ_BL_LEN(pSd)) +#define SD_CSD_TOTAL_SIZE(pSd) (SD_CSD_BLOCKNR(pSd) * SD_CSD_BLOCK_LEN(pSd)) +#define SD_CSD_TOTAL_SIZE_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 512* 1024) +#define SD_TOTAL_SIZE(pSd) ((pSd)->totalSize) +#define SD_TOTAL_BLOCK(pSd) ((pSd)->blockNr) + +// SCR register access macros (64 bits, 2 * 32 bits, 8 * 8 bits). +#define SD_EXT_OFFSET_SD_SCR 0 // DW +#define SD_SCR(pSd, bitfield, bits) \ + ( ((char*)(pSd)->extData)[7 - ((bitfield)/8)] >> ((bitfield)%8) \ + & ((1 << (bits)) - 1) \ + ) +#define SD_SCR_SCR_STRUCTURE(pSd) SD_SCR(pSd, 60, 4) +#define SD_SCR_SCR_STRUCTURE_1_0 0 +#define SD_SCR_SD_SPEC(pSd) SD_SCR(pSd, 56, 4) +#define SD_SCR_SD_SPEC_1_0_01 0 +#define SD_SCR_SD_SPEC_1_10 1 +#define SD_SCR_SD_SPEC_2_00 2 +#define SD_SCR_DATA_STAT_AFTER_ERASE(pSd) SD_SCR(pSd, 55, 1) +#define SD_SCR_SD_SECURITY(pSd) SD_SCR(pSd, 52, 3) +#define SD_SCR_SD_SECURITY_NO 0 +#define SD_SCR_SD_SECURITY_NOTUSED 1 +#define SD_SCR_SD_SECURITY_1_01 2 +#define SD_SCR_SD_SECURITY_2_00 3 +#define SD_SCR_SD_BUS_WIDTHS(pSd) SD_SCR(pSd, 48, 4) +#define SD_SCR_SD_BUS_WIDTH_1BITS (1 << 0) +#define SD_SCR_SD_BUS_WIDTH_4BITS (1 << 2) + +// SD Status access macros (512 bits, 16 * 32 bits, 64 * 8 bits). +#define SD_EXT_OFFSET_SD_STAT 2 // DW +#define SD_STAT(pSd, bitfield, bits) \ + ( ((char*)&(pSd)->extData[2])[63 - ((bitfield)/8)] >> ((bitfield)%8) \ + & ((1 << (bits)) - 1) \ + ) +/// Bus width, 00: default, 10:4-bit +#define SD_STAT_DAT_BUS_WIDTH(pSd) SD_STAT(pSd, 510, 2) +#define SD_STAT_DATA_BUS_WIDTH_1BIT 0x0 +#define SD_STAT_DATA_BUS_WIDTH_4BIT 0x2 +/// Secured Mode +#define SD_STAT_SECURED_MODE(pSd) SD_STAT(pSd, 509, 1) +/// SD Memory Cards as defined in 1.01~2.00 +#define SD_STAT_SD_CARD_TYPE(pSd) (SD_STAT(pSd, 480, 8) \ + + (SD_STAT(pSd, 488, 8) << 8) ) +/// STD: ThisSize*Multi*BlockLen, HC: Size in bytes +#define SD_STAT_SIZE_OF_PROTECTED_AREA(pSd) (SD_STAT(pSd, 448, 8) \ + + (SD_STAT(pSd, 456, 8) << 8) \ + + (SD_STAT(pSd, 464, 8) << 16) \ + + (SD_STAT(pSd, 472, 8) << 24) ) +/// Speed Class, value can be calculated by Pw/2 +#define SD_STAT_SPEED_CLASS(pSd) SD_STAT(pSd, 440, 8) +#define SD_STAT_SPEED_CLASS_0 0 +#define SD_STAT_SPEED_CLASS_2 1 // >= 2MB/s +#define SD_STAT_SPEED_CLASS_4 2 // >= 4MB/s +#define SD_STAT_SPEED_CLASS_6 3 // >= 6MB/s +/// 8-bit, by 1MB/s step. +#define SD_STAT_PERFORMANCE_MOVE(pSd) SD_STAT(pSd, 432, 8) +/// AU Size, in power of 2 from 16KB +#define SD_STAT_AU_SIZE(pSd) SD_STAT(pSd, 428, 4) +#define SD_STAT_AU_SIZE_16K 1 +#define SD_STAT_AU_SIZE_32K 2 +#define SD_STAT_AU_SIZE_64K 3 +#define SD_STAT_AU_SIZE_128K 4 +#define SD_STAT_AU_SIZE_256K 5 +#define SD_STAT_AU_SIZE_512K 6 +#define SD_STAT_AU_SIZE_1M 7 +#define SD_STAT_AU_SIZE_2M 8 +#define SD_STAT_AU_SIZE_4M 9 +/// 16-bit, number of AUs erased. +#define SD_STAT_ERASE_SIZE(pSd) (SD_STAT(pSd, 408, 8) \ + + (SD_STAT(pSd, 416, 8) << 8) ) +#define SD_STAT_ERASE_TIMEOUT(pSd) SD_STAT(pSd, 402, 6) +#define SD_STAT_ERASE_OFFSET(pSd) SD_STAT(pSd, 400, 2) + +// SD Switch Status access macros (512 bits, 16 * 32 bits, 64 * 8 bits). +#define SD_SW_STAT(p, bitfield, bits) \ + ( ((char*)(p))[63 - ((bitfield)/8)] >> ((bitfield)%8) \ + & ((1 << (bits)) - 1) \ + ) +#define SD_SW_STAT_MAX_CURRENT_CONSUMPTION(p) ( SD_SW_STAT(p, 496, 8) \ + + (SD_SW_STAT(p, 504, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP6_INFO(p) ( SD_SW_STAT(p, 480, 8) \ + + (SD_SW_STAT(p, 488, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP5_INFO(p) ( SD_SW_STAT(p, 464, 8) \ + + (SD_SW_STAT(p, 472, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP4_INFO(p) ( SD_SW_STAT(p, 448, 8) \ + + (SD_SW_STAT(p, 456, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP3_INFO(p) ( SD_SW_STAT(p, 432, 8) \ + + (SD_SW_STAT(p, 440, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP2_INFO(p) ( SD_SW_STAT(p, 416, 8) \ + + (SD_SW_STAT(p, 424, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP1_INFO(p) ( SD_SW_STAT(p, 400, 8) \ + + (SD_SW_STAT(p, 408, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP6_RC(p) SD_SW_STAT(p, 396, 4) +#define SD_SW_STAT_FUN_GRP5_RC(p) SD_SW_STAT(p, 392, 4) +#define SD_SW_STAT_FUN_GRP4_RC(p) SD_SW_STAT(p, 388, 4) +#define SD_SW_STAT_FUN_GRP3_RC(p) SD_SW_STAT(p, 384, 4) +#define SD_SW_STAT_FUN_GRP2_RC(p) SD_SW_STAT(p, 380, 4) +#define SD_SW_STAT_FUN_GRP1_RC(p) SD_SW_STAT(p, 376, 4) +#define SD_SW_STAT_FUN_GRP_RC_ERROR 0xF +#define SD_SW_STAT_DATA_STRUCT_VER(p) SD_SW_STAT(p, 368, 8) +#define SD_SW_STAT_FUN_GRP6_BUSY(p) ( SD_SW_STAT(p, 352, 8) \ + + (SD_SW_STAT(p, 360, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP5_BUSY(p) ( SD_SW_STAT(p, 336, 8) \ + + (SD_SW_STAT(p, 344, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP4_BUSY(p) ( SD_SW_STAT(p, 320, 8) \ + + (SD_SW_STAT(p, 328, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP3_BUSY(p) ( SD_SW_STAT(p, 304, 8) \ + + (SD_SW_STAT(p, 312, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP2_BUSY(p) ( SD_SW_STAT(p, 288, 8) \ + + (SD_SW_STAT(p, 296, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP1_BUSY(p) ( SD_SW_STAT(p, 272, 8) \ + + (SD_SW_STAT(p, 280, 8) << 8) ) +#define SD_SW_STAT_FUN_GRP_FUN_BUSY(funNdx) (1 << (funNdx)) + +// EXT_CSD register definition. +#define SD_EXTCSD_S_CMD_SET_INDEX 504 // Below belongs to Properties Segment +#define SD_EXTCSD_BOOT_INFO_INDEX 228 +#define SD_EXTCSD_BOOT_SIZE_MULTI_INDEX 226 +#define SD_EXTCSD_ACC_SIZE_INDEX 225 +#define SD_EXTCSD_HC_ERASE_GRP_SIZE_INDEX 224 +#define SD_EXTCSD_ERASE_TIMEOUT_MULT_INDEX 223 +#define SD_EXTCSD_REL_WR_SEC_C_INDEX 222 +#define SD_EXTCSD_HC_WP_GRP_SIZE_INDEX 221 +#define SD_EXTCSD_S_C_VCC_INDEX 220 +#define SD_EXTCSD_S_C_VCCQ_INDEX 219 +#define SD_EXTCSD_S_A_TIMEOUT_INDEX 217 +#define SD_EXTCSD_SEC_COUNT_INDEX 212 +#define SD_EXTCSD_MIN_PERF_W_8_52_INDEX 210 +#define SD_EXTCSD_MIN_PERF_R_8_52_INDEX 209 +#define SD_EXTCSD_MIN_PERF_W_8_26_4_52_INDEX 208 +#define SD_EXTCSD_MIN_PERF_R_8_26_4_52_INDEX 207 +#define SD_EXTCSD_MIN_PERF_W_4_26_INDEX 206 +#define SD_EXTCSD_MIN_PERF_R_4_26_INDEX 205 +#define SD_EXTCSD_PWR_CL_26_360_INDEX 203 +#define SD_EXTCSD_PWR_CL_52_360_INDEX 202 +#define SD_EXTCSD_PWR_CL_26_195_INDEX 201 +#define SD_EXTCSD_PWR_CL_52_195_INDEX 200 +#define SD_EXTCSD_CARD_TYPE_INDEX 196 +#define SD_EXTCSD_CSD_STRUCTURE_INDEX 194 +#define SD_EXTCSD_EXT_CSD_REV_INDEX 192 + +#define SD_EXTCSD_CMD_SET_INDEX 191 //Below belongs to Mode Segment +#define SD_EXTCSD_CMD_SET_REV_INDEX 189 +#define SD_EXTCSD_POWER_CLASS_INDEX 187 +#define SD_EXTCSD_HS_TIMING_INDEX 185 +#define SD_EXTCSD_BUS_WIDTH_INDEX 183 +#define SD_EXTCSD_ERASED_MEM_CONT_INDEX 181 +#define SD_EXTCSD_BOOT_CONFIG_INDEX 179 +#define SD_EXTCSD_BOOT_BUS_WIDTH_INDEX 177 +#define SD_EXTCSD_ERASE_GROUP_DEF_INDEX 175 + +// Ext_CSD register access marco +#define MMC_EXTCSD_U8(pSd, bytefield) \ + ( ((unsigned char*)((pSd)->extData))[(bytefield)] ) +#define MMC_EXTCSD_U16(pSd, bytefield) \ + ( (((unsigned char*)((pSd)->extData))[(bytefield) ] << 0) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 1] << 8) ) +#define MMC_EXTCSD_U32(pSd, bytefield) \ + ( (((unsigned char*)((pSd)->extData))[(bytefield) ] << 0) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 1] << 8) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 2] << 16) + \ + (((unsigned char*)((pSd)->extData))[(bytefield) + 24] << 24) ) +#define MMC_EXTCSD(pSd) ((unsigned char*)((pSd)->extData)) +#define SD_EXTCSD_S_CMD_SET(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_CMD_SET_INDEX]) // Supported Command Sets +#define SD_EXTCSD_BOOT_INFO(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_INFO_INDEX]) // Boot information +#define SD_EXTCSD_BOOT_SIZE_MULTI(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_SIZE_MULTI_INDEX]) // Boot partition size +#define SD_EXTCSD_ACC_SIZE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ACC_SIZE_INDEX]) // Access size +#define SD_EXTCSD_HC_ERASE_GRP_SIZE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_HC_ERASE_GRP_SIZE_INDEX]) // High-capacity erase unit size +#define SD_EXTCSD_ERASE_TIMEOUT_MULT(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ERASE_TIMEOUT_MULT_INDEX]) // High-capacity erase timeout +#define SD_EXTCSD_REL_WR_SEC_C(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_REL_WR_SEC_C_INDEX]) // Reliable write sector count +#define SD_EXTCSD_HC_WP_GRP_SIZE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_HC_WP_GRP_SIZE_INDEX]) // High-capacity write protect group size +#define SD_EXTCSD_S_C_VCC(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_C_VCC_INDEX]) // Sleep current(VCC) +#define SD_EXTCSD_S_C_VCCQ(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_C_VCCQ_INDEX]) // Sleep current(VCCQ) +#define SD_EXTCSD_S_A_TIMEOUT(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_S_A_TIMEOUT_INDEX]) // Sleep/awake timeout +#define SD_EXTCSD_SEC_COUNT(pSd) ((MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX]) + \ + (MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX+1] << 8 ) + \ + (MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX+2] << 16 ) + \ + (MMC_EXTCSD(pSd)[SD_EXTCSD_SEC_COUNT_INDEX+3] << 24 )) //Sector Count +#define SD_EXTCSD_MIN_PERF_W_8_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_W_8_52_INDEX]) // Minimum Write Performance for 8bit at 52MHz +#define SD_EXTCSD_MIN_PERF_R_8_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_R_8_52_INDEX]) // Minimum Read Performance for 8bit at 52MHz +#define SD_EXTCSD_MIN_PERF_W_8_26_4_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_W_8_26_4_52_INDEX]) // Minimum Write Performance for 8bit at 26MHz, for 4bit at 52MHz +#define SD_EXTCSD_MIN_PERF_R_8_26_4_52(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_R_8_26_4_52_INDEX]) // Minimum Read Performance for 8bit at 26MHz, for 4bit at 52MHz +#define SD_EXTCSD_MIN_PERF_W_4_26(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_W_4_26_INDEX]) // Minimum Write Performance for 4bit at 26MHz +#define SD_EXTCSD_MIN_PERF_R_4_26(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_MIN_PERF_R_4_26_INDEX]) // Minimum Read Performance for 4bit at 26MHz +#define SD_EXTCSD_PWR_CL_26_360(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_26_360_INDEX]) // Power class for 26MHz at 3.6v +#define SD_EXTCSD_PWR_CL_52_360(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_52_360_INDEX]) // Power class for 52MHz at 3.6v +#define SD_EXTCSD_PWR_CL_26_195(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_26_195_INDEX]) // Power class for 26MHz at 1.95v +#define SD_EXTCSD_PWR_CL_52_195(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_PWR_CL_52_195_INDEX]) // Power class for 52MHz at 1.95v +#define SD_EXTCSD_CARD_TYPE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CARD_TYPE_INDEX]) // Card type +#define SD_EXTCSD_CSD_STRUCTURE(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CSD_STRUCTURE_INDEX]) // CSD structure version +#define SD_EXTCSD_EXT_CSD_REV(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_EXT_CSD_REV_INDEX]) // Extended CSD structure version +#define SD_EXTCSD_CMD_SET(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CMD_SET_INDEX]) // Command set +#define SD_EXTCSD_CMD_SET_REV(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_CMD_SET_REV_INDEX]) // Command set revision +#define SD_EXTCSD_POWER_CLASS(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_POWER_CLASS_INDEX]) // Power class +#define SD_EXTCSD_HS_TIMING(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_HS_TIMING_INDEX]) // High-speed interface timing +#define SD_EXTCSD_BUS_WIDTH(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BUS_WIDTH_INDEX]) // Bus width mode +#define SD_EXTCSD_ERASED_MEM_CONT(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ERASED_MEM_CONT_INDEX]) // Erased memory content +#define SD_EXTCSD_BOOT_CONFIG(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_CONFIG_INDEX]) // Boot configuration +#define SD_EXTCSD_BOOT_BUS_WIDTH(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_BOOT_BUS_WIDTH_INDEX]) // Boot bus width +#define SD_EXTCSD_ERASE_GROUP_DEF(pSd) (MMC_EXTCSD(pSd)[SD_EXTCSD_ERASE_GROUP_DEF_INDEX]) // High-density erase group definition + +// EXTCSD total size and block number +#define SD_EXTCSD_TOTAL_SIZE(pSd) (SD_EXTCSD_SEC_COUNT(pSd)*512) +#define SD_EXTCSD_BLOCKNR(pSd) (SD_EXTCSD_SEC_COUNT(pSd)) + +// Bus width Byte +#define SD_EXTCSD_BUS_WIDTH_1BIT (0x0UL) +#define SD_EXTCSD_BUS_WIDTH_4BIT (0x1UL) +#define SD_EXTCSD_BUS_WIDTH_8BIT (0x2UL) + +// High speed mode +#define SD_EXTCSD_HS_TIMING_ENABLE (0x1UL) +#define SD_EXTCSD_HS_TIMING_DISABLE (0x0UL) + +// Boot config +#define SD_EXTCSD_BOOT_PARTITION_ACCESS (0x7UL) // boot partition access +#define SD_EXTCSD_BOOT_PART_NO_ACCESS (0x0UL) +#define SD_EXTCSD_BOOT_PART_RW_PART1 (0x1UL) +#define SD_EXTCSD_BOOT_PART_RW_PART2 (0x2UL) +#define SD_EXTCSD_BOOT_PARTITION_ENABLE (0x7UL << 3) // boot partition enable +#define SD_EXTCSD_BOOT_PART_DISABLE (0x0UL << 3) +#define SD_EXTCSD_BOOT_PART_ENABLE_PART1 (0x1UL << 3) +#define SD_EXTCSD_BOOT_PART_ENABLE_PART2 (0x2UL << 3) +#define SD_EXTCSD_BOOT_PART_ENABLE_USER (0x7UL << 3) +#define SD_EXTCSD_BOOT_PARTITION_ACK (0x1UL << 7) // boot acknowledge +#define SD_EXTCSD_BOOT_PART_NOACK (0x0UL << 7) +#define SD_EXTCSD_BOOT_PART_ACK (0x1UL << 7) + +// Boot bus width +#define SD_EXTCSD_BOOT_BUS_WIDTH_BIT (0x3UL) // boot bus width +#define SD_EXTCSD_BOOT_BUS_1BIT (0x0UL) +#define SD_EXTCSD_BOOT_BUS_4BIT (0x1UL) +#define SD_EXTCSD_BOOT_BUS_8BIT (0x2UL) +#define SD_EXTCSD_RESET_BOOT_BUS_WIDTH_BIT (0x1UL << 2) // boot bus width +#define SD_EXTCSD_RESET_BOOT_BUS (0x0UL << 2) +#define SD_EXTCSD_RETAIN_BOOT_BUS (0x1UL << 2) + +// Mode Switch Arguments for CMD6 +#define MMC_CMD6_ARG_ACCESS +#define MMC_CMD6_ARG_INDEX +#define MMC_CMD6_ARG_VALUE +#define MMC_CMD6_ARG_CMDSET + +// SCR register access macros. +#define SD_SCR_BUS_WIDTHS(pScr) ((pScr[1] >> 16) & 0xF) ///< Describes all the DAT bus that are supported by this card +#define SD_SCR_BUS_WIDTH_4BITS (1 << 1) ///< 4bit Bus Width is supported +#define SD_SCR_BUS_WIDTH_1BIT (1 << 0) ///< 1bit Bus Width is supported + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// SD end-of-transfer callback function. +typedef void (*SdCallback)(unsigned char status, void *pCommand); + +//------------------------------------------------------------------------------ +/// SD Transfer Request prepared by the application upper layer. This structure +/// is sent to the SD_SendCommand function to start the transfer. At the end of +/// the transfer, the callback is invoked by the interrupt handler. +//------------------------------------------------------------------------------ +typedef struct _SdCmd { + + /// Command status. + volatile char status; + /// Command code. + unsigned int cmd; + /// Command argument. + unsigned int arg; + /// Data buffer. + unsigned char *pData; + /// Size of data buffer in bytes. + unsigned short blockSize; + /// Number of blocks to be transfered + unsigned short nbBlock; + /// Indicate if continue to transfer data + unsigned char conTrans; + /// Indicates if the command is a read operation. + unsigned char isRead; + /// Response buffer. + unsigned int *pResp; + /// SD card response type. + unsigned char resType; + /// Optional user-provided callback function. + SdCallback callback; + /// Optional argument to the callback function. + void *pArg; + +} SdCmd; + +//------------------------------------------------------------------------------ +/// SD driver structure. Holds the internal state of the SD driver and +/// prevents parallel access to a SPI peripheral. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to a SPI peripheral. + //AT91S_MCI *pSdHw; + /// SPI peripheral identifier. + unsigned char spiId; + /// MCI HW mode + unsigned char mciMode; + /// Pointer to currently executing command. + SdCmd *pCommand; + /// Mutex. + volatile char semaphore; + +} SdDriver; + +//------------------------------------------------------------------------------ +/// Sdcard driver structure. It holds the current command being processed and +/// the SD card address. +//------------------------------------------------------------------------------ +typedef struct _SdCard { + + /// Pointer to the underlying MCI driver. + SdDriver *pSdDriver; + /// Current MCI command being processed. + SdCmd command; + /// Card-specific data (CSD register) + unsigned int csd[4]; + /// Previous access block number. + unsigned int preBlock; + /// Card total size + unsigned int totalSize; + /// Card block number + unsigned int blockNr; + /// SD card current address. + unsigned short cardAddress; + /// Card type + unsigned char cardType; + /// Card access mode + unsigned char mode; + /// State after sd command complete + unsigned char state; +} SdCard; + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +extern unsigned char SD_Init(SdCard *pSd, + SdDriver *pSdDriver); + +extern unsigned char SD_ReadBlock( + SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData); + +extern unsigned char SD_WriteBlock( + SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + const unsigned char *pData); + +extern unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver); + +extern void SD_DisplayRegisterCSD(SdCard *pSd); + +#endif //#ifndef SDCARD_H + diff --git a/memories/sdmmc/sdspi.c b/memories/sdmmc/sdspi.c new file mode 100644 index 0000000..93b7629 --- /dev/null +++ b/memories/sdmmc/sdspi.c @@ -0,0 +1,711 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "sdspi.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Transfer is pending. +#define SDSPI_STATUS_PENDING 1 +/// Transfer has been aborted because an error occured. +#define SDSPI_STATUS_ERROR 2 + +/// SPI driver is currently in use. +#define SDSPI_ERROR_LOCK 1 + +// Data Tokens +#define SDSPI_START_BLOCK_1 0xFE // Single/Multiple read, single write +#define SDSPI_START_BLOCK_2 0xFC // Multiple block write +#define SDSPI_STOP_TRAN 0xFD // Cmd12 + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the SD Spi structure and the corresponding SPI hardware. +/// \param pSpid Pointer to a Spid instance. +/// \param pSpiHw Associated SPI peripheral. +/// \param spiId SPI peripheral identifier. +//------------------------------------------------------------------------------ +void SDSPI_Configure(SdSpi *pSdSpi, + AT91PS_SPI pSpiHw, + unsigned char spiId) +{ + // Initialize the SPI structure + pSdSpi->pSpiHw = pSpiHw; + pSdSpi->spiId = spiId; + pSdSpi->semaphore = 1; + + // Enable the SPI clock + AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + + // Execute a software reset of the SPI twice + pSpiHw->SPI_CR = AT91C_SPI_SWRST; + pSpiHw->SPI_CR = AT91C_SPI_SWRST; + + // Configure SPI in Master Mode with No CS selected !!! + pSpiHw->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS; + + // Disables the receiver PDC transfer requests + // Disables the transmitter PDC transfer requests. + pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Enable the SPI + pSpiHw->SPI_CR = AT91C_SPI_SPIEN; + + // Disable the SPI clock + AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); +} + +//------------------------------------------------------------------------------ +/// Configures the parameters for the device corresponding to the cs. +/// \param pSdSpi Pointer to a SdSpi instance. +/// \param cs number corresponding to the SPI chip select. +/// \param csr SPI_CSR value to setup. +//------------------------------------------------------------------------------ +void SDSPI_ConfigureCS(SdSpi *pSdSpi, unsigned char cs, unsigned int csr) +{ + unsigned int spiMr; + AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + + // Enable the SPI clock + AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + + //TRACE_DEBUG("CSR[%d]=0x%8X\n\r", cs, csr); + pSpiHw->SPI_CSR[cs] = csr; + +//jcb to put in sendcommand + // Write to the MR register + spiMr = pSpiHw->SPI_MR; + spiMr |= AT91C_SPI_PCS; + spiMr &= ~((1 << cs) << 16); + pSpiHw->SPI_MR = spiMr; + + // Disable the SPI clock + AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); +} + +//------------------------------------------------------------------------------ +/// Use PDC for SPI data transfer. +/// Return 0 if no error, otherwise return error status. +/// \param pSdSpi Pointer to a SdSpi instance. +/// \param pData Data pointer. +/// \param size Data transfer byte count. +//------------------------------------------------------------------------------ +unsigned char SDSPI_PDC(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) +{ + AT91PS_SPI pSpiHw = pSdSpi->pSpiHw; + unsigned int spiIer; + + if (pSdSpi->semaphore == 0) { + TRACE_DEBUG("No semaphore\n\r"); + return SDSPI_ERROR_LOCK; + } + pSdSpi->semaphore--; + + // Enable the SPI clock + AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + + // Disable transmitter and receiver + pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Receive Pointer Register + pSpiHw->SPI_RPR = (int)pData; + // Receive Counter Register + pSpiHw->SPI_RCR = size; + // Transmit Pointer Register + pSpiHw->SPI_TPR = (int) pData; + // Transmit Counter Register + pSpiHw->SPI_TCR = size; + + spiIer = AT91C_SPI_RXBUFF; + + // Enable transmitter and receiver + pSpiHw->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN; + + // Interrupt enable shall be done after PDC TXTEN and RXTEN + pSpiHw->SPI_IER = spiIer; + + return 0; +} + +//! Should be moved to a new file +//------------------------------------------------------------------------------ +/// Read data on SPI data bus; +/// Returns 1 if read fails, returns 0 if no error. +/// \param pSdSpi Pointer to a SD SPI driver instance. +/// \param pData Data pointer. +/// \param size Data size. +//------------------------------------------------------------------------------ +unsigned char SDSPI_Read(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) +{ + unsigned char error; + + // MOSI should hold high during read, or there will be wrong data in received data. + memset(pData, 0xff, size); + + error = SDSPI_PDC(pSdSpi, pData, size); + + while(SDSPI_IsBusy(pSdSpi) == 1); + + if( error == 0 ) { + return 0; + } + else { + TRACE_DEBUG("PB SDSPI_Read\n\r"); + return 1; + } +} + +//------------------------------------------------------------------------------ +/// Write data on SPI data bus; +/// Returns 1 if write fails, returns 0 if no error. +/// \param pSdSpi Pointer to a SD SPI driver instance. +/// \param pData Data pointer. +/// \param size Data size. +//------------------------------------------------------------------------------ +unsigned char SDSPI_Write(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) +{ + unsigned char error; + + error = SDSPI_PDC(pSdSpi, pData, size); + + while(SDSPI_IsBusy(pSdSpi) == 1); + + if( error == 0 ) { + return 0; + } + else { + TRACE_DEBUG("PB SDSPI_Write\n\r"); + return 1; + } +} + +//------------------------------------------------------------------------------ +/// Return 1 if data busy. +//------------------------------------------------------------------------------ +unsigned char SDSPI_WaitDataBusy(SdSpi *pSdSpi) +{ + unsigned char busyData; + + SDSPI_Read(pSdSpi, &busyData, 1); + + if (busyData != 0xff) { + return 1; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Convert SD MCI command to a SPI mode command token. +/// \param pCmdToken Pointer to the SD command token. +/// \param arg SD command argument +//------------------------------------------------------------------------------ +void SDSPI_MakeCmd(unsigned char *pCmdToken, unsigned int arg) +{ + unsigned char sdCmdNum; + unsigned char crc = 0; + unsigned char crcPrev = 0; + + sdCmdNum = 0x3f & *pCmdToken; + *pCmdToken = sdCmdNum | 0x40; + *(pCmdToken+1) = (arg >> 24) & 0xff; + *(pCmdToken+2) = (arg >> 16) & 0xff; + *(pCmdToken+3) = (arg >> 8) & 0xff; + *(pCmdToken+4) = arg & 0xff; + + crc = crc7(crcPrev, (unsigned char *)(pCmdToken), 5); + + *(pCmdToken+5) = (crc << 1) | 1; +} + +//------------------------------------------------------------------------------ +/// Get response after send SD command. +/// Return 0 if no error, otherwise indicate an error. +/// \param pSdSpi Pointer to the SD SPI instance. +/// \param pCommand Pointer to the SD command +//------------------------------------------------------------------------------ +unsigned char SDSPI_GetCmdResp(SdSpi *pSdSpi, SdSpiCmd *pCommand) +{ + unsigned char resp[8]; // response + unsigned char error; + unsigned int respRetry = 8; //NCR max 8, refer to card datasheet + + memset(resp, 0, 8); + + // NCR: 1 ~ 8 * (8) + // Wait for response start bit. + do { + error = SDSPI_Read(pSdSpi, &resp[0], 1); + if (error) { + TRACE_INFO("_GetCmdResp Err: 0x%X\n\r", error); + return error; + } + if ((resp[0]&0x80) == 0) { + break; + } + respRetry--; + } while(respRetry > 0); + + if (respRetry == 0) { + TRACE_WARNING("Cmd %d No Resp\n\r", pCommand->cmd & 0x3F); + return SDSPI_NO_RESPONSE; + } + + switch (pCommand->resType) { + case 1: + *(pCommand->pResp) = resp[0]; + break; + + case 2: + error = SDSPI_Read(pSdSpi, &resp[1], 1); + if (error) { + return SDSPI_ERROR; + } + *(pCommand->pResp) = resp[0] + | (resp[1] << 8); + break; + + // Response 3, get OCR + case 3: + error = SDSPI_Read(pSdSpi, &resp[1], 4); + if (error) { + return SDSPI_ERROR; + } + *(pCommand->pResp) = resp[0] + | (resp[1] << 8) + | (resp[2] << 16) + | (resp[3] << 24); + *(pCommand->pResp+1) = resp[4]; + break; + + case 7: + TRACE_DEBUG("case 7\n\r"); + error = SDSPI_Read(pSdSpi, &resp[1], 4); + if (error) { + return SDSPI_ERROR; + } + *(pCommand->pResp) = resp[0] + | (resp[1] << 8) + | (resp[2] << 16) + | (resp[3] << 24); + *(pCommand->pResp+1) = resp[4]; + break; + + default: + TRACE_DEBUG("No Resp Type?\n\r"); + break; + } + + return SDSPI_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Get response after send data. +/// Return SDSPI_DATA_NO_RESP or data response token. +/// \param pSdSpi Pointer to the SD SPI instance. +/// \param pCommand Pointer to the SD command +//------------------------------------------------------------------------------ +unsigned char SDSPI_GetDataResp(SdSpi *pSdSpi, SdSpiCmd *pCommand) +{ + unsigned char resp = 0; // response + unsigned char error; + unsigned int respRetry = 100; //NCR max 8, refer to card datasheet + + // Wait for response start bit. + do { + error = SDSPI_Read(pSdSpi, &resp, 1); + if (error) { + return SDSPI_ERROR; + } + + // Data Response Token + if ((resp & 0x11) == 0x1) + return (resp & 0x1F); + + //if ((resp & 0xF0) == 0) + // break; + + respRetry--; + } while(respRetry > 0); + + return SDSPI_DATA_NO_RESP; +} + +//------------------------------------------------------------------------------ +/// Starts a SPI master transfer. This is a non blocking function. It will +/// return as soon as the transfer is started. +/// Returns 0 if the transfer has been started successfully; otherwise returns +/// error. +/// \param pSdSpi Pointer to a SdSpi instance. +/// \param pCommand Pointer to the SPI command to execute. +//------------------------------------------------------------------------------ +unsigned char SDSPI_SendCommand(SdSpi *pSdSpi, SdSpiCmd *pCommand) +{ + AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + unsigned char CmdToken[6]; + unsigned char *pData; + unsigned int blockSize; + unsigned int i; + unsigned char error; + unsigned char dataHeader; + unsigned int dataRetry1 = 200000; + unsigned int dataRetry2 = 200000; + unsigned char crc[2]; + unsigned char crcPrev = 0; + unsigned char crcPrev2 = 0; + + SANITY_CHECK(pSdSpi); + SANITY_CHECK(pSpiHw); + SANITY_CHECK(pCommand); + + CmdToken[0] = pCommand->cmd & 0x3F; + pData = pCommand->pData; + blockSize = pCommand->blockSize; + + SDSPI_MakeCmd((unsigned char *)&CmdToken, pCommand->arg); + + // Command is now being executed + pSdSpi->pCommand = pCommand; + pCommand->status = SDSPI_STATUS_PENDING; + + // Send the command + if((pCommand->conTrans == SPI_NEW_TRANSFER) || (blockSize == 0)) { + + TRACE_DEBUG("SendCmd%d\n\r", pCommand->cmd & 0x3F); + for(i = 0; i < 6; i++) { + error = SDSPI_Write(pSdSpi, &CmdToken[i], 1); + if (error) { + TRACE_DEBUG("Error WrCmd[i]: %d\n\r", i, error); + return error; + } + } + error = SDSPI_GetCmdResp(pSdSpi, pCommand); + if (error) { + TRACE_DEBUG("Error GetResp: %d\n\r", error); + return error; + } + } + + if( (blockSize > 0) && (pCommand->nbBlock == 0) ) { + pCommand->nbBlock = 1; + } + + // For data block operations + while (pCommand->nbBlock > 0) { + + // If data block size is invalid, return error + if (blockSize == 0) { + TRACE_DEBUG("Block Size = 0\n\r"); + return 1; + } + + // DATA transfer from card to host + if (pCommand->isRead) { + + TRACE_DEBUG("RD\n\r"); + + do { + SDSPI_Read(pSdSpi, &dataHeader, 1); + TRACE_DEBUG("R Hdr %x\n\r", dataHeader); + if (dataHeader == SDSPI_START_BLOCK_1) { + break; + } + else if((dataHeader & 0xf0) == 0x00) { + pCommand->status = SDSPI_STATUS_ERROR; + TRACE_ERROR("R Data Hdr 0x%X!\n\r", dataHeader); + return SDSPI_ERROR; + } + dataRetry1 --; + } while(dataRetry1 > 0); + + if (dataRetry1 == 0) { + TRACE_DEBUG("Timeout data RD retry\n\r"); + return SDSPI_ERROR; + } + + SDSPI_Read(pSdSpi, pData, blockSize); + + // CRC is not for R3, CSD, CID + if ((pCommand->cmd & 0x3f) != 9 && + (pCommand->cmd & 0x3f) != 10 && + pCommand->resType != 3) + { + + SDSPI_Read(pSdSpi, crc, 2); +#ifdef SDSPI_CRC_ON + // Check data CRC + TRACE_DEBUG("Check Data CRC\n\r"); + crcPrev = 0; + crcPrev2 = 0; + if (crc[0] != ((crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8 ) + || crc[1] != (crc_itu_t(crcPrev2, pData, blockSize) & 0xff)) { + TRACE_ERROR("CRC error 0x%X 0x%X 0x%X\n\r", \ + crc[0], crc[1], crc_itu_t(pData, blockSize)); + return SDSPI_ERROR; + } +#endif + } + } + + // DATA transfer from host to card + else { + + TRACE_DEBUG("WR\n\r"); + + SANITY_CHECK(pCommand->pResp); + + //SDSPI_NCS(pSdSpi); + if ( (pCommand->conTrans == SPI_CONTINUE_TRANSFER) + || ((pCommand->cmd & 0x3f) == 25)) { + dataHeader = SDSPI_START_BLOCK_2; + } + else { + dataHeader = SDSPI_START_BLOCK_1; + } + + crcPrev = 0; + crc[0] = (crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8; + crcPrev2 = 0; + crc[1] = (crc_itu_t(crcPrev2, pData, blockSize) & 0xff); + SDSPI_Write(pSdSpi, &dataHeader, 1); + SDSPI_Write(pSdSpi, pData, blockSize); + SDSPI_Write(pSdSpi, crc, 2); + + // Check response status + error = SDSPI_GetDataResp(pSdSpi, pCommand); + switch(error) { + + case SDSPI_DATA_ACCEPTED: break; + + case SDSPI_DATA_CRC_ERR: + TRACE_ERROR("WR CRC\n\r"); + return SDSPI_ERROR; + + case SDSPI_DATA_WR_ERR: + TRACE_ERROR("WR ERR\n\r"); + return SDSPI_ERROR; + + default: + TRACE_ERROR("WR RESP %x\n\r", error); + return SDSPI_ERROR; + } + + do { + if (SDSPI_WaitDataBusy(pSdSpi) == 0) { + break; + } + dataRetry2--; + } while(dataRetry2 > 0); + + if (dataRetry2 == 0) { + TRACE_ERROR("WR Busy timeout\n\r"); + return SDSPI_BUSY; + } + } + pData += blockSize; + pCommand->nbBlock--; + } + + if (pCommand->status == SDSPI_STATUS_PENDING) { + pCommand->status = 0; + } + + //TRACE_DEBUG("end SDSPI_SendCommand\n\r"); + return 0; +} +//! + +//------------------------------------------------------------------------------ +/// The SPI_Handler must be called by the SPI Interrupt Service Routine with the +/// corresponding Spi instance. +/// The SPI_Handler will unlock the Spi semaphore and invoke the upper application +/// callback. +/// \param pSdSpi Pointer to a SdSpi instance. +//------------------------------------------------------------------------------ +void SDSPI_Handler(SdSpi *pSdSpi) +{ + SdSpiCmd *pCommand = pSdSpi->pCommand; + AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + volatile unsigned int spiSr; + + // Read the status register + spiSr = pSpiHw->SPI_SR; + if(spiSr & AT91C_SPI_RXBUFF) { + + if (pCommand->status == SDSPI_STATUS_PENDING) { + pCommand->status = 0; + } + // Disable transmitter and receiver + pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Disable the SPI clock + AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); + + // Disable buffer complete interrupt + pSpiHw->SPI_IDR = AT91C_SPI_RXBUFF | AT91C_SPI_ENDTX; + + // Release the SPI semaphore + pSdSpi->semaphore++; + } + + // Invoke the callback associated with the current command + if (pCommand && pCommand->callback) { + pCommand->callback(0, pCommand); + } +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the given SPI transfer is complete; otherwise returns 0. +/// \param pCommand Pointer to a SdSpiCmd instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_IsTxComplete(SdSpiCmd *pCommand) +{ + if (pCommand->status != SDSPI_STATUS_PENDING) { + if (pCommand->status != 0){ + TRACE_DEBUG("SPI_IsTxComplete %d\n\r", pCommand->status); + } + return 1; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Close a SPI driver instance and the underlying peripheral. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +void SDSPI_Close(SdSpi *pSdSpi) +{ + AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + + SANITY_CHECK(pSdSpi); + SANITY_CHECK(pSpiHw); + + // Enable the SPI clock + AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + + // Disable the PDC transfer + pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Disable the SPI + pSpiHw->SPI_CR = AT91C_SPI_SPIDIS; + + // Disable the SPI clock + AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); + + // Disable all the interrupts + pSpiHw->SPI_IDR = 0xFFFFFFFF; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the SPI driver is currently busy programming; +/// otherwise returns 0. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_IsBusy(SdSpi *pSdSpi) +{ + if (pSdSpi->semaphore == 0) { + return 1; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Wait several cycles on SPI bus; +/// Returns 0 to indicates no error, otherwise return 1. +/// \param pSdSpi Pointer to a SD SPI driver instance. +/// \param cycles Wait data cycles. +//------------------------------------------------------------------------------ +unsigned char SDSPI_Wait(SdSpi *pSdSpi, unsigned int cycles) +{ + unsigned int i = cycles; + unsigned char data = 0xff; + + for (; i > 0; i--) { + if (SDSPI_Read(pSdSpi, &data, 1)) { + return 1; + } + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Send stop transfer data token; +/// Returns 0 to indicates no error, otherwise return 1. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_StopTranToken(SdSpi *pSdSpi) +{ + unsigned char stopToken = SDSPI_STOP_TRAN; + + TRACE_DEBUG("SDSPI_StopTranToken\n\r"); + return SDSPI_Write(pSdSpi, &stopToken, 1); +} + +//------------------------------------------------------------------------------ +/// Wait, SD card Ncs cycles; +/// Returns 0 to indicates no error, otherwise return 1. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_NCS(SdSpi *pSdSpi) +{ + unsigned int i; + unsigned char ncs; + + for(i = 0; i < 15; i++) { + ncs = 0xff; + if (SDSPI_Write(pSdSpi, &ncs, 1)) { + return 1; + } + } + return 0; +} + + diff --git a/memories/sdmmc/sdspi.h b/memories/sdmmc/sdspi.h new file mode 100644 index 0000000..9f267ec --- /dev/null +++ b/memories/sdmmc/sdspi.h @@ -0,0 +1,184 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "sdspi" +/// +/// !Purpose +/// +/// sdcard spi-bus driver +/// +/// !Usage +/// +/// -# SDSPI_Configure: Initializes the SD Spi structure and the corresponding SPI hardware +/// -# SDSPI_ConfigureCS : Configures the parameters for the device corresponding to the cs +/// -# SDSPI_Read: Read data on SPI data bus +/// -# SDSPI_Write : Write data on SPI data bus +/// -# SDSPI_SendCommand : Starts a SPI master transfer +/// -# SDSPI_StopTranToken : Send stop transfer data token +//------------------------------------------------------------------------------ + +#ifndef SDSPI_H +#define SDSPI_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +#define SDSPI_SUCCESS 0 +#define SDSPI_ERROR 1 +#define SDSPI_NO_RESPONSE 2 +#define SDSPI_BUSY 4 + +// Data Response Token RC +#define SDSPI_DATA_NO_RESP 0x1 +#define SDSPI_DATA_ACCEPTED 0x5 +#define SDSPI_DATA_CRC_ERR 0xB +#define SDSPI_DATA_WR_ERR 0xD + +/// SPI CSR value +//#define SDSPI_CSR(scbr) ( AT91C_SPI_CPOL \ +// | AT91C_SPI_BITS_8 \ +// | (((scbr)<< 8) & AT91C_SPI_SCBR) \ +// | ( (0x08 << 16) & AT91C_SPI_DLYBS)\ +// | ( (0x01 << 24) & AT91C_SPI_DLYBCT) ) + +/// Calculates the value of the SCBR field of the Chip Select Register given +/// MCK and SPCK. +#define SPID_CSR_SCBR(mck, spck) ((((mck) / (spck)) << 8) & AT91C_SPI_SCBR) + +/// Calculates the value of the DLYBS field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBS(mck, delay) \ + ((((((delay) * ((mck) / 1000000)) / 1000) + 1) << 16) & AT91C_SPI_DLYBS) + +/// Calculates the value of the DLYBCT field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBCT(mck, delay) \ + ((((((delay) / 32 * ((mck) / 1000000)) / 1000) + 1) << 24) & AT91C_SPI_DLYBCT) + +#define SDSPI_CSR(mck, spck) \ + (AT91C_SPI_NCPHA | SPID_CSR_DLYBCT(mck, 20) \ + | SPID_CSR_DLYBS(mck, 20) | SPID_CSR_SCBR(mck, spck) \ + | 0 /*AT91C_SPI_CSAAT*/ \ + ) + + +/// Start new data transfer +#define SPI_NEW_TRANSFER 0 +/// Continue data transfer +#define SPI_CONTINUE_TRANSFER 1 + +/// SD end-of-transfer callback function. +typedef void (*SdSpiCallback)(unsigned char status, void *pCommand); + +//------------------------------------------------------------------------------ +/// SPI Transfer Request prepared by the application upper layer. This structure +/// is sent to the SDSPI_SendCommand function to start the transfer. At the end of +/// the transfer, the callback is invoked by the interrupt handler. +//------------------------------------------------------------------------------ +typedef struct _SdSpiCmd { + + /// Command status. + volatile char status; + /// Command code. + unsigned int cmd; + /// Command argument. + unsigned int arg; + /// Data buffer. + unsigned char *pData; + /// Size of data buffer in bytes. + unsigned short blockSize; + /// Number of blocks to be transfered + unsigned short nbBlock; + /// Indicate if continue to transfer data + unsigned char conTrans; + /// Indicates if the command is a read operation. + unsigned char isRead; + /// Response buffer. + unsigned int *pResp; + /// Size of SD card response in bytes. + unsigned char resType; + /// Optional user-provided callback function. + SdSpiCallback callback; + /// Optional argument to the callback function. + void *pArg; + +} SdSpiCmd; + +/// Above should be put into SD card header file. + +//------------------------------------------------------------------------------ +/// SPI driver structure. Holds the internal state of the SPI driver and +/// prevents parallel access to a SPI peripheral. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to a SPI peripheral. + AT91S_SPI *pSpiHw; + /// Pointer to currently executing command. + SdSpiCmd *pCommand; + /// Default max timeout (calculated from TAAC & NSAC) + ///unsigned int max; + /// SPI peripheral identifier. + unsigned char spiId; + /// Mutex. + volatile char semaphore; + +} SdSpi; + +extern void SDSPI_Configure(SdSpi *pSdSpi,AT91PS_SPI pSpiHw,unsigned char spiId); + +extern void SDSPI_SetSpeed(SdSpi *pSdSpi, unsigned int spiSpeed); + +extern unsigned char SDSPI_SendCommand(SdSpi *pSdSpi, SdSpiCmd *pSdSpiCmd); + +extern void SDSPI_Handler(SdSpi *pSdSpi); + +extern unsigned char SDSPI_IsTxComplete(SdSpiCmd *pSdSpiCmd); + +extern unsigned char SDSPI_IsBusy(SdSpi *pSdSpi); + +extern unsigned char SDSPI_NCS(SdSpi *pSdSpi); + +extern void SDSPI_Close(SdSpi *pSdSpi); + +extern void SDSPI_ConfigureCS(SdSpi *pSdSpi, unsigned char cs, unsigned int csr); + +extern unsigned char SDSPI_StopTranToken(SdSpi *pSdSpi); + +extern unsigned char SDSPI_Wait(SdSpi *pSdSpi, unsigned int cycles); + +extern unsigned char SDSPI_WaitDataBusy(SdSpi *pSdSpi); + +#endif + diff --git a/memories/spi-flash/at26.c b/memories/spi-flash/at26.c new file mode 100644 index 0000000..4ac80c2 --- /dev/null +++ b/memories/spi-flash/at26.c @@ -0,0 +1,232 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at26.h" +#include +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// SPI clock frequency used in Hz. +#if defined (BOARD_AT26F004) +// SPI runs at a faster frequency for devices with smaller throughput +#define SPCK 10000000 +#else +#define SPCK 1000000 +#endif + +/// SPI chip select configuration value. +#define CSR (AT91C_SPI_NCPHA | \ + SPID_CSR_DLYBCT(BOARD_MCK, 100) | \ + SPID_CSR_DLYBS(BOARD_MCK, 5) | \ + SPID_CSR_SCBR(BOARD_MCK, SPCK)) + +/// Number of recognized dataflash. +#define NUMDATAFLASH (sizeof(at26Devices) / sizeof(At26Desc)) + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Array of recognized serial firmware dataflash chips. +static const At26Desc at26Devices[] = { + // name, Jedec ID, size, page size, block size, block erase command + {"AT25DF041A" , 0x0001441F, 512 * 1024 , 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT25DF161" , 0x0002461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26F004" , 0x0000041F, 512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF081A" , 0x0001451F, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF0161" , 0x0000461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF161A" , 0x0001461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF321" , 0x0000471F, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT25DF512B" , 0x0001651F, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K}, + {"AT25DF512B" , 0x0000651F, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K}, + {"AT25DF021" , 0x0000431F, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF641" , 0x0000481F, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Manufacturer: ST + {"M25P05" , 0x00102020, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P10" , 0x00112020, 128 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P20" , 0x00122020, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P40" , 0x00132020, 512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P80" , 0x00142020, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P16" , 0x00152020, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P32" , 0x00162020, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P64" , 0x00172020, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Manufacturer: Windbond + {"W25X10" , 0x001130EF, 128 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"W25X20" , 0x001230EF, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"W25X40" , 0x001330EF, 512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"W25X80" , 0x001430EF, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Manufacturer: Macronix + {"MX25L512" , 0x001020C2, 64 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"MX25L3205" , 0x001620C2, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"MX25L6405" , 0x001720C2, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Other + {"SST25VF512" , 0x000048BF, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K} +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an AT26 driver instance with the given SPI driver and chip +/// select value. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param pSpid Pointer to an SPI driver instance. +/// \param cs Chip select value to communicate with the serial flash. +//------------------------------------------------------------------------------ +void AT26_Configure(At26 *pAt26, Spid *pSpid, unsigned char cs) +{ + SpidCmd *pCommand; + + SANITY_CHECK(pAt26); + SANITY_CHECK(pSpid); + SANITY_CHECK(cs < 4); + + // Configure the SPI chip select for the serial flash + SPID_ConfigureCS(pSpid, cs, CSR); + + // Initialize the AT26 fields + pAt26->pSpid = pSpid; + pAt26->pDesc = 0; + + // Initialize the command structure + pCommand = &(pAt26->command); + pCommand->pCmd = (unsigned char *) pAt26->pCmdBuffer; + pCommand->callback = 0; + pCommand->pArgument = 0; + pCommand->spiCs = cs; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the serial flash driver is currently busy executing a command; +/// otherwise returns 0. +/// \param pAt26 Pointer to an At26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26_IsBusy(At26 *pAt26) +{ + return SPID_IsBusy(pAt26->pSpid); +} + +//------------------------------------------------------------------------------ +/// Sends a command to the serial flash through the SPI. The command is made up +/// of two parts: the first is used to transmit the command byte and optionally, +/// address and dummy bytes. The second part is the data to send or receive. +/// This function does not block: it returns as soon as the transfer has been +/// started. An optional callback can be invoked to notify the end of transfer. +/// Return 0 if successful; otherwise, returns AT26_ERROR_BUSY if the AT26 +/// driver is currently executing a command, or AT26_ERROR_SPI if the command +/// cannot be sent because of a SPI error. +/// \param pAt26 Pointer to an At26 driver instance. +/// \param cmd Command byte. +/// \param cmdSize Size of command (command byte + address bytes + dummy bytes). +/// \param pData Data buffer. +/// \param dataSize Number of bytes to send/receive. +/// \param address Address to transmit. +/// \param callback Optional user-provided callback to invoke at end of transfer. +/// \param pArgument Optional argument to the callback function. +//------------------------------------------------------------------------------ +unsigned char AT26_SendCommand( + At26 *pAt26, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument) + +{ + SpidCmd *pCommand; + + SANITY_CHECK(pAt26); + + // Check if the SPI driver is available + if (AT26_IsBusy(pAt26)) { + + return AT26_ERROR_BUSY; + } + + // Store command and address in command buffer + pAt26->pCmdBuffer[0] = (cmd & 0x000000FF) + | ((address & 0x0000FF) << 24) + | ((address & 0x00FF00) << 8) + | ((address & 0xFF0000) >> 8); + + // Update the SPI transfer descriptor + pCommand = &(pAt26->command); + pCommand->cmdSize = cmdSize; + pCommand->pData = pData; + pCommand->dataSize = dataSize; + pCommand->callback = callback; + pCommand->pArgument = pArgument; + + // Start the SPI transfer + if (SPID_SendCommand(pAt26->pSpid, pCommand)) { + + return AT26_ERROR_SPI; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Tries to detect a serial firmware flash device given its JEDEC identifier. +/// The JEDEC id can be retrieved by sending the correct command to the device. +/// Returns the corresponding AT26 descriptor if found; otherwise returns 0. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param jedecId JEDEC identifier of device. +//------------------------------------------------------------------------------ +const At26Desc * AT26_FindDevice(At26 *pAt26, unsigned int jedecId) +{ + unsigned int i = 0; + + SANITY_CHECK(pAt26); + + // Search if device is recognized + pAt26->pDesc = 0; + while ((i < NUMDATAFLASH) && !(pAt26->pDesc)) { + + if (jedecId == at26Devices[i].jedecId) { + + pAt26->pDesc = &(at26Devices[i]); + } + + i++; + } + + return pAt26->pDesc; +} + diff --git a/memories/spi-flash/at26.h b/memories/spi-flash/at26.h new file mode 100644 index 0000000..4e3aa6d --- /dev/null +++ b/memories/spi-flash/at26.h @@ -0,0 +1,252 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !Purpose +/// +/// The AT26 serial firmware Dataflash driver is based on top of the +/// corresponding Spi driver. A Dataflash structure instance has to be +/// initialized using the DF_Init function. Then basic dataflash +/// operations can be launched using macros such as DF_continuous_read. +/// These macros invoke the DF_Command() function which invokes the +/// DPI low driver using the SPI_SendCommand() function. +/// Beware to compute the dataflash internal address, the dataflash sector +/// description must be known (DataflashDesc). Dataflash can be automatically +/// detected using the DF_Scan() function. +/// +/// !Usage +/// +/// -# Initializes an AT26 instance and configures SPI chip select pin +/// using AT26_Configure(). +/// -# Detect DF and returns DF description corresponding to the device +/// connected using AT26_FindDevice().This function shall be called by +/// the application before AT26_SendCommand(). +/// -# Sends a command to the DF through the SPI using AT26_SendCommand(). +/// The command is identified by its command code and the number of +/// bytes to transfer. +/// -# Example code for sending command to write a page to DF. +/// \code +/// // Program page +/// error = AT26_SendCommand(pAt26, AT26_BYTE_PAGE_PROGRAM, 4, +/// pData, writeSize, address, 0, 0); +/// \endcode +/// -# Example code for sending command to read a page from DF. +/// If data needs to be received, then a data buffer must be +/// provided. +/// \code +/// // Start a read operation +/// error = AT26_SendCommand(pAt26, AT26_READ_ARRAY_LF, +/// 4, pData, size, address, 0, 0); +/// \endcode +/// -# This function does not block; its optional callback will +/// be invoked when the transfer completes. +/// -# Check the AT26 driver is ready or not by polling AT26_IsBusy(). +/// +//------------------------------------------------------------------------------ +#ifndef AT26_H +#define AT26_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "spid.h" + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +#define AT26_Size(pAt26) ((pAt26)->pDesc->size) +#define AT26_PageSize(pAt26) ((pAt26)->pDesc->pageSize) +#define AT26_BlockSize(pAt26) ((pAt26)->pDesc->blockSize) +#define AT26_Name(pAt26) ((pAt26)->pDesc->name) +#define AT26_PageNumber(pAt26) (AT26_Size(pAt26) / AT26_PageSize(pAt26)) +#define AT26_BlockNumber(pAt26) (AT26_Size(pAt26) / AT26_BlockSize(pAt26)) +#define AT26_PagePerBlock(pAt26) (AT26_BlockSize(pAt26) / AT26_PageSize(pAt26)) +#define AT26_BlockEraseCmd(pAt26) ((pAt26)->pDesc->blockEraseCmd) + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Device is protected, operation cannot be carried out. +#define AT26_ERROR_PROTECTED 1 +/// Device is busy executing a command. +#define AT26_ERROR_BUSY 2 +/// There was a problem while trying to program page data. +#define AT26_ERROR_PROGRAM 3 +/// There was an SPI communication error. +#define AT26_ERROR_SPI 4 + +/// Device ready/busy status bit. +#define AT26_STATUS_RDYBSY (1 << 0) +/// Device is ready. +#define AT26_STATUS_RDYBSY_READY (0 << 0) +/// Device is busy with internal operations. +#define AT26_STATUS_RDYBSY_BUSY (1 << 0) +/// Write enable latch status bit. +#define AT26_STATUS_WEL (1 << 1) +/// Device is not write enabled. +#define AT26_STATUS_WEL_DISABLED (0 << 1) +/// Device is write enabled. +#define AT26_STATUS_WEL_ENABLED (1 << 1) +/// Software protection status bitfield. +#define AT26_STATUS_SWP (3 << 2) +/// All sectors are software protected. +#define AT26_STATUS_SWP_PROTALL (3 << 2) +/// Some sectors are software protected. +#define AT26_STATUS_SWP_PROTSOME (1 << 2) +/// No sector is software protected. +#define AT26_STATUS_SWP_PROTNONE (0 << 2) +/// Write protect pin status bit. +#define AT26_STATUS_WPP (1 << 4) +/// Write protect signal is not asserted. +#define AT26_STATUS_WPP_NOTASSERTED (0 << 4) +/// Write protect signal is asserted. +#define AT26_STATUS_WPP_ASSERTED (1 << 4) +/// Erase/program error bit. +#define AT26_STATUS_EPE (1 << 5) +/// Erase or program operation was successful. +#define AT26_STATUS_EPE_SUCCESS (0 << 5) +/// Erase or program error detected. +#define AT26_STATUS_EPE_ERROR (1 << 5) +/// Sector protection registers locked bit. +#define AT26_STATUS_SPRL (1 << 7) +/// Sector protection registers are unlocked. +#define AT26_STATUS_SPRL_UNLOCKED (0 << 7) +/// Sector protection registers are locked. +#define AT26_STATUS_SPRL_LOCKED (1 << 7) + +/// Read array command code. +#define AT26_READ_ARRAY 0x0B +/// Read array (low frequency) command code. +#define AT26_READ_ARRAY_LF 0x03 +/// Block erase command code (4K block). +#define AT26_BLOCK_ERASE_4K 0x20 +/// Block erase command code (32K block). +#define AT26_BLOCK_ERASE_32K 0x52 +/// Block erase command code (64K block). +#define AT26_BLOCK_ERASE_64K 0xD8 +/// Chip erase command code 1. +#define AT26_CHIP_ERASE_1 0x60 +/// Chip erase command code 2. +#define AT26_CHIP_ERASE_2 0xC7 +/// Byte/page program command code. +#define AT26_BYTE_PAGE_PROGRAM 0x02 +/// Sequential program mode command code 1. +#define AT26_SEQUENTIAL_PROGRAM_1 0xAD +/// Sequential program mode command code 2. +#define AT26_SEQUENTIAL_PROGRAM_2 0xAF +/// Write enable command code. +#define AT26_WRITE_ENABLE 0x06 +/// Write disable command code. +#define AT26_WRITE_DISABLE 0x04 +/// Protect sector command code. +#define AT26_PROTECT_SECTOR 0x36 +/// Unprotect sector command code. +#define AT26_UNPROTECT_SECTOR 0x39 +/// Read sector protection registers command code. +#define AT26_READ_SECTOR_PROT 0x3C +/// Read status register command code. +#define AT26_READ_STATUS 0x05 +/// Write status register command code. +#define AT26_WRITE_STATUS 0x01 +/// Read manufacturer and device ID command code. +#define AT26_READ_JEDEC_ID 0x9F +/// Deep power-down command code. +#define AT26_DEEP_PDOWN 0xB9 +/// Resume from deep power-down command code. +#define AT26_RES_DEEP_PDOWN 0xAB + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Describes a serial firmware flash device parameters. +//------------------------------------------------------------------------------ +typedef struct _At26Desc { + + /// Device string name. + const char *name; + /// JEDEC ID of device. + unsigned int jedecId; + /// Size of device in bytes. + unsigned int size; + /// Size of one page in bytes. + unsigned int pageSize; + /// Block erase size in bytes. + unsigned int blockSize; + /// Block erase command. + unsigned int blockEraseCmd; + +} At26Desc; + +//------------------------------------------------------------------------------ +/// Serial flash driver structure. Holds the current state of the driver, +/// including the current command and the descriptor for the underlying device. +//------------------------------------------------------------------------------ +typedef struct _At26 { + + /// Pointer to the underlying SPI driver. + Spid *pSpid; + /// Current SPI command sent to the SPI driver. + SpidCmd command; + /// Pointer to a descriptor for the serial firmware flash device. + const At26Desc *pDesc; + /// Command buffer. + unsigned int pCmdBuffer[2]; + +} At26; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void AT26_Configure(At26 *pAt26, Spid *pSpid, unsigned char cs); + +extern unsigned char AT26_SendCommand( + At26 *pAt26, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument); + +extern unsigned char AT26_IsBusy(At26 *pAt26); + +extern const At26Desc * AT26_FindDevice( + At26 *pAt26, + unsigned int jedecId); + +#endif //#ifndef AT26_H + diff --git a/memories/spi-flash/at26d.c b/memories/spi-flash/at26d.c new file mode 100644 index 0000000..123fde1 --- /dev/null +++ b/memories/spi-flash/at26d.c @@ -0,0 +1,409 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at26.h" +#include "at26d.h" +#include "board.h" +#include +#include + +/*****/ +#include +/*****/ + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Wait for transfer to finish calling the SPI driver ISR. (interrupts are disabled) +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +static void AT26D_Wait(At26 *pAt26) +{ + // Wait for transfer to finish + while (AT26_IsBusy(pAt26)) + SPID_Handler(pAt26->pSpid); +} + +//------------------------------------------------------------------------------ +/// Reads and returns the status register of the serial flash. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +static unsigned char AT26D_ReadStatus(At26 *pAt26) +{ + unsigned char error, status; + + SANITY_CHECK(pAt26); + + // Issue a status read command + error = AT26_SendCommand(pAt26, AT26_READ_STATUS, 1, &status, 1, 0, 0, 0); + ASSERT(!error, "-F- AT26_GetStatus: Failed to issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); + + return status; +} + +//------------------------------------------------------------------------------ +/// Writes the given value in the status register of the serial flash device. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param status Status to write. +//------------------------------------------------------------------------------ +static void AT26D_WriteStatus(At26 *pAt26, unsigned char status) +{ + unsigned char error; + + SANITY_CHECK(pAt26); + + // Issue a write status command + error = AT26_SendCommand(pAt26, AT26_WRITE_STATUS, 1, &status, 1, 0, 0, 0); + ASSERT(!error, "-F- AT26_WriteStatus: Failed to issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); +} + + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Waits for the serial flash device to become ready to accept new commands. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +void AT26D_WaitReady(At26 *pAt26) +{ + unsigned char ready = 0; + + SANITY_CHECK(pAt26); + + // Read status register and check busy bit + while (!ready) { + + ready = ((AT26D_ReadStatus(pAt26) & AT26_STATUS_RDYBSY) == AT26_STATUS_RDYBSY_READY); + } +} + +//------------------------------------------------------------------------------ +/// Reads and returns the serial flash device ID. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned int AT26D_ReadJedecId(At26 *pAt26) +{ + unsigned char error; + unsigned int id = 0; + + SANITY_CHECK(pAt26); + + // Issue a read ID command + error = AT26_SendCommand(pAt26, AT26_READ_JEDEC_ID, 1, + (unsigned char *) &id, 3, 0, 0, 0); + ASSERT(!error, "-F- AT26_GetJedecId: Could not issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); + + return id; +} + +//------------------------------------------------------------------------------ +/// Enables critical writes operation on a serial flash device, such as sector +/// protection, status register, etc. +/// \para pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +void AT26D_EnableWrite(At26 *pAt26) +{ + unsigned char error; + + SANITY_CHECK(pAt26); + + // Issue a write enable command + error = AT26_SendCommand(pAt26, AT26_WRITE_ENABLE, 1, 0, 0, 0, 0, 0); + ASSERT(!error, "-F- AT26_EnableWrite: Could not issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); +} + +#if defined(BOARD_AT26F004) + +//------------------------------------------------------------------------------ +// Reads and returns the status register of the serial flash. +// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26F004_Unprotect(At26 *pAt26) +{ + unsigned char error; + unsigned int sector, sectorAddress = 0; + + for (sector = 0 ; sector < 11 ; sector ++) + { + sectorAddress = 0x0; + + if (sector == 10) + sectorAddress = 0x7C000; + else if (sector == 9) + sectorAddress = 0x7A000; + else if (sector == 8) + sectorAddress = 0x78000; + else if (sector == 7) + sectorAddress = 0x70000; + else + sectorAddress = sectorAddress + (0x10000 * sector); + + // Write Enable + AT26D_EnableWrite(pAt26); + + error = AT26_SendCommand(pAt26, AT26_UNPROTECT_SECTOR, 4, 0, 0, sectorAddress, 0, 0); + ASSERT(!error, "-F- AT26_SectorUnprotect: Failed to issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); + } + + return 0; +} +#endif + +//------------------------------------------------------------------------------ +/// Unprotects the contents of the serial flash device. +/// Returns 0 if the device has been unprotected; otherwise returns +/// SF_PROTECTED. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26D_Unprotect(At26 *pAt26) +{ + unsigned char status; + + SANITY_CHECK(pAt26); + + // Get the status register value to check the current protection + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_SWP) == AT26_STATUS_SWP_PROTNONE) { + + // Protection already disabled + return 0; + } + + // Check if sector protection registers are locked + if ((status & AT26_STATUS_SPRL) == AT26_STATUS_SPRL_LOCKED) { + + // Unprotect sector protection registers by writing the status reg. + AT26D_EnableWrite(pAt26); + AT26D_WriteStatus(pAt26, 0); + } + + // Perform a global unprotect command + AT26D_EnableWrite(pAt26); + + #if defined(BOARD_AT26F004) + AT26F004_Unprotect(pAt26); + #endif + + AT26D_WriteStatus(pAt26, 0); + + // Check the new status + status = AT26D_ReadStatus(pAt26); + if ((status & (AT26_STATUS_SPRL | AT26_STATUS_SWP)) != 0) { + + return AT26_ERROR_PROTECTED; + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Erases all the content of the memory chip. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26D_EraseChip(At26 *pAt26) +{ + unsigned char status; + unsigned char error; + + SANITY_CHECK(pAt26); + + // Check that the flash is unprotected + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_SWP) != AT26_STATUS_SWP_PROTNONE) { + return AT26_ERROR_PROTECTED; + } + + // Enable critical write operation + AT26D_EnableWrite(pAt26); + + // Erase the chip + error = AT26_SendCommand(pAt26, AT26_CHIP_ERASE_2, 1, 0, 0, 0, 0, 0); + ASSERT(!error, "-F- AT26_ChipErase: Could not issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + // Poll the Serial flash status register until the operation is achieved + AT26D_WaitReady(pAt26); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Erases the specified 64KB block of the serial firmware dataflash. +/// Returns 0 if successful; otherwise returns AT26_ERROR_PROTECTED if the +/// device is protected or AT26_ERROR_BUSY if it is busy executing a command. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param address Address of the block to erase. +//------------------------------------------------------------------------------ +unsigned char AT26D_EraseBlock(At26 *pAt26, unsigned int address) +{ + unsigned char status; + unsigned char error; + + SANITY_CHECK(pAt26); + + // Check that the flash is ready and unprotected + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_RDYBSY) != AT26_STATUS_RDYBSY_READY) { + TRACE_ERROR("AT26D_EraseBlock : Flash busy\n\r"); + return AT26_ERROR_BUSY; + } + else if ((status & AT26_STATUS_SWP) != AT26_STATUS_SWP_PROTNONE) { + TRACE_ERROR("AT26D_EraseBlock : Flash protected\n\r"); + return AT26_ERROR_PROTECTED; + } + + // Enable critical write operation + AT26D_EnableWrite(pAt26); + + // Start the block erase command + error = AT26_SendCommand(pAt26, AT26_BlockEraseCmd(pAt26), 4, 0, 0, address, 0, 0); + ASSERT(!error, "-F- AT26_EraseBlock: Could not issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + // Poll the Serial flash status register until the operation is achieved + AT26D_WaitReady(pAt26); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes data at the specified address on the serial firmware dataflash. The +/// page(s) to program must have been erased prior to writing. This function +/// handles page boundary crossing automatically. +/// Returns 0 if successful; otherwise, returns AT26_ERROR_PROGRAM is there has +/// been an error during the data programming. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param pData Data buffer. +/// \param size Number of bytes in buffer. +/// \param address Write address. +//------------------------------------------------------------------------------ +unsigned char AT26D_Write( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address) +{ + unsigned int pageSize; + unsigned int writeSize; + unsigned char error; + #if !defined(BOARD_AT26F004) + unsigned char status; + #endif + + SANITY_CHECK(pAt26); + SANITY_CHECK(pData); + + // Retrieve device page size + pageSize = AT26_PageSize(pAt26); + + // Program one page after the other + while (size > 0) { + // Compute number of bytes to program in page + #if defined(BOARD_AT26F004) + writeSize = 1; + #else + writeSize = min(size, pageSize - (address % pageSize)); + #endif + + // Enable critical write operation + AT26D_EnableWrite(pAt26); + + // Program page + error = AT26_SendCommand(pAt26, AT26_BYTE_PAGE_PROGRAM, 4, + pData, writeSize, address, 0, 0); + ASSERT(!error, "-F- AT26_WritePage: Failed to issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + // Poll the Serial flash status register until the operation is achieved + AT26D_WaitReady(pAt26); + + #if !defined(AT26F004) + // Make sure that write was without error + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_EPE) == AT26_STATUS_EPE_ERROR) { + + return AT26_ERROR_PROGRAM; + } + #endif + + pData += writeSize; + size -= writeSize; + address += writeSize; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Reads data from the specified address on the serial flash. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param pData Data buffer. +/// \param size Number of bytes to read. +/// \param address Read address. +//------------------------------------------------------------------------------ +unsigned char AT26D_Read( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address) +{ + unsigned char error; + + // Start a read operation + error = AT26_SendCommand(pAt26, AT26_READ_ARRAY_LF, 4, pData, size, address, 0, 0); + ASSERT(!error, "-F- AT26_Read: Could not issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + + return error; +} diff --git a/memories/spi-flash/at26d.h b/memories/spi-flash/at26d.h new file mode 100644 index 0000000..9bed206 --- /dev/null +++ b/memories/spi-flash/at26d.h @@ -0,0 +1,79 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// The AT26 Serialflash driver. +/// +/// !Usage +//------------------------------------------------------------------------------ + +#ifndef AT26D_H +#define AT26D_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at26.h" + + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +extern void AT26D_WaitReady(At26 *pAt26); + +extern unsigned int AT26D_ReadJedecId(At26 *pAt26); + +extern unsigned char AT26D_Unprotect(At26 *pAt26); + +extern unsigned char AT26D_EraseChip(At26 *pAt26); + +extern unsigned char AT26D_EraseBlock(At26 *pAt26, unsigned int address); + +extern unsigned char AT26D_Write( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address); + +extern unsigned char AT26D_Read( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address); + +#endif // #ifndef AT26D_H + diff --git a/memories/spi-flash/at45.c b/memories/spi-flash/at45.c new file mode 100644 index 0000000..142e7ed --- /dev/null +++ b/memories/spi-flash/at45.c @@ -0,0 +1,257 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at45.h" +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Number of dataflash which can be recognized. +#define NUMDATAFLASH (sizeof(at45Devices) / sizeof(At45Desc)) + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +/// indicate if the device is configured as binary page or not. +static unsigned char configuredBinaryPage; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +static const At45Desc at45Devices[] = { + { 512, 1, 264, 9, 0x0C, "AT45DB011D"}, + { 1024, 1, 264, 9, 0x14, "AT45DB021D"}, + { 2048, 1, 264, 9, 0x1C, "AT45DB041D"}, + { 4096, 1, 264, 9, 0x24, "AT45DB081D"}, + { 4096, 1, 528, 10, 0x2C, "AT45DB161D"}, + { 8192, 1, 528, 10, 0x34, "AT45DB321D"}, + { 8192, 1, 1056, 11, 0x3C, "AT45DB642D"}, + {16384, 1, 1056, 11, 0x10, "AT45DB1282"}, + {16384, 1, 2112, 12, 0x18, "AT45DB2562"}, + {32768, 1, 2112, 12, 0x20, "AT45DB5122"} +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an AT45 instance and configures SPI chip select register. +/// Always returns 0. +/// \param pAt45 Pointer to the At45 instance to initialize. +/// \param pSpid Pointer to the underlying SPI driver. +/// \param spiCs Chip select value to connect to the At45. +//------------------------------------------------------------------------------ +unsigned char AT45_Configure(At45 *pAt45, Spid *pSpid, unsigned char spiCs) +{ + SpidCmd *pCommand; + + // Sanity checks + ASSERT(pSpid, "AT45_Configure: pSpid is 0.\n\r"); + ASSERT(pAt45, "AT45_Configure: pAt45 is 0.\n\r"); + + // Initialize the At45 instance + pAt45->pSpid = pSpid; + pAt45->pDesc = 0; + memset(pAt45->pCmdBuffer, 0, 8); + + // Initialize the spidCmd structure + pCommand = &(pAt45->command); + pCommand->pCmd = pAt45->pCmdBuffer; + pCommand->callback = 0; + pCommand->pArgument = 0; + pCommand->spiCs = spiCs; + + return 0; +} + +//------------------------------------------------------------------------------ +/// This function returns 1 if the At45 driver is not executing any command; +/// otherwise it returns 0. +/// \param pAt45 Pointer to an At45 instance. +//------------------------------------------------------------------------------ +unsigned char AT45_IsBusy(At45 *pAt45) +{ + return SPID_IsBusy(pAt45->pSpid); +} + +//------------------------------------------------------------------------------ +/// Sends a command to the dataflash through the SPI. The command is identified +/// by its command code and the number of bytes to transfer (1 + number of +/// address bytes + number of dummy bytes). If data needs to be received, then +/// a data buffer must be provided. +/// This function does not block; its optional callback will be invoked when +/// the transfer completes. +/// \param pAt45 Pointer to an At45 driver instance. +/// \param cmd Command code. +/// \param cmdSize Size of command code + address bytes + dummy bytes. +/// \param pData Data buffer. +/// \param dataSize Number of data bytes to send/receive. +/// \param address Address at which the command is performed if meaningful. +/// \param callback Optional callback to invoke at end of transfer. +/// \param pArgument Optional parameter to the callback function. +//------------------------------------------------------------------------------ +unsigned char AT45_SendCommand( + At45 *pAt45, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument) +{ + SpidCmd *pCommand; + const At45Desc *pDesc = pAt45->pDesc; + unsigned int dfAddress = 0; + + // Sanity checks + ASSERT(pAt45, "AT45_Command: pAt45 is 0.\n\r"); + ASSERT(pDesc || (cmd == AT45_STATUS_READ), + "AT45_Command: Device has no descriptor, only STATUS_READ command allowed\n\r"); + + // Check if the SPI driver is available + if (AT45_IsBusy(pAt45)) { + + return AT45_ERROR_LOCK; + } + + // Compute command pattern + pAt45->pCmdBuffer[0] = cmd; + + // Add address bytes if necessary + if (cmdSize > 1) { + + ASSERT(pDesc, "AT45_Command: No descriptor for dataflash.\n\r"); + if (!configuredBinaryPage) { + dfAddress = + ((address / (pDesc->pageSize)) << pDesc->pageOffset) + + (address % (pDesc->pageSize)); + } + else { + dfAddress = address; + } + // Write address bytes + if (pDesc->pageNumber >= 16384) { + + pAt45->pCmdBuffer[1] = ((dfAddress & 0x0F000000) >> 24); + pAt45->pCmdBuffer[2] = ((dfAddress & 0x00FF0000) >> 16); + pAt45->pCmdBuffer[3] = ((dfAddress & 0x0000FF00) >> 8); + pAt45->pCmdBuffer[4] = ((dfAddress & 0x000000FF) >> 0); + + if ((cmd != AT45_CONTINUOUS_READ) && (cmd != AT45_PAGE_READ)) { + + cmdSize++; + } + } + else { + + pAt45->pCmdBuffer[1] = ((dfAddress & 0x00FF0000) >> 16); + pAt45->pCmdBuffer[2] = ((dfAddress & 0x0000FF00) >> 8); + pAt45->pCmdBuffer[3] = ((dfAddress & 0x000000FF) >> 0); + } + } + + // Update the SPI Transfer descriptors + pCommand = &(pAt45->command); + pCommand->cmdSize = cmdSize; + pCommand->pData = pData; + pCommand->dataSize = dataSize; + pCommand->callback = callback; + pCommand->pArgument = pArgument; + + // Send Command and data through the SPI + if (SPID_SendCommand(pAt45->pSpid, pCommand)) { + + return AT45_ERROR_SPI; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// This function returns the At45Desc structure corresponding to the device +/// connected +/// It automatically initializes pAt45->pDesc field structure. +/// This function shall be called by the application before AT45_SendCommand. +/// Returns 0 if successful; Otherwise, returns AT45_ERROR_LOCK if the At45 +/// driver is in use or AT45_ERROR_SPI if there was an error with the SPI driver. +/// \param pAt45 Pointer to an AT45 driver instance. +/// \param status Device status register value. +//------------------------------------------------------------------------------ +const At45Desc * AT45_FindDevice(At45 *pAt45, unsigned char status) +{ + unsigned int i; + unsigned char id = AT45_STATUS_ID(status); + + // Check if status is all one; in which case, it is assumed that no device + // is connected + if (status == 0xFF) { + + return 0; + } + + // Look in device array + i = 0; + pAt45->pDesc = 0; + while ((i < NUMDATAFLASH) && !(pAt45->pDesc)) { + + if (at45Devices[i].id == id) { + + pAt45->pDesc = &(at45Devices[i]); + } + i++; + } + configuredBinaryPage = AT45_STATUS_BINARY(status); + return pAt45->pDesc; +} + +//------------------------------------------------------------------------------ +/// This function returns the pagesize corresponding to the device connected +/// \param pAt45 Pointer to an AT45 driver instance. +//------------------------------------------------------------------------------ +unsigned int AT45_PageSize(At45 *pAt45) +{ + unsigned int pagesize = pAt45->pDesc->pageSize; + if(((pAt45->pDesc->hasBinaryPage) == 0) || !configuredBinaryPage){ + return pagesize; + } + return ((pagesize >> 8) << 8); +} diff --git a/memories/spi-flash/at45.h b/memories/spi-flash/at45.h new file mode 100644 index 0000000..ef7ef26 --- /dev/null +++ b/memories/spi-flash/at45.h @@ -0,0 +1,255 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// The Dataflash driver is based on top of the corresponding Spi driver. +/// A Dataflash structure instance has to be initialized using the DF_Init +/// function. Then basic dataflash operations can be launched using macros such +/// as DF_continuous_read. These macros invoke the DF_Command() function which +/// invokes the DPI low driver using the SPI_SendCommand() function. +/// Beware to compute the dataflash internal address, the dataflash sector +/// description must be known (DataflashDesc). Dataflash can be automatically +/// detected using the DF_Scan() function. +/// !!!Usage +/// +/// -# Initializes an AT45 instance and configures SPI chip select pin +/// using AT45_Configure(). +/// -# Detect DF and returns DF description corresponding to the device +/// connected using AT45_FindDevice().This function shall be called by +/// the application before AT45_SendCommand. +/// -# Sends a command to the DF through the SPI using AT45_SendCommand(). +/// The command is identified by its command code and the number of +/// bytes to transfer. +/// -# Example code for sending command to write a page to DF. +/// \code +/// // Issue a page write through buffer 1 command +/// error = AT45_SendCommand(pAt45, AT45_PAGE_WRITE_BUF1, 4, +/// pBuffer, size, address, 0, 0); +/// \endcode +/// -# Example code for sending command to read a page from DF. +/// If data needs to be received, then a data buffer must be +/// provided. +/// \code +/// // Issue a continuous read array command +/// error = AT45_SendCommand(pAt45, AT45_CONTINUOUS_READ_LEG, 8, +/// pBuffer, size, address, 0, 0); +/// \endcode +/// -# This function does not block; its optional callback will +/// be invoked when the transfer completes. +/// -# Check the AT45 driver is ready or not by polling AT45_IsBusy(). +/// +//------------------------------------------------------------------------------ + +#ifndef AT45_H +#define AT45_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "spid.h" + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// The dataflash driver is currently in use. +#define AT45_ERROR_LOCK 1 +/// There was an error with the SPI driver. +#define AT45_ERROR_SPI 2 + +/// AT45 dataflash SPI CSR settings given MCK and SPCK. +#define AT45_CSR(mck, spck) \ + (AT91C_SPI_NCPHA | SPID_CSR_DLYBCT(mck, 250) \ + | SPID_CSR_DLYBS(mck, 250) | SPID_CSR_SCBR(mck, spck)) + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +#define AT45_PageOffset(pAt45) ((pAt45)->pDesc->pageOffset) +#define AT45_PageNumber(pAt45) ((pAt45)->pDesc->pageNumber) + +/// Returns 1 if the device is ready; otherwise 0. +#define AT45_STATUS_READY(status) (status & 0x80) +/// Returns the device ID code. +#define AT45_STATUS_ID(status) (status & 0x3c) +/// Returns 1 if the device is configured in binary page mode; otherwise 0. +#define AT45_STATUS_BINARY(status) (status & 0x01) + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Main memory page read command code. +#define AT45_PAGE_READ 0xD2 +/// Continous array read (legacy) command code. +#define AT45_CONTINUOUS_READ_LEG 0xE8 +/// Continous array read (low frequency) command code. +#define AT45_CONTINUOUS_READ_LF 0x03 +/// Continous array read command code. +#define AT45_CONTINUOUS_READ 0x0B +/// Buffer 1 read (low frequency) command code. +#define AT45_BUF1_READ_LF 0xD1 +/// Buffer 2 read (low frequency) command code. +#define AT45_BUF2_READ_LF 0xD3 +/// Buffer 1 read (serial) command code. +#define AT45_BUF1_READ_SER 0xD4 +/// Buffer 2 read (serial) command code. +#define AT45_BUF2_READ_SER 0xD6 +/// Buffer 1 read (8-bit) command code. +#define AT45_BUF1_READ_8B 0x54 +/// Buffer 2 read (8-bit) command code. +#define AT45_BUF2_READ_8B 0x56 + +/// Buffer 1 write command code. +#define AT45_BUF1_WRITE 0x84 +/// Buffer 2 write command code. +#define AT45_BUF2_WRITE 0x87 +/// Buffer 1 to main memory page program with erase command code. +#define AT45_BUF1_MEM_ERASE 0x83 +/// Buffer 2 to main memory page program with erase command code. +#define AT45_BUF2_MEM_ERASE 0x86 +/// Buffer 1 to main memory page program without erase command code. +#define AT45_BUF1_MEM_NOERASE 0x88 +/// Buffer 2 to main memory page program without erase command code. +#define AT45_BUF2_MEM_NOERASE 0x89 +/// Page erase command code. +#define AT45_PAGE_ERASE 0x81 +/// Block erase command code. +#define AT45_BLOCK_ERASE 0x50 +/// Sector erase command code. +#define AT45_SECTOR_ERASE 0x7C +/// Chip erase command code. +#define AT45_CHIP_ERASE 0xC7, 0x94, 0x80, 0x9A +/// Main memory page program through buffer 1 command code. +#define AT45_PAGE_WRITE_BUF1 0x82 +/// Main memory page program through buffer 2 command code. +#define AT45_PAGE_WRITE_BUF2 0x85 + +/// Main memory page to buffer 1 transfer command code. +#define AT45_PAGE_BUF1_TX 0x53 +/// Main memory page to buffer 2 transfer command code. +#define AT45_PAGE_BUF2_TX 0x55 +/// Main memory page to buffer 1 compare command code. +#define AT45_PAGE_BUF1_CMP 0x60 +/// Main memory page to buffer 2 compare command code. +#define AT45_PAGE_BUF2_CMP 0x61 +/// Auto page rewrite through buffer 1 command code. +#define AT45_AUTO_REWRITE_BUF1 0x58 +/// Auto page rewrite through buffer 2 command code. +#define AT45_AUTO_REWRITE_BUF2 0x59 +/// Deep power-down command code. +#define AT45_DEEP_PDOWN 0xB9 +/// Resume from deep power-down command code. +#define AT45_RES_DEEP_PDOWN 0xAB +/// Status register read command code. +#define AT45_STATUS_READ 0xD7 +/// Manufacturer and device ID read command code. +#define AT45_ID_READ 0x9F + +/// Power-of-2 binary page size configuration command code. +#define AT45_BINARY_PAGE_FIRST_OPCODE 0x3D +#define AT45_BINARY_PAGE 0x2A, 0x80, 0xA6 + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Dataflash description. A constant array of DataflashDesc instance is defined +/// in at45.c. The DF_Scan() function returns the corresponding descriptor +/// according to the dataflash ID detected. +/// This description (page_size, page_offset) is used to compute the internal +/// dataflash address by the DF_Command() function. +//------------------------------------------------------------------------------ +typedef struct { + + /// dataflash page number. + unsigned int pageNumber; + // indicate if power-of-2 binary page supported. + unsigned int hasBinaryPage; + /// dataflash page size. + unsigned int pageSize; + /// page offset in command. + unsigned int pageOffset; + /// Dataflash ID. + unsigned char id; + /// Identifier. + const char *name; + +} At45Desc; + +//------------------------------------------------------------------------------ +/// Dataflash driver structure. It holds the current command being processed. +/// This structure is initialized by the DF_Init() command. +/// pDfDesc field can be initialized by the DF_Scan() function. +/// cmdBuffer is a private driver area used to compute the dataflash address to +/// be sent to the dataflash. +/// Beware the PDC master must have access to this area. +//------------------------------------------------------------------------------ +typedef struct _Dataflash { + + /// Pointer to Spi Structure (SPI low level driver). + Spid *pSpid; + /// Current SPI command sent to the SPI low level driver. + SpidCmd command; + /// Pointer to the dataflash description. + const At45Desc *pDesc; + /// Buffer to store the current command (opcode + dataflash address. + unsigned char pCmdBuffer[8]; + +} At45; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char AT45_Configure(At45 *pAt45, Spid *pSpid, unsigned char spiCs); + +extern unsigned char AT45_IsBusy(At45 *pAt45); + +extern unsigned char AT45_SendCommand( + At45 *pAt45, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument); + +extern const At45Desc * AT45_FindDevice(At45 *pAt45, unsigned char status); + +extern unsigned int AT45_PageSize(At45 *pAt45); +#endif // #ifndef AT45_H + diff --git a/memories/spi-flash/at45d.c b/memories/spi-flash/at45d.c new file mode 100644 index 0000000..228a1b6 --- /dev/null +++ b/memories/spi-flash/at45d.c @@ -0,0 +1,223 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at45.h" +#include "at45d.h" +#include +#include + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Wait for transfer to finish calling the SPI driver ISR (interrupts are +/// disabled). +/// \param pAt45 Pointer to an AT45 driver instance. +//------------------------------------------------------------------------------ +static void AT45D_Wait(At45 *pAt45) +{ + SANITY_CHECK(pAt45); + + // Wait for transfer to finish + while (AT45_IsBusy(pAt45)) { + + SPID_Handler(pAt45->pSpid); + } +} + + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Waits for the At45 to be ready to accept new commands. +/// \param pAt45 Pointer to a At45 driver instance. +//------------------------------------------------------------------------------ +void AT45D_WaitReady(At45 *pAt45) +{ + unsigned char ready = 0; + + SANITY_CHECK(pAt45); + + // Poll device until it is ready + while (!ready) { + + ready = AT45_STATUS_READY(AT45D_GetStatus(pAt45)); + } +} + +//------------------------------------------------------------------------------ +/// Retrieves and returns the At45 current status, or 0 if an error +/// happened. +/// \param pAt45 Pointer to a At45 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT45D_GetStatus(At45 *pAt45) +{ + unsigned char error; + unsigned char status; + + SANITY_CHECK(pAt45); + + // Issue a status register read command + error = AT45_SendCommand(pAt45, AT45_STATUS_READ, 1, &status, 1, 0, 0, 0); + ASSERT(!error, "-F- AT45_GetStatus: Failed to issue command.\n\r"); + + // Wait for command to terminate + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + return status; +} + +//------------------------------------------------------------------------------ +/// Reads data from the At45 inside the provided buffer. Since a continuous +/// read command is used, there is no restriction on the buffer size and read +/// address. +/// \param pAt45 Pointer to a At45 driver instance. +/// \param pBuffer Data buffer. +/// \param size Number of bytes to read. +/// \param address Address at which data shall be read. +//------------------------------------------------------------------------------ +void AT45D_Read( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address) +{ + unsigned char error; + + SANITY_CHECK(pAt45); + SANITY_CHECK(pBuffer); + + // Issue a continuous read array command + error = AT45_SendCommand(pAt45, AT45_CONTINUOUS_READ_LEG, 8, pBuffer, size, address, 0, 0); + ASSERT(!error, "-F- AT45_Read: Failed to issue command\n\r"); + + // Wait for the read command to execute + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } +} + +//------------------------------------------------------------------------------ +/// Writes data on the At45 at the specified address. Only one page of +/// data is written that way; if the address is not at the beginning of the +/// page, the data is written starting from this address and wraps around to +/// the beginning of the page. +/// \param pAt45 Pointer to a At45 driver instance. +/// \param pBuffer Buffer containing the data to write. +/// \param size Number of bytes to write. +/// \param address Destination address on the At45. +//------------------------------------------------------------------------------ +void AT45D_Write( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address) +{ + unsigned char error; + + SANITY_CHECK(pAt45); + SANITY_CHECK(pBuffer); + SANITY_CHECK(size <= pAt45->pDesc->pageSize); + + // Issue a page write through buffer 1 command + error = AT45_SendCommand(pAt45, AT45_PAGE_WRITE_BUF1, 4, pBuffer, size, address, 0, 0); + ASSERT(!error, "-F- AT45_Write: Could not issue command.\n\r"); + + // Wait until the command is sent + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + // Wait until the At45 becomes ready again + AT45D_WaitReady(pAt45); +} + +//------------------------------------------------------------------------------ +/// Erases a page of data at the given address in the At45. +/// \param pAt45 Pointer to a At45 driver instance. +/// \param address Address of page to erase. +//------------------------------------------------------------------------------ +void AT45D_Erase(At45 *pAt45, unsigned int address) +{ + unsigned char error; + + SANITY_CHECK(pAt45); + + // Issue a page erase command. + error = AT45_SendCommand(pAt45, AT45_PAGE_ERASE, 4, 0, 0, address, 0, 0); + ASSERT(!error, "-F- AT45_Erase: Could not issue command.\n\r"); + + // Wait for end of transfer + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + // Poll until the At45 has completed the erase operation + AT45D_WaitReady(pAt45); +} + +//------------------------------------------------------------------------------ +/// Configure power-of-2 binary page size in the At45. +/// \param pAt45 Pointer to a At45 driver instance. +//------------------------------------------------------------------------------ + +void AT45D_BinaryPage(At45 *pAt45) +{ + unsigned char error; + unsigned char opcode[3]= {AT45_BINARY_PAGE}; + SANITY_CHECK(pAt45); + + // Issue a binary page command. + + error = AT45_SendCommand(pAt45, AT45_BINARY_PAGE_FIRST_OPCODE, 1, opcode, 3, 0, 0, 0); + + ASSERT(!error, "-F- AT45_Erase: Could not issue command.\n\r"); + + // Wait for end of transfer + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + // Wait until the At45 becomes ready again + AT45D_WaitReady(pAt45); +} diff --git a/memories/spi-flash/at45d.h b/memories/spi-flash/at45d.h new file mode 100644 index 0000000..b512445 --- /dev/null +++ b/memories/spi-flash/at45d.h @@ -0,0 +1,86 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// The AT45 Dataflash driver is based on the corresponding AT45 driver. +/// A AT45 instance has to be initialized using the Dataflash levle function +/// AT45_Configure(). AT45 Dataflash can be automatically detected using +/// the AT45_FindDevice() function. Then AT45 dataflash operations such as +/// read, write and erase DF can be launched using AT45_SendCommand function +/// with corresponding AT45 command set. +/// +/// !!!Usage +/// +/// -# Reads data from the At45 at the specified address using AT45D_Read(). +/// -# Writes data on the At45 at the specified address using AT45D_Write(). +/// -# Erases a page of data at the given address using AT45D_Erase(). +/// -# Poll until the At45 has completed of corresponding operations using +/// AT45D_WaitReady(). +/// -# Retrieves and returns the At45 current using AT45D_GetStatus(). +//------------------------------------------------------------------------------ + + +#ifndef AT45D_H +#define AT45D_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at45.h" + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +extern void AT45D_WaitReady(At45 *pAt45); + +extern unsigned char AT45D_GetStatus(At45 *pAt45); + +extern void AT45D_Read( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address); + +extern void AT45D_Write( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address); + +extern void AT45D_Erase(At45 *pAt45, unsigned int address); + +extern void AT45D_BinaryPage(At45 *pAt45); + +#endif //#ifndef AT45D_H + diff --git a/memories/spi-flash/spi-flash.dir b/memories/spi-flash/spi-flash.dir new file mode 100644 index 0000000..c2f3c2f --- /dev/null +++ b/memories/spi-flash/spi-flash.dir @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// Contains the %spi-flash driver for the SPI compatible Dataflash. +//------------------------------------------------------------------------------ + diff --git a/memories/spi-flash/spid.c b/memories/spi-flash/spid.c new file mode 100644 index 0000000..3dd0409 --- /dev/null +++ b/memories/spi-flash/spid.c @@ -0,0 +1,215 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "spid.h" +#include + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Write PMC register +#define WRITE_PMC(pPmc, regName, value) pPmc->regName = (value) + +/// Write SPI register +#define WRITE_SPI(pSpi, regName, value) pSpi->regName = (value) + +/// Read SPI registers +#define READ_SPI(pSpi, regName) (pSpi->regName) + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the Spid structure and the corresponding SPI hardware. +/// Always returns 0. +/// \param pSpid Pointer to a Spid instance. +/// \param pSpiHw Associated SPI peripheral. +/// \param spiId SPI peripheral identifier. +//------------------------------------------------------------------------------ +unsigned char SPID_Configure(Spid *pSpid, AT91S_SPI *pSpiHw, unsigned char spiId) +{ + // Initialize the SPI structure + pSpid->pSpiHw = pSpiHw; + pSpid->spiId = spiId; + pSpid->semaphore = 1; + pSpid->pCurrentCommand = 0; + + // Enable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pSpid->spiId)); + + // Execute a software reset of the SPI twice + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); + + // Configure SPI in Master Mode with No CS selected !!! + WRITE_SPI(pSpiHw, SPI_MR, AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS); + + // Disable the PDC transfer + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + // Enable the SPI + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIEN); + + // Enable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pSpid->spiId)); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Configures the parameters for the device corresponding to the cs. +/// \param pSpid Pointer to a Spid instance. +/// \param cs number corresponding to the SPI chip select. +/// \param csr SPI_CSR value to setup. +//------------------------------------------------------------------------------ +void SPID_ConfigureCS(Spid *pSpid, unsigned char cs, unsigned int csr) +{ + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + WRITE_SPI(pSpiHw, SPI_CSR[cs], csr); +} + +//------------------------------------------------------------------------------ +/// Starts a SPI master transfer. This is a non blocking function. It will +/// return as soon as the transfer is started. +/// Returns 0 if the transfer has been started successfully; otherwise returns +/// SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not +/// valid. +/// \param pSpid Pointer to a Spid instance. +/// \param pCommand Pointer to the SPI command to execute. +//------------------------------------------------------------------------------ +unsigned char SPID_SendCommand(Spid *pSpid, SpidCmd *pCommand) +{ + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + unsigned int spiMr; + + // Try to get the dataflash semaphore + if (pSpid->semaphore == 0) { + + return SPID_ERROR_LOCK; + } + pSpid->semaphore--; + + // Enable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pSpid->spiId)); + + // Disable transmitter and receiver + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + // Write to the MR register + spiMr = READ_SPI(pSpiHw, SPI_MR); + spiMr |= AT91C_SPI_PCS; + spiMr &= ~((1 << pCommand->spiCs) << 16); + WRITE_SPI(pSpiHw, SPI_MR, spiMr); + + // Initialize the two SPI PDC buffer + WRITE_SPI(pSpiHw, SPI_RPR, (int) pCommand->pCmd); + WRITE_SPI(pSpiHw, SPI_RCR, pCommand->cmdSize); + WRITE_SPI(pSpiHw, SPI_TPR, (int) pCommand->pCmd); + WRITE_SPI(pSpiHw, SPI_TCR, pCommand->cmdSize); + + WRITE_SPI(pSpiHw, SPI_RNPR, (int) pCommand->pData); + WRITE_SPI(pSpiHw, SPI_RNCR, pCommand->dataSize); + WRITE_SPI(pSpiHw, SPI_TNPR, (int) pCommand->pData); + WRITE_SPI(pSpiHw, SPI_TNCR, pCommand->dataSize); + + // Initialize the callback + pSpid->pCurrentCommand = pCommand; + + // Enable transmitter and receiver + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); + + // Enable buffer complete interrupt + WRITE_SPI(pSpiHw, SPI_IER, AT91C_SPI_RXBUFF); + + return 0; +} + +//------------------------------------------------------------------------------ +/// The SPI_Handler must be called by the SPI Interrupt Service Routine with the +/// corresponding Spi instance. +/// The SPI_Handler will unlock the Spi semaphore and invoke the upper application +/// callback. +/// \param pSpid Pointer to a Spid instance. +//------------------------------------------------------------------------------ +void SPID_Handler(Spid *pSpid) +{ + SpidCmd *pSpidCmd = pSpid->pCurrentCommand; + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + volatile unsigned int spiSr; + + // Read the status register + spiSr = READ_SPI(pSpiHw, SPI_SR); + if (spiSr & AT91C_SPI_RXBUFF) { + + // Disable transmitter and receiver + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + // Disable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pSpid->spiId)); + + // Disable buffer complete interrupt + WRITE_SPI(pSpiHw, SPI_IDR, AT91C_SPI_RXBUFF); + + // Release the dataflash semaphore + pSpid->semaphore++; + + // Invoke the callback associated with the current command + if (pSpidCmd && pSpidCmd->callback) { + + pSpidCmd->callback(0, pSpidCmd->pArgument); + } + + // Nothing must be done after. A new DF operation may have been started + // in the callback function. + } +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the SPI driver is currently busy executing a command; otherwise +/// returns 0. +/// \param pSpid Pointer to a SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SPID_IsBusy(const Spid *pSpid) +{ + if (pSpid->semaphore == 0) { + + return 1; + } + else { + + return 0; + } +} + diff --git a/memories/spi-flash/spid.h b/memories/spi-flash/spid.h new file mode 100644 index 0000000..654a27e --- /dev/null +++ b/memories/spi-flash/spid.h @@ -0,0 +1,212 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// The Spi driver is a low level spi driver which performs SPI device Initializes, +/// spi transfer and receive. It can be used by upper SPI driver such as AT45 +/// driver and AT26 driver. +/// +/// !!!Usage +/// +/// -# Initializes a SPI instance and the corresponding SPI hardware, +/// Configure SPI in Master Mode using SPID_Configure(). +/// -# Configures the SPI characteristics (such as Clock Polarity, Phase, +/// transfers delay and Baud Rate) for the device corresponding to the +/// chip select using SPID_ConfigureCS(). +/// -# Starts a SPI master transfer using SPID_SendCommand(). +/// The transfer may perform in 2 ways (depending on the implement): +/// -# The transfer is performed using the PDC channels. +/// -# It enable the SPI clock. +/// -# Set the corresponding peripheral chip select. +/// -# Initialize the two SPI PDC buffers. +/// - Initialize SPI_TPR and SPI_TCR with SPI command data and size +/// to send command data first. +/// - Initialize SPI_RPR and SPI_RCR with SPI command data and size +/// as dummy value. +/// - Initialize SPI_TNPR and SPI_TNCR with rest of the data to be +/// transfered.(if the data specified in cmd structure) +/// - Initialize SPI_RNPR and SPI_RNCR with rest of the data to be +/// received.(if the data specified in cmd structure) +/// -# Initialize the callback function if specified. +/// -# Enable transmitter and receiver. +/// -# The transfer is performed using the DMA channels 0 & 1. +/// -# Enable SPI peripheral. +/// -# Set the corresponding peripheral chip select. +/// -# Initialize the two DMA channels. +/// - Configure channel 0 as RX channel. +/// - Source DMA interface is SPI RX. +/// - Source is HW triggerred. +/// - Configure channel 1 as TX channel +/// - Destination DMA interface is SPI TX +/// - Destination is HW triggerred. +/// - Initialize DMA Link List of two items for RX. +/// - Source address pointer to SPI_RDR. +/// - Destination address pointer to command or data position. +/// - Transfer width always BYTE, size is command or data length. +/// - Uses Peripheral-to-Memory Flow Control. +/// - Set DMA channel 0 DMA_DSCR to RX link list. +/// - Set DMA channel 0 DMA_CTRLB to 0 to load link list. +/// - Initialize DMA Link List of two items for TX. +/// - Source address pointer to command or data position. +/// - Destination address pointer to SPI_TDR. +/// - Transfer width always BYTE, size is command or data length. +/// - Uses Memory-to-Peripheral Flow Control. +/// - Set DMA channel 1 DMA_DSCR to TX link list. +/// - Set DMA channel 1 DMA_CTRLB to 0 to load link list. +/// -# Initialize the callback function if specified. +/// -# Enable transmitter and receiver. +/// -# Enable two DMA channels at the same time. +/// +/// - Example for sending a command to the dataflash through the SPI. +/// \code +/// /// Build command to be sent. +/// ... +/// // Send Command and data through the SPI +/// if (SPID_SendCommand(pAt45->pSpid, pCommand)) { +/// return AT45_ERROR_SPI; +/// } +/// \endcode +/// -# The SPID_Handler() must be called by the SPI Interrupt Service Routine +/// with the corresponding Spi instance. It is invokes to check for pending +/// interrupts. +/// - Example for initializing SPI interrupt handler in upper application. +/// \code +/// AIC_ConfigureIT(AT91C_ID_SPI, 0, SPI_Handler); +/// \endcode +//------------------------------------------------------------------------------ + +#ifndef SPID_H +#define SPID_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// An unspecified error has occured. +#define SPID_ERROR 1 + +/// SPI driver is currently in use. +#define SPID_ERROR_LOCK 2 + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Calculates the value of the SCBR field of the Chip Select Register given +/// MCK and SPCK. +#define SPID_CSR_SCBR(mck, spck) ((((mck) / (spck)) << 8) & AT91C_SPI_SCBR) + +/// Calculates the value of the DLYBS field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBS(mck, delay) \ + ((((((delay) * ((mck) / 1000000)) / 1000) + 1) << 16) & AT91C_SPI_DLYBS) + +/// Calculates the value of the DLYBCT field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBCT(mck, delay) \ + ((((((delay) / 32 * ((mck) / 1000000)) / 1000) + 1) << 24) & AT91C_SPI_DLYBCT) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// SPI transfer complete callback. +typedef void (*SpidCallback )(unsigned char, void *); + +//------------------------------------------------------------------------------ +/// Spi Transfer Request prepared by the application upper layer. This structure +/// is sent to the SPI_SendCommand function to start the transfer. At the end of +/// the transfer, the callback is invoked by the interrupt handler. +//------------------------------------------------------------------------------ +typedef struct _SpidCmd { + + /// Pointer to the command data. + unsigned char *pCmd; + /// Command size in bytes. + unsigned char cmdSize; + /// Pointer to the data to be sent. + unsigned char *pData; + /// Data size in bytes. + unsigned short dataSize; + /// SPI chip select. + unsigned char spiCs; + /// Callback function invoked at the end of transfer. + SpidCallback callback; + /// Callback arguments. + void *pArgument; + +} SpidCmd; + +//------------------------------------------------------------------------------ +/// Constant structure associated with SPI port. This structure prevents +/// client applications to have access in the same time. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to SPI Hardware registers + AT91S_SPI *pSpiHw; + /// SPI Id as defined in the product datasheet + char spiId; + /// Current SpiCommand being processed + SpidCmd *pCurrentCommand; + /// Mutual exclusion semaphore. + volatile char semaphore; + +} Spid; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char SPID_Configure( + Spid *pSpid, + AT91S_SPI *pSpiHw, + unsigned char spiId); + +extern void SPID_ConfigureCS(Spid *pSpid, unsigned char cs, unsigned int csr); + +extern unsigned char SPID_SendCommand( + Spid *pSpid, + SpidCmd *pCommand); + +extern void SPID_Handler(Spid *pSpid); + +extern unsigned char SPID_IsBusy(const Spid *pSpid); + +#endif // #ifndef SPID_H + diff --git a/memories/spi-flash/spid_dma.c b/memories/spi-flash/spid_dma.c new file mode 100644 index 0000000..dc8139d --- /dev/null +++ b/memories/spi-flash/spid_dma.c @@ -0,0 +1,392 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "spid.h" +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Defines +//------------------------------------------------------------------------------ + +/// DMA Link List size +#define SIZE_LL 2 + +/// DMA Width BYTE +#define DMA_WIDTH 0 + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Write PMC register +#define WRITE_PMC(pPmc, regName, value) pPmc->regName = (value) + +/// Write SPI register +#define WRITE_SPI(pSpi, regName, value) pSpi->regName = (value) + +/// Read SPI registers +#define READ_SPI(pSpi, regName) (pSpi->regName) + +/// Enable Peripheral +#define PERIPH_ENABLE(id) \ + WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << (id))) +/// Disable Peripheral +#define PERIPH_DISABLE(id) \ + WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << (id))) + + +//------------------------------------------------------------------------------ +// Local Variables +//------------------------------------------------------------------------------ + +/// Linked lists for multi transfer buffer chaining structure instance. +static DmaLinkList dmaTxLinkList[SIZE_LL]; +static DmaLinkList dmaRxLinkList[SIZE_LL]; + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Configure the DMA Channels: 0 RX, 1 TX. +/// Channels are disabled after configure. +//------------------------------------------------------------------------------ +static void configureDmaChannels(void) +{ + // Enable DMA Peripheral + PERIPH_ENABLE(AT91C_ID_HDMA); + // Enable DMA + DMA_Enable(); + + // Free status + DMA_DisableIt(0xFFFFFFFF); + DMA_GetChannelStatus(); + DMA_GetStatus(); + DMA_DisableChannels((1 << DMA_CHANNEL_0) | (1 << DMA_CHANNEL_1)); + // RX channel 0 + DMA_SetConfiguration(DMA_CHANNEL_0, + AT91C_HDMA_SRC_PER_2 + | AT91C_HDMA_DST_PER_2 + | AT91C_HDMA_SRC_H2SEL_HW + | AT91C_HDMA_DST_H2SEL_SW + | AT91C_HDMA_SOD_ENABLE + | AT91C_HDMA_FIFOCFG_LARGESTBURST + ); + + // TX channel 1 + DMA_SetConfiguration(DMA_CHANNEL_1, + AT91C_HDMA_SRC_PER_1 + | AT91C_HDMA_DST_PER_1 + | AT91C_HDMA_SRC_H2SEL_SW + | AT91C_HDMA_DST_H2SEL_HW + | AT91C_HDMA_SOD_ENABLE + | AT91C_HDMA_FIFOCFG_LARGESTBURST + ); +} + +//------------------------------------------------------------------------------ +/// Configure the DMA source and destination with Linker List mode. +/// \param pCommand Pointer to command +//------------------------------------------------------------------------------ +static void configureLinkList(AT91S_SPI *pSpiHw, + SpidCmd *pCommand) +{ + // Setup RX Link List + dmaRxLinkList[0].sourceAddress = (unsigned int)&pSpiHw->SPI_RDR; + dmaRxLinkList[0].destAddress = (unsigned int)pCommand->pCmd; + dmaRxLinkList[0].controlA = pCommand->cmdSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaRxLinkList[0].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_FC_PER2MEM + | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED + | AT91C_HDMA_DST_ADDRESS_MODE_INCR + ; + dmaTxLinkList[0].sourceAddress = (unsigned int)pCommand->pCmd; + dmaTxLinkList[0].destAddress = (unsigned int)&pSpiHw->SPI_TDR; + dmaTxLinkList[0].controlA = pCommand->cmdSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaTxLinkList[0].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_FC_MEM2PER + | AT91C_HDMA_SRC_ADDRESS_MODE_INCR + | AT91C_HDMA_DST_ADDRESS_MODE_FIXED + ; + // Only command + if (pCommand->pData == 0) { + + dmaRxLinkList[0].descriptor = 0; + dmaTxLinkList[0].descriptor = 0; + } + // Command & Data + else { + + dmaRxLinkList[0].descriptor = (unsigned int)&dmaRxLinkList[1]; + dmaRxLinkList[1].sourceAddress = (unsigned int)&pSpiHw->SPI_RDR; + dmaRxLinkList[1].destAddress = (unsigned int)pCommand->pData; + dmaRxLinkList[1].controlA = pCommand->dataSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaRxLinkList[1].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE + | AT91C_HDMA_DST_DSCR_FETCH_DISABLE + | AT91C_HDMA_FC_PER2MEM + | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED + | AT91C_HDMA_DST_ADDRESS_MODE_INCR + ; + dmaRxLinkList[1].descriptor = 0; + + dmaTxLinkList[0].descriptor = (unsigned int)&dmaTxLinkList[1]; + dmaTxLinkList[1].sourceAddress = (unsigned int)pCommand->pData; + dmaTxLinkList[1].destAddress = (unsigned int)&pSpiHw->SPI_TDR; + dmaTxLinkList[1].controlA = pCommand->dataSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaTxLinkList[1].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE + | AT91C_HDMA_DST_DSCR_FETCH_DISABLE + | AT91C_HDMA_FC_MEM2PER + | AT91C_HDMA_SRC_ADDRESS_MODE_INCR + | AT91C_HDMA_DST_ADDRESS_MODE_FIXED + ; + dmaTxLinkList[1].descriptor = 0; + } + + + // Setup registers + DMA_SetDescriptorAddr(DMA_CHANNEL_0, (unsigned int)&dmaRxLinkList[0]); + DMA_SetDescriptorAddr(DMA_CHANNEL_1, (unsigned int)&dmaTxLinkList[0]); + AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_0].HDMA_CTRLB = 0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + ; + AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_1].HDMA_CTRLB = 0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + ; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the Spid structure and the corresponding SPI & DMA hardware. +/// The driver will uses DMA channel 0 for RX and DMA channel 1 for TX. +/// The DMA channels are freed automatically when no SPI command processing. +/// \param pSpid Pointer to a Spid instance. +/// \param pSpiHw Associated SPI peripheral. +/// \param spiId SPI peripheral identifier. +/// \return Always 0. +//------------------------------------------------------------------------------ +unsigned char SPID_Configure(Spid *pSpid, AT91S_SPI *pSpiHw, unsigned char spiId) +{ + // Initialize the SPI structure + pSpid->pSpiHw = pSpiHw; + pSpid->spiId = spiId; + pSpid->semaphore = 1; + pSpid->pCurrentCommand = 0; + + // Enable the SPI Peripheral + PERIPH_ENABLE(pSpid->spiId); + + // Execute a software reset of the SPI twice + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); + + // Configure SPI in Master Mode with No CS selected !!! + WRITE_SPI(pSpiHw, SPI_MR, AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS); + + // Disable the PDC transfer + #if !defined(at91sam3u) + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + #endif + + // Disable the SPI TX & RX + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS); + + // Disable the SPI Peripheral + PERIPH_DISABLE(pSpid->spiId); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Configures the parameters for the device corresponding to the cs. +/// \param pSpid Pointer to a Spid instance. +/// \param cs number corresponding to the SPI chip select. +/// \param csr SPI_CSR value to setup. +//------------------------------------------------------------------------------ +void SPID_ConfigureCS(Spid *pSpid, unsigned char cs, unsigned int csr) +{ + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + + // Enable the SPI Peripheral + PERIPH_ENABLE(pSpid->spiId); + + // Write CS + WRITE_SPI(pSpiHw, SPI_CSR[cs], csr); + + // Disable the SPI Peripheral + PERIPH_DISABLE(pSpid->spiId); +} + +//------------------------------------------------------------------------------ +/// Starts a SPI master transfer. This is a non blocking function. It will +/// return as soon as the transfer is started. +/// Returns 0 if the transfer has been started successfully; otherwise returns +/// SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not +/// valid. +/// \param pSpid Pointer to a Spid instance. +/// \param pCommand Pointer to the SPI command to execute. +//------------------------------------------------------------------------------ +unsigned char SPID_SendCommand(Spid *pSpid, SpidCmd *pCommand) +{ + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + unsigned int spiMr; + + // Try to get the dataflash semaphore + if (pSpid->semaphore == 0) { + + return SPID_ERROR_LOCK; + } + pSpid->semaphore--; + + // Enable the SPI Peripheral + PERIPH_ENABLE(pSpid->spiId); + + // Disable PDC transmitter and receiver + #if !defined(at91sam3u) + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + #endif + + // Write to the MR register + spiMr = READ_SPI(pSpiHw, SPI_MR); + spiMr |= AT91C_SPI_PCS; + spiMr &= ~((1 << pCommand->spiCs) << 16); + WRITE_SPI(pSpiHw, SPI_MR, spiMr); + + // Initialize DMA controller using channel 0 for RX, 1 for TX. + configureDmaChannels(); + configureLinkList(pSpiHw, pCommand); + + // Initialize the callback + pSpid->pCurrentCommand = pCommand; + + // Enable the SPI TX & RX + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIEN); + + // Start DMA 0(RX) && 1(TX) + DMA_EnableChannels((1 << DMA_CHANNEL_0) | (1 << DMA_CHANNEL_1)); + + // Enable DMA Interrupts + DMA_EnableIt( (DMA_CBTC << DMA_CHANNEL_0) + | (DMA_CBTC << DMA_CHANNEL_1)); + + return 0; +} + +//------------------------------------------------------------------------------ +/// SPI DMA transfer ISR, Handle RX complete +//------------------------------------------------------------------------------ +void SPID_Handler(Spid *pSpid) +{ + unsigned int dmaStatus; + SpidCmd *pSpidCmd = pSpid->pCurrentCommand; + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + + dmaStatus = DMA_GetStatus(); + + if ((dmaStatus & AT91C_CBTC) == 0) + return; + + if ((dmaStatus & (DMA_CBTC << DMA_CHANNEL_0)) == 0) + return; + + // Disable the SPI TX & RX + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS); + // Disable the SPI Peripheral + PERIPH_DISABLE(pSpid->spiId); + + // Disable DMA + DMA_Disable(); + // Disable DMA Peripheral + PERIPH_DISABLE(AT91C_ID_HDMA); + + // Release the dataflash semaphore + pSpid->semaphore++; + + // Invoke the callback associated with the current command + if (pSpidCmd && pSpidCmd->callback) { + + pSpidCmd->callback(0, pSpidCmd->pArgument); + } + +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the SPI driver is currently busy executing a command; otherwise +/// returns 0. +/// \param pSpid Pointer to a SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SPID_IsBusy(const Spid *pSpid) +{ + if (pSpid->semaphore == 0) { + + return 1; + } + else { + + return 0; + } +} + -- cgit v1.2.3