/*******************************************************************************
* Copyright 2010, Cypress Semiconductor Corporation.  All rights reserved.
* You may use this file only in accordance with the license, terms, conditions, 
* disclaimers, and limitations in the end user license agreement accompanying 
* the software package with which this file was provided.
********************************************************************************/

#include <string.h>
#include <cytypes.h>
#include <cydevice.h>
#include <cydevice_trm.h>
#include <cyfitter.h>
#include <CyLib.h>
#include <CyDmac.h>
#include <cyfitter_cfg.h>

/* Clock startup error codes                                                   */
#define CYCLOCKSTART_NO_ERROR    0
#define CYCLOCKSTART_XTAL_ERROR  1
#define CYCLOCKSTART_32KHZ_ERROR 2
#define CYCLOCKSTART_PLL_ERROR   3

/*******************************************************************************
* Function Name: CyClockStartupError
********************************************************************************
* Summary:
*  If an error is encountered during clock configuration (crystal startup error,
*  PLL lock error, etc) the system will end up here.  Unless reimplemented by 
*  the customer, an infinite loop will be encountered.
*
* Parameters:  
*   void.
*
*
* Return:
*   void.
*
*******************************************************************************/
#if defined(__GNUC__) || defined(__ARMCC_VERSION)
__attribute__ ((unused))
#endif
static void CyClockStartupError(uint8 errorCode)
{
    /* To remove the compiler warning if errorCode not used.                */
    errorCode = errorCode;

    /* `#START CyClockStartupError` */

    /* `#END` */

    /* If nothing else, stop here since the clocks have not started         */
    /* correctly.                                                           */
    while(1) {}
}

/* This file contains the data used to initialize the device. */

#undef CYCODE
#undef CYDATA
#undef CYXDATA
#undef CYFAR
#if defined(__C51__) || defined(__CX51__)
#define CYCODE code
#define CYDATA data
#define CYXDATA xdata
#define CYFAR far
extern void cymemzero(void CYFAR *addr, uint16 size);
extern void cyconfigcpy(uint16 size, const void CYFAR *src, void CYFAR *dest) small;
extern void cyconfigcpycode(uint16 size, const void CYCODE *src, void CYFAR *dest);
#define CYMEMZERO(a,c) cymemzero((a),(c))
#define CYCONFIGCPY(d,s,c) cyconfigcpy((c),(s),(d))
#define CYCONFIGCPYCODE(d,s,c) cyconfigcpycode((c),(s),(d))
#else
#define CYCODE
#define CYDATA
#define CYXDATA
#define CYFAR
#define CYMEMZERO(a,c) cymemset((a),0,(c))
#define CYCONFIGCPY(d,s,c) cymemcpy((d),(s),(c))
#define CYCONFIGCPYCODE(d,s,c) cymemcpy((d),(s),(c))
#endif

#if defined(__GNUC__) || defined(__ARMCC_VERSION)
#define CYPACKED __attribute__ ((packed))
#define CYALIGNED __attribute__ ((aligned))
#if CYDEV_CONFIGURATION_DMA
#define DMAINITSEC __attribute__ ((section (".dma_init")))
#else
#define DMAINITSEC
#endif
#if defined(__ARMCC_VERSION)
#define CY_CFG_MEMORY_BARRIER() __memory_changed()
#else
#define CY_CFG_MEMORY_BARRIER() __sync_synchronize()
#endif
#else
/* Keil C51 and others */
#define CYPACKED
#define CYALIGNED
#define DMAINITSEC
#define CY_CFG_MEMORY_BARRIER()
#endif

typedef struct {
	void CYFAR *address;
	uint16 size;
} CYPACKED cfg_memset_t;

typedef struct {
	void CYFAR *dest;
	const void CYCODE *src;
	uint16 size;
} CYPACKED cfg_memcpy_t;


