/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2007 Advanced Micro Devices, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

#define	LX_STACK_BASE		DCACHE_RAM_BASE		/* this is where the DCache will be mapped and be used as stack, It would be cool if it was the same base as coreboot normal stack */
#define	LX_STACK_END		LX_STACK_BASE+(DCACHE_RAM_SIZE-1)

#define	LX_NUM_CACHELINES	0x080	/* there are 128lines per way */
#define	LX_CACHELINE_SIZE	0x020	/* there are 32bytes per line */
#define	LX_CACHEWAY_SIZE	(LX_NUM_CACHELINES * LX_CACHELINE_SIZE)
#define	CR0_CD				0x40000000	/* bit 30 = Cache Disable */
#define	CR0_NW				0x20000000	/* bit 29 = Not Write Through */
#include <cpu/amd/lxdef.h>
/***************************************************************************
/**
/**	DCacheSetup
/**
/**	Setup data cache for  use as RAM for a stack.
/**
/***************************************************************************/
DCacheSetup:

	invd
	/* set cache properties */
	movl	$CPU_RCONF_DEFAULT, %ecx
	rdmsr
	movl	$0x010010000, %eax		/*1MB system memory in write back 1|00100|00 */
	wrmsr

	/* in LX DCDIS is set after POR which disables the cache..., clear this bit */
	movl	CPU_DM_CONFIG0,%ecx
	rdmsr
	andl	$(~(DM_CONFIG0_LOWER_DCDIS_SET)), %eax	/* TODO: make consistent with i$ init,	either whole reg = 0,  or just this bit... */
	wrmsr

	/* get cache timing params from BIOS config data locations and apply */
	/* fix delay controls for DM and IM arrays */
	/* fix delay controls for DM and IM arrays */
	movl	$CPU_BC_MSS_ARRAY_CTL0, %ecx
	xorl	%edx, %edx
	movl	$0x2814D352, %eax
	wrmsr

	movl	$CPU_BC_MSS_ARRAY_CTL1, %ecx
	xorl	%edx, %edx
	movl	$0x1068334D, %eax
	wrmsr

	movl	$CPU_BC_MSS_ARRAY_CTL2, %ecx
	movl	$0x00000106, %edx
	movl	$0x83104104, %eax
	wrmsr

	movl	$GLCP_FIFOCTL, %ecx
	rdmsr
	movl	$0x00000005, %edx
	wrmsr

	/* Enable setting */
	movl	$CPU_BC_MSS_ARRAY_CTL_ENA, %ecx
	xorl	%edx, %edx
	movl	$0x01, %eax
	wrmsr

	/* Get cleaned up. */
	xorl	%edi, %edi
	xorl	%esi, %esi
	xorl	%ebp, %ebp

	/* DCache Ways0 through Ways7 will be tagged for LX_STACK_BASE + DCACHE_RAM_SIZE for holding stack */
	/* remember,  there is NO stack yet... */

	/* Tell cache we want to fill WAY 0 starting at the top */
	xorl	%edx, %edx
	xorl	%eax, %eax
	movl	$CPU_DC_INDEX, %ecx
	wrmsr

	/* startaddress for tag of Way0: ebp will hold the incrementing address. dont destroy! */
	movl	$LX_STACK_BASE, %ebp	/* init to start address */
	orl		$1, %ebp				/* set valid bit and tag for this Way (B[31:12] : Cache tag value for line/way curr. selected by CPU_DC_INDEX */

	/* start tag Ways 0 with 128 lines with 32bytes each: edi will hold the line counter. dont destroy! */
	movl	$LX_NUM_CACHELINES, %edi
DCacheSetupFillWay:

	/* fill with dummy data: zero it so we can tell it from PCI memory space (returns FFs). */
	/* We will now store a line (32 bytes = 4 x 8bytes = 4 quadWords) */
	movw	$0x04, %si
	xorl	%edx, %edx
	xorl	%eax, %eax
	movl	$CPU_DC_DATA, %ecx
