#ifndef CPU_X86_MTRR_H
#define CPU_X86_MTRR_H

#ifndef __ASSEMBLER__
#include <cpu/x86/msr.h>
#include <arch/cpu.h>
#endif

/*  These are the region types  */
#define MTRR_TYPE_UNCACHEABLE		0
#define MTRR_TYPE_WRCOMB		1
#define MTRR_TYPE_WRTHROUGH		4
#define MTRR_TYPE_WRPROT		5
#define MTRR_TYPE_WRBACK		6
#define MTRR_NUM_TYPES			7

#define MTRR_CAP_MSR			0x0fe

#define MTRR_CAP_PRMRR			(1 << 12)
#define MTRR_CAP_SMRR			(1 << 11)
#define MTRR_CAP_WC			(1 << 10)
#define MTRR_CAP_FIX			(1 << 8)
#define MTRR_CAP_VCNT			0xff

#define MTRR_DEF_TYPE_MSR		0x2ff
#define MTRR_DEF_TYPE_MASK		0xff
#define MTRR_DEF_TYPE_EN		(1 << 11)
#define MTRR_DEF_TYPE_FIX_EN		(1 << 10)

#define IA32_SMRR_PHYS_BASE		0x1f2
#define IA32_SMRR_PHYS_MASK		0x1f3
#define SMRR_PHYS_MASK_LOCK		(1 << 10)

/* Specific to model_6fx and model_1067x.
   These are named MSR_SMRR_PHYSBASE in the SDM. */
#define CORE2_SMRR_PHYS_BASE		0xa0
#define CORE2_SMRR_PHYS_MASK		0xa1

#define MTRR_PHYS_BASE(reg)		(0x200 + 2 * (reg))
#define MTRR_PHYS_MASK(reg)		(MTRR_PHYS_BASE(reg) + 1)
#define  MTRR_PHYS_MASK_VALID		(1 << 11)

#define NUM_FIXED_RANGES		88
#define RANGES_PER_FIXED_MTRR		8
#define MTRR_FIX_64K_00000		0x250
#define MTRR_FIX_16K_80000		0x258
#define MTRR_FIX_16K_A0000		0x259
#define MTRR_FIX_4K_C0000		0x268
#define MTRR_FIX_4K_C8000		0x269
#define MTRR_FIX_4K_D0000		0x26a
#define MTRR_FIX_4K_D8000		0x26b
#define MTRR_FIX_4K_E0000		0x26c
#define MTRR_FIX_4K_E8000		0x26d
#define MTRR_FIX_4K_F0000		0x26e
#define MTRR_FIX_4K_F8000		0x26f

#if !defined(__ASSEMBLER__)

#include <stdint.h>
#include <stddef.h>

/*
 * The MTRR code has some side effects that the callers should be aware for.
 * 1. The call sequence matters. x86_setup_mtrrs() calls
 *    x86_setup_fixed_mtrrs_no_enable() then enable_fixed_mtrrs() (equivalent
 *    of x86_setup_fixed_mtrrs()) then x86_setup_var_mtrrs(). If the callers
 *    want to call the components of x86_setup_mtrrs() because of other
 *    requirements the ordering should still preserved.
 * 2. enable_fixed_mtrr() will enable both variable and fixed MTRRs because
 *    of the nature of the global MTRR enable flag. Therefore, all direct
 *    or indirect callers of enable_fixed_mtrr() should ensure that the
 *    variable MTRR MSRs do not contain bad ranges.
 *
 * Note that this function sets up MTRRs for addresses above 4GiB.
 */
void x86_setup_mtrrs(void);
/*
 * x86_setup_mtrrs_with_detect() does the same thing as x86_setup_mtrrs(), but
 * it always dynamically detects the number of variable MTRRs available.
 */
void x86_setup_mtrrs_with_detect(void);
void x86_setup_mtrrs_with_detect_no_above_4gb(void);
/*
 * x86_setup_var_mtrrs() parameters:
 * address_bits - number of physical address bits supported by cpu
 * above4gb - if set setup MTRRs for addresses above 4GiB else ignore
 *            memory ranges above 4GiB
 */
void x86_setup_var_mtrrs(unsigned int address_bits, unsigned int above4gb);
void enable_fixed_mtrr(void);
/* Unhide Rd/WrDram bits and allow modification for AMD. */
void fixed_mtrrs_expose_amd_rwdram(void);
/* Hide Rd/WrDram bits and allow modification for AMD. */
void fixed_mtrrs_hide_amd_rwdram(void);
void x86_setup_fixed_mtrrs(void);
/* Set up fixed MTRRs but do not enable them. */
void x86_setup_fixed_mtrrs_no_enable(void);
void x86_mtrr_check(void);

/* Insert a temporary MTRR range for the duration of coreboot's runtime.
 * This function needs to be called after the first MTRR solution is derived. */
void mtrr_use_temp_range(uintptr_t begin, size_t size, int type);

static inline int get_var_mtrr_count(void)
{
	return rdmsr(MTRR_CAP_MSR).lo & MTRR_CAP_VCNT;
}

void set_var_mtrr(unsigned int reg, unsigned int base, unsigned int size,
	unsigned int type);
int get_free_var_mtrr(void);
void clear_all_var_mtrr(void);

asmlinkage void display_mtrrs(void);

/* Variable MTRR structure to help track and set MTRRs prior to ramstage. This
   and the following APIs can be used to set up more complex MTRR solutions
   instead of open coding get_free_var_mtrr() and set_var_mtrr() or for determining
   a future solution, such as postcar_loader. */
