/*
- * intel_mtrr.c: setting MTRR to decent values for cache initialization on P6
+ * mtrr.c: setting MTRR to decent values for cache initialization on P6
*
* Derived from intel_set_mtrr in intel_subr.c and mtrr.c in linux kernel
*
* Reference: Intel Architecture Software Developer's Manual, Volume 3: System Programming
*/
+/*
+ 2005.1 yhlu add NC support to spare mtrrs for 64G memory above installed
+ 2005.6 Eric add address bit in x86_setup_mtrrs
+ 2005.6 yhlu split x86_setup_var_mtrrs and x86_setup_fixed_mtrrs,
+ for AMD, it will not use x86_setup_fixed_mtrrs
+*/
+
+#include <stddef.h>
#include <console/console.h>
#include <device/device.h>
#include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h>
#include <cpu/x86/cache.h>
-#warning "FIXME I do not properly handle address more than 36 physical address bits"
-#ifdef k8
-# define ADDRESS_BITS 40
-#else
-# define ADDRESS_BITS 36
+#ifdef CONFIG_GFXUMA
+extern uint64_t uma_memory_base, uma_memory_size;
#endif
-#define ADDRESS_BITS_HIGH (ADDRESS_BITS - 32)
-#define ADDRESS_MASK_HIGH ((1u << ADDRESS_BITS_HIGH) - 1)
static unsigned int mtrr_msr[] = {
MTRRfix64K_00000_MSR, MTRRfix16K_80000_MSR, MTRRfix16K_A0000_MSR,
};
-static void enable_fixed_mtrr(void)
+void enable_fixed_mtrr(void)
{
msr_t msr;
}
/* setting variable mtrr, comes from linux kernel source */
-static void set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek, unsigned char type)
+static void set_var_mtrr(
+ unsigned int reg, unsigned long basek, unsigned long sizek,
+ unsigned char type, unsigned address_bits)
{
msr_t base, mask;
+ unsigned address_mask_high;
+
+ if (reg >= 8)
+ return;
+
+ // it is recommended that we disable and enable cache when we
+ // do this.
+ if (sizek == 0) {
+ disable_cache();
+
+ msr_t zero;
+ zero.lo = zero.hi = 0;
+ /* The invalid bit is kept in the mask, so we simply clear the
+ relevant mask register to disable a range. */
+ wrmsr (MTRRphysMask_MSR(reg), zero);
+
+ enable_cache();
+ return;
+ }
+
+
+ address_mask_high = ((1u << (address_bits - 32u)) - 1u);
base.hi = basek >> 22;
base.lo = basek << 10;
- //printk_debug("ADDRESS_MASK_HIGH=%#x\n", ADDRESS_MASK_HIGH);
+ printk_spew("ADDRESS_MASK_HIGH=%#x\n", address_mask_high);
if (sizek < 4*1024*1024) {
- mask.hi = ADDRESS_MASK_HIGH;
+ mask.hi = address_mask_high;
mask.lo = ~((sizek << 10) -1);
}
else {
- mask.hi = ADDRESS_MASK_HIGH & (~((sizek >> 22) -1));
+ mask.hi = address_mask_high & (~((sizek >> 22) -1));
mask.lo = 0;
}
- if (reg >= 8)
- return;
-
// it is recommended that we disable and enable cache when we
// do this.
disable_cache();
- if (sizek == 0) {
- msr_t zero;
- zero.lo = zero.hi = 0;
- /* The invalid bit is kept in the mask, so we simply clear the
- relevant mask register to disable a range. */
- wrmsr (MTRRphysMask_MSR(reg), zero);
- } else {
- /* Bit 32-35 of MTRRphysMask should be set to 1 */
- base.lo |= type;
- mask.lo |= 0x800;
- wrmsr (MTRRphysBase_MSR(reg), base);
- wrmsr (MTRRphysMask_MSR(reg), mask);
- }
+
+ /* Bit 32-35 of MTRRphysMask should be set to 1 */
+ base.lo |= type;
+ mask.lo |= 0x800;
+ wrmsr (MTRRphysBase_MSR(reg), base);
+ wrmsr (MTRRphysMask_MSR(reg), mask);
+
enable_cache();
}
return r;
}
-/* fms: find least sigificant bit set */
+/* fls: find least sigificant bit set */
static inline unsigned int fls(unsigned int x)
{
int r;
* ramsize = 156MB == 128MB WB (at 0MB) + 32MB WB (at 128MB) + 4MB UC (at 156MB)
*/
/* 2 MTRRS are reserved for the operating system */
-#if 0
+#if 1
#define BIOS_MTRRS 6
#define OS_MTRRS 2
#else
static unsigned int range_to_mtrr(unsigned int reg,
unsigned long range_startk, unsigned long range_sizek,
- unsigned long next_range_startk)
+ unsigned long next_range_startk, unsigned char type, unsigned address_bits)
{
- if (!range_sizek || (reg >= BIOS_MTRRS)) {
+ if (!range_sizek) {
+ /* If there's no MTRR hole, this function will bail out
+ * here when called for the hole.
+ */
+ printk_spew("Zero-sized MTRR range @%ldKB\n", range_startk);
+ return reg;
+ }
+
+ if (reg >= BIOS_MTRRS) {
+ printk_err("Warning: Out of MTRRs for base: %4ldMB, range: %ldMB, type %s\n",
+ range_startk >>10, range_sizek >> 10,
+ (type==MTRR_TYPE_UNCACHEABLE)?"UC":
+ ((type==MTRR_TYPE_WRBACK)?"WB":"Other") );
return reg;
}
+
while(range_sizek) {
unsigned long max_align, align;
unsigned long sizek;
align = max_align;
}
sizek = 1 << align;
- printk_debug("Setting variable MTRR %d, base: %4dMB, range: %4dMB, type WB\n",
- reg, range_startk >>10, sizek >> 10);
- set_var_mtrr(reg++, range_startk, sizek, MTRR_TYPE_WRBACK);
+ printk_debug("Setting variable MTRR %d, base: %4ldMB, range: %4ldMB, type %s\n",
+ reg, range_startk >>10, sizek >> 10,
+ (type==MTRR_TYPE_UNCACHEABLE)?"UC":
+ ((type==MTRR_TYPE_WRBACK)?"WB":"Other")
+ );
+ set_var_mtrr(reg++, range_startk, sizek, type, address_bits);
range_startk += sizek;
range_sizek -= sizek;
- if (reg >= BIOS_MTRRS)
+ if (reg >= BIOS_MTRRS) {
+ printk_err("Running out of variable MTRRs!\n");
break;
+ }
}
return reg;
}
return resultk;
}
-void x86_setup_mtrrs(void)
+static void set_fixed_mtrr_resource(void *gp, struct device *dev, struct resource *res)
{
- /* Try this the simple way of incrementally adding together
- * mtrrs. If this doesn't work out we can get smart again
- * and clear out the mtrrs.
- */
- struct device *dev;
+ unsigned int start_mtrr;
+ unsigned int last_mtrr;
+ start_mtrr = fixed_mtrr_index(resk(res->base));
+ last_mtrr = fixed_mtrr_index(resk((res->base + res->size)));
+ if (start_mtrr >= NUM_FIXED_RANGES) {
+ return;
+ }
+ printk_debug("Setting fixed MTRRs(%d-%d) Type: WB\n",
+ start_mtrr, last_mtrr);
+ set_fixed_mtrrs(start_mtrr, last_mtrr, MTRR_TYPE_WRBACK);
+
+}
+
+#ifndef CONFIG_VAR_MTRR_HOLE
+#define CONFIG_VAR_MTRR_HOLE 1
+#endif
+
+struct var_mtrr_state {
unsigned long range_startk, range_sizek;
unsigned int reg;
+#if CONFIG_VAR_MTRR_HOLE
+ unsigned long hole_startk, hole_sizek;
+#endif
+ unsigned address_bits;
+};
- printk_debug("\n");
- /* Initialized the fixed_mtrrs to uncached */
- printk_debug("Setting fixed MTRRs(%d-%d) type: UC\n",
- 0, NUM_FIXED_RANGES);
- set_fixed_mtrrs(0, NUM_FIXED_RANGES, MTRR_TYPE_UNCACHEABLE);
-
- /* Now see which of the fixed mtrrs cover ram.
+void set_var_mtrr_resource(void *gp, struct device *dev, struct resource *res)
+{
+ struct var_mtrr_state *state = gp;
+ unsigned long basek, sizek;
+ if (state->reg >= BIOS_MTRRS)
+ return;
+ basek = resk(res->base);
+ sizek = resk(res->size);
+ /* See if I can merge with the last range
+ * Either I am below 1M and the fixed mtrrs handle it, or
+ * the ranges touch.
*/
- for(dev = all_devices; dev; dev = dev->next) {
- struct resource *res, *last;
- last = &dev->resource[dev->resources];
- for(res = &dev->resource[0]; res < last; res++) {
- unsigned int start_mtrr;
- unsigned int last_mtrr;
- if (!(res->flags & IORESOURCE_MEM) ||
- !(res->flags & IORESOURCE_CACHEABLE))
- {
- continue;
- }
- start_mtrr = fixed_mtrr_index(resk(res->base));
- last_mtrr = fixed_mtrr_index(resk((res->base + res->size)));
- if (start_mtrr >= NUM_FIXED_RANGES) {
- continue;
- }
- printk_debug("Setting fixed MTRRs(%d-%d) Type: WB\n",
- start_mtrr, last_mtrr);
- set_fixed_mtrrs(start_mtrr, last_mtrr, MTRR_TYPE_WRBACK);
+ if ((basek <= 1024) || (state->range_startk + state->range_sizek == basek)) {
+ unsigned long endk = basek + sizek;
+ state->range_sizek = endk - state->range_startk;
+ return;
+ }
+ /* Write the range mtrrs */
+ if (state->range_sizek != 0) {
+#if CONFIG_VAR_MTRR_HOLE
+ if (state->hole_sizek == 0) {
+ /* We need to put that on to hole */
+ unsigned long endk = basek + sizek;
+ state->hole_startk = state->range_startk + state->range_sizek;
+ state->hole_sizek = basek - state->hole_startk;
+ state->range_sizek = endk - state->range_startk;
+ return;
}
+#endif
+ state->reg = range_to_mtrr(state->reg, state->range_startk,
+ state->range_sizek, basek, MTRR_TYPE_WRBACK, state->address_bits);
+#if CONFIG_VAR_MTRR_HOLE
+ state->reg = range_to_mtrr(state->reg, state->hole_startk,
+ state->hole_sizek, basek, MTRR_TYPE_UNCACHEABLE, state->address_bits);
+#endif
+ state->range_startk = 0;
+ state->range_sizek = 0;
+#if CONFIG_VAR_MTRR_HOLE
+ state->hole_startk = 0;
+ state->hole_sizek = 0;
+#endif
}
- printk_debug("DONE fixed MTRRs\n");
+ /* Allocate an msr */
+ printk_spew(" Allocate an msr - basek = %08lx, sizek = %08lx,\n", basek, sizek);
+ state->range_startk = basek;
+ state->range_sizek = sizek;
+}
+
+void x86_setup_fixed_mtrrs(void)
+{
+ /* Try this the simple way of incrementally adding together
+ * mtrrs. If this doesn't work out we can get smart again
+ * and clear out the mtrrs.
+ */
+
+ printk_debug("\n");
+ /* Initialized the fixed_mtrrs to uncached */
+ printk_debug("Setting fixed MTRRs(%d-%d) Type: UC\n",
+ 0, NUM_FIXED_RANGES);
+ set_fixed_mtrrs(0, NUM_FIXED_RANGES, MTRR_TYPE_UNCACHEABLE);
+
+ /* Now see which of the fixed mtrrs cover ram.
+ */
+ search_global_resources(
+ IORESOURCE_MEM | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_CACHEABLE,
+ set_fixed_mtrr_resource, NULL);
+ printk_debug("DONE fixed MTRRs\n");
+
+ /* enable fixed MTRR */
+ printk_spew("call enable_fixed_mtrr()\n");
+ enable_fixed_mtrr();
+
+}
+
+void x86_setup_var_mtrrs(unsigned address_bits)
+/* this routine needs to know how many address bits a given processor
+ * supports. CPUs get grumpy when you set too many bits in
+ * their mtrr registers :( I would generically call cpuid here
+ * and find out how many physically supported but some cpus are
+ * buggy, and report more bits then they actually support.
+ */
+{
+ /* Try this the simple way of incrementally adding together
+ * mtrrs. If this doesn't work out we can get smart again
+ * and clear out the mtrrs.
+ */
+ struct var_mtrr_state var_state;
+
/* Cache as many memory areas as possible */
/* FIXME is there an algorithm for computing the optimal set of mtrrs?
* In some cases it is definitely possible to do better.
*/
- range_startk = 0;
- range_sizek = 0;
- reg = 0;
- for(dev = all_devices; dev; dev = dev->next) {
- struct resource *res, *last;
- last = &dev->resource[dev->resources];
- for(res = &dev->resource[0]; res < last; res++) {
- unsigned long basek, sizek;
- if (!(res->flags & IORESOURCE_MEM) ||
- !(res->flags & IORESOURCE_CACHEABLE)) {
- continue;
- }
- basek = resk(res->base);
- sizek = resk(res->size);
- /* See if I can merge with the last range
- * Either I am below 1M and the fixed mtrrs handle it, or
- * the ranges touch.
- */
- if ((basek <= 1024) || (range_startk + range_sizek == basek)) {
- unsigned long endk = basek + sizek;
- range_sizek = endk - range_startk;
- continue;
- }
- /* Write the range mtrrs */
- if (range_sizek != 0) {
- reg = range_to_mtrr(reg, range_startk, range_sizek, basek);
- range_startk = 0;
- range_sizek = 0;
- if (reg >= BIOS_MTRRS)
- goto last_msr;
- }
- /* Allocate an msr */
- range_startk = basek;
- range_sizek = sizek;
- }
+ var_state.range_startk = 0;
+ var_state.range_sizek = 0;
+#if CONFIG_VAR_MTRR_HOLE
+ var_state.hole_startk = 0;
+ var_state.hole_sizek = 0;
+#endif
+ var_state.reg = 0;
+ var_state.address_bits = address_bits;
+
+ search_global_resources(
+ IORESOURCE_MEM | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_CACHEABLE,
+ set_var_mtrr_resource, &var_state);
+#ifdef CONFIG_GFXUMA
+ // For now we assume the UMA space is at the end of memory
+ if (var_state.hole_startk || var_state.hole_sizek) {
+ printk_debug("Warning: Can't set up MTRR hole for UMA due to pre-existing MTRR hole.\n");
+ } else {
+ // Increase the base range and set up UMA as an UC hole instead
+ var_state.range_sizek += (uma_memory_size >> 10);
+
+ var_state.hole_startk = (uma_memory_base >> 10);
+ var_state.hole_sizek = (uma_memory_size >> 10);
}
- last_msr:
+#endif
/* Write the last range */
- reg = range_to_mtrr(reg, range_startk, range_sizek, 0);
+ var_state.reg = range_to_mtrr(var_state.reg, var_state.range_startk,
+ var_state.range_sizek, 0, MTRR_TYPE_WRBACK, var_state.address_bits);
+#if CONFIG_VAR_MTRR_HOLE
+ var_state.reg = range_to_mtrr(var_state.reg, var_state.hole_startk,
+ var_state.hole_sizek, 0, MTRR_TYPE_UNCACHEABLE, var_state.address_bits);
+#endif
printk_debug("DONE variable MTRRs\n");
printk_debug("Clear out the extra MTRR's\n");
/* Clear out the extra MTRR's */
- while(reg < MTRRS) {
- set_var_mtrr(reg++, 0, 0, 0);
+ while(var_state.reg < MTRRS) {
+ set_var_mtrr(var_state.reg++, 0, 0, 0, var_state.address_bits);
}
- /* enable fixed MTRR */
- printk_spew("call enable_fixed_mtrr()\n");
- enable_fixed_mtrr();
printk_spew("call enable_var_mtrr()\n");
enable_var_mtrr();
- printk_spew("Leave %s\n", __FUNCTION__);
+ printk_spew("Leave %s\n", __func__);
post_code(0x6A);
}
+void x86_setup_mtrrs(unsigned address_bits)
+{
+ x86_setup_fixed_mtrrs();
+ x86_setup_var_mtrrs(address_bits);
+}
+
+
int x86_mtrr_check(void)
{
/* Only Pentium Pro and later have MTRR */