mirror of
https://gitea.osmocom.org/sim-card/simtrace2.git
synced 2026-03-16 21:28:33 +03:00
317 lines
11 KiB
C
317 lines
11 KiB
C
/* ----------------------------------------------------------------------------
|
|
* 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
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
/** 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 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 spiCs Chip select value to connect to the At45.
|
|
* \return 0.
|
|
*/
|
|
extern uint32_t AT45_Configure( At45* pAt45, Spid* pSpid, uint8_t ucSpiCs )
|
|
{
|
|
SpidCmd* pCommand ;
|
|
|
|
/* Sanity checks */
|
|
assert( pSpid != NULL ) ;
|
|
assert( pAt45 != NULL ) ;
|
|
|
|
/* 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 = 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 )
|
|
{
|
|
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 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.
|
|
* \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;
|
|
uint32_t dfAddress = 0 ;
|
|
|
|
/* Sanity checks */
|
|
assert( pAt45 != NULL ) ;
|
|
|
|
pDesc = pAt45->pDesc ;
|
|
|
|
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->pageSize)) << pDesc->pageOffset)
|
|
+ (dwAddress % (pDesc->pageSize));
|
|
}
|
|
else
|
|
{
|
|
dfAddress = dwAddress ;
|
|
}
|
|
|
|
/* 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 ( (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 ( 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 status 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 status )
|
|
{
|
|
uint32_t i;
|
|
uint8_t 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 ;
|
|
}
|
|
|
|
/**
|
|
* \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->pageSize ;
|
|
|
|
if ( ((pAt45->pDesc->hasBinaryPage) == 0) || !configuredBinaryPage )
|
|
{
|
|
return dwPageSize ;
|
|
}
|
|
|
|
return ((dwPageSize >> 8) << 8) ;
|
|
}
|