/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2010 Advanced Micro Devices, Inc.
 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
 *
 * 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 of the License.
 *
 * 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.
 */

static inline void _WRMSR(u32 addr, u32 lo, u32 hi)
{
	__asm__ volatile (
		"wrmsr"
		:
		:"c"(addr),"a"(lo), "d" (hi)
		);
}

static inline void _RDMSR(u32 addr, u32 *lo, u32 *hi)
{
	__asm__ volatile (
		"rdmsr"
		:"=a"(*lo), "=d" (*hi)
		:"c"(addr)
		);
}

static inline void _RDTSC(u32 *lo, u32 *hi)
{
	__asm__ volatile (
		 "rdtsc"
		 : "=a" (*lo), "=d"(*hi)
		 );
}

static inline void _cpu_id(u32 addr, u32 *val)
{
	__asm__ volatile(
		 "cpuid"
		 : "=a" (val[0]),
		   "=b" (val[1]),
		   "=c" (val[2]),
		   "=d" (val[3])
		 : "0" (addr));

}

static u32 bsr(u32 x)
{
	u8 i;
	u32 ret = 0;

	for(i=31; i>0; i--) {
		if(x & (1<<i)) {
			ret = i;
			break;
		}
	}

	return ret;

}

static u32 bsf(u32 x)
{
	u8 i;
	u32 ret = 32;

	for(i=0; i<32; i++) {
		if(x & (1<<i)) {
			ret = i;
			break;
		}
	}

	return ret;
}

#define _MFENCE asm volatile ( "mfence")

#define _SFENCE asm volatile ( "sfence" )

/* prevent speculative execution of following instructions */
#define _EXECFENCE asm volatile ("outb %al, $0xed")

#include <cpu/x86/cr.h>

u32 SetUpperFSbase(u32 addr_hi);

static void proc_MFENCE(void)
{
	__asm__ volatile (
		"outb %%al, $0xed\n\t"  /* _EXECFENCE */
		"mfence\n\t"
		:::"memory"
	);
}

static void proc_CLFLUSH(u32 addr_hi)
{
	SetUpperFSbase(addr_hi);

	__asm__ volatile (
		/* clflush fs:[eax] */
		"outb	%%al, $0xed\n\t"	/* _EXECFENCE */
		"clflush	%%fs:(%0)\n\t"
		"mfence\n\t"
		::"a" (addr_hi<<8)
	);
}


static void WriteLNTestPattern(u32 addr_lo, u8 *buf_a, u32 line_num)
{
	uint32_t step = 16;
	uint32_t count = line_num * 4;

	__asm__ volatile (
		/*prevent speculative execution of following instructions*/
		/* FIXME: needed ? */
		"outb %%al, $0xed\n\t"	/* _EXECFENCE */
		"1:\n\t"
		"movdqa (%3), %%xmm0\n\t"
		"movntdq %%xmm0, %%fs:(%0)\n\t"	/* xmm0 is 128 bit */
		"addl %1, %0\n\t"
		"addl %1, %3\n\t"
		"loop 1b\n\t"
		"mfence\n\t"

		 : "+a" (addr_lo), "+d" (step), "+c" (count), "+b" (buf_a) : :
	);

}

static u32 read32_fs(u32 addr_lo)
{
	u32 value;
	__asm__ volatile (
		"outb %%al, $0xed\n\t"	/* _EXECFENCE */
		"movl %%fs:(%1), %0\n\t"
		:"=b"(value): "a" (addr_lo)
	);
	return value;
}

static uint64_t read64_fs(uint32_t addr_lo)
{
	uint64_t value = 0;
	uint32_t value_lo;
	uint32_t value_hi;

	__asm__ volatile (
		"outb %%al, $0xed\n\t"  /* _EXECFENCE */
		"mfence\n\t"
		"movl %%fs:(%2), %0\n\t"
		"movl %%fs:(%3), %1\n\t"
		:"=c"(value_lo), "=d"(value_hi): "a" (addr_lo), "b" (addr_lo + 4) : "memory"
	);
	value |= value_lo;
	value |= ((uint64_t)value_hi) << 32;
	return value;
}

#ifdef UNUSED_CODE
static u8 read8_fs(u32 addr_lo)
{
	u8 byte;
	__asm__ volatile (
		"outb %%al, $0xed\n\t"	/* _EXECFENCE */
		"movb %%fs:(%1), %b0\n\t"
		"mfence\n\t"
		:"=b"(byte): "a" (addr_lo)
	);
	return byte;
}
#endif

