/*****************************************************************************
 *
 * \file
 *
 * \brief SPI driver for AVR32 UC3.
 *
 * This file defines a useful set of functions for the SPI interface on AVR32
 * devices.
 *
 * Copyright (c) 2009-2012 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 "spi.h"

/** \name SPI Writable Bit-Field Registers */
/** @{ */

typedef union {
	unsigned long cr;
	avr32_spi_cr_t CR;
} u_avr32_spi_cr_t;

typedef union {
	unsigned long mr;
	avr32_spi_mr_t MR;
} u_avr32_spi_mr_t;

typedef union {
	unsigned long tdr;
	avr32_spi_tdr_t TDR;
} u_avr32_spi_tdr_t;

typedef union {
	unsigned long ier;
	avr32_spi_ier_t IER;
} u_avr32_spi_ier_t;

typedef union {
	unsigned long idr;
	avr32_spi_idr_t IDR;
} u_avr32_spi_idr_t;

typedef union {
	unsigned long csr;
	avr32_spi_csr0_t CSR;
} u_avr32_spi_csr_t;

/** @} */

#ifdef FREERTOS_USED
/** The SPI mutex. */
xSemaphoreHandle xSPIMutex;
#endif

int16_t getBaudDiv(const uint32_t baudrate, uint32_t pb_hz)
{
	uint32_t baudDiv = div_ceil(pb_hz, baudrate);

	if (baudDiv <= 0 || baudDiv > 255) {
		return -1;
	}

	return baudDiv;
}

spi_status_t spi_initSlave(volatile avr32_spi_t *spi,
		uint8_t bits,
		uint8_t spi_mode)
{
	if (spi_mode > 3 ||
			bits < 8 || bits > 16) {
		return SPI_ERROR_ARGUMENT;
	}

	/* Reset. */
	spi->cr = AVR32_SPI_CR_SWRST_MASK;

	/* Will use CSR0 offsets; these are the same for CSR0 to CSR3. */
	spi->csr0 = ((spi_mode >> 1) << AVR32_SPI_CSR0_CPOL_OFFSET) |
			(((spi_mode &
			0x1) ^ 0x1) << AVR32_SPI_CSR0_NCPHA_OFFSET) |
			((bits - 8) << AVR32_SPI_CSR0_BITS_OFFSET);

	return SPI_OK;
}

spi_status_t spi_initTest(volatile avr32_spi_t *spi)
{
	/* Reset. */
	spi->cr = AVR32_SPI_CR_SWRST_MASK;
	spi->mr |= AVR32_SPI_MR_MSTR_MASK | /* Master Mode. */
			AVR32_SPI_MR_LLB_MASK; /* Local Loopback. */

	return SPI_OK;
}

spi_status_t spi_initMaster(volatile avr32_spi_t *spi,
		const spi_options_t *options)
{
	u_avr32_spi_mr_t u_avr32_spi_mr;

	if (options->modfdis > 1) {
		return SPI_ERROR_ARGUMENT;
	}

	/* Reset. */
	spi->cr = AVR32_SPI_CR_SWRST_MASK;

	/* Master Mode. */
	u_avr32_spi_mr.mr = spi->mr;
	u_avr32_spi_mr.MR.mstr = 1;
	u_avr32_spi_mr.MR.modfdis = options->modfdis;
	u_avr32_spi_mr.MR.llb = 0;
	u_avr32_spi_mr.MR.pcs = (1 << AVR32_SPI_MR_PCS_SIZE) - 1;
	spi->mr = u_avr32_spi_mr.mr;

	return SPI_OK;
}

