Patchwork ASUS P2B-LS support, RAM detection for 440BX, add Slot 1 CPU, Microcode for Intel Tualatin CPUs

login
register
about
Submitter Joseph Smith
Date 2010-03-04 13:26:57
Message ID <4B8FB521.7040503@settoplinux.org>
Download mbox | patch
Permalink /patch/1004/
State Accepted
Headers show

Comments

Joseph Smith - 2010-03-04 13:26:57
On 03/02/2010 11:19 PM, Keith Hui wrote:
> Hi all,
>
> This thing is now ready for more exposure. Scratch my previous "patch" -
> this is my first real deal.
>
> - Adds Asus P2B-LS mainboard
> - Adds RAM detection for i440bx (based on i82830 code). We're no longer
> hard coded for 64MB on one row!
> - Adds a proper Slot 1 cpu under src/cpu/intel/slot_1. It's a stub
> copied from slot_2 but addresses a few FIXMEs. My P2B-LS code refers to
> this.
> - Adds microcode for Intel Tualatin CPUs, cpuid 6B1 and 6B4.* Actually
> loading them is pending.
>
> Signed-off-by: Keith Hui <buurin@gmail.com <mailto:buurin@gmail.com>>
>
> Enjoy.
>
> Keith
>
> * Microcodes for all Intel CPUs can be downloaded from Intel -
> downloadcenter.intel.com <http://downloadcenter.intel.com>. So TODO for
> me is to add all microcode updates from Klamath to Tualatin as the Asus
> P2B family, with the right mods and/or adapter, can run anything in
> between that can fit either Slot 1 or Socket 370.
>
>
I would not worry about the microcode updates right now. CAR for Intel 
6bx is coming real soon and the microcode updates will be included :-)

See other comments below.


  Macros and definitions.
@@ -65,12 +67,24 @@
   * [4] == Extended(4x)    62.5 us ->  62.4 us
   * [5] == Extended(8x)     125 us -> 124.8 us
   */
-static const uint32_t refresh_rate_map[] = {
-	1, 5, 5, 2, 3, 4
+static const unsigned int refresh_rate_map[] = {
+	1, 1, 1, 2, 3, 4
  };

  /* Table format: register, bitmask, value. */
  static const long register_values[] = {
+	/* MLT - Master Latency Timer Register
+	 * 0x0d
+	 *
+	 * [07:03] Master Latency Timer Count Value for PCI Bus Access.
+	 *         MLT is an 8-bit register that controls the amount of
+	 *		   time the 82443BX, as a PCI bus master, can burst data
+	 *		   on the PCI Bus. The default value of MLT is 00h and
+	 *		   disables this function. For example, if the MLT is
+	 *		   programmed to 18h, then the value is 24 PCI clocks.
+	 * [02:00] Reserved
+	 */
+	MLT, 0x00, 0x40,
  	/* NBXCFG - NBX Configuration Register
  	 * 0x50 - 0x53
  	 *
@@ -188,14 +202,21 @@
  	 * 10 = Write Only (Writes to DRAM, reads to memory mapped I/O space)
  	 * 11 = Read/Write (all access goes to DRAM)
  	 */
-	// TODO
-	PAM0, 0x00, 0x00,
-	PAM1, 0x00, 0x00,
-	PAM2, 0x00, 0x00,
-	PAM3, 0x00, 0x00,
-	PAM4, 0x00, 0x00,
-	PAM5, 0x00, 0x00,
-	PAM6, 0x00, 0x00,
+	/* Map all legacy regions to RAM (read/write). This is required if
+	 * you want to use the RAM area from 768 KB - 1 MB. If the PAM
+	 * registers are not set here appropriately, the RAM in that region
+	 * will not be accessible, thus a RAM check of it will also fail.
+	
+	 * TODO: This was set in sdram_set_spd_registers()
+	 * Test if it still works when set here
+	 */
+	PAM0, 0x00, 0x30,
+	PAM1, 0x00, 0x33,
+	PAM2, 0x00, 0x33,
+	PAM3, 0x00, 0x33,
+	PAM4, 0x00, 0x33,
+	PAM5, 0x00, 0x33,
+	PAM6, 0x00, 0x33,

  	/* DRB[0:7] - DRAM Row Boundary Registers
  	 * 0x60 - 0x67
@@ -343,6 +364,8 @@
  	// PMCR, 0x00, 0x14,
  	// PMCR, 0x00, 0x10,
  	PMCR, 0x00, 0x00,
+	/* Enable SCRR.SRRAEN and let BX choose the SRR */
+	SCRR+1, 0x00, 0x10,
  };

 
/*-----------------------------------------------------------------------------
@@ -396,7 +419,7 @@

  		dimm_end = pci_read_config8(NB, DRB + i);

-		addr = (dimm_start * 8 * 1024 * 1024) + addr_offset;
+		addr = (dimm_start * 8 * 1048576) + addr_offset;
  		if (dimm_end > dimm_start) {
  #if 0
  			PRINT_DEBUG("    Sending RAM command 0x");
@@ -414,6 +437,22 @@
  	}
  }

+static void set_dram_buffer_strength(void)
+{
+	/* TODO: This needs to be set according to the DRAM tech
+	 * (x8, x16, or x32). Argh, Intel provides no docs on this!
+	 * Currently, it needs to be pulled from the output of
+	 * lspci -xxx Rx92
+	   Relevant registers:
+	 * MBSC
+	 * MBFS, BUFFC
+	 */
+
+	pci_write_config8(NB, MBSC, 0x03);
+
+}
+
+
 
