/*
 * Low-level initialization for EP93xx
 *
 * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
 * Copyright (C) 2013
 * Sergey Kostanabev <sergey.kostanbaev <at> fairwaves.ru>
 *
 * Copyright (C) 2006 Dominic Rath <Dominic.Rath@gmx.de>
 * Copyright (C) 2006 Cirrus Logic Inc.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <config.h>
#include <asm/arch-ep93xx/ep93xx.h>

/*
/* Configure the SDRAM based on the supplied settings.
 *
 * Input:	r0 - SDRAM DEVCFG register
 *		r2 - configuration for SDRAM chips
 * Output:	none
 * Modifies:	r3, r4
 */
ep93xx_sdram_config:
	/* Program the SDRAM device configuration register. */
	ldr	r3, =SDRAM_BASE
#ifdef CONFIG_EDB93XX_SDCS0
	str	r0, [r3, #SDRAM_OFF_DEVCFG0]
#endif
#ifdef CONFIG_EDB93XX_SDCS1
	str	r0, [r3, #SDRAM_OFF_DEVCFG1]
#endif
#ifdef CONFIG_EDB93XX_SDCS2
	str	r0, [r3, #SDRAM_OFF_DEVCFG2]
#endif
#ifdef CONFIG_EDB93XX_SDCS3
	str	r0, [r3, #SDRAM_OFF_DEVCFG3]
#endif

	/* Set the Initialize and MRS bits (issue continuous NOP commands
	 * (INIT & MRS set))
	 */
	ldr	r4, =(EP93XX_SDRAMCTRL_GLOBALCFG_INIT | \
			EP93XX_SDRAMCTRL_GLOBALCFG_MRS | \
			EP93XX_SDRAMCTRL_GLOBALCFG_CKE)
	str	r4, [r3, #SDRAM_OFF_GLCONFIG]

	/* Delay for 200us. */
	mov	r4, #0x3000
delay1:
	subs	r4, r4, #1
	bne	delay1

	/* Clear the MRS bit to issue a precharge all. */
	ldr	r4, =(EP93XX_SDRAMCTRL_GLOBALCFG_INIT | \
			EP93XX_SDRAMCTRL_GLOBALCFG_CKE)
	str	r4, [r3, #SDRAM_OFF_GLCONFIG]

	/* Temporarily set the refresh timer to 0x10. Make it really low so
	 * that refresh cycles are generated.
	 */
	ldr	r4, =0x10
	str	r4, [r3, #SDRAM_OFF_REFRSHTIMR]

	/* Delay for at least 80 SDRAM clock cycles. */
	mov	r4, #80
delay2:
	subs	r4, r4, #1
	bne	delay2

	/* Set the refresh timer to the fastest required for any device
	 * that might be used. Set 9.6 ms refresh time.
	 */
	ldr	r4, =0x01e0
	str	r4, [r3, #SDRAM_OFF_REFRSHTIMR]

	/* Select mode register update mode. */
	ldr	r4, =(EP93XX_SDRAMCTRL_GLOBALCFG_CKE | \
			EP93XX_SDRAMCTRL_GLOBALCFG_MRS)
	str	r4, [r3, #SDRAM_OFF_GLCONFIG]

	/* Program the mode register on the SDRAM by performing fake read */
	ldr	r4, [r2]

	/* Select normal operating mode. */
	ldr	r4, =EP93XX_SDRAMCTRL_GLOBALCFG_CKE
	str	r4, [r3, #SDRAM_OFF_GLCONFIG]

	/* Return to the caller. */
	mov	pc, lr

/*
 * Test to see if the SDRAM has been configured in a usable mode.
 *
 * Input:	r0 - Test address of SDRAM
 * Output:	r0 - 0 -- Test OK, -1 -- Failed
 * Modifies:	r0-r5
 */
ep93xx_sdram_test:
	/* Load the test patterns to be written to SDRAM. */
	ldr	r1, =0xf00dface
	ldr	r2, =0xdeadbeef
	ldr	r3, =0x08675309
	ldr	r4, =0xdeafc0ed

	/* Store the test patterns to SDRAM. */
	stmia	r0, {r1-r4}

	/* Load the test patterns from SDRAM one at a time and compare them
	 * to the actual pattern.
	 */
	ldr	r5, [r0]
	cmp	r5, r1
	ldreq	r5, [r0, #0x0004]
	cmpeq	r5, r2
	ldreq	r5, [r0, #0x0008]
	cmpeq	r5, r3
	ldreq	r5, [r0, #0x000c]
	cmpeq	r5, r4

	/* Return -1 if a mismatch was encountered, 0 otherwise. */
	mvnne	r0, #0xffffffff
	moveq	r0, #0x00000000

	/* Return to the caller. */
	mov	pc, lr

/*
 * Determine the size of the SDRAM. Use data=address for the scan.
 *
 * Input:	r0 - Start SDRAM address
 * Return:	r0 - Single block size
 *		r1 - Valid block mask
 *		r2 - Total block count
 * Modifies:	r0-r5
 */
ep93xx_sdram_size:
	/* Store zero at offset zero. */
	str	r0, [r0]

	/* Start checking for an alias at 1MB into SDRAM. */
	ldr	r1, =0x00100000

	/* Store the offset at the current offset. */
check_block_size:
	str	r1, [r0, r1]

	/* Read back from zero. */
	ldr	r2, [r0]

	/* Stop searching of an alias was found. */
	cmp	r1, r2
	beq	found_block_size

	/* Advance to the next power of two boundary. */
	mov	r1, r1, lsl #1

	/* Loop back if the size has not reached 256MB. */
	cmp	r1, #0x10000000
	bne	check_block_size

	/* A full 256MB of memory was found, so return it now. */
	ldr	r0, =0x10000000
	ldr	r1, =0x00000000
	ldr	r2, =0x00000001
	mov	pc, lr

	/* An alias was found. See if the first block is 128MB in size. */
found_block_size:
	cmp	r1, #0x08000000

	/* The first block is 128MB, so there is no further memory. Return it
	 * now.
	 */
	ldreq	r0, =0x08000000
	ldreq	r1, =0x00000000
	ldreq	r2, =0x00000001
	moveq	pc, lr

	/* Save the block size, set the block address bits to zero, and
	 * initialize the block count to one.
	 */
	mov	r3, r1
	ldr	r4, =0x00000000
	ldr	r5, =0x00000001

	/* Look for additional blocks of memory by searching for non-aliases. */
find_blocks:
	/* Store zero back to address zero. It may be overwritten. */
	str	r0, [r0]

	/* Advance to the next power of two boundary. */
	mov	r1, r1, lsl #1

	/* Store the offset at the current offset. */
	str	r1, [r0, r1]

	/* Read back from zero. */
	ldr	r2, [r0]

	/* See if a non-alias was found. */
	cmp	r1, r2

	/* If a non-alias was found, then or in the block address bit and
	 * multiply the block count by two (since there are two unique
	 * blocks, one with this bit zero and one with it one).
	 */
	orrne	r4, r4, r1
	movne	r5, r5, lsl #1

	/* Continue searching if there are more address bits to check. */
	cmp	r1, #0x08000000
	bne	find_blocks

	/* Return the block size, address mask, and count. */
	mov	r0, r3
	mov	r1, r4
	mov	r2, r5

	/* Return to the caller. */
	mov	pc, lr


.globl lowlevel_init
lowlevel_init:

	mov	r6, lr

	/* Make sure caches are off and invalidated. */
	ldr	r0, =0x00000000
	mcr	p15, 0, r0, c1, c0, 0
	nop
	nop
	nop
	nop
	nop

	/* Turn off the green LED and turn on the red LED. If the red LED
	 * is left on for too long, the external reset circuit described
	 * by application note AN258 will cause the system to reset.
	 */
	ldr	r1, =EP93XX_LED_DATA
	ldr	r0, [r1]
	bic	r0, r0, #EP93XX_LED_GREEN_ON
	orr	r0, r0, #EP93XX_LED_RED_ON
	str	r0, [r1]

	/* Undo the silly static memory controller programming performed
	 * by the boot rom.
	 */
	ldr	r0, =SMC_BASE

	/* Set WST1 and WST2 to 31 HCLK cycles (slowest access) */
	ldr	r1, =0x0000fbe0

	/* Reset EP93XX_OFF_SMCBCR0 */
	ldr	r2, [r0]
	orr	r2, r2, r1
	str	r2, [r0]

	ldr	r2, [r0, #EP93XX_OFF_SMCBCR1]
	orr	r2, r2, r1
	str	r2, [r0, #EP93XX_OFF_SMCBCR1]

	ldr	r2, [r0, #EP93XX_OFF_SMCBCR2]
	orr	r2, r2, r1
	str	r2, [r0, #EP93XX_OFF_SMCBCR2]

	ldr	r2, [r0, #EP93XX_OFF_SMCBCR3]
	orr	r2, r2, r1
	str	r2, [r0, #EP93XX_OFF_SMCBCR3]

	ldr	r2, [r0, #EP93XX_OFF_SMCBCR6]
	orr	r2, r2, r1
	str	r2, [r0, #EP93XX_OFF_SMCBCR6]

	ldr	r2, [r0, #EP93XX_OFF_SMCBCR7]
	orr	r2, r2, r1
	str	r2, [r0, #EP93XX_OFF_SMCBCR7]

	/* Set the PLL1 and processor clock. */
	ldr	r0, =SYSCON_BASE
#ifdef CONFIG_EDB9301
	/* 332MHz, giving a 166MHz processor clock. */
	ldr	r1, = 0x02b49907
#else

#ifdef CONFIG_EDB93XX_INDUSTRIAL
	/* 384MHz, giving a 196MHz processor clock. */
	ldr	r1, =0x02a4bb38
#else
	/* 400MHz, giving a 200MHz processor clock. */
	ldr	r1, =0x02a4e39e
#endif
#endif
	str	r1, [r0, #SYSCON_OFF_CLKSET1]

	nop
	nop
	nop
	nop
	nop

	/* Need to make sure that SDRAM is configured correctly before
	 * coping the code into it.
	 */

#ifdef CONFIG_EDB93XX_SDCS0
	mov	r11, #SDRAM_DEVCFG0_BASE
#endif
#ifdef CONFIG_EDB93XX_SDCS1
	mov	r11, #SDRAM_DEVCFG1_BASE
#endif
#ifdef CONFIG_EDB93XX_SDCS2
	mov	r11, #SDRAM_DEVCFG2_BASE
#endif
#ifdef CONFIG_EDB93XX_SDCS3
	ldr	r0, =SYSCON_BASE
	ldr	r0, [r0, #SYSCON_OFF_SYSCFG]
	ands	r0, r0, #SYSCON_SYSCFG_LASDO
	moveq	r11, #SDRAM_DEVCFG3_ASD0_BASE
	movne	r11, #SDRAM_DEVCFG3_ASD1_BASE
#endif
	/* See Table 13-5 in EP93xx datasheet for more info about DRAM
	 * register mapping */

	/* Try a 32-bit wide configuration of SDRAM. */
	ldr	r0, =(EP93XX_SDRAMCTRL_DEVCFG_BANKCOUNT | \
			EP93XX_SDRAMCTRL_DEVCFG_SROMLL | \
			EP93XX_SDRAMCTRL_DEVCFG_CASLAT_2 | \
			EP93XX_SDRAMCTRL_DEVCFG_RASTOCAS_2)

	/* Set burst count: 4 and CAS: 2
	 * Burst mode [A11:A10]; CAS [A16:A14]
	 */
	orr	r2, r11, #0x00008800
	bl	ep93xx_sdram_config

	/* Test the SDRAM. */
	mov	r0, r11
	bl	ep93xx_sdram_test
	cmp	r0, #0x00000000
	beq	ep93xx_sdram_done

	/* Try a 16-bit wide configuration of SDRAM. */
	ldr	r0, =(EP93XX_SDRAMCTRL_DEVCFG_BANKCOUNT | \
			EP93XX_SDRAMCTRL_DEVCFG_SROMLL | \
			EP93XX_SDRAMCTRL_DEVCFG_CASLAT_2 | \
			EP93XX_SDRAMCTRL_DEVCFG_RASTOCAS_2 | \
			EP93XX_SDRAMCTRL_DEVCFG_EXTBUSWIDTH)

	/* Set burst count: 8, CAS: 2, sequential burst
	 * Accoring to Table 13-3 for 16bit operations mapping must be shifted.
	 * Burst mode [A10:A9]; CAS [A15:A13]
	 */
	orr	r2, r11, #0x00004600
	bl	ep93xx_sdram_config

	/* Test the SDRAM. */
	mov	r0, r11
	bl	ep93xx_sdram_test
	cmp	r0, #0x00000000
	beq	ep93xx_sdram_done

	/* Turn off the red LED. */
	ldr	r0, =EP93XX_LED_DATA
	ldr	r1, [r0]
	bic	r1, r1, #EP93XX_LED_RED_ON
	str	r1, [r0]

	/* There is no SDRAM so flash the green LED. */
flash_green:
	orr	r1, r1, #EP93XX_LED_GREEN_ON
	str	r1, [r0]
	ldr	r2, =0x00010000
flash_green_delay_1:
	subs	r2, r2, #1
	bne	flash_green_delay_1
	bic	r1, r1, #EP93XX_LED_GREEN_ON
	str	r1, [r0]
	ldr	r2, =0x00010000
flash_green_delay_2:
	subs	r2, r2, #1
	bne	flash_green_delay_2
	orr	r1, r1, #EP93XX_LED_GREEN_ON
	str	r1, [r0]
	ldr	r2, =0x00010000
flash_green_delay_3:
	subs	r2, r2, #1
	bne	flash_green_delay_3
	bic	r1, r1, #EP93XX_LED_GREEN_ON
	str	r1, [r0]
	ldr	r2, =0x00050000
flash_green_delay_4:
	subs	r2, r2, #1
	bne	flash_green_delay_4
	b	flash_green


ep93xx_sdram_done:
	ldr	r1, =EP93XX_LED_DATA
	ldr	r0, [r1]
	bic	r0, r0, #EP93XX_LED_RED_ON
	str	r0, [r1]

	/* Determine the size of the SDRAM. */
	mov	r0, r11
	bl	ep93xx_sdram_size

	/* Save the SDRAM characteristics. */
	mov	r8, r0
	mov	r9, r1
	mov	r10, r2

	/* Compute total memory size into r1 */
	mul	r1, r8, r10
#ifdef CONFIG_EDB93XX_SDCS0
	ldr	r2, [r0, #SDRAM_OFF_DEVCFG0]
#endif
#ifdef CONFIG_EDB93XX_SDCS1
	ldr	r2, [r0, #SDRAM_OFF_DEVCFG1]
#endif
#ifdef CONFIG_EDB93XX_SDCS2
	ldr	r2, [r0, #SDRAM_OFF_DEVCFG2]
#endif
#ifdef CONFIG_EDB93XX_SDCS3
	ldr	r2, [r0, #SDRAM_OFF_DEVCFG3]
#endif

	/* Consider small DRAM size as:
	 * < 32Mb for 32bit bus
	 * < 64Mb for 16bit bus
	 */
	tst	r2, #EP93XX_SDRAMCTRL_DEVCFG_EXTBUSWIDTH
	moveq	r1, r1, lsr #1
	cmp	r1, #0x02000000

#if defined(CONFIG_EDB9301)
	/* Set refresh counter to 20ms for small DRAM size, otherwise 9.6ms */
	movlt	r1, #0x03f0
	movge	r1, #0x01e0
#else
	/* Set refresh counter to 30.7ms for small DRAM size, otherwise 15ms */
	movlt	r1, #0x0600
	movge	r1, #0x2f0
#endif
	str	r1, [r0, #SDRAM_OFF_REFRSHTIMR]

	/* Save the memory configuration information. */
	orr	r0, r11, #UBOOT_MEMORYCNF_BANK_SIZE
	stmia	r0, {r8-r11}

	mov	lr, r6
	mov	pc, lr