spi_status_t spi_selectionMode(volatile avr32_spi_t *spi,
		uint8_t variable_ps,
		uint8_t pcs_decode,
		uint8_t delay)
{
	u_avr32_spi_mr_t u_avr32_spi_mr;

	if (variable_ps > 1 ||
			pcs_decode > 1) {
		return SPI_ERROR_ARGUMENT;
	}

	u_avr32_spi_mr.mr = spi->mr;
	u_avr32_spi_mr.MR.ps = variable_ps;
	u_avr32_spi_mr.MR.pcsdec = pcs_decode;
	u_avr32_spi_mr.MR.dlybcs = delay;
	spi->mr = u_avr32_spi_mr.mr;

	return SPI_OK;
}

spi_status_t spi_selectChip(volatile avr32_spi_t *spi, uint8_t chip)
{
#ifdef FREERTOS_USED
	while (pdFALSE == xSemaphoreTake(xSPIMutex, 20)) {
	}
#endif

	/* Assert all lines; no peripheral is selected. */
	spi->mr |= AVR32_SPI_MR_PCS_MASK;

	if (spi->mr & AVR32_SPI_MR_PCSDEC_MASK) {
		/* The signal is decoded; allow up to 15 chips. */
		if (chip > 14) {
			return SPI_ERROR_ARGUMENT;
		}

		spi->mr &= ~AVR32_SPI_MR_PCS_MASK |
				(chip << AVR32_SPI_MR_PCS_OFFSET);
	} else {
		if (chip > 3) {
			return SPI_ERROR_ARGUMENT;
		}

		spi->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + chip));
	}

	return SPI_OK;
}

spi_status_t spi_unselectChip(volatile avr32_spi_t *spi, uint8_t chip)
{
	uint32_t timeout = SPI_TIMEOUT;

	while (!(spi->sr & AVR32_SPI_SR_TXEMPTY_MASK)) {
		if (!timeout--) {
			return SPI_ERROR_TIMEOUT;
		}
	}

	/* Assert all lines; no peripheral is selected. */
	spi->mr |= AVR32_SPI_MR_PCS_MASK;

	/* Last transfer, so de-assert the current NPCS if CSAAT is set. */
	spi->cr = AVR32_SPI_CR_LASTXFER_MASK;

#ifdef FREERTOS_USED
	xSemaphoreGive(xSPIMutex);
#endif

	return SPI_OK;
}

spi_status_t spi_setupChipReg(volatile avr32_spi_t *spi,
		const spi_options_t *options,
		uint32_t pb_hz)
{
	u_avr32_spi_csr_t u_avr32_spi_csr;

	if (options->spi_mode > 3 ||
			options->stay_act > 1 ||
			options->bits < 8 || options->bits > 16) {
		return SPI_ERROR_ARGUMENT;
	}

	int baudDiv = getBaudDiv(options->baudrate, pb_hz);

	if (baudDiv < 0) {
		return SPI_ERROR_ARGUMENT;
	}

	/* Will use CSR0 offsets; these are the same for CSR0 to CSR3. */
	u_avr32_spi_csr.csr = 0;
	u_avr32_spi_csr.CSR.cpol   = options->spi_mode >> 1;
	u_avr32_spi_csr.CSR.ncpha  = (options->spi_mode & 0x1) ^ 0x1;
	u_avr32_spi_csr.CSR.csaat  = options->stay_act;
	u_avr32_spi_csr.CSR.bits   = options->bits - 8;
	u_avr32_spi_csr.CSR.scbr   = baudDiv;
	u_avr32_spi_csr.CSR.dlybs  = options->spck_delay;
	u_avr32_spi_csr.CSR.dlybct = options->trans_delay;

	switch (options->reg) {
	case 0:
		spi->csr0 = u_avr32_spi_csr.csr;
		break;

	case 1:
		spi->csr1 = u_avr32_spi_csr.csr;
		break;

	case 2:
		spi->csr2 = u_avr32_spi_csr.csr;
		break;

	case 3:
		spi->csr3 = u_avr32_spi_csr.csr;
		break;

	default:
		return SPI_ERROR_ARGUMENT;
	}

#ifdef FREERTOS_USED
	if (!xSPIMutex) {
		/* Create the SPI mutex. */
		vSemaphoreCreateBinary(xSPIMutex);
		if (!xSPIMutex) {
			while (1) {
			}
		}
	}
#endif

	return SPI_OK;
}

