//******************************************************************************
//* Driver for the LOGSYS SPI peripheral.                                      *
//* Written by   : Tamas Raikovich                                             *
//* Version      : 1.0                                                         *
//* Last modified: 2012.10.11.                                                 *
//******************************************************************************
#include <xparameters.h>
#include <stdio.h>
#include "spi.h"


//******************************************************************************
//* Macro for register access.                                                 *
//******************************************************************************
#define REG32(addr) (*(volatile unsigned long *)(addr))
#define REG16(addr) (*(volatile unsigned short *)(addr))
#define REG8(addr)  (*(volatile unsigned char *)(addr))


//******************************************************************************
//* Register addresses.                                                        *
//******************************************************************************
#define SPI_CONTROL_REG     (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x00)
#define SPI_STATUS_REG      (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x00)
#define SPI_CLKDIV_REG      (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x02)
#define SPI_TX_OCCUPANCY    (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x04)
#define SPI_RX_OCCUPANCY    (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x05)
#define SPI_INT_EN_REG      (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x06)
#define SPI_INT_FLAG_REG    (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x07)
#define SPI_DATA_REG        (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x08)
#define SPI_CRC_REG         (XPAR_LOGSYS_AXI_SPI_IF_0_BASEADDR + 0x0c)


//******************************************************************************
//* Bits of the SPI control/status register.                                   *
//******************************************************************************
//Slave select.
#define SPI_SEL_NONE        (0 << 0)
#define SPI_SEL_FLASH       (1 << 0)
#define SPI_SEL_LCD         (2 << 0)
#define SPI_SEL_SDCARD      (3 << 0)
#define SPI_SEL_MASK        (3 << 0)

//Transfer enable.
#define SPI_TRANSFER_EN		(1 << 2)

//CRC mode and source.
#define SPI_CRC7            (0 << 3)
#define SPI_CRC16           (1 << 3)
#define SPI_CRC_SRC_MOSI    (0 << 4)
#define SPI_CRC_SRC_MISO    (1 << 4)

//Store or discard the received data.
#define SPI_MISO_STORE		(0 << 5)
#define SPI_MISO_DISCARD	(1 << 5)

//FIFO clear.
#define SPI_TX_CLEAR		(1 << 6)
#define SPI_RX_CLEAR		(1 << 7)

//Status bits.
#define SPI_TX_NOT_FULL		(1 << 9)
#define SPI_TX_HALF_EMPTY	(1 << 10)
#define SPI_TX_EMPTY		(1 << 11)
#define SPI_RX_FULL			(1 << 12)
#define SPI_RX_HALF_FULL	(1 << 13)
#define SPI_RX_NOT_EMPTY	(1 << 14)
#define SPI_BUSY     		(1 << 15)


//******************************************************************************
//* Selects the given SPI peripheral.                                          *
//******************************************************************************
void SPISelect(unsigned long device, unsigned long clkFreq)
{
    volatile unsigned short tmp;

    tmp = REG16(SPI_CONTROL_REG) & ~SPI_SEL_MASK;

    switch (device)
    {
        case SPI_DEVICE_FLASH:
            tmp |= SPI_SEL_FLASH;
            break;
        case SPI_DEVICE_LCD:
            tmp |= SPI_SEL_LCD;
            break;
        case SPI_DEVICE_SDCARD:
            tmp |= SPI_SEL_SDCARD;
            break;
    }

    REG16(SPI_CLKDIV_REG) = ((XPAR_PROC_BUS_0_FREQ_HZ / 2) / clkFreq) - 1;
    REG16(SPI_CONTROL_REG) = tmp | SPI_TRANSFER_EN;

    for (tmp = 0; tmp < 8; tmp++);
}

void SPIDeselect()
{
    volatile unsigned long tmp;

    REG16(SPI_CONTROL_REG) &= ~SPI_SEL_MASK;
    for (tmp = 0; tmp < 8; tmp++);
}


//******************************************************************************
//* SPI single-byte transfer.                                                  *
//******************************************************************************
unsigned long SPIReadWriteByte(unsigned long data)
{
    REG16(SPI_DATA_REG) = data & 0x01ff;
    while (REG16(SPI_STATUS_REG) & SPI_BUSY);

    return REG16(SPI_DATA_REG) & 0x00ff;
}


//******************************************************************************
//* SPI multi-byte transfers.                                                  *
//******************************************************************************
void SPIWriteBytes(unsigned char *src, unsigned long size)
{
	volatile unsigned char tmp;

    while (size > 0)
    {
        REG8(SPI_DATA_REG) = *src;
        src++;
        size--;
        while (REG16(SPI_STATUS_REG) & SPI_BUSY);
        tmp = REG8(SPI_DATA_REG);
    }
}

void SPIReadBytes(unsigned char *dst, unsigned long size, unsigned char valueToSend)
{
    while (size > 0)
    {
        REG8(SPI_DATA_REG) = valueToSend;
        while (REG16(SPI_STATUS_REG) & SPI_BUSY);
        *dst = REG8(SPI_DATA_REG);
        dst++;
        size--;
    }
}


//******************************************************************************
//* CRC initialization.                                                        *
//******************************************************************************
void SPIInitCRC(unsigned long type, unsigned long src)
{
    unsigned long tmp;

    type = (type & 0x01) << 2;
    src = (src & 0x01) << 3;

    tmp = REG16(SPI_CONTROL_REG);
    tmp &= ~(SPI_CRC16 | SPI_CRC_SRC_MISO);
    tmp |= type | src;
    REG16(SPI_CONTROL_REG) = (unsigned short)tmp;
    REG16(SPI_CRC_REG) = 0x0000;
}


//******************************************************************************
//* CRC send.                                                                  *
//******************************************************************************
void SPISendCRC(unsigned long type)
{
    unsigned long crc;

    crc = REG16(SPI_CRC_REG);

    if (type == SPI_CRC_TYPE_CRC7)
    {
        crc = (crc << 1) | 0x01;
        SPIReadWriteByte(crc & 0xff);
    }
    else
    {
        SPIReadWriteByte((crc >> 8) & 0xff);
        SPIReadWriteByte(crc & 0xff);
    }
}


//******************************************************************************
//* CRC check.                                                                 *
//******************************************************************************
unsigned long SPICheckCRC16()
{
    SPIReadWriteByte(0xff);
    SPIReadWriteByte(0xff);

    return REG16(SPI_CRC_REG) & 0xffff;
}

