/*****************************************************************************
 * Copyright (c) 2010 Rowley Associates Limited.                             *
 *                                                                           *
 * This file may be distributed under the terms of the License Agreement     *
 * provided with this software.                                              *
 *                                                                           *
 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE   *
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
 *****************************************************************************/

/*****************************************************************************
 *                           Preprocessor Definitions
 *                           ------------------------
 *
 * MAMCR_VAL & MAMTIM_VAL
 *
 *   Override the default MAM configuration (fully enabled, 3 fetch cycles)
 *   by defining MAMCR_VAL and MAMTIM_VAL.
 *
 *****************************************************************************/

#include <targets/LPC2478.h>

/* Fosc = 12Mhz, Fcco = 288Mhz, cclk = 72Mhz, usbclk=48Mhz */
#define PLLCFG_VAL ((11 << PLLCFG_MSEL_BIT) | (0 << PLLCFG_NSEL_BIT))
#define CCLKCFG_VAL 3
#define USBCLKCFG_VAL 5
#define fCCLK (72000000)
#define fPCLK (fCCLK / 4) 

#ifndef MAMCR_VAL
#define MAMCR_VAL 2
#endif

#ifndef MAMTIM_VAL
#define MAMTIM_VAL 3
#endif

  .global _configure

  .section .init, "ax"
  .code 32
  .align 0

.macro delay_ms ms=
  ldr r2, =(\ms * (fPCLK / 1000))
  bl delay_pclk_ticks
.endm

/*****************************************************************************
 * Function    : delay_pclk_ticks                                            *
 * Input       : r2 = The number of pclk cycles to delay.
 *****************************************************************************/
