/*header soubory nutne pro spravnou funkci*/
#include "xparameters.h"	/* parametry -- generuje EDK */
#include "xintc.h"		/* Driver preruseni */
#include "xspi.h"		/* SPI driver */
#include "mb_interface.h"	/* Microblaze interface */

/*konstanty*/
#define SPI_DEVICE_ID			XPAR_SPI_0_DEVICE_ID
#define INTC_DEVICE_ID			XPAR_INTC_0_DEVICE_ID
#define SPI_INTR_ID			XPAR_INTC_0_SPI_0_VEC_ID

/* slave select*/
#define STM_SPI_SELECT 0x01

/*prikazy pro SPI*/
#define STM_COMMAND_RANDOM_READ		0x03 /* cteni */
#define STM_COMMAND_PAGEPROGRAM_WRITE	0x02 /* Page Program prikaz */
#define	STM_COMMAND_WRITE_ENABLE	0x06 /* Write Enable */
#define STM_COMMAND_SECTOR_ERASE	0xD8 /* Sector Erase */
#define STM_COMMAND_BULK_ERASE		0xC7 /* Bulk Erase */
#define STM_COMMAND_STATUSREG_READ	0x05 /* Status read */


/*specifikuje pocet posilanych bytu v prikazu pro SPI*/
#define STM_READ_WRITE_EXTRA_BYTES	4 /* pocet read/write extra bytu */
#define	STM_WRITE_ENABLE_BYTES		1 /* pocet prite Enable bytu */
#define STM_SECTOR_ERASE_BYTES		4 /* pocet sector erase bytu */
#define STM_BULK_ERASE_BYTES		1 /* pocet bulk erase bytu */
#define STM_STATUS_READ_BYTES		2 /* pocet status read bytu */
#define STM_STATUS_WRITE_BYTES		2 /* pocet status write bytu */

/*maska status registru SPI FLASH*/
#define STM_FLASH_SR_IS_READY_MASK	0x01 /* Ready mask */

/*Velikost stranky*/
#define STM_PAGE_SIZE		256

/*Adresa pro zapis do SPI FLASH*/
#define STM_FLASH_TEST_ADDRESS	0x00

/*Testovaci byte -- jde o vzorec*/
#define STM_FLASH_TEST_BYTE	0x20

/*Pozice bytu ve zprave SPI FLASH*/
#define BYTE1				0 /* 1 */
#define BYTE2				1 /* 2 */
#define BYTE3				2 /* 3 */
#define BYTE4				3 /* 4 */
#define BYTE5				4 /* 5 */

#define STM_DUMMYBYTE			0xFF /* Dummy byte  */

/*prototypy*/

int SpiStmFlashWriteEnable(XSpi *SpiPtr);
int SpiStmFlashWrite(XSpi *SpiPtr, u32 Addr, u32 ByteCount);
int SpiStmFlashRead(XSpi *SpiPtr, u32 Addr, u32 ByteCount);
int SpiStmFlashBulkErase(XSpi *SpiPtr);
int SpiStmFlashSectorErase(XSpi *SpiPtr, u32 Addr);
int SpiStmFlashGetStatus(XSpi *SpiPtr);
static int SpiStmFlashWaitForFlashNotBusy(void);
void SpiHandler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount);
static int SetupInterruptSystem(XSpi *SpiPtr);

/*globalni promenne*/
static XIntc InterruptController;
static XSpi Spi;

/*prommenna prenosu -- sdilena s presusenim*/
volatile static int TransferInProgress;

/*pocet chyb*/
int ErrorCount;

/*buffery pro cteni/zapis*/
u8 ReadBuffer[STM_PAGE_SIZE + STM_READ_WRITE_EXTRA_BYTES];
u8 WriteBuffer[STM_PAGE_SIZE + STM_READ_WRITE_EXTRA_BYTES];