/*-----------------------------------------------------------------------------
  DIMM-independant configuration functions.
 
-----------------------------------------------------------------------------*/
@@ -461,71 +500,305 @@
  		reg &= register_values[i + 1];
  		reg |= register_values[i + 2] & ~(register_values[i + 1]);
  		pci_write_config8(NB, register_values[i], reg);
-
+#if 0
  		PRINT_DEBUG("    Set register 0x");
  		PRINT_DEBUG_HEX8(register_values[i]);
  		PRINT_DEBUG(" to 0x");
  		PRINT_DEBUG_HEX8(reg);
  		PRINT_DEBUG("\r\n");
+#endif
  	}
  }

-static void sdram_set_spd_registers(void)
+/* Copied from i82830 northbridge code */
+struct dimm_size {
+	unsigned long side1;
+	unsigned long side2;
+};
+
+static struct dimm_size spd_get_dimm_size(unsigned device)
  {
-	/* TODO: Don't hardcode the values here, get info via SPD. */
+	struct dimm_size sz;
+	int i, module_density, dimm_banks;
+	sz.side1 = 0;
+	module_density = spd_read_byte(device, SPD_DENSITY_OF_EACH_ROW_ON_MODULE);
+	dimm_banks = spd_read_byte(device, SPD_NUM_DIMM_BANKS);

-	/* Map all legacy regions to RAM (read/write). This is required if
-	 * you want to use the RAM area from 768 KB - 1 MB. If the PAM
-	 * registers are not set here appropriately, the RAM in that region
-	 * will not be accessible, thus a RAM check of it will also fail.
+	/* Find the size of side1. */
+	/* Find the larger value. The larger value is always side1. */
+	for (i = 512; i >= 0; i >>= 1) {
+		if ((module_density & i) == i) {
+			sz.side1 = i;
+			break;
+		}
+	}
+
+	/* Set to 0 in case it's single sided. */
+	sz.side2 = 0;
+
+	/* Test if it's a dual-sided DIMM. */
+	if (dimm_banks > 1) {
+		/* Test to see if there's a second value, if so it's asymmetrical. */
+		if (module_density != i) {
+			/* Find the second value, picking up where we left off. */
+			/* i >>= 1 done initially to make sure we don't get the same value 
again. */
+			for (i >>= 1; i >= 0; i >>= 1) {
+				if (module_density == (sz.side1 | i)) {
+					sz.side2 = i;
+					break;
+				}
+			}
+			/* If not, it's symmetrical */
+		} else {
+			sz.side2 = sz.side1;
+		}
+	}
+
+	/* SPD byte 31 is the memory size divided by 4 so we
+	 * need to muliply by 4 to get the total size.
  	 */
-	pci_write_config8(NB, PAM0, 0x30);
-	pci_write_config8(NB, PAM1, 0x33);
-	pci_write_config8(NB, PAM2, 0x33);
-	pci_write_config8(NB, PAM3, 0x33);
-	pci_write_config8(NB, PAM4, 0x33);
-	pci_write_config8(NB, PAM5, 0x33);
-	pci_write_config8(NB, PAM6, 0x33);
+	sz.side1 *= 4;
+	sz.side2 *= 4;
+	return sz;
+}
+/*
+   Sets DRAM attributes one DIMM at a time, based on SPD data
+   Northbridge settings that got set here:
+
+   NBXCFG[31:24]
+   DRB0-DRB7
+   RPS
+   DRAMC
+ */
+static void set_dram_row_attributes(void)


