/* $Id: xflash_cfi.c,v 1.6 2002/04/19 16:19:42 robertm Exp $ */
/******************************************************************************
*
*       XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
*       AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
*       SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
*       OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
*       APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
*       THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
*       AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
*       FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
*       WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
*       IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
*       REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
*       INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*       FOR A PARTICULAR PURPOSE.
*
*       (c) Copyright 2002 Xilinx Inc.
*       All rights reserved.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xflash_cfi.c
*
* This module implements the helper component XFlashCFI.
*
* The helper component is responsible for retrieval and translation of CFI data
* from a complient flash device. CFI contains data that defines part geometry,
* write/erase timing, and programming data.
*
* Data is retrieved using macros defined in this component's header file. The
* macros simplify data extraction because they have been written to take into
* account the layout of parts on the data bus. To the driver, CFI data appears
* as if it were always being read from a single 8-bit part (XFL_LAYOUT_X8_X8_X1)
* Otherwise, the retrieval code would have to contend with all the formats
* illustrated below. The illustration shows how the first three bytes of the CFI
* query data "QRY" appear in flash memory space for various part layouts.
* <pre>
*
*                     Byte Offset (big-Endian)
*                        0123456789ABCDEF
*                        ----------------
*  XFL_LAYOUT_X16_X16_X1   Q R Y
*  XFL_LAYOUT_X16_X16_X2   Q Q R R Y Y
* </pre>
*
* Where the holes between Q, R, and Y are NULL (all bits 0)
*
* @note
*
* This helper component is intended to be RTOS and processor independent.
* It works with physical addresses only. Any needs for dynamic memory
* management, threads, mutual exclusion, virtual memory, or cache control
* management must be satisfied by the layer above this driver.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a rmm  03/15/02 Added 64 bit access
* 1.00a rmm  07/16/01 First release
* </pre>
*
* @internal
*
* This module has some dependencies when being unit tested. These areas are
* noted with conditional compilation based on whether XENV_UNITTEST is defined.
* Some areas of code must not be compiled because they interfere with testing
* due to the fact that unit testing occurs without real flash devices.
*
****************************************************************************/

/***************************** Include Files *********************************/
#include "xbasic_types.h"
#include "xflash.h"
#include "xflash_cfi.h"

/************************** Constant Definitions *****************************/


/**************************** Type Definitions *******************************/


/***************** Macros (Inline Functions) Definitions *********************/


/************************** Function Prototypes ******************************/

/************************** Variable Definitions *****************************/