int main()
{
	int Status;
	u32 Index;
	u32 Address;

	/*Inicializace SPI driveru*/
	Status = XSpi_Initialize(&Spi, SPI_DEVICE_ID);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*inicializuje driver preruseni a propoji ho s SPI driverem*/
	Status = SetupInterruptSystem(&Spi);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
	print("1");
	/*nastavi handler pro komunikaci mezi preruresim a SPI*/
	XSpi_SetStatusHandler(&Spi, &Spi, (XSpi_StatusHandler)SpiHandler);

	/*nastavi parametry driveru*/
	Status = XSpi_SetOptions(&Spi, XSP_MASTER_OPTION |
						XSP_MANUAL_SSELECT_OPTION);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*nastavi adresu slave selectu*/
	Status = XSpi_SetSlaveSelect(&Spi, STM_SPI_SELECT);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*start SPI driveru*/
	XSpi_Start(&Spi);
print("2");
	
	Address = STM_FLASH_TEST_ADDRESS;

	/*posle write enable*/
	Status = SpiStmFlashWriteEnable(&Spi);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*kontroluje konec operace na SPI*/
	Status = SpiStmFlashWaitForFlashNotBusy();
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*vysle prikaz Sector Erase -- 64kB*/
	Status = SpiStmFlashSectorErase(&Spi, Address);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
print("3");
	/*kontroluje konec operace na SPI*/
	Status = SpiStmFlashWaitForFlashNotBusy();
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*posle write enable*/
	Status = SpiStmFlashWriteEnable(&Spi);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*kontroluje konec operace na SPI*/
	Status = SpiStmFlashWaitForFlashNotBusy();
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
print("4");
	/*operace zapisu dat*/
	Status = SpiStmFlashWrite(&Spi, Address, STM_PAGE_SIZE);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*kontroluje konec operace na SPI*/
	Status = SpiStmFlashWaitForFlashNotBusy();
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*nulovani readbufferu*/
	for(Index = 0; Index < STM_PAGE_SIZE + STM_READ_WRITE_EXTRA_BYTES;
		Index++) {
		ReadBuffer[Index] = 0x0;
	}

	/*operace cteni*/
	Status = SpiStmFlashRead(&Spi, Address, STM_PAGE_SIZE);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
print("5");
	/*porovnani zapsanych a prectenych*/
	for(Index = 0; Index < STM_PAGE_SIZE; Index++) {
		if(ReadBuffer[Index + STM_READ_WRITE_EXTRA_BYTES] !=
					(u8)(Index + STM_FLASH_TEST_BYTE)) {
			return XST_FAILURE;
		}
	}
print("6");
	return XST_SUCCESS;
}

