/*
 * uncached_memory.c
 *
 * This module will declare 1 MB of memory and switch off the caching for it.
 *
 * pucGetUncachedMemory( ulSize ) returns a trunc of this memory with a length
 * rounded up to a multiple of 4 KB
 *
 * ucIsCachedMemory( pucBuffer ) returns non-zero if a given pointer is NOT
 * within the range of the 1 MB non-cached memory.
 *
 */

/*
 * After "_end", 1 MB of uncached memory will be allocated for DMA transfers.
 * Both the DMA descriptors as well as all EMAC TX-buffers will be allocated in
 * uncached memory.
 */

/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"

#include "Zynq/x_emacpsif.h"
#include "Zynq/x_topology.h"
#include "xstatus.h"

#include "xparameters.h"
#include "xparameters_ps.h"
#include "xil_exception.h"
#include "xil_mmu.h"

#include "uncached_memory.h"

#define UNCACHED_MEMORY_SIZE	0x100000ul

#define DDR_MEMORY_END	(XPAR_PS7_DDR_0_S_AXI_HIGHADDR+1)

static void vInitialiseUncachedMemory( void );

static uint8_t *pucHeadOfMemory;
static uint32_t ulMemorySize;
static uint8_t *pucStartOfMemory = NULL;

uint8_t ucIsCachedMemory( const uint8_t *pucBuffer )
{
uint8_t ucReturn;

	if( ( pucStartOfMemory != NULL ) &&
		( pucBuffer >= pucStartOfMemory ) &&
		( pucBuffer < ( pucStartOfMemory + UNCACHED_MEMORY_SIZE ) ) )
	{
		ucReturn = pdFALSE;
	}
	else
	{
		ucReturn = pdTRUE;
	}

	return ucReturn;
}

uint8_t *pucGetUncachedMemory( uint32_t ulSize )
{
uint8_t *pucReturn;

	if( pucStartOfMemory == NULL )
	{
		vInitialiseUncachedMemory( );
	}
	if( ( pucStartOfMemory == NULL ) || ( ulSize > ulMemorySize ) )
	{
		pucReturn = NULL;
	}
	else
	{
	uint32_t ulSkipSize;

		pucReturn = pucHeadOfMemory;
		ulSkipSize = ( ulSize + 0x1000ul ) & ~0xffful;
		pucHeadOfMemory += ulSkipSize;
		ulMemorySize -= ulSkipSize;
	}

	return pucReturn;
}

extern u8 _end;

static void vInitialiseUncachedMemory( )
{
	/* At the end of program's space... */
	pucStartOfMemory = (uint8_t *) &_end;
	/*
	 * Align the start address to 1 MB boundary.
	 */
	pucStartOfMemory = (uint8_t *)( ( ( uint32_t )pucStartOfMemory + UNCACHED_MEMORY_SIZE ) & ( ~( UNCACHED_MEMORY_SIZE - 1 ) ) );

	if( ( ( u32 )pucStartOfMemory ) + UNCACHED_MEMORY_SIZE > DDR_MEMORY_END )
	{
//		vLoggingPrintf("vInitialiseUncachedMemory: Can not allocate uncached memory\n" );
	}
	else
	{
		/*
		 * Some objects want to be stored in uncached memory. Hence the 1 MB
		 * address range that starts after "_end" is made uncached
		 * by setting appropriate attributes in the translation table.
		 */
		/* FIXME claudio rossi. Modified to prevent data abort exception (misaligned access)
		 * when application is compiled with -O1 or more optimization flag.
		 */
/*		Xil_SetTlbAttributes( ( uint32_t )pucStartOfMemory, 0xc02 ); // addr, attr */
		Xil_SetTlbAttributes( ( uint32_t )pucStartOfMemory, 0x1c02 ); // addr, attr

		/* For experiments in the SDIO driver, make the remaining uncached memory public */
		pucHeadOfMemory = pucStartOfMemory;
		ulMemorySize = UNCACHED_MEMORY_SIZE;
		memset( pucStartOfMemory, '\0', UNCACHED_MEMORY_SIZE );
	}
}