/*****************************************************************************/
/**
*
* Retrieves the standard CFI data from the part(s), interpret the data, and
* update the provided geometry and properties structures.
*
* Extended CFI data is part specific and ignored here. This data must be read
* by the specific part component driver.
*
* @param    GeometryPtr is an input/output parameter. This function expects the
*           BaseAddress and  MemoryLayout attributes to be correctly initialized.
*           All other attributes of this structure will be setup using translated
*           CFI data read from the part.
* @param    PropertiesPtr is an output parameter. Timing, identification, and
*           programming CFI data will be translated and written to this structure.
*
* @return
*
* - XST_SUCCESS                    if successful.
* - XST_FLASH_CFI_QUERY_ERROR      if an error occurred interpreting the data.
* - XST_FLASH_PART_NOT_SUPPORTED   if invalid Layout parameter
*
* @note
*
* None.
*
******************************************************************************/
XStatus XFlashCFI_ReadCommon(XFlashGeometry *GeometryPtr,
                             XFlashProperties *PropertiesPtr)
{
    Xuint8 *Ptr;
    Xuint8  Data8;
    Xuint16 Data16;
    Xuint8  DataQRY[3];
    Xuint32 SizeMultiplier;
    Xuint32 CurrentAbsoluteOffset;
    Xuint16 CurrentAbsoluteBlock;
    XStatus Status = XST_SUCCESS;
    int i;
    int Interleave;

    XASSERT_NONVOID(GeometryPtr != XNULL);
    XASSERT_NONVOID(PropertiesPtr != XNULL);


    /*
     * Enter CFI query mode. The command is the same for all CFI enabled
     * devices. The command (0x98) is issued on an address compatible with
     * all devices.
     */
#ifndef XENV_UNITTEST
    switch (GeometryPtr->MemoryLayout)
    {
        case XFL_LAYOUT_X16_X16_X1:
            WRITE_FLASH_16(GeometryPtr->BaseAddress + 0xAA, 0x9898);
            break;

        case XFL_LAYOUT_X16_X16_X2:
            WRITE_FLASH_32(GeometryPtr->BaseAddress + 0xAA, 0x98989898);
            break;

        case XFL_LAYOUT_X16_X16_X4:
            WRITE_FLASH_64x2(GeometryPtr->BaseAddress + 0xAA, 0x98989898,
                             0x98989898);
            break;

        default:
            return (XST_FLASH_NOT_SUPPORTED);
    }
#endif

    /*
     * To stay consistent when retrieving the CFI data for all part layouts
     * we use the XFL_CFI_READ macros supplying the correct interleave based
     * on the layout.
     *
     * The size of a block for an instance is the block size reported
     * by the device multiplied by the number of devices (SizeMultiplier).
     */
    Interleave = (GeometryPtr->MemoryLayout & XFL_LAYOUT_CFI_INTERL_MASK) >> 24;
    SizeMultiplier = (GeometryPtr->MemoryLayout & XFL_LAYOUT_NUM_PARTS_MASK);

    /*
     * Begin reading the data. Each datum is documented in comments with
     * its offset range.
     */

    /*
     * 10h-12h : Contains the "QRY" string. Must be present
     */
    XFL_CFI_POSITION_PTR(Ptr, GeometryPtr->BaseAddress, Interleave, 0x10);
    DataQRY[0] = XFL_CFI_READ8(Ptr, Interleave);
    XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);
    DataQRY[1] = XFL_CFI_READ8(Ptr, Interleave);
    XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);
    DataQRY[2] = XFL_CFI_READ8(Ptr, Interleave);
    if ((DataQRY[0] != 'Q') || (DataQRY[1] != 'R') || (DataQRY[2] != 'Y'))
    {
        Status = XST_FLASH_CFI_QUERY_ERROR;
    }
    else
    {
        /*
         * 13h-14h : Primary vender command set
         */
        XFL_CFI_POSITION_PTR(Ptr, GeometryPtr->BaseAddress, Interleave, 0x13);
        XFL_CFI_READ16(Ptr, Interleave, PropertiesPtr->PartID.CommandSet);

        /*
         * 15h-1Ah : Vendor data to be interpreted by part (ignored here)
         * 1Bh-1Eh : Voltage requirements (ignored by this driver)
         *
         * Interpret the timing requirements starting here.
         * 1Fh     : Typical timeout for single byte/word program cycle (2^N Us)
         */
        XFL_CFI_POSITION_PTR(Ptr, GeometryPtr->BaseAddress, Interleave, 0x1F);
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        if (Data8 != 0)
        {
            PropertiesPtr->TimeTypical.WriteSingle_Us = 1 << Data8;
        }
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 20h     : Typical timeout for max buffer program cycle (2^N Us)
         *           = 0 if not supported
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        if (Data8 != 0)
        {
            PropertiesPtr->TimeTypical.WriteBuffer_Us = 1 << Data8;
        }
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 21h     : Typical timeout for single block erase (2^N Ms)
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        if (Data8 != 0)
        {
            PropertiesPtr->TimeTypical.EraseBlock_Ms = 1 << Data8;
        }
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 22h     : Typical timeout for full chip erase (2^N Ms)
         *           = 0 if not supported
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        if (Data8 != 0)
        {
            PropertiesPtr->TimeTypical.EraseChip_Ms = 1 << Data8;
        }
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 23h     : Maximum timeout for single byte/word program cycle
         *           (2^N * typical time)
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        PropertiesPtr->TimeMax.WriteSingle_Us =
            PropertiesPtr->TimeTypical.WriteSingle_Us * (1 << Data8);
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 24h     : Maximum timeout for max buffer program cycle
         *           (2^N * typical time)
         *           = 0 if not supported
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        PropertiesPtr->TimeMax.WriteBuffer_Us =
            PropertiesPtr->TimeTypical.WriteBuffer_Us * (1 << Data8);
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 25h     : Maximum timeout for single block erase
         *           (2^N * typical time)
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        PropertiesPtr->TimeMax.EraseBlock_Ms =
            PropertiesPtr->TimeTypical.EraseBlock_Ms * (1 << Data8);
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 26h     : Maximum timeout for full chip erase
         *           (2^N * typical time)
         *           = 0 if not supported
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        PropertiesPtr->TimeMax.EraseChip_Ms =
            PropertiesPtr->TimeTypical.EraseChip_Ms * (1 << Data8);
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 27h     : Device size in bytes
         *           = 2^N bytes * (Number of parts)
         */
        Data8 = XFL_CFI_READ8(Ptr, Interleave);
        GeometryPtr->DeviceSize = (1 << Data8) * SizeMultiplier;
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 28h-29h : Device interface description (ignored)
         */
        XFL_CFI_ADVANCE_PTR16(Ptr, Interleave);

        /*
         * 2Ah-2Bh : Maximum number of bytes in write buffer
         *           = 2^N bytes * (Number of parts)
         */
        XFL_CFI_READ16(Ptr, Interleave, Data16);
        if (Data16 != 0)
        {
            PropertiesPtr->ProgCap.WriteBufferSize = (1 << Data16) * SizeMultiplier;
        }
        XFL_CFI_ADVANCE_PTR16(Ptr, Interleave);

        /*
         * 2Ch     : Number of erase regions
         *           Make sure there are not too many to contain in the instance.
         *           This will ensure the for loop below doesn't go out of bounds
         *           on the Geometry.EraseRegion array.
         */
        GeometryPtr->NumEraseRegions = XFL_CFI_READ8(Ptr, Interleave);
        if (GeometryPtr->NumEraseRegions > XFL_MAX_ERASE_REGIONS)
        {
            return (XST_FLASH_TOO_MANY_REGIONS);
        }
        XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);

        /*
         * 2Dh-30h : Erase region #1 definition
         * 31h-34h : Erase region #2 definition
         * 35h-39h : Erase region #3 definition,  etc.....
         */
        CurrentAbsoluteOffset = 0;
        CurrentAbsoluteBlock = 0;
        for (i=0; i<GeometryPtr->NumEraseRegions; i++)
        {
            /*
             * Offset 0-1 : Number of blocks in the region
             *              = N + 1
             */
            XFL_CFI_READ16(Ptr, Interleave, Data16);
            GeometryPtr->EraseRegion[i].Number = Data16 + 1;
            GeometryPtr->NumBlocks += GeometryPtr->EraseRegion[i].Number;
            XFL_CFI_ADVANCE_PTR16(Ptr, Interleave);

            /*
             * Offset 2-3 : Size of erase blocks in the region
             *              = N * 256 * (Number of parts)
             */
            XFL_CFI_READ16(Ptr, Interleave, Data16);
            GeometryPtr->EraseRegion[i].Size = Data16 * 256 * SizeMultiplier;
            XFL_CFI_ADVANCE_PTR16(Ptr, Interleave);

            /*
             * Calculate the part offset where this region begins
             */
            GeometryPtr->EraseRegion[i].AbsoluteOffset = CurrentAbsoluteOffset;
            GeometryPtr->EraseRegion[i].AbsoluteBlock = CurrentAbsoluteBlock;

            /*
             * Increment absolute counters
             */
            CurrentAbsoluteOffset += (GeometryPtr->EraseRegion[i].Size *
                                      GeometryPtr->EraseRegion[i].Number);
            CurrentAbsoluteBlock += GeometryPtr->EraseRegion[i].Number;
        }

        /*
         * Set the absolute offsets for NumEraseRegions+1. This is not a real
         * region, but marks one unit past the part's addressable region. For
         * example, if the device(s) are a total of 1000 bytes in size with a
         * total of 10 blocks then 1000 and 10 are written to the Absolute
         * parameters. The Size & Number are left as zero.
         */
        GeometryPtr->EraseRegion[i].AbsoluteOffset = CurrentAbsoluteOffset;
        GeometryPtr->EraseRegion[i].AbsoluteBlock = CurrentAbsoluteBlock;

        /*
         * This ends the query. The following summarizes what attributes of the
         * InstancePtr were initialized:
         *
         *   Properties.PartID
         *     - CommandSet defined
         *     - ManufacturerID is defined by the part's Initialize function
         *     - DeviceID is defined by the part's Initialize function
         *
         *   Properties.TimeTypical
         *     Completely defined
         *
         *   Properties.TimeMax
         *     Completely defined
         *
         *   Properties.ProgCap
         *     - WriteBufferAlignment must be defined by the device. It defaults to
         *       0 here.
         *     - EraseQueueSize must be defined by the device. It defaults to 1
         *       here.
         *
         *   Geometry
         *     Completely defined
         *
         */
        PropertiesPtr->ProgCap.EraseQueueSize = 1;
    }

    /*
     * Exit CFI query mode.
     */
#ifndef XENV_UNITTEST
    switch (GeometryPtr->MemoryLayout)
    {
        case XFL_LAYOUT_X16_X16_X1:
            WRITE_FLASH_16(GeometryPtr->BaseAddress + 0xAA, 0xFFFF);
            break;

        case XFL_LAYOUT_X16_X16_X2:
            WRITE_FLASH_32(GeometryPtr->BaseAddress + 0xAA, 0xFFFFFFFF);
            break;

        case XFL_LAYOUT_X16_X16_X4:
            WRITE_FLASH_64x2(GeometryPtr->BaseAddress + 0xAA, 0xFFFFFFFF,
                             0xFFFFFFFF);
            break;
    }
#endif
    return (Status);
}