#if defined(__C51__) || defined(__CX51__)
#define CYMEMZERO8(a,c) cymemzero((a),(c))
#define CYCONFIGCPY8(d,s,c) cyconfigcpy((c),(s),(d))
#define CYCONFIGCPYCODE8(d,s,c) cyconfigcpycode((c),(s),(d))
#else
#if defined(__GNUC__) || defined(__ARMCC_VERSION)
__attribute__ ((unused))
#endif
static void cymemzero8(void *addr, unsigned int count)
{
	volatile uint8 *addr8 = (uint8 *)addr;
	while (count--)
	{
		*addr8 = 0;
		addr8++;
	}
}

#if defined(__GNUC__) || defined(__ARMCC_VERSION)
__attribute__ ((unused))
#endif
static void cymemcpy8(void *dest, const void *src, unsigned int count)
{
	volatile uint8 *dest8 = (uint8 *)dest;
	const uint8 *src8 = (const uint8 *)src;

	while (count--)
	{
		*dest8 = *src8;
		dest8++;
		src8++;
	}
}

#define CYMEMZERO8(a,c) cymemzero8((a),(c))
#define CYCONFIGCPY8(d,s,c) cymemcpy8((d),(s),(c))
#define CYCONFIGCPYCODE8(d,s,c) cymemcpy8((d),(s),(c))
#endif

#define CFG_MEMSET_COUNT (sizeof(cfg_memset_list)/sizeof(*cfg_memset_list))
static const cfg_memset_t CYCODE cfg_memset_list [] = {
	/* address, size */
	{(void CYFAR *)(CYDEV_IO_PRT_PRT0_DR), 112},
	{(void CYFAR *)(CYDEV_IO_PRT_PRT12_DR), 16},
	{(void CYFAR *)(CYDEV_UCFG_B0_P0_U0_BASE), 4096},
	{(void CYFAR *)(CYDEV_UCFG_B1_P2_U0_BASE), 2048},
	{(void CYFAR *)(CYDEV_UCFG_DSI0_BASE), 2560},
	{(void CYFAR *)(CYDEV_UCFG_DSI12_BASE), 512},
};

const uint8 CYCODE cfg_byte_table[] = {
	0x06,
	0x01, 0x40, 0x05, /* Base address 0x014000 */
	/* CYDEV_UCFG_DSI0_BASE */
	0x32, 0x20, 0x34, 0x10, 0x4A, 0x20, 0x4B, 0x02, 
	/* CYDEV_UCFG_DSI0_BASE + 0x00000080 */
	0xCC, 0x30, 
	0x01, 0x41, 0x04, /* Base address 0x014100 */
	/* CYDEV_UCFG_DSI1_BASE + 0x00000080 */
	0x97, 0x02, 0x9C, 0x10, 0xC6, 0x10, 0xCA, 0x20, 
	0x01, 0x42, 0x02, /* Base address 0x014200 */
	/* CYDEV_UCFG_DSI2_BASE + 0x00000080 */
	0x97, 0x02, 0x9C, 0x10, 
	0x01, 0x43, 0x04, /* Base address 0x014300 */
	/* CYDEV_UCFG_DSI3_BASE */
	0x35, 0x80, 
	/* CYDEV_UCFG_DSI3_BASE + 0x00000080 */
	0x97, 0x02, 0x9C, 0x10, 0xCC, 0x20, 
	0x01, 0x48, 0x0F, /* Base address 0x014800 */
	/* CYDEV_UCFG_DSI8_BASE */
	0x23, 0x20, 0x26, 0x01, 0x29, 0x81, 0x2C, 0x10, 0x2F, 0x01, 0x31, 0x01, 0x33, 0x10, 0x36, 0x01, 
	0x47, 0x30, 
	/* CYDEV_UCFG_DSI8_BASE + 0x00000080 */
	0x97, 0x02, 0x9C, 0x10, 0x9D, 0x80, 0xC8, 0xC0, 0xCA, 0xF0, 0xCC, 0xD0, 
	0x01, 0x49, 0x0F, /* Base address 0x014900 */
	/* CYDEV_UCFG_DSI9_BASE */
	0x07, 0x60, 0x23, 0x20, 0x25, 0x10, 0x26, 0x01, 0x2A, 0x10, 0x2D, 0x80, 0x33, 0x40, 0x35, 0x80, 
	0x36, 0x01, 0x3A, 0x10, 0x3D, 0x20, 
	/* CYDEV_UCFG_DSI9_BASE + 0x00000080 */
	0xC8, 0xE0, 0xCA, 0x30, 0xCC, 0xB0, 0xCE, 0x30, 
};
/* BYTES: 109 */

