/* ---------------------------------------------------------------------------- | |
* 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. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** \addtogroup spi_at45_module SPI AT45 driver | |
* \ingroup at45d_module | |
* 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. | |
* | |
* \section Usage | |
* <ul> | |
* <li> Initializes an AT45 instance and configures SPI chip select pin | |
* using AT45_Configure(). </li> | |
* <li> 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.</li> | |
* <li>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.</li> | |
* <li>Example code for sending command to write a page to DF. </li> | |
* \code | |
* // Issue a page write through buffer 1 command | |
* error = AT45_SendCommand(pAt45, AT45_PAGE_WRITE_BUF1, 4, | |
* pBuffer, size, address, 0, 0); | |
* \endcode | |
* <li>Example code for sending command to read a page from DF. | |
* If data needs to be received, then a data buffer must be | |
* provided.</li> | |
* \code | |
* // Issue a continuous read array command | |
* error = AT45_SendCommand(pAt45, AT45_CONTINUOUS_READ_LEG, 8, | |
* pBuffer, size, address, 0, 0); | |
* \endcode | |
* <li> This function does not block; its optional callback will | |
* be invoked when the transfer completes.</li> | |
* <li> Check the AT45 driver is ready or not by polling AT45_IsBusy(). | |
* </ul> | |
* Related files :\n | |
* \ref spi_at45.c\n | |
* \ref spi_at45.h.\n | |
*/ | |
/*@{*/ | |
/*@}*/ | |
/** | |
* \file | |
* | |
* Implementation of SPI At45 driver. | |
* | |
*/ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "board.h" | |
#include <assert.h> | |
#include <string.h> | |
/*---------------------------------------------------------------------------- | |
* Internal definitions | |
*----------------------------------------------------------------------------*/ | |
/** SPI clock frequency, in Hz.*/ | |
#define SPCK 1000000 | |
/** Number of dataflash which can be recognized.*/ | |
#define NUMDATAFLASH (sizeof(at45Devices) / sizeof(At45Desc)) | |
/** SPI chip select configuration value. */ | |
#define CSR (SPI_CSR_NCPHA | \ | |
SPID_CSR_DLYBCT(BOARD_MCK, 250) | \ | |
SPID_CSR_DLYBS(BOARD_MCK, 250) | \ | |
SPID_CSR_SCBR(BOARD_MCK, SPCK)) | |
/*---------------------------------------------------------------------------- | |
* Local variables | |
*----------------------------------------------------------------------------*/ | |
/** indicate if the device is configured as binary page or not.*/ | |
static uint8_t configuredBinaryPage; | |
/** At45 device descriptor structure. */ | |
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 | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Initializes an AT45 instance and configures SPI chip select register. | |
* | |
* \param pAt45 Pointer to the At45 instance to initialize. | |
* \param pSpid Pointer to the underlying SPI driver. | |
* \param ucSpiCs Chip select value to connect to the At45. | |
* \param ucPolling Operation mode for the AT45 device. | |
* \return 0. | |
*/ | |
extern uint32_t AT45_Configure( At45* pAt45, void* pSpid, uint8_t ucSpiCs,uint8_t ucPolling ) | |
{ | |
SpidCmd *pCommand; | |
/* Sanity checks */ | |
assert( pSpid != NULL ) ; | |
assert( pAt45 != NULL ) ; | |
if (pAt45->dwInterfaceType == AT45_INTERFACE_TYPE_USART_SPI) | |
{ | |
pAt45->pUsartd = (Usartd *)pSpid ; | |
} | |
else | |
{ | |
/* Configure the SPI chip select for the serial flash */ | |
SPID_ConfigureCS(pSpid, ucSpiCs, CSR); | |
/* Initialize the AT45 fields */ | |
pAt45->pSpid = (Spid *)pSpid; | |
} | |
pAt45->pDesc = 0; | |
pAt45->dwPollingMode = ucPolling; | |
memset( pAt45->pCmdBuffer, 0, 8 ) ; | |
/* Initialize the command structure */ | |
pCommand = &(pAt45->command); | |
pCommand->pCmd = pAt45->pCmdBuffer ; | |
pCommand->callback = 0; | |
pCommand->pArgument = 0; | |
pCommand->spiCs = ucSpiCs; | |
return 0 ; | |
} | |
/** | |
* \brief Check if the At45 driver is in busy. | |
* | |
* \param pAt45 Pointer to the At45 instance to initialize. | |
* \return 1 if the At45 driver is not executing any command,otherwise it returns 0. | |
*/ | |
extern uint32_t AT45_IsBusy( At45* pAt45 ) | |
{ | |
if (pAt45->dwInterfaceType == AT45_INTERFACE_TYPE_USART_SPI) | |
{ | |
return USART_SPIDIsBusy(pAt45->pUsartd); | |
} | |
else | |
{ | |
return SPID_IsBusy( pAt45->pSpid ) ; | |
} | |
} | |
/** | |
* \brief 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. | |
* \note This function does not block; its optional callback will be invoked when | |
* the transfer completes. | |
* \param pAt45 Pointer to the At45 instance to initialize. | |
* \param ucCmd Command code. | |
* \param ucCmdSize Size of command code + address bytes + dummy bytes. | |
* \param pucData Data buffer. | |
* \param dwDataSize Number of data bytes to send/receive. | |
* \param dwAddress Address at which the command is performed if meaningful. | |
* \param pCallback Optional callback to invoke at end of transfer. | |
* \param pArgument Optional parameter to the callback function. | |
* \return 0. | |
*/ | |
extern uint32_t AT45_SendCommand( At45* pAt45, | |
uint8_t ucCmd, | |
uint8_t ucCmdSize, | |
uint8_t *pucData, | |
uint32_t dwDataSize, | |
uint32_t dwAddress, | |
SpidCallback pCallback, | |
void *pArgument ) | |
{ | |
SpidCmd *pCommand ; | |
const At45Desc *pDesc = pAt45->pDesc ; | |
uint32_t dfAddress = 0 ; | |
/* Sanity checks */ | |
assert( pDesc || (ucCmd == AT45_STATUS_READ) ) ; | |
/* Check if the SPI driver is available*/ | |
if ( AT45_IsBusy( pAt45 ) ) | |
{ | |
return AT45_ERROR_LOCK ; | |
} | |
/* Compute command pattern*/ | |
pAt45->pCmdBuffer[0] = ucCmd ; | |
/* Add address bytes if necessary*/ | |
if ( ucCmdSize > 1 ) | |
{ | |
assert( pDesc != NULL ) ; | |
if ( !configuredBinaryPage ) | |
{ | |
dfAddress = ((dwAddress / (pDesc->dwPageSize)) << pDesc->dwPageOffset) | |
+ (dwAddress % (pDesc->dwPageSize)); | |
} | |
else | |
{ | |
dfAddress = dwAddress ; | |
} | |
/* Write address bytes */ | |
if ( pDesc->dwPageNumber >= 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 ( (ucCmd != AT45_CONTINUOUS_READ) && (ucCmd != AT45_PAGE_READ) ) | |
{ | |
ucCmdSize++ ; | |
} | |
} | |
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 = ucCmdSize ; | |
pCommand->pData = pucData ; | |
pCommand->dataSize = dwDataSize ; | |
pCommand->callback = pCallback ; | |
pCommand->pArgument = pArgument ; | |
/* Send Command and data through the SPI */ | |
if (pAt45->dwInterfaceType == AT45_INTERFACE_TYPE_USART_SPI) | |
{ | |
if ( USART_SPIDSendCommand( pAt45->pUsartd, pCommand ) ) | |
{ | |
return AT45_ERROR_SPI ; | |
} | |
} | |
else | |
{ | |
if ( SPID_SendCommand( pAt45->pSpid, pCommand ) ) | |
{ | |
return AT45_ERROR_SPI ; | |
} | |
} | |
return 0 ; | |
} | |
/** | |
* \brief returns the At45Desc structure corresponding to the device connected. | |
* It automatically initializes pAt45->pDesc field structure. | |
* | |
* \note This function shall be called by the application before AT45_SendCommand. | |
* | |
* \param pAt45 Pointer to the At45 instance to initialize. | |
* \param ucStatus Device status register value. | |
* | |
* \return 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. | |
*/ | |
extern const At45Desc * AT45_FindDevice( At45 *pAt45, uint8_t ucStatus ) | |
{ | |
uint32_t i; | |
uint8_t id = AT45_STATUS_ID(ucStatus); | |
/* Check if status is all one; in which case, it is assumed that no device is connected*/ | |
if ( ucStatus == 0xFF ) | |
{ | |
return 0 ; | |
} | |
/* Look in device array */ | |
i = 0 ; | |
pAt45->pDesc = 0 ; | |
while ( (i < NUMDATAFLASH) && !(pAt45->pDesc) ) | |
{ | |
if ( at45Devices[i].ucID == id ) | |
{ | |
pAt45->pDesc = &(at45Devices[i]) ; | |
} | |
i++ ; | |
} | |
configuredBinaryPage = AT45_STATUS_BINARY(ucStatus); | |
return pAt45->pDesc ; | |
} | |
/** | |
* \brief returns the pagesize corresponding to the device connected. | |
* \param pAt45 Pointer to the At45 instance to initialize. | |
* \return page size. | |
*/ | |
extern uint32_t AT45_PageSize( At45 *pAt45 ) | |
{ | |
uint32_t dwPageSize = pAt45->pDesc->dwPageSize ; | |
if ( ((pAt45->pDesc->dwHasBinaryPage) == 0) || !configuredBinaryPage ) | |
{ | |
return dwPageSize ; | |
} | |
return ((dwPageSize >> 8) << 8) ; | |
} |