/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
 */



#include "spi_flash/spi_flash.h"
#include "spi_flash/fsl_spi_abstraction.h"
#include "spi_flash_internal.h"
#include "fsl_misc_utilities.h"
#include <stdio.h>

/*!
 * @name DataFlash family IDs, as obtained from the second idcode byte
 * @{
 */
#define DF_FAMILY_AT26F			0
#define DF_FAMILY_AT45			1
#define DF_FAMILY_AT26DF		2    /* AT25DF and AT26DF */
/*!
 * @}
 */

/*!
 * @brief Atmel SPI flash configuration parameters.
 *
 * This structure holds special parameters for atmel spi flash.
 */
typedef struct AtmelSpiFlashParams
{
    uint8_t        idcode1;
    /* Log2 of page size in power-of-two mode */
    uint8_t        l2_page_size;
    uint8_t        pages_per_block;
    uint8_t        blocks_per_sector;
    uint8_t        nr_sectors;
    const char     *name;
} atmel_spi_flash_params;

/*!
 * @brief Atmel SPI flash instance.
 *
 * This structure holds two structure. One is spi_flash, which is common
 * for all spi flash chips and will be used in spi flash framework.
 * Another is atmel_spi_flash_params, which is atmel flash's special data.
 * Note that spi_flash needs to be first so upper layers can access and free it.
 */
typedef struct AtmelSpiFlash
{
    spi_flash flash;
    const atmel_spi_flash_params *params;
} atmel_spi_flash;

static atmel_spi_flash atmel_sf;

/*!
 * @brief Each atmel spi flashs' parameters.
 */
static const atmel_spi_flash_params atmel_spi_flash_table[] =
{
#ifdef SPI_FLASH_AT26_FAMILY
    {
        .idcode1        = 0x45,
        .l2_page_size        = 8,
        .pages_per_block    = 128,
        .blocks_per_sector    = 2,
        .nr_sectors        = 16,
        .name            = "AT26DF081A",
    },
#endif
};

/*!
 * @brief    Probe a atmel spi flash.
 *
 * @param[in] instance     spi master bus index.
 * @param[in] spi          spi slave device.
 * @param[in] idcode       idcode of flash.
 *
 * @return    status       error code.
 */
spi_flash *spi_flash_probe_atmel(uint32_t instance, spi_slave_dev *spi, uint8_t *idcode)
{
    const atmel_spi_flash_params *params;
    unsigned page_size;
    unsigned int family;
    atmel_spi_flash *asf = &atmel_sf;
    unsigned int i;

    for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++)
    {
        params = &atmel_spi_flash_table[i];
        if (params->idcode1 == idcode[1])
        {
            break;
        }
    }

    if (i == ARRAY_SIZE(atmel_spi_flash_table))
    {
        printf("SF: Unsupported DataFlash ID %02x\r\n",
                idcode[1]);
        return NULL;
    }

    spi_flash_alloc(sf_offsetof(atmel_spi_flash, flash), sizeof(atmel_spi_flash), asf, spi, params->name);
    asf->params = params;

    /* Assuming power-of-two page size initially. */
    page_size = 1 << params->l2_page_size;

    family = idcode[1] >> 5;

    switch (family)
    {
#ifdef SPI_FLASH_AT26_FAMILY
    case DF_FAMILY_AT26F:
    case DF_FAMILY_AT26DF:
        asf->flash.page_size = page_size;
        asf->flash.sector_size = 4096;
        /* clear SPRL# bit for locked flash */
        spi_flash_cmd_write_status(&asf->flash, 0);
        break;
#endif
    default:
        printf("SF: Unsupported DataFlash family %u\r\n", family);
        return NULL;
    }

    asf->flash.size = page_size * params->pages_per_block
                * params->blocks_per_sector
                * params->nr_sectors;

    return &asf->flash;
}

/*******************************************************************************
 * EOF
 ******************************************************************************/