struct var_mtrr_context {
	uint32_t upper_mask;
	int max_var_mtrrs;
	int used_var_mtrrs;
	void *arg; /* optional callback parameter */
};

/* Returns 0-relative MTRR from context. Use MTRR_PHYS_BASE|MASK macros for calculating
   MSR address value. */
static inline int var_mtrr_context_current_mtrr(const struct var_mtrr_context *ctx)
{
	return ctx->used_var_mtrrs;
}

/* Initialize var_mtrr_context object. Assumes all variable MTRRs are not yet used. */
void var_mtrr_context_init(struct var_mtrr_context *ctx, void *arg);
/* Allocate a variable mtrr base and mask, calling the provided callback for each MTRR
   MSR base-mask pair needed to accommodate the address and size request.
   Returns < 0 on error and 0 on success. */
int var_mtrr_set_with_cb(struct var_mtrr_context *ctx,
			uintptr_t addr, size_t size, int type,
			void (*callback)(const struct var_mtrr_context *ctx,
						uintptr_t base_addr, size_t size,
						msr_t base, msr_t mask));
/* Same as var_mtrr_set_with_cb() but just write the MSRs directly. */
int var_mtrr_set(struct var_mtrr_context *ctx, uintptr_t addr, size_t size, int type);

/*
 * Set the MTRRs using the data on the stack from setup_stack_and_mtrrs.
 * Return a new top_of_stack value which removes the setup_stack_and_mtrrs data.
 */
asmlinkage void *soc_set_mtrrs(void *top_of_stack);
asmlinkage void soc_enable_mtrrs(void);

/* fms: find most significant bit set, stolen from Linux Kernel Source. */
static inline unsigned int fms(unsigned int x)
{
	unsigned int r;

	__asm__("bsrl %1,%0\n\t"
		"jnz 1f\n\t"
		"movl $0,%0\n"
		"1:" : "=r" (r) : "mr" (x));
	return r;
}

/* fls: find least significant bit set */
static inline unsigned int fls(unsigned int x)
{
	unsigned int r;

	__asm__("bsfl %1,%0\n\t"
		"jnz 1f\n\t"
		"movl $32,%0\n"
		"1:" : "=r" (r) : "mr" (x));
	return r;
}
#endif /* !defined(__ASSEMBLER__) */

/* Align up/down to next power of 2, suitable for assembler
   too. Range of result 256kB to 128MB is good enough here. */
#define _POW2_MASK(x)	((x>>1)|(x>>2)|(x>>3)|(x>>4)|(x>>5)| \
					(x>>6)|(x>>7)|(x>>8)|((1<<18)-1))
#define _ALIGN_UP_POW2(x)	((x + _POW2_MASK(x)) & ~_POW2_MASK(x))
#define _ALIGN_DOWN_POW2(x)	((x) & ~_POW2_MASK(x))

/* Calculate `4GiB - x` (e.g. absolute address for offset from 4GiB) */
#define _FROM_4G_TOP(x) ((0xffffffff - (x)) + 1)

/* At the end of romstage, low RAM 0..CACHE_TM_RAMTOP may be set
 * as write-back cacheable to speed up ramstage decompression.
 * Note MTRR boundaries, must be power of two.
 */
#define CACHE_TMP_RAMTOP (16<<20)

/* For ROM caching, generally, try to use the next power of 2. */
#define OPTIMAL_CACHE_ROM_SIZE _ALIGN_UP_POW2(CONFIG_ROM_SIZE)
#define OPTIMAL_CACHE_ROM_BASE _FROM_4G_TOP(OPTIMAL_CACHE_ROM_SIZE)
#if (OPTIMAL_CACHE_ROM_SIZE < CONFIG_ROM_SIZE) || \
    (OPTIMAL_CACHE_ROM_SIZE >= (2 * CONFIG_ROM_SIZE))
# error "Optimal CACHE_ROM_SIZE can't be derived, _POW2_MASK needs refinement."
#endif

/* Make sure it doesn't overlap CAR, though. If the gap between
   CAR and 4GiB is too small, make it at most the size of this
   gap. As we can't align up (might overlap again), align down
   to get a power of 2 again, for a single MTRR. */
#define CAR_END (CONFIG_DCACHE_RAM_BASE + CONFIG_DCACHE_RAM_SIZE)
#if CAR_END > OPTIMAL_CACHE_ROM_BASE
# define CAR_CACHE_ROM_SIZE _ALIGN_DOWN_POW2(_FROM_4G_TOP(CAR_END))
#else
# define CAR_CACHE_ROM_SIZE OPTIMAL_CACHE_ROM_SIZE
#endif
#if ((CAR_CACHE_ROM_SIZE & (CAR_CACHE_ROM_SIZE - 1)) != 0)
# error "CAR CACHE_ROM_SIZE is not a power of 2, _POW2_MASK needs refinement."
#endif

/* Last but not least, most (if not all) chipsets have MMIO
   between 0xfe000000 and 0xff000000, so limit to 16MiB. */
#if CAR_CACHE_ROM_SIZE >= 16 << 20
# define CACHE_ROM_SIZE (16 << 20)
#else
# define CACHE_ROM_SIZE CAR_CACHE_ROM_SIZE
#endif

#define CACHE_ROM_BASE _FROM_4G_TOP(CACHE_ROM_SIZE)

#endif /* CPU_X86_MTRR_H */
