/* ----------------------------------------------------------------------------
 *         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_ManId(pAt26)           (((pAt26)->pDesc->jedecId) & 0xFF)
#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

/** SPI Flash Manufacturer JEDEC ID */
#define ATMEL_SPI_FLASH             0x1F
#define ST_SPI_FLASH                0x20
#define WINBOND_SPI_FLASH           0xEF
#define MACRONIX_SPI_FLASH          0xC2
#define SST_SPI_FLASH               0xBF

//------------------------------------------------------------------------------
//         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