/*funkce pro poslani write enable*/
int SpiStmFlashWriteEnable(XSpi *SpiPtr)
{
	int Status;

	/*napln writebuffer*/
	WriteBuffer[BYTE1] = STM_COMMAND_WRITE_ENABLE;

	/*zacni prenos*/
	TransferInProgress = TRUE;
	Status = XSpi_Transfer(SpiPtr, WriteBuffer, NULL,
				STM_WRITE_ENABLE_BYTES);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*cekej do konce prenosu a kontroluj chyby*/
	while(TransferInProgress);
	if(ErrorCount != 0) {
		ErrorCount = 0;
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/*funkce pro zapis dat so SPI*/
int SpiStmFlashWrite(XSpi *SpiPtr, u32 Addr, u32 ByteCount)
{
	u32 Index;
	int Status;

	/*napln writebuffer*/
	WriteBuffer[BYTE1] = STM_COMMAND_PAGEPROGRAM_WRITE;
	WriteBuffer[BYTE2] = (u8) (Addr >> 16);
	WriteBuffer[BYTE3] = (u8) (Addr >> 8);
	WriteBuffer[BYTE4] = (u8) Addr;


	/*napln writebuffer testovacimi daty*/
	for(Index = 4; Index < ByteCount + STM_READ_WRITE_EXTRA_BYTES;
								Index++) {
		WriteBuffer[Index] = (u8)((Index - 4) + STM_FLASH_TEST_BYTE);
	}

	/*zacni prenos*/
	TransferInProgress = TRUE;
	Status = XSpi_Transfer(SpiPtr, WriteBuffer, NULL,
				(ByteCount + STM_READ_WRITE_EXTRA_BYTES));
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*cekej do konce prenosu a kontroluj chyby*/
	while(TransferInProgress);
	if(ErrorCount != 0) {
		ErrorCount = 0;
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/*funkce pro ctemi z flash*/
int SpiStmFlashRead(XSpi *SpiPtr, u32 Addr, u32 ByteCount)
{
	int Status;

	/*napln writebuffer*/
	WriteBuffer[BYTE1] = STM_COMMAND_RANDOM_READ;
	WriteBuffer[BYTE2] = (u8) (Addr >> 16);
	WriteBuffer[BYTE3] = (u8) (Addr >> 8);
	WriteBuffer[BYTE4] = (u8) Addr;

	/*zacni prenos*/
	TransferInProgress = TRUE;
	Status = XSpi_Transfer( SpiPtr, WriteBuffer, ReadBuffer,
				(ByteCount + STM_READ_WRITE_EXTRA_BYTES));
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*cekej do konce prenosu a kontroluj chyby*/
	while(TransferInProgress);
	if(ErrorCount != 0) {
		ErrorCount = 0;
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/*funkce pro vymazani cele FLASH*/
int SpiStmFlashBulkErase(XSpi *SpiPtr)
{
	int Status;

	/*napln writebuffer*/
	WriteBuffer[BYTE1] = STM_COMMAND_BULK_ERASE;

	/*zacni prenos*/
	TransferInProgress = TRUE;
	Status = XSpi_Transfer(SpiPtr, WriteBuffer, NULL,
						STM_BULK_ERASE_BYTES);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*cekej do konce prenosu a kontroluj chyby*/
	while(TransferInProgress);
	if(ErrorCount != 0) {
		ErrorCount = 0;
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/*funkce pro vymazani sectoru*/
int SpiStmFlashSectorErase(XSpi *SpiPtr, u32 Addr)
{
	int Status;

	/*napln writebuffer*/
	WriteBuffer[BYTE1] = STM_COMMAND_SECTOR_ERASE;
	WriteBuffer[BYTE2] = (u8) (Addr >> 16);
	WriteBuffer[BYTE3] = (u8) (Addr >> 8);
	WriteBuffer[BYTE4] = (u8) (Addr);

	/*zacni prenos*/
	TransferInProgress = TRUE;
	Status = XSpi_Transfer(SpiPtr, WriteBuffer, NULL,
					STM_SECTOR_ERASE_BYTES);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*cekej do konce prenosu a kontroluj chyby*/
	while(TransferInProgress);
	if(ErrorCount != 0) {
		ErrorCount = 0;
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/*funkce pro cteni status registru SPI*/
int SpiStmFlashGetStatus(XSpi *SpiPtr)
{
	int Status;

	/*priprav buffer*/
	WriteBuffer[BYTE1] = STM_COMMAND_STATUSREG_READ;

	/*zacni prenos*/
	TransferInProgress = TRUE;
	Status = XSpi_Transfer(SpiPtr, WriteBuffer, ReadBuffer,
						STM_STATUS_READ_BYTES);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*cekej do konce prenosu a kontroluj chyby*/
	while(TransferInProgress);
	if(ErrorCount != 0) {
		ErrorCount = 0;
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/*funkce pro kontrolu konce operace*/
int SpiStmFlashWaitForFlashNotBusy(void)
{
	int Status;
	u8 StatusReg;

	while(1) {

		/*precti status registr SPI a cykly dokud nebudes moci vysilat znovu*/
		Status = SpiStmFlashGetStatus(&Spi);
		if(Status != XST_SUCCESS) {
			return XST_FAILURE;
		}

		/*vyskoci ze smycky, pokud uz znova muzes vysilat*/
		StatusReg = ReadBuffer[1];
		if((StatusReg & STM_FLASH_SR_IS_READY_MASK) == 0) {
			break;
		}
	}

	return XST_SUCCESS;
}

/*funkce handleru -- vola se pri ukonceni operace*/
void SpiHandler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount)
{
	/*indikuje ukonceni procesu -- */
	TransferInProgress = FALSE;

	/* -- jinak error*/
	if (StatusEvent != XST_SPI_TRANSFER_DONE) {
		ErrorCount++;
	}
}

/*funkce pro nastaveni systemu preruseni a propojeni s SPI*/
static int SetupInterruptSystem(XSpi *SpiPtr)
{

	int Status;

	/*inicializuje radice preruseni*/
	Status = XIntc_Initialize(&InterruptController, INTC_DEVICE_ID);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*propoji radic preruseni s SPI*/
	Status = XIntc_Connect(&InterruptController,
				SPI_INTR_ID,
				(XInterruptHandler)XSpi_InterruptHandler,
				(void *)SpiPtr);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*start radice preruseni*/
	Status = XIntc_Start(&InterruptController, XIN_REAL_MODE);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*povoli preruseni pro SPI driver*/
	XIntc_Enable(&InterruptController, SPI_INTR_ID);

	/*povoli preruseni od procesoru*/
	microblaze_enable_interrupts();

	return XST_SUCCESS;
}
 
 