void spi_enable(volatile avr32_spi_t *spi)
{
	spi->cr = AVR32_SPI_CR_SPIEN_MASK;
}

void spi_disable(volatile avr32_spi_t *spi)
{
	spi->cr = AVR32_SPI_CR_SPIDIS_MASK;
}

bool spi_is_enabled(volatile avr32_spi_t *spi)
{
	return (spi->sr & AVR32_SPI_SR_SPIENS_MASK) != 0;
}

uint8_t spi_writeRegisterEmptyCheck(volatile avr32_spi_t *spi)
{
	return ((spi->sr & AVR32_SPI_SR_TDRE_MASK) != 0);
}

spi_status_t spi_write(volatile avr32_spi_t *spi, uint16_t data)
{
	uint32_t timeout = SPI_TIMEOUT;

	while (!(spi->sr & AVR32_SPI_SR_TDRE_MASK)) {
		if (!timeout--) {
			return SPI_ERROR_TIMEOUT;
		}
	}

	spi->tdr = data << AVR32_SPI_TDR_TD_OFFSET;

	return SPI_OK;
}

spi_status_t spi_variableSlaveWrite(volatile avr32_spi_t *spi, uint16_t data,
		uint8_t pcs, uint8_t lastxfer)
{
	uint32_t timeout = SPI_TIMEOUT;

	if (pcs > 14 || lastxfer > 1) {
		return SPI_ERROR_ARGUMENT;
	}

	while (!(spi->sr & AVR32_SPI_SR_TDRE_MASK)) {
		if (!timeout--) {
			return SPI_ERROR_TIMEOUT;
		}
	}

	spi->tdr = (data << AVR32_SPI_TDR_TD_OFFSET) |
			(pcs << AVR32_SPI_TDR_PCS_OFFSET) |
			(lastxfer << AVR32_SPI_TDR_LASTXFER_OFFSET);

	return SPI_OK;
}

uint8_t spi_writeEndCheck(volatile avr32_spi_t *spi)
{
	return ((spi->sr & AVR32_SPI_SR_TXEMPTY_MASK) != 0);
}

uint8_t spi_readRegisterFullCheck(volatile avr32_spi_t *spi)
{
	return ((spi->sr & AVR32_SPI_SR_RDRF_MASK) != 0);
}

spi_status_t spi_read(volatile avr32_spi_t *spi, uint16_t *data)
{
	unsigned int timeout = SPI_TIMEOUT;

	while ((spi->sr &
			(AVR32_SPI_SR_RDRF_MASK | AVR32_SPI_SR_TXEMPTY_MASK)) !=
			(AVR32_SPI_SR_RDRF_MASK | AVR32_SPI_SR_TXEMPTY_MASK)) {
		if (!timeout--) {
			return SPI_ERROR_TIMEOUT;
		}
	}

	*data = spi->rdr >> AVR32_SPI_RDR_RD_OFFSET;

	return SPI_OK;
}

uint8_t spi_getStatus(volatile avr32_spi_t *spi)
{
	spi_status_t ret = SPI_OK;
	uint32_t sr = spi->sr;

	if (sr & AVR32_SPI_SR_OVRES_MASK) {
		ret = SPI_ERROR_OVERRUN;
	}

	if (sr & AVR32_SPI_SR_MODF_MASK) {
		ret += SPI_ERROR_MODE_FAULT;
	}

	if (ret == (SPI_ERROR_OVERRUN + SPI_ERROR_MODE_FAULT)) {
		return SPI_ERROR_OVERRUN_AND_MODE_FAULT;
	} else if (ret > 0) {
		return ret;
	} else {
		return SPI_OK;
	}
}
