/*****************************************************************************
 *
 * \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-2011 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * 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"

#ifdef FREERTOS_USED

#include "FreeRTOS.h"
#include "semphr.h"

#endif


/*! \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.
static xSemaphoreHandle xSPIMutex;

#endif


int16_t getBaudDiv(const unsigned int baudrate, uint32_t pb_hz)
{
  int baudDiv = div_ceil((pb_hz + baudrate / 2), baudrate);

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

  return baudDiv;
}

spi_status_t spi_initSlave(volatile avr32_spi_t *spi,
                           unsigned char bits,
                           unsigned char 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,
                               unsigned char variable_ps,
                               unsigned char pcs_decode,
                               unsigned char 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, unsigned char 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, unsigned char chip)
{
  unsigned int 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 deassert 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;
}


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

unsigned char 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)
{
  unsigned int 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)
{
  unsigned int 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;
}


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


unsigned char 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, unsigned short *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;
}


unsigned char spi_getStatus(volatile avr32_spi_t *spi)
{
  spi_status_t ret = SPI_OK;
  unsigned long 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;
  }
}