delay_pclk_ticks:
  ldr r3, =TIMER1_BASE
  /* Stop and reset timer */
  ldr r4, =0x02
  str r4, [r3, #T1TCR_OFFSET]
  /* Set prescaler to zero */
  ldr r4, =0x00
  str r4, [r3, #T1PR_OFFSET]
  /* Set the match register */
  str r2, [r3, #T1MR0_OFFSET]

  /* Reset all interrupt flags */
  ldr r4, =0xFF
  str r4, [r3, #T1IR_OFFSET]
  /* Stop timer on match */
  ldr r4, =0x04
  str r4, [r3, #T1MCR_OFFSET]
  /* Start timer */
  ldr r4, =0x01
  str r4, [r3, #T1TCR_OFFSET]
  /* Wait until delay time has elapsed */
1:
  ldr r4, [r3, #T1TCR_OFFSET]
  tst r4, #0x01
  bne 1b
  bx lr

/*****************************************************************************
 * Function    : _configure                                                  *
 * Description : Configure clocks and memory.
 * Input       : r2 = The number of pclk cycles to delay.
 *****************************************************************************/
_configure:
  ldr r0, =SCB_BASE

  mov r5, lr

  /* Configure PLL Multiplier/Divider */
  ldr r1, [r0, #PLLSTAT_OFFSET]
  tst r1, #PLLSTAT_PLLC
  beq 1f

  /* Disconnect PLL */
  ldr r1, =PLLCON_PLLE
  str r1, [r0, #PLLCON_OFFSET]
  mov r1, #0xAA
  str r1, [r0, #PLLFEED_OFFSET]
  mov r1, #0x55
  str r1, [r0, #PLLFEED_OFFSET]
1:
  /* Disable PLL */
  ldr r1, =0
  str r1, [r0, #PLLCON_OFFSET]
  mov r1, #0xAA
  str r1, [r0, #PLLFEED_OFFSET]
  mov r1, #0x55
  str r1, [r0, #PLLFEED_OFFSET]

  /* Enable main oscillator */
  ldr r1, [r0, #SCS_OFFSET]
  orr r1, r1, #SCS_OSCEN
  str r1, [r0, #SCS_OFFSET]
1:
  ldr r1, [r0, #SCS_OFFSET]
  tst r1, #SCS_OSCSTAT
  beq 1b

  /* Select main oscillator as the PLL clock source */
  ldr r1, =1
  str r1, [r0, #CLKSRCSEL_OFFSET]

  /* Set PLLCFG */
  ldr r1, =PLLCFG_VAL
  str r1, [r0, #PLLCFG_OFFSET]
  mov r1, #0xAA
  str r1, [r0, #PLLFEED_OFFSET]
  mov r1, #0x55
  str r1, [r0, #PLLFEED_OFFSET]

  /* Enable PLL */
  ldr r1, =PLLCON_PLLE
  str r1, [r0, #PLLCON_OFFSET]
  mov r1, #0xAA
  str r1, [r0, #PLLFEED_OFFSET]
  mov r1, #0x55
  str r1, [r0, #PLLFEED_OFFSET]

  /* Set the CPU clock divider */
  ldr r1, =CCLKCFG_VAL
  str r1, [r0, #CCLKCFG_OFFSET]

  /* Set the CPU clock divider */
  ldr r1, =USBCLKCFG_VAL
  str r1, [r0, #USBCLKCFG_OFFSET]
  
  /* Wait for PLL to lock */
1:
  ldr r1, [r0, #PLLSTAT_OFFSET]
  tst r1, #PLLSTAT_PLOCK
  beq 1b
  /* PLL Locked, connect PLL as clock source */
  mov r1, #(PLLCON_PLLE | PLLCON_PLLC)
  str r1, [r0, #PLLCON_OFFSET]
  mov r1, #0xAA
  str r1, [r0, #PLLFEED_OFFSET]
  mov r1, #0x55
  str r1, [r0, #PLLFEED_OFFSET]
  /* Wait for PLL to connect */
1:
  ldr r1, [r0, #PLLSTAT_OFFSET]
  tst r1, #PLLSTAT_PLLC
  beq 1b

  /* Initialise memory accelerator module */
  mov r1, #0
  str r1, [r0, #MAMCR_OFFSET]
  ldr r1, =MAMTIM_VAL
  str r1, [r0, #MAMTIM_OFFSET]
  ldr r1, =MAMCR_VAL
  str r1, [r0, #MAMCR_OFFSET]

  /* Initialize EMC and SDRAM */
  ldr r1, [r0, #PCONP_OFFSET]
  orr r1, r1, #0x00000800
  str r1, [r0, #PCONP_OFFSET]

  ldr r0, =PCB_BASE
  ldr r1, =0x50000000
  str r1, [r0, #PINSEL4_OFFSET]
  ldr r1, =0x55050555
  str r1, [r0, #PINSEL5_OFFSET]
  ldr r1, =0x55555555
  str r1, [r0, #PINSEL6_OFFSET]
   ldr r1, =0x55555555
  str r1, [r0, #PINSEL7_OFFSET]
    ldr r1, =0x54555555
  str r1, [r0, #PINSEL8_OFFSET]
  ldr r1, =0x50555555
  str r1, [r0, #PINSEL9_OFFSET]

  ldr r0, =EMC_BASE
  ldr r1, =0x00000001
  str r1, [r0, #EMCControl_OFFSET]

  ldr r1, =2
  str r1, [r0, #EMCDynamictRP_OFFSET]
  ldr r1, =10
  str r1, [r0, #EMCDynamictRAS_OFFSET]
  ldr r1, =8
  str r1, [r0, #EMCDynamictSREX_OFFSET]
  ldr r1, =3
  str r1, [r0, #EMCDynamictAPR_OFFSET]
  ldr r1, =6
  str r1, [r0, #EMCDynamictDAL_OFFSET]
  ldr r1, =1
   ldr r1, =2
  str r1, [r0, #EMCDynamictWR_OFFSET]
  ldr r1, =5
   ldr r1, =6
  str r1, [r0, #EMCDynamictRC_OFFSET]
  str r1, [r0, #EMCDynamictRFC_OFFSET]
  ldr r1, =7
  str r1, [r0, #EMCDynamictXSR_OFFSET]
  ldr r1, =5
  str r1, [r0, #EMCDynamictRRD_OFFSET]
  ldr r1, =4
  str r1, [r0, #EMCDynamictMRD_OFFSET]
  ldr r1, =1
  str r1, [r0, #EMCDynamicReadConfig_OFFSET]

  ldr r1, =0x00000303
  str r1, [r0, #EMCDynamicRasCas0_OFFSET]

  //ldr r1, =0x00000680
   ldr r1, =0x00004500
  str r1, [r0, #EMCDynamicConfig0_OFFSET]

  delay_ms 100

  /* Send command: NOP */
  ldr r1, =0x00000183
  str r1, [r0, #EMCDynamicControl_OFFSET]

  delay_ms 200

  /* Send command: PRECHARGE-ALL, shortest possible refresh period */
  ldr r1, =0x00000103
  str r1, [r0, #EMCDynamicControl_OFFSET]
  ldr r1, =0x00000002
  str r1, [r0, #EMCDynamicRefresh_OFFSET]

  /* Wait 128 ABH clock cycles */
  ldr r1, =0x40
1:
  subs r1, r1, #1
  bne 1b

  /* Set correct refresh period */
  ldr r1, =28
  str r1, [r0, #EMCDynamicRefresh_OFFSET]

  /* Send command: MODE */
  ldr r1, =0x00000083
  str r1, [r0, #EMCDynamicControl_OFFSET]

  /* Set mode register in SDRAM */
  ldr r1, =0xA0032000
  ldr r1, [r1]

  /* Send command: NORMAL */
  ldr r1, =0x00000000
  str r1, [r0, #EMCDynamicControl_OFFSET]

  /* Enable buffer */
  ldr r1, [r0, #EMCDynamicConfig0_OFFSET]
  orr r1, r1, #0x00080000
  str r1, [r0, #EMCDynamicConfig0_OFFSET]

  delay_ms 1

  ldr r1, =0x2
  str r1, [r0, #EMCStaticWaitWen0_OFFSET]
  str r1, [r0, #EMCStaticWaitOen0_OFFSET]
  ldr r1, =0x1F
  str r1, [r0, #EMCStaticWaitRd0_OFFSET]
  str r1, [r0, #EMCStaticWaitPage0_OFFSET]
  str r1, [r0, #EMCStaticWaitWr0_OFFSET]
  ldr r1, =0xF
  str r1, [r0, #EMCStaticWaitTurn0_OFFSET]
  ldr r1, =0x81
  str r1, [r0, #EMCStaticConfig0_OFFSET]

  ldr r1, =0x2
  str r1, [r0, #EMCStaticWaitWen1_OFFSET]
  str r1, [r0, #EMCStaticWaitOen1_OFFSET]
  ldr r1, =0x8
  str r1, [r0, #EMCStaticWaitRd1_OFFSET]
  ldr r1, =0x1F
  str r1, [r0, #EMCStaticWaitPage1_OFFSET]
  ldr r1, =0x8
  str r1, [r0, #EMCStaticWaitWr1_OFFSET]
  ldr r1, =0xF
  str r1, [r0, #EMCStaticWaitTurn1_OFFSET]
  ldr r1, =0x80
  str r1, [r0, #EMCStaticConfig1_OFFSET]

  bx r5