/* IDMUX_IRQ Address: CYDEV_IDMUX_IRQ_CTL0 Size (bytes): 8 */
static const uint8 CYCODE BS_IDMUX_IRQ_VAL[] DMAINITSEC = {
	0x0A, 0x00, 0x00, 0x00, 0x08, 0x88, 0xA2, 0xA8};

#define CFG_MEMCPY_COUNT (sizeof(cfg_memcpy_list)/sizeof(*cfg_memcpy_list))
static const cfg_memcpy_t CYCODE cfg_memcpy_list [] = {
	/* dest, src, size */
	{(void CYFAR *)(CYDEV_IDMUX_IRQ_CTL0), BS_IDMUX_IRQ_VAL, 8},
};


#if defined(__C51__) || defined(__CX51__)
extern void cfg_write_bytes_code(void CYCODE *table);
extern void cfg_write_bytes(void CYFAR *table);
#else
/* For 32-bit little-endian architectures */
static void cfg_write_bytes32(void)
{
	unsigned int i, j = 0;
	for (i = 0; i < CY_CFG_BASE_ADDR_COUNT; i++)
	{
		uint32 baseAddr = cy_cfg_addr_table[i];
		uint8 count = baseAddr & 0xFFu;
		baseAddr &= 0xFFFFFF00u;
		while (count--)
		{
			CY_SET_XTND_REG8((void CYFAR *)(baseAddr + cy_cfg_data_table[j].offset), cy_cfg_data_table[j].value);
			j++;
		}
	}
}
#endif

static void ClockSetup(void)
{
	reg32  timeout;
	reg32* timeout_p = &timeout;
	reg8   pllLock;

	CY_SET_REG8((void CYXDATA *)(CYDEV_SLOWCLK_ILO_CR0), 0x04);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_CR), 0x00);
	CY_SET_REG8((void CYXDATA *)(CYDEV_MFGCFG_IMO_TR1), (CY_GET_XTND_REG8(CYDEV_FLSHID_CUST_TABLES_IMO_USB)));
	CY_SET_REG8((void CYXDATA *)(CYDEV_FASTCLK_IMO_CR), 0x52);
	CY_SET_XTND_REG16((void CYFAR *)(CYDEV_FASTCLK_PLL_P), 0x0710);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_CR), 0x00);
	CY_SET_XTND_REG16((void CYFAR *)(CYDEV_FASTCLK_PLL_CFG0), 0x1051);
	pllLock = 0;
	for (timeout = 14; *timeout_p && (pllLock != 0x03u); timeout--) { pllLock = 0x03u & ((pllLock << 1) | ((CY_GET_REG8(CYDEV_FASTCLK_PLL_SR) & 0x01u) >> 0)); }
	CY_SET_XTND_REG16((void CYFAR *)(CYDEV_CLKDIST_MSTR0), 0x0103);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_MSTR0), 0x00);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_MSTR0), 0x07);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_BCFG0), 0x00);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_BCFG2), 0x48);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_MSTR0), 0x00);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_UCFG), 0x00);
	CY_SET_REG8((void CYXDATA *)(CYDEV_CLKDIST_LD), 0x02);
}

/* Analog API Functions */
void AnalogSetDefault(void)
{
	CY_SET_REG8(CYDEV_IO_PRT_PRT12_AG, 0xAA);
	CY_SET_REG8(CYDEV_ANAIF_CFG_PUMP_CR0, 0x44);
}

