/**
 * \file
 *
 * \brief Static Memory Controller (SMC) driver for SAM.
 *
 * Copyright (c) 2011-2013 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * 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
 * EXPRESSLY AND SPECIFICALLY 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.
 *
 * \asf_license_stop
 *
 */

#include "smc.h"
#include "board.h"

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
extern "C" {
#endif
/**INDENT-ON**/
/// @endcond

/**
 * \defgroup sam_drivers_smc_group Static Memory Controller (SMC)
 *
 * Driver for the Static Memory Controller. It provides functions for configuring
 * and using the on-chip SMC.
 *
 * @{
 */

#if ((SAM3S) || (SAM3U) || (SAM3XA) || (SAM4S) || (SAM4E))
#define SMC_WPKEY_VALUE (0x534D43)
/**
 * \brief Configure the SMC Setup timing for the specified Chip Select.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_cs Chip Select number to be set.
 * \param ul_setup_timing Setup timing for NWE, NCS, NRD.
 */
void smc_set_setup_timing(Smc *p_smc, uint32_t ul_cs,
		uint32_t ul_setup_timing)
{
	p_smc->SMC_CS_NUMBER[ul_cs].SMC_SETUP = ul_setup_timing;
}

/**
 * \brief Configure the SMC pulse timing for the specified Chip Select.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_cs Chip Select number to be set.
 * \param ul_pulse_timing Pulse timing for NWE,NCS,NRD.
 */
void smc_set_pulse_timing(Smc *p_smc, uint32_t ul_cs,
		uint32_t ul_pulse_timing)
{
	p_smc->SMC_CS_NUMBER[ul_cs].SMC_PULSE = ul_pulse_timing;
}

/**
 * \brief Configure the SMC cycle timing for the specified Chip Select.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_cs Chip Select number to be set.
 * \param ul_cycle_timing Cycle timing for NWE and NRD.
 */
void smc_set_cycle_timing(Smc *p_smc, uint32_t ul_cs,
		uint32_t ul_cycle_timing)
{
	p_smc->SMC_CS_NUMBER[ul_cs].SMC_CYCLE = ul_cycle_timing;
}

/**
 * \brief Configure the SMC mode for the specified Chip Select.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_cs Chip select number to be set.
 * \param ul_mode SMC mode.
 */
void smc_set_mode(Smc *p_smc, uint32_t ul_cs, uint32_t ul_mode)
{
	p_smc->SMC_CS_NUMBER[ul_cs].SMC_MODE = ul_mode;
}

/**
 * \brief Get the SMC mode of the specified Chip Select.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_cs Chip select number to be set.
 *
 * \return SMC mode.
 */
uint32_t smc_get_mode(Smc *p_smc, uint32_t ul_cs)
{
	return p_smc->SMC_CS_NUMBER[ul_cs].SMC_MODE;
}

/**
 * \brief Set write protection of SMC registers.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_enable 1 to enable, 0 to disable.
 */
void smc_enable_writeprotect(Smc *p_smc, uint32_t ul_enable)
{
#if (SAM3S || SAM4S || SAM4E)
	if (ul_enable)
		p_smc->SMC_WPMR =
				SMC_WPMR_WPKEY(SMC_WPKEY_VALUE) | SMC_WPMR_WPEN;
	else
		p_smc->SMC_WPMR = SMC_WPMR_WPKEY(SMC_WPKEY_VALUE);
#else
	if (ul_enable)
		p_smc->SMC_WPCR =
				SMC_WPCR_WP_KEY(SMC_WPKEY_VALUE) |
				SMC_WPCR_WP_EN;
	else
		p_smc->SMC_WPCR = SMC_WPCR_WP_KEY(SMC_WPKEY_VALUE);
#endif
}

/**
 * \brief Get the status of SMC write protection register.
 *
 * \param p_smc Pointer to an SMC instance.
 *
 * \return Write protect status.
 */
uint32_t smc_get_writeprotect_status(Smc *p_smc)
{
	return p_smc->SMC_WPSR;
}
#endif /* ((SAM3S) || (SAM3U) || (SAM3XA)) */

#if ((SAM3U) || (SAM3XA))
/**
 * \brief Configure the SMC nand timing for the specified Chip Select.
 * \param p_smc Pointer to an SMC instance.
 * \param ul_cs Chip Select number to be set.
 * \param ul_nand_timing nand timing for related signal.
 */
void smc_set_nand_timing(Smc *p_smc, uint32_t ul_cs,
		uint32_t ul_nand_timing)
{
	p_smc->SMC_CS_NUMBER[ul_cs].SMC_TIMINGS= ul_nand_timing;
}

/**
 * \brief Initialize NFC configuration.
 * \param p_smc Pointer to an SMC instance.
 * \param ul_config SMC NFC Configuration.
 */
void smc_nfc_init(Smc *p_smc, uint32_t ul_config)
{
	p_smc->SMC_CFG = ul_config;
}

/**
 * \brief Set NFC page size.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_page_size Use pattern defined in the device header file.
 */
void smc_nfc_set_page_size(Smc *p_smc, uint32_t ul_page_size)
{
	p_smc->SMC_CFG &= (~SMC_CFG_PAGESIZE_Msk);
	p_smc->SMC_CFG |= ul_page_size;
}

/**
 * \brief Enable NFC controller to read both main and spare area in read mode.
 *
 * \param p_smc Pointer to an SMC instance.
 */
void smc_nfc_enable_spare_read(Smc *p_smc)
{
	p_smc->SMC_CFG |= SMC_CFG_RSPARE;
}

/**
 * \brief Prevent NFC controller from reading the spare area in read mode.
 *
 * \param p_smc Pointer to an SMC instance.
 */
void smc_nfc_disable_spare_read(Smc *p_smc)
{
	p_smc->SMC_CFG &= (~SMC_CFG_RSPARE);
}

/**
 * \brief Enable NFC controller to write both main and spare area in write mode.
 *
 * \param p_smc Pointer to an SMC instance.
 */
void smc_nfc_enable_spare_write(Smc *p_smc)
{
	p_smc->SMC_CFG |= SMC_CFG_WSPARE;
}

/**
 * \brief Prevent NFC controller from writing the spare area in read mode.
 *
 * \param p_smc Pointer to an SMC instance.
 */
void smc_nfc_disable_spare_write(Smc *p_smc)
{
	p_smc->SMC_CFG &= (~SMC_CFG_WSPARE);
}

/**
 * \brief Enable NFC controller.
 *
 * \param p_smc Pointer to an SMC instance.
 */
void smc_nfc_enable(Smc *p_smc)
{
	p_smc->SMC_CTRL = SMC_CTRL_NFCEN;
}

/**
 * \brief Disable NFC controller.
 *
 * \param p_smc Pointer to an SMC instance.
 */
void smc_nfc_disable(Smc *p_smc)
{
	p_smc->SMC_CTRL = SMC_CTRL_NFCDIS;
}

/**
 * \brief Get the NFC Status.
 *
 * \param p_smc Pointer to an SMC instance.
 *
 * \return Returns the current status register of SMC NFC Status Register.
 * This resets the internal value of the status register, so further
 * read may yield different values.
 */
uint32_t smc_nfc_get_status(Smc *p_smc)
{
	return p_smc->SMC_SR;
}

/**
 * \brief Enable SMC interrupts.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_sources Interrupt source bitmap.
 */
void smc_nfc_enable_interrupt(Smc *p_smc, uint32_t ul_sources)
{
	p_smc->SMC_IER = ul_sources;
}

/**
 * \brief Disable SMC interrupts.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_sources Interrupt source bitmap.
 */
void smc_nfc_disable_interrupt(Smc *p_smc, uint32_t ul_sources)
{
	p_smc->SMC_IDR = ul_sources;
}

/**
 * \brief Get the interrupt mask.
 *
 * \param p_smc Pointer to an SMC instance.
 *
 * \return Interrupt mask bitmap.
 */
uint32_t smc_nfc_get_interrupt_mask(Smc *p_smc)
{
	return p_smc->SMC_IMR;
}

/**
 * \brief Set flash cycle 0 address.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param uc_address0 Address cycle 0 in 5 address cycles.
 */
void smc_nfc_set_address0(Smc *p_smc, uint8_t uc_address0)
{
	p_smc->SMC_ADDR = uc_address0;
}

/**
 * \brief Set NFC sram bank.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_bank NFC sram bank.
 */
void smc_nfc_set_bank(Smc *p_smc, uint32_t ul_bank)
{
	p_smc->SMC_BANK = SMC_BANK_BANK(ul_bank);
}

/**
 * \brief Use the HOST nandflash controller to send a command.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_cmd Command to send.
 * \param ul_address_cycle Address cycle when command access is decoded.
 * \param ul_cycle0 Address at first cycle.
 */
void smc_nfc_send_command(Smc *p_smc, uint32_t ul_cmd,
		uint32_t ul_address_cycle, uint32_t ul_cycle0)
{
	volatile uint32_t *p_command_address;

	/* Wait until host controller is not busy. */
	while (((*((volatile uint32_t *)(BOARD_NF_DATA_ADDR + NFCADDR_CMD_NFCCMD)))
			& NFC_BUSY_FLAG) == NFC_BUSY_FLAG) {
	}
	/* Send the command plus the ADDR_CYCLE. */
	p_command_address = (volatile uint32_t *)(ul_cmd + BOARD_NF_DATA_ADDR);
	p_smc->SMC_ADDR = ul_cycle0;
	*p_command_address = ul_address_cycle;
	while (!((p_smc->SMC_SR & SMC_SR_CMDDONE) == SMC_SR_CMDDONE)) {
	}
}

/**
 * \brief Initialize ECC mode.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_type Type of correction, use pattern defined in the device header file.
 * \param ul_pagesize Page size of NAND Flash device, use pattern defined in
 * the device header file.
 */
void smc_ecc_init(Smc *p_smc, uint32_t ul_type, uint32_t ul_pagesize)
{
	/* Software Reset ECC. */
	p_smc->SMC_ECC_CTRL = SMC_ECC_CTRL_SWRST;
	p_smc->SMC_ECC_MD = ul_type | ul_pagesize;
}

/**
 * \brief Get ECC status by giving ecc number.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param ul_parity_number ECC parity number from 0 to 15.
 *
 * \return ECC status by giving ECC number.
 */
uint32_t smc_ecc_get_status(Smc *p_smc, uint32_t ul_parity_number)
{
	uint32_t status;

	if (ul_parity_number < 8) {
		status = p_smc->SMC_ECC_SR1;
	} else {
		status = p_smc->SMC_ECC_SR2;
		ul_parity_number -= 8;
	}

	return ((status >> (ul_parity_number * 4)) & ECC_STATUS_MASK);
}

/**
 * \brief Get all ECC parity registers value.
 *
 * \param p_smc Pointer to an SMC instance.
 * \param p_ecc Pointer to a parity buffer.
 */
void smc_ecc_get_value(Smc *p_smc, uint32_t *p_ecc)
{
	p_ecc[0] = p_smc->SMC_ECC_PR0;
	p_ecc[1] = p_smc->SMC_ECC_PR1;
	p_ecc[2] = p_smc->SMC_ECC_PR2;
	p_ecc[3] = p_smc->SMC_ECC_PR3;
	p_ecc[4] = p_smc->SMC_ECC_PR4;
	p_ecc[5] = p_smc->SMC_ECC_PR5;
	p_ecc[6] = p_smc->SMC_ECC_PR6;
	p_ecc[7] = p_smc->SMC_ECC_PR7;
	p_ecc[8] = p_smc->SMC_ECC_PR8;
	p_ecc[9] = p_smc->SMC_ECC_PR9;
	p_ecc[10] = p_smc->SMC_ECC_PR10;
	p_ecc[11] = p_smc->SMC_ECC_PR11;
	p_ecc[12] = p_smc->SMC_ECC_PR12;
	p_ecc[13] = p_smc->SMC_ECC_PR13;
	p_ecc[14] = p_smc->SMC_ECC_PR14;
	p_ecc[15] = p_smc->SMC_ECC_PR15;
}
#endif /* ((SAM3U) || (SAM3XA)) */

//@}

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
}
#endif
/**INDENT-ON**/
/// @endcond