static void FlushDQSTestPattern_L9(u32 addr_lo)
{
	__asm__ volatile (
		"outb %%al, $0xed\n\t"	/* _EXECFENCE */
		"clflush %%fs:-128(%%ecx)\n\t"
		"clflush %%fs:-64(%%ecx)\n\t"
		"clflush %%fs:(%%ecx)\n\t"
		"clflush %%fs:64(%%ecx)\n\t"

		"clflush %%fs:-128(%%eax)\n\t"
		"clflush %%fs:-64(%%eax)\n\t"
		"clflush %%fs:(%%eax)\n\t"
		"clflush %%fs:64(%%eax)\n\t"

		"clflush %%fs:-128(%%ebx)\n\t"

		 ::  "b" (addr_lo+128+8*64), "c"(addr_lo+128),
		     "a"(addr_lo+128+4*64)
	);

}

static __attribute__((noinline)) void FlushDQSTestPattern_L18(u32 addr_lo)
{
	__asm__ volatile (
		"outb %%al, $0xed\n\t"	/* _EXECFENCE */
		"clflush %%fs:-128(%%eax)\n\t"
		"clflush %%fs:-64(%%eax)\n\t"
		"clflush %%fs:(%%eax)\n\t"
		"clflush %%fs:64(%%eax)\n\t"

		"clflush %%fs:-128(%%edi)\n\t"
		"clflush %%fs:-64(%%edi)\n\t"
		"clflush %%fs:(%%edi)\n\t"
		"clflush %%fs:64(%%edi)\n\t"

		"clflush %%fs:-128(%%ebx)\n\t"
		"clflush %%fs:-64(%%ebx)\n\t"
		"clflush %%fs:(%%ebx)\n\t"
		"clflush %%fs:64(%%ebx)\n\t"

		"clflush %%fs:-128(%%ecx)\n\t"
		"clflush %%fs:-64(%%ecx)\n\t"
		"clflush %%fs:(%%ecx)\n\t"
		"clflush %%fs:64(%%ecx)\n\t"

		"clflush %%fs:-128(%%edx)\n\t"
		"clflush %%fs:-64(%%edx)\n\t"

		 :: "b" (addr_lo+128+8*64), "c" (addr_lo+128+12*64),
		    "d" (addr_lo +128+16*64), "a"(addr_lo+128),
		    "D"(addr_lo+128+4*64)
	);
}

static void ReadMaxRdLat1CLTestPattern_D(u32 addr)
{
	SetUpperFSbase(addr);

	__asm__ volatile (
		"outb %%al, $0xed\n\t"			/* _EXECFENCE */
		"movl %%fs:-128(%%esi), %%eax\n\t"	/* TestAddr cache line */
		"movl %%fs:-64(%%esi), %%eax\n\t"	/* +1 */
		"movl %%fs:(%%esi), %%eax\n\t"		/* +2 */
		"mfence\n\t"
		 :: "a"(0), "S"((addr<<8)+128)
	);

}

static void WriteMaxRdLat1CLTestPattern_D(u32 buf, u32 addr)
{
	uint32_t addr_phys = addr << 8;
	uint32_t step = 16;
	uint32_t count = 3 * 4;

	SetUpperFSbase(addr);

	__asm__ volatile (
		"outb %%al, $0xed\n\t"	/* _EXECFENCE */
		"1:\n\t"
		"movdqa (%3), %%xmm0\n\t"
		"movntdq %%xmm0, %%fs:(%0)\n\t" /* xmm0 is 128 bit */
		"addl %1, %0\n\t"
		"addl %1, %3\n\t"
		"loop 1b\n\t"
		"mfence\n\t"

		 : "+a" (addr_phys), "+d" (step), "+c" (count), "+b" (buf) : :
	);
}

static void FlushMaxRdLatTestPattern_D(u32 addr)
{
	/*  Flush a pattern of 72 bit times (per DQ) from cache.
	 * This procedure is used to ensure cache miss on the next read training.
	 */

	SetUpperFSbase(addr);

	__asm__ volatile (
		"outb %%al, $0xed\n\t"	/* _EXECFENCE */
		"clflush %%fs:-128(%%esi)\n\t"	 /* TestAddr cache line */
		"clflush %%fs:-64(%%esi)\n\t"	 /* +1 */
		"clflush %%fs:(%%esi)\n\t"  /* +2 */
		"mfence\n\t"

		 :: "S"((addr<<8)+128)
	);
}

static u32 stream_to_int(u8 *p)
{
	int i;
	u32 val;
	u32 valx;

	val = 0;

	for(i=3; i>=0; i--) {
		val <<= 8;
		valx = *(p+i);
		val |= valx;
	}

	return val;
}

#ifdef UNUSED_CODE
static void oemSet_NB32(u32 addr, u32 val, u8 *valid)
{
}

static u32 oemGet_NB32(u32 addr,  u8 *valid)
{
	*valid = 0;
	return 0xffffffff;
}
#endif

static u8 oemNodePresent_D(u8 Node, u8 *ret)
{
	*ret = 0;
	return 0;
}
