/*
 * TI Booting and Flashing Utilities
 *
 * This file provides low-level init functions for use in the UBL for booting
 * an application.
 *
 * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation version 2.
 *
 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/* --------------------------------------------------------------------------
  AUTHOR      : Daniel Allred
-------------------------------------------------------------------------- */ 



// This module's header file
#include "device.h"


/************************************************************
* Explicit External Declarations                            *
************************************************************/


/************************************************************
* Local Macro Declarations                                  *
************************************************************/


/************************************************************
* Local Typedef Declarations                                *
************************************************************/


/************************************************************
* Local Function Declarations                               *
************************************************************/

static Uint32 DEVICE_set_ARM_reset_vector (Uint32 address);


/************************************************************
* Local Variable Definitions                                *
\***********************************************************/


/************************************************************
* Global Variable Definitions                               *
************************************************************/


/************************************************************
* Global Function Definitions                               *
************************************************************/


void DEVICE_LPSCTransition(Uint8 pscnum, Uint8 module, Uint8 domain, Uint8 state)
{
  DEVICE_PSCRegs *PSC;
  
  if (pscnum == 0)
    PSC = PSC0;
  else if(pscnum == 1)
    PSC = PSC1;
  else
    return; 

  // Wait for any outstanding transition to complete
  while ( (PSC->PTSTAT) & (0x00000001 << domain) );
  
  // If we are already in that state, just return
  if (((PSC->MDCTL[module]) & 0x1F) == state) return;
    
  // Perform transition
  PSC->MDCTL[module] = ((PSC->MDCTL[module]) & (0xFFFFFFE0)) | (state);
  PSC->PTCMD |= (0x00000001 << domain);

  // Wait for transition to complete
  while ( (PSC->PTSTAT) & (0x00000001 << domain) );
  
  // Wait and verify the state
  while (((PSC->MDSTAT[module]) & 0x1F) != state);	
}

/*Enable Function for PSC0*/
DEVICE_LPSC_enableARM () 
{

  // Wait for any outstanding transition to complete
  while ( (PSC0->PTSTAT) & (0x00000001) );
  
  // If we are already in that state, just return
  if (((PSC0->MDCTL[14]) & 0x1F) == 0x3) return;
    
  // Perform transition
  PSC0->MDCTL[14] = ((PSC0->MDCTL[14]) & (0xFFFFFFE0)) | (0x103);
  PSC0->PTCMD |= (0x00000001);

  // Wait for transition to complete
  while ( (PSC0->PTSTAT) & (0x00000001) );
  
  // Wait and verify the state
  while (((PSC0->MDSTAT[14]) & 0x1F) != 0x3);	
}

void DEVICE_enable_ARM(void)
{
    /* Turn on ARM RAM */
	DEVICE_LPSCTransition(0, 7, 0, PSC_ENABLE);

	DEVICE_set_ARM_reset_vector(DEVICE_ARM_UBL_LOAD_ADDR);

    /* Turn on ARM */
	DEVICE_LPSCTransition(0, 14, 0, PSC_ENABLE);

	/* de-assert ARM local reset */
	PSC0->MDCTL[14] |= 0x100;
}


/************************************************************
* Local Function Definitions                                *
************************************************************/
static const unsigned int armBootDmaxCodeConst[] = {
	0x24000080,												// Load ARM vector table address to r0
	0x24ffffc0,
	0x24000081,												// Load ARM code address to r1
	0x240000c1,
	0xf500e182,												// Read ARM code from *r1 to r2,...
	0xe500e082,												// Write ARM code from r2,... to *r0
	0x79000000,												// Self loop
};

static const unsigned int armBootArmCodeConst[] = {
	0xEA000007,												// vt0: B boot
	0xEAFFFFFE,												// vt0: Self loop
	0xEAFFFFFE,												// vt0: Self loop
	0xEAFFFFFE,												// vt0: Self loop
	0xEAFFFFFE,												// vt0: Self loop
	0xEAFFFFFE,												// vt0: Self loop
	0xEAFFFFFE,												// vt0: Self loop
	0xEAFFFFFE,												// vt0: Self loop
	// jump:, DATA:
	0x00000000,
	// boot:
	0xE51F000C,												// LDR R0, jump
	0xE1A0F000,												// MOV PC, R0
};

static Uint32 DEVICE_set_ARM_reset_vector (Uint32 address)
{
	unsigned int i, *p = (unsigned int *) DMAXPDSP0_IRAM_BASEADDR;
	unsigned int *armCode = (unsigned int *) armBootArmCodeConst;
	unsigned int *dmaxCode = (unsigned int *) armBootDmaxCodeConst;

	// Update ARM entry point address
	armCode[8] = address;

	// Tell location of armCode to PRUSS code
	dmaxCode[2] |= ((int) armCode & 0xffff) << 8;
	dmaxCode[3] |= ((int) armCode & 0xffff0000) >> 8;

	// power on PRUSS
	DEVICE_LPSCTransition(0, 13, 0, PSC_ENABLE);

	// Reset PRUSS
	DMAXPDSP0->CONTROL = 0;

	// Copy PRUSS code to its instruction RAM
	for (i = 0; i < sizeof (armBootDmaxCodeConst) / sizeof (int); i++)
		*p++ = dmaxCode[i];

	// Enable PRUSS, let it execute the code we just copied
	DMAXPDSP0->CONTROL |= (1 << 3);
	DMAXPDSP0->CONTROL |= (1 << 1);

	// Wait for PRUSS to finish
	while (DMAXPDSP0->STATUS != (sizeof (armBootDmaxCodeConst) / sizeof (int)) - 1);

	// Reset PRUSS
	DMAXPDSP0->CONTROL = 0;

	// Wake-up ARM
	SYSTEM->HOSTCFG[0] = 0x00000001;

	return E_PASS;
}



/***********************************************************
* End file                                                 *
***********************************************************/