DCacheSetup_quadWordLoop:
	wrmsr
	decw	%si
	jnz	DCacheSetup_quadWordLoop

	/* Set the tag for this line,  need to do this for every new cache line to validate it! */
	/* accessing CPU_DC_TAG_I makes the LINE field in CPU_DC_INDEX increment and thus cont. in the next cache line... */
	xorl	%edx, %edx
	movl	%ebp, %eax
	movl	$CPU_DC_TAG, %ecx
	wrmsr

	/* switch to next line */
	/* lines are in Bits10:4 */
	/* when index is crossing 0x7F -> 0x80	writing a RSVD bit as 0x80 is not a valid CL anymore! */
	movl	$CPU_DC_INDEX, %ecx
	rdmsr
	addl	$0x010, %eax /* TODO: prob. would be more elegant to calc. this from counter var edi... */
	wrmsr

	decl	%edi
	jnz	DCacheSetupFillWay

	/* 1 Way has been filled,  forward start address for next Way,	terminate if we have reached end of desired address range */
	addl	$LX_CACHEWAY_SIZE, %ebp
	cmpl	$LX_STACK_END, %ebp
	jge	leave_DCacheSetup
	movl	$LX_NUM_CACHELINES, %edi

	/* switch to next way */
	movl	$CPU_DC_INDEX, %ecx
	rdmsr
	addl	$0x01, %eax
	andl	$0xFFFFF80F, %eax /* lets be sure: reset line index Bits10:4 */
	wrmsr

	jmp	DCacheSetupFillWay

leave_DCacheSetup:
	xorl	%edi, %edi
	xorl	%esi, %esi
	xorl	%ebp, %ebp

	/* Disable the cache,  but ... DO NOT INVALIDATE the tags. */
	/* Memory reads and writes will all hit in the cache. */
	/* Cache updates and memory write-backs will not occur ! */
	movl	%cr0, %eax
	orl		$(CR0_CD + CR0_NW), %eax	/* set the CD and NW bits */
	movl	%eax, %cr0

	/* Now point sp to the cached stack. */
	/* The stack will be fully functional at this location. No system memory is required at all ! */
	/* set up the stack pointer */
	movl	$LX_STACK_END, %eax
	movl	%eax, %esp

	/* test the stack*/
	movl	$0x0F0F05A5A, %edx
	pushl	%edx
	popl	%ecx
	cmpl	%ecx, %edx
	je	DCacheSetupGood
	movb	$0xC5, %al
	outb	%al, $0x80
DCacheSetupBad:
	hlt		/* issues */
	jmp DCacheSetupBad
DCacheSetupGood:

	/* Go do early init and memory setup */
	call	cache_as_ram_main
done_cache_as_ram_main:

	/* If you wanted to maintain the stack in memory you would need to set the tags as dirty
	  so the wbinvd would push out the old stack contents to memory */
	/* Clear the cache, the following code from crt0.S.lb will setup a new stack*/
	wbinvd

/* the following code is from crt0.S.lb */
/* This takes the place of the post-CAR funtions that the K8 uses to setup the stack and copy LB low.*/

#ifndef CONSOLE_DEBUG_TX_STRING
	/* uses:	 esp, ebx, ax, dx */
# define __CRT_CONSOLE_TX_STRING(string) \
	mov	string, %ebx	; \
	CALLSP(crt_console_tx_string)

# if defined(TTYS0_BASE) && (ASM_CONSOLE_LOGLEVEL > BIOS_DEBUG)
#  define CONSOLE_DEBUG_TX_STRING(string)        __CRT_CONSOLE_TX_STRING(string)
# else
#  define CONSOLE_DEBUG_TX_STRING(string)
# endif
#endif

	/* clear boot_complete flag */
	xorl	%ebp, %ebp
__main:
	CONSOLE_DEBUG_TX_STRING($str_copying_to_ram)

	/*
	 *	Copy data into RAM and clear the BSS. Since these segments
	 *	isn\'t really that big we just copy/clear using bytes, not
	 *	double words.
	 */
	intel_chip_post_macro(0x11)		/* post 11 */

	cld				/* clear direction flag */

	/* copy coreboot from it's initial load location to
	 * the location it is compiled to run at.
	 * Normally this is copying from FLASH ROM to RAM.
	 */
#if !CONFIG_COMPRESS
	movl	$_liseg, %esi
	movl	$_iseg,  %edi
	movl	$_eiseg, %ecx
	subl	%edi, %ecx
	rep	movsb
#else
	leal	4+_liseg, %esi
	leal	_iseg, %edi
	movl	%ebp, %esp	/* preserve %ebp */
	movl	$-1, %ebp	/* last_m_off = -1 */
	jmp	dcl1_n2b

/* ------------- DECOMPRESSION -------------

 Input:
   %esi - source
   %edi - dest
   %ebp - -1
   cld

 Output:
   %eax - 0
   %ecx - 0
*/

.macro getbit bits
.if	\bits == 1
	addl	%ebx, %ebx
	jnz	1f
.endif
	movl	(%esi), %ebx
	subl	$-4, %esi	/* sets carry flag */
	adcl	%ebx, %ebx
1:
.endm