#define CY_AMUX_UNUSED CYDEV_BOOST_SR

/* This function is called by the start-up code for the selected device. */
void cyfitter_cfg(void)
{
	uint8 CYDATA i;

	CY_SET_REG8((void CYXDATA *)(CYDEV_CACHE_CR), ((CYDEV_INSTRUCT_CACHE_ENABLED) ? 0x50 : 0x40));
	CY_SET_REG8((void CYXDATA *)(CYDEV_PHUB_CH0_BASIC_CFG), 0x00);
	CY_SET_REG8((void CYXDATA *)(CYDEV_PHUB_CH1_BASIC_CFG), 0x00);

	CY_SET_XTND_REG8(CYDEV_MFGCFG_MLOGIC_DBG_EN_DBG_EN, (CY_GET_XTND_REG8(CYDEV_MFGCFG_MLOGIC_DBG_EN_DBG_EN) | 0x01));
	for (i = 0; i < CFG_MEMSET_COUNT; i++)
	{
		const cfg_memset_t CYCODE * CYDATA ms = &cfg_memset_list[i];
		CYMEMZERO(ms->address, ms->size);
	}

	for (i = 0; i < CFG_MEMCPY_COUNT; i++)
	{
		const cfg_memcpy_t CYCODE * CYDATA mc = &cfg_memcpy_list[i];
		void * CYDATA dest = mc->dest;
		const void CYCODE * CYDATA src = mc->src;
		uint16 CYDATA size = mc->size;
		CYCONFIGCPYCODE(dest, src, size);
	}

	cfg_write_bytes_code(cfg_byte_table);

	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT0_BASE), 7);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT1_BASE), 7);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT2_BASE), 7);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT3_BASE), 7);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT4_BASE), 7);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT5_BASE), 7);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT6_BASE), 7);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT12_BASE), 6);
	CYMEMZERO8((void CYFAR *)(CYDEV_PRTDSI_PRT15_BASE), 7);
	/* BYTES WRITTEN: 62 */

	/* Enable digital routing */
	CY_SET_XTND_REG8(CYDEV_UCFG_BCTL0_BANK_CTL, CY_GET_XTND_REG8(CYDEV_UCFG_BCTL0_BANK_CTL) | 0x02u);
	CY_SET_XTND_REG8(CYDEV_UCFG_BCTL1_BANK_CTL, CY_GET_XTND_REG8(CYDEV_UCFG_BCTL1_BANK_CTL) | 0x02u);

	/* Enable UDB array */
	CY_SET_REG8(CYDEV_PM_ACT_CFG0, CY_GET_REG8(CYDEV_PM_ACT_CFG0) | 0x40u);
	CY_SET_REG8(CYDEV_PM_AVAIL_CR2, CY_GET_REG8(CYDEV_PM_AVAIL_CR2) | 0x10u);

	CY_SET_REG8((void CYXDATA *)(CYDEV_INTC_CLOCK_EN), 0x01);
	/* BYTES WRITTEN: 1 */

	CY_SET_REG8((void CYXDATA *)(CYDEV_CACHE_CR), ((CYDEV_INSTRUCT_CACHE_ENABLED) ? 0x10 : 0x00));
	ClockSetup();
	CY_SET_REG8((void CYXDATA *)(CYDEV_CACHE_CR), ((CYDEV_INSTRUCT_CACHE_ENABLED) ? 0x90 : 0x80));

	AnalogSetDefault();

	/* Configure alternate active mode */
	CYCONFIGCPY((void CYFAR *)CYDEV_PM_STBY_BASE, (void CYFAR *)CYDEV_PM_ACT_BASE, 12);
	CY_SET_REG8(CYDEV_PM_STBY_CFG0, CY_GET_REG8(CYDEV_PM_STBY_CFG0) & ~0x02u);	/* Disable CPU */
}