**********You are setting alot more than just dra here, I would rename 
this function something like sdram_setup_registers().



+{
+	int i, dra, drb, col, width, value, rps, edosd, ecc, nbxecc;
+	u8 bpr; // Top 8 bits of PGPOL
+	
+	edosd = 0;
+	rps = 0;
+	drb = 0;
+	bpr = 0;
+	nbxecc=0xff;
+	
+	for (i = 0; i < DIMM_SOCKETS; i++) {
+		unsigned device;
+		device = DIMM_SPD_BASE + i;
+	   	bpr >>= 2;

-	/* TODO: Set DRB0-DRB7. */
-	/* Currently this is hardcoded to one 64 MB DIMM in slot 0. */
-	pci_write_config8(NB, DRB0, 0x08);
-	pci_write_config8(NB, DRB1, 0x08);
-	pci_write_config8(NB, DRB2, 0x08);
-	pci_write_config8(NB, DRB3, 0x08);
-	pci_write_config8(NB, DRB4, 0x08);
-	pci_write_config8(NB, DRB5, 0x08);
-	pci_write_config8(NB, DRB6, 0x08);
-	pci_write_config8(NB, DRB7, 0x08);
+		/* First check if a DIMM is actually present. */
+		value = spd_read_byte(device, SPD_MEMORY_TYPE);
+		/* This is BX! We do EDO too! */
+		if (value == SPD_MEMORY_TYPE_EDO || value == SPD_MEMORY_TYPE_SDRAM) {

-	/* TODO: Set DRAMC. Don't enable refresh for now. */
-	pci_write_config8(NB, DRAMC, 0x08);
+			PRINT_DEBUG("Found ");
+			if (value == SPD_MEMORY_TYPE_EDO) {
+				edosd |= 0x02;
+			} else if (value == SPD_MEMORY_TYPE_SDRAM) {
+				edosd = edosd | 0x04;
+			}
+			PRINT_DEBUG("DIMM in slot ");
+			PRINT_DEBUG_HEX8(i);
+			PRINT_DEBUG("\r\n");

-	/* TODO: Set RPS. Needs to be fixed for multiple DIMM support. */
-	pci_write_config16(NB, RPS, 0x0001);
+			if (edosd == 0x06) {
+				print_err("Mixing EDO/SDRAM not supported\r\n");
+				die("HALT\r\n");
+			}
+			
+			/* "DRA" is our RPS for the two rows on this DIMM */
+			dra = 0;

+			/* columns */
+			col = spd_read_byte(device, SPD_NUM_COLUMNS);
+
+			/* Is this an ECC DIMM? (Actually this will be a 2 if so) */
+			/* TODO: Other register than NBXCFG also needs this ECC information */
+			ecc = spd_read_byte(device, SPD_DIMM_CONFIG_TYPE);
+			
+			/* data width */
+			width = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB);
+			
+			/* Exclude error checking data width from page size calculations */
+			if (ecc) {
+			   value = spd_read_byte(device, SPD_ERROR_CHECKING_SDRAM_WIDTH);
+			   width -= value;
+			   /* ### ECC */
+			   /* Top 2 bits are clear to help set up NBXCFG */
+			   ecc &= 0x3f;
+			} else {
+			   /* Without ECC, these top 2 bits should be 11 */
+			   ecc |= 0xc0;
+			}
+			
+			/* calculate page size in bits */
+			value = ((1 << col) * width);
+
+			/* convert to Kilobytes */
+			dra = (value >> 13);
+
+			/* # of banks of DIMM (single or double sided) */
+			value = spd_read_byte(device, SPD_NUM_DIMM_BANKS);
+			
+			/* Once we have dra, col is done and can be reused,
+			 * So it's reused for number of banks
+			 */
+			col = spd_read_byte(device, SPD_NUM_BANKS_PER_SDRAM);
+
+			if (value == 1) {
+			   	/* Second bank of 1-bank DIMMs "doesn't have ECC" - or anything */
+				ecc |=0x80;
+				if (dra == 2) {
+				    dra = 0x0; /* 2KB */
+				} else if (dra == 4) {
+					dra = 0x1; /* 4KB */
+				} else if (dra == 8) {
+					dra = 0x2; /* 8KB */
+				} else {
+					dra = -1;
+				}
+				/* Sets a flag in PGPOL[BPR] if this DIMM has 4 banks per row */
+				if (col == 4) {
+					bpr |= 0x40;
+				}
+			} else if (value == 2) {
+				if (dra == 2) {
+				    dra = 0x0; /* 2KB */
+				} else if (dra == 4) {
+					dra = 0x05; /* 4KB */
+				} else if (dra == 8) {
+					dra = 0x0a; /* 8KB */
+				} else {
+					dra = -1;
+				}
+				/* Ditto */
+				if (col == 4) {
+					bpr |= 0xc0;
+				}
+			} else {
+				print_err("# of banks of DIMM not supported\r\n");
+				die("HALT\r\n");
+			}
+			if (dra == -1) {
+				print_err("Page size not supported\r\n");
+				die("HALT\r\n");
+			}
+
+			/* The 440BX supports asymmetrical dual-sided dimms (I can't test 
though)
+			 * but can't handle DIMMs smaller than 8MB per
+			 * side or larger than 128MB per side.
+			 */
+			struct dimm_size sz = spd_get_dimm_size(device);
+			if ((sz.side1 < 8)) {
+				print_err("DIMMs smaller than 8MB per side\r\nare not supported on 
this northbridge\r\n");
+				die("HALT\r\n");
+			}
+			if ((sz.side1 > 128)) {
+				print_err ("DIMMs larger than 128MB per side\r\nare not supported 
on this northbridge\r\n");
+				die("HALT\r\n");
+			}
+			/* - End Memory compatibility checks - */
+
+			/* We need to divide size by 8 to set up the
+			 * DRB registers.
+			 */
+			drb += (sz.side1 / 8);
+			/* Builds the DRB for the next row in MSB so it gets placed in DRB[n+1]
+			 * where it belongs when written as a 16-bit word.
+			 */
+			drb &= 0xff;
+			drb |= (drb + (sz.side2 / 8)) << 8;
+
+		} else {
+#if 0
+			PRINT_DEBUG("No DIMM found in slot ");
+			PRINT_DEBUG_HEX8(i);
+			PRINT_DEBUG("\r\n");
+#endif			
+
+			/* If there's no DIMM in the slot, set dra value to 0x00. */
+			dra = 0x00;
+			ecc = 0xc0;
+			/* Still have to propagate DRB over */
+			drb &= 0xff;
+			drb |= (drb << 8);
+		}
+
+		pci_write_config16(NB, DRB+(2*i), drb);
+#if 0
+		PRINT_DEBUG("DRB has been set to 0x");
+		PRINT_DEBUG_HEX16(drb);
+		PRINT_DEBUG("\r\n");
+#endif		
+			
+		/* Brings the upper DRB back down to be base for
+		 * DRB calculations for the next two rows.
+		 */
+		drb >>= 8;
+			
+		rps |= (dra & 0x0f) << (i*4);
+		nbxecc = (nbxecc >> 2) | (ecc & 0xc0);
+	}
+	/* Set Paging Policy Register */
+	pci_write_config8(NB, PGPOL+1, bpr);
+	PRINT_DEBUG("PGPOL[BPR] has been set to 0x");
+	PRINT_DEBUG_HEX8(bpr);
+	PRINT_DEBUG("\r\n");
+	/* Set DRAM Row Page Size Register */
+	pci_write_config16(NB, RPS, rps);
+	PRINT_DEBUG("RPS has been set to 0x");
+	PRINT_DEBUG_HEX16(rps);
+	PRINT_DEBUG("\r\n");
+	/* ### ECC */
+	pci_write_config8(NB, NBXCFG+3, nbxecc);
+	PRINT_DEBUG("NBXECC[31:24] has been set to 0x");
+	PRINT_DEBUG_HEX8(nbxecc);
+	PRINT_DEBUG("\r\n");
+
+	/* Set DRAMC[4:3] to proper memory type (EDO/SDRAM)
+	 * TODO: Account for registered SDRAM
+	 */
+	edosd &= 0x07;
+	if (edosd & 0x02) {
+	   edosd |= 0x00;
+	} else if (edosd & 0x04) {
+	   edosd |= 0x08;
+	}
+	edosd &= 0x18;
+	/* edosd by now has been transformed to the value needed for DRAMC[4:3] */
+	value = pci_read_config8(NB, DRAMC) & 0xe7;
+	value |= edosd;
+	pci_write_config8(NB, DRAMC, value);
+	PRINT_DEBUG("DRAMC has been set to 0x");
+	PRINT_DEBUG_HEX8(value);
+	PRINT_DEBUG("\r\n");
+	
+}
+
+static void sdram_set_spd_registers(void)
+{
+	/* Setup DRAM Row Boundary Registers and other attributes */
+	set_dram_row_attributes();
+
  	/* TODO: Set SDRAMC. */
  	pci_write_config16(NB, SDRAMC, 0x0010);	/* SDRAMPWR=1: 4 DIMM config */

-	/* TODO: Set PGPOL. */
-	// pci_write_config16(NB, PGPOL, 0x0107);
-	pci_write_config16(NB, PGPOL, 0x0123);
-
-	/* TODO: Set NBXCFG. */
-	// pci_write_config32(NB, NBXCFG, 0x0100220c); // FIXME?
-	pci_write_config32(NB, NBXCFG, 0xff00800c);
-
+	/* TODO */
+	set_dram_buffer_strength();
+	
  	/* TODO: Set PMCR? */
  	// pci_write_config8(NB, PMCR, 0x14);
  	pci_write_config8(NB, PMCR, 0x10);

  	/* TODO? */
-	pci_write_config8(NB, PCI_LATENCY_TIMER, 0x40);
  	pci_write_config8(NB, DRAMT, 0x03);
-	pci_write_config8(NB, MBSC, 0x03);
-	pci_write_config8(NB, SCRR, 0x38);
  }

+static void sdram_set_timing(unsigned int mhz) {
+	   /* TODO */
+
+}
+
  static void sdram_enable(void)
  {
  	int i;

************I also noticed you did not use the memory initialize each 
row/side code from the i830. That code is extremely important for 
multiple memory sticks. Besides that everything else looks really good, 
great work!
Keith Hui - 2010-03-05 05:53:13
On Thu, Mar 4, 2010 at 8:26 AM, Joseph Smith <joe@settoplinux.org> wrote:

> I would not worry about the microcode updates right now. CAR for Intel 6bx
> is coming real soon and the microcode updates will be included :-)
>
> CAN'T WAIT! :D Then I can say goodbye to the messiness that is romcc, lol.


>
> **********You are setting alot more than just dra here, I would rename this
> function something like sdram_setup_registers().
>
> Good point. Eventually I wanted to name it sdram_initalize() just like
i830, but there are a couple other references to the current names
elsewhere. One step at a time I guess.

>
>
> ************I also noticed you did not use the memory initialize each
> row/side code from the i830. That code is extremely important for multiple
> memory sticks. Besides that everything else looks really good, great work!
>
> There were some code that send RAM commands to the modules in the BX code.
I just kept them around, thinking that this code in i830 may be specific to
i830.

Mark, the problem you saw might be MBFS and MBSC not being set properly. I
have reversed how the factory BIOS programmed them and have the code in my
working copy. I'll see if that makes a difference. We are still hardcoded to
CAS3 latency. One step at a time again I guess.

On another front, with the board running factory BIOS, I dumped the BX's
config space (lspci -s 0:0:0.0 -xxx) with various DIMM configurations,
especially with two sticks in DIMM0&1, DIMM2&3, and 3 sticks. These three
scenarios are where most of the logics are. I can post them if anyone wants
to look at them. All my RAMs are double sided, one 128MB and the others are
256MB.

To figure out how this gets coded for the 3-slot P2B, Someone would need to
reverse the vendor bios for that board or do same as above.

Also, P3B-F has 4 DIMM slots as well.

Thanks
Keith
Joseph Smith - 2010-03-05 12:02:53
On 03/05/2010 12:53 AM, Keith Hui wrote:
>
>
> On Thu, Mar 4, 2010 at 8:26 AM, Joseph Smith <joe@settoplinux.org
> <mailto:joe@settoplinux.org>> wrote:
>
>     I would not worry about the microcode updates right now. CAR for
>     Intel 6bx is coming real soon and the microcode updates will be
>     included :-)
>
> CAN'T WAIT! :D Then I can say goodbye to the messiness that is romcc, lol.
>
>
>     **********You are setting alot more than just dra here, I would
>     rename this function something like sdram_setup_registers().
>
> Good point. Eventually I wanted to name it sdram_initalize() just like
> i830, but there are a couple other references to the current names
> elsewhere. One step at a time I guess.
>
>
>
>     ************I also noticed you did not use the memory initialize
>     each row/side code from the i830. That code is extremely important
>     for multiple memory sticks. Besides that everything else looks
>     really good, great work!
>
> There were some code that send RAM commands to the modules in the BX
> code. I just kept them around, thinking that this code in i830 may be
> specific to i830.
>
The only code that may be specific to the i830 is the DRC regs. Also you 
may have to tweek:

dimm_end = pci_read_config8(NORTHBRIDGE, DRB + row);

for the 440bx. This basicly reading the sdram size to set as the start 
of the next row.

The rest of the routine (and I have researched it extensively) is pretty 
much the standard for sdram initialization.

> Mark, the problem you saw might be MBFS and MBSC not being set properly.
> I have reversed how the factory BIOS programmed them and have the code
> in my working copy. I'll see if that makes a difference. We are still
> hardcoded to CAS3 latency. One step at a time again I guess.
>
> On another front, with the board running factory BIOS, I dumped the BX's
> config space (lspci -s 0:0:0.0 -xxx) with various DIMM configurations,
> especially with two sticks in DIMM0&1, DIMM2&3, and 3 sticks. These
> three scenarios are where most of the logics are. I can post them if
> anyone wants to look at them. All my RAMs are double sided, one 128MB
> and the others are 256MB.
>
> To figure out how this gets coded for the 3-slot P2B, Someone would need
> to reverse the vendor bios for that board or do same as above.
>
FYI, serialice is awesome at dumping raminit routines :-)

Patch

Index: src/northbridge/intel/i440bx/raminit.c
===================================================================
--- src/northbridge/intel/i440bx/raminit.c	(revision 5184)
+++ src/northbridge/intel/i440bx/raminit.c	(working copy)
@@ -2,6 +2,7 @@ 
   * This file is part of the coreboot project.
   *
   * Copyright (C) 2007-2008 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2010 Keith Hui <buurin@gmail.com>
   *
   * 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
@@ -23,6 +24,7 @@ 
  #include <delay.h>
  #include <stdlib.h>
  #include "i440bx.h"
+#include "raminit.h"

 
/*-----------------------------------------------------------------------------