decompr_literals_n2b:
	movsb

decompr_loop_n2b:
	addl	%ebx, %ebx
	jnz	dcl2_n2b
dcl1_n2b:
	getbit	32
dcl2_n2b:
	jc	decompr_literals_n2b
	xorl	%eax, %eax
	incl	%eax		/* m_off = 1 */
loop1_n2b:
	getbit	1
	adcl	%eax, %eax	/* m_off = m_off*2 + getbit() */
	getbit	1
	jnc	loop1_n2b	/* while(!getbit()) */
	xorl	%ecx, %ecx
	subl	$3, %eax
	jb	decompr_ebpeax_n2b	/* if (m_off == 2) goto decompr_ebpeax_n2b ? */
	shll	$8, %eax
	movb	(%esi), %al	/* m_off = (m_off - 3)*256 + src[ilen++] */
	incl	%esi
	xorl	$-1, %eax
	jz	decompr_end_n2b	/* if (m_off == 0xffffffff) goto decomp_end_n2b */
	movl	%eax, %ebp	/* last_m_off = m_off ?*/
decompr_ebpeax_n2b:
	getbit	1
	adcl	%ecx, %ecx	/* m_len = getbit() */
	getbit	1
	adcl	%ecx, %ecx	/* m_len = m_len*2 + getbit()) */
	jnz	decompr_got_mlen_n2b	/* if (m_len == 0) goto decompr_got_mlen_n2b */
	incl	%ecx		/* m_len++ */
loop2_n2b:
	getbit	1
	adcl	%ecx, %ecx	/* m_len = m_len*2 + getbit() */
	getbit	1
	jnc	loop2_n2b	/* while(!getbit()) */
	incl	%ecx
	incl	%ecx		/* m_len += 2 */
decompr_got_mlen_n2b:
	cmpl	$-0xd00, %ebp
	adcl	$1, %ecx	/* m_len = m_len + 1 + (last_m_off > 0xd00) */
	movl	%esi, %edx
	leal	(%edi,%ebp), %esi	/* m_pos = dst + olen + -m_off  */
	rep
	movsb			/* dst[olen++] = *m_pos++ while(m_len > 0) */
	movl	%edx, %esi
	jmp	decompr_loop_n2b
decompr_end_n2b:
	intel_chip_post_macro(0x12)		/* post 12 */

	movl	%esp, %ebp
#endif

	CONSOLE_DEBUG_TX_STRING($str_pre_main)
	leal	_iseg, %edi
	jmp	*%edi

.Lhlt:
	intel_chip_post_macro(0xee)	/* post fail ee */
	hlt
	jmp	.Lhlt

#ifdef __CRT_CONSOLE_TX_STRING
	/* Uses esp, ebx, ax, dx  */
crt_console_tx_string:
	mov	(%ebx), %al
	inc	%ebx
	cmp	$0, %al
	jne	9f
	RETSP
9:
/* Base Address */
#ifndef TTYS0_BASE
#define TTYS0_BASE	0x3f8
#endif
/* Data */
#define TTYS0_RBR (TTYS0_BASE+0x00)

/* Control */
#define TTYS0_TBR TTYS0_RBR
#define TTYS0_IER (TTYS0_BASE+0x01)
#define TTYS0_IIR (TTYS0_BASE+0x02)
#define TTYS0_FCR TTYS0_IIR
#define TTYS0_LCR (TTYS0_BASE+0x03)
#define TTYS0_MCR (TTYS0_BASE+0x04)
#define TTYS0_DLL TTYS0_RBR
#define TTYS0_DLM TTYS0_IER

/* Status */
#define TTYS0_LSR (TTYS0_BASE+0x05)
#define TTYS0_MSR (TTYS0_BASE+0x06)
#define TTYS0_SCR (TTYS0_BASE+0x07)

	mov	%al, %ah
10:	mov	$TTYS0_LSR, %dx
	inb	%dx, %al
	test	$0x20, %al
	je	10b
	mov	$TTYS0_TBR, %dx
	mov	%ah, %al
	outb	%al, %dx

	jmp crt_console_tx_string
#endif /* __CRT_CONSOLE_TX_STRING */

#if defined(CONSOLE_DEBUG_TX_STRING) && (ASM_CONSOLE_LOGLEVEL > BIOS_DEBUG)
.section ".rom.data"
str_copying_to_ram:  .string "Copying coreboot to ram.\r\n"
str_pre_main:        .string "Jumping to coreboot.\r\n"
.previous

#endif /* ASM_CONSOLE_LOGLEVEL > BIOS_DEBUG */
