Index: ../src/northbridge/intel/i440bx/raminit.c
===================================================================
--- ../src/northbridge/intel/i440bx/raminit.c	(revision 3531)
+++ ../src/northbridge/intel/i440bx/raminit.c	(arbetskopia)
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2007 Uwe Hermann <uwe@hermann-uwe.de>
+ * 				 2008 Mats Erik Andersson <mats.andersson@gisladisker.se>
  *
  * 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
@@ -27,11 +28,26 @@
 Macros and definitions.
 -----------------------------------------------------------------------------*/
 
-/* Uncomment this to enable debugging output. */
+/* Switches for alternative coding. */
+
+/* USE_DIMM2 needs FIXED_PAGESIZE_4KB. Otherwise the rom image is too large. */
+#define USE_DIMM2			0
+#define USE_DIMM3			0
+#define FIXED_PAGESIZE_4KB	0
+/* This uses a single function to calculate both row sizes of
+ * a double sided SDRAM, and returns a uint16_t typed value.
+ * The alternative is to use two functions, resulting in easier
+ * arithmetic. */
+#define SPD_UNIFIED_SIZE	1
+/* Use repeated calls to a single function handling
+ * the calculation of DRB# registers. Hopefully saves space. */
+#define COMMON_DRB_UPDATE	1
+
+/* Change this to '1' in order to enable debugging output. */
 #define DEBUG_RAM_SETUP 1
 
 /* Debugging macros. */
-#if defined(DEBUG_RAM_SETUP)
+#if DEBUG_RAM_SETUP
 #define PRINT_DEBUG(x)		print_debug(x)
 #define PRINT_DEBUG_HEX8(x)	print_debug_hex8(x)
 #define PRINT_DEBUG_HEX16(x)	print_debug_hex16(x)
@@ -67,7 +83,8 @@
 };
 
 /* Table format: register, bitmask, value. */
-static const long register_values[] = {
+static const unsigned int register_values[] = {
+//static const long register_values[] = {
 	/* NBXCFG - NBX Configuration Register
 	 * 0x50 - 0x53
 	 *
@@ -130,8 +147,8 @@
 	 */
 	// TODO
 	NBXCFG + 0, 0x00, 0x0c,
-	// NBXCFG + 1, 0x00, 0xa0,
-	NBXCFG + 1, 0x00, 0x80,
+	// NBXCFG + 1, 0x00, 0xa0,	/* 66 MHz */
+	NBXCFG + 1, 0x00, 0x80,		/* 100 MHz */
 	NBXCFG + 2, 0x00, 0x00,
 	NBXCFG + 3, 0x00, 0xff,
 
@@ -159,6 +176,18 @@
 	 */
 	/* Choose SDRAM (not registered), and disable refresh for now. */
 	DRAMC, 0x00, 0x08,
+	/* DRAMT - DRAM Timing (0x03).
+	 * 0x58
+	 *
+	 * [7:2] Reserved
+	 *   [1] EDO RASx# Wait state (RWS)
+	 *   	 0 = 1 tASR
+	 *   	 1 = 2 tASR
+	 *   [0] EDO CASx# Wait state (CWS)
+	 *       0 = 1 Tasc
+	 *       1 = 2 Tasc
+	 */
+	DRAMT, 0x00, 0x03,
 
 	/*
 	 * PAM[6:0] - Programmable Attribute Map Registers
@@ -174,10 +203,10 @@
 	 * 0x5c [5:4] 0xD4000 - 0xD7FFF ISA add-on BIOS
 	 * 0x5d [1:0] 0xD8000 - 0xDBFFF ISA add-on BIOS
 	 * 0x5d [5:4] 0xDC000 - 0xDFFFF ISA add-on BIOS
-	 * 0x5e [1:0] 0xE0000 - 0xE3FFF BIOS entension
-	 * 0x5e [5:4] 0xE4000 - 0xE7FFF BIOS entension
-	 * 0x5f [1:0] 0xE8000 - 0xEBFFF BIOS entension
-	 * 0x5f [5:4] 0xEC000 - 0xEFFFF BIOS entension
+	 * 0x5e [1:0] 0xE0000 - 0xE3FFF BIOS extension
+	 * 0x5e [5:4] 0xE4000 - 0xE7FFF BIOS extension
+	 * 0x5f [1:0] 0xE8000 - 0xEBFFF BIOS extension
+	 * 0x5f [5:4] 0xEC000 - 0xEFFFF BIOS extension
 	 *
 	 * Bit assignment:
 	 * 00 = DRAM Disabled (all access goes to memory mapped I/O space)
@@ -234,6 +263,16 @@
 	/* No memory holes. */
 	FDHC, 0x00, 0x00,
 
+	/* MBSC - Memory Buffer Strength Control
+	 * 0x69 - 0x6e
+	 */
+	MBSC + 0, 0x00, 0x00,
+	MBSC + 1, 0x00, 0x00,
+	MBSC + 2, 0x00, 0x00,
+	MBSC + 3, 0x00, 0x00,
+	MBSC + 4, 0x00, 0x00,
+	MBSC + 5, 0x00, 0x00,
+	
 	/* RPS - SDRAM Row Page Size Register
 	 * 0x74 - 0x75
 	 *
@@ -241,7 +280,19 @@
 	 * size is fixed at 2 KB.
 	 *
 	 * [15:0] Page Size (PS)
-	 *        TODO
+	 *
+	 *  [1:0] for row 0, DRB[0]
+	 *        00 = 2 kB page size
+	 *        01 = 4 kB
+	 *        10 = 8 kB
+	 *        11 = Illegal
+	 *  [3:2] for row 1, DRB[1]
+	 *  [5:4] for row 2, DRB[2]
+	 *  [7:6] for row 3, DRB[3]
+	 *  [9:8] row 4
+	 *  [11:10] row 5
+	 *  [13:12] row 6
+	 *  [15:14] row 7
 	 */
 	// TODO
 	RPS + 0, 0x00, 0x00,
@@ -282,7 +333,7 @@
 	 *         1 = 2 clocks of RAS# precharge
 	 */
 	SDRAMC + 0, 0x00, 0x00,
-	SDRAMC + 0, 0x00, 0x00,
+	SDRAMC + 1, 0x00, 0x00,
 
 	/* PGPOL - Paging Policy Register
 	 * 0x78 - 0x79
@@ -356,10 +407,12 @@
 static void do_ram_command(const struct mem_controller *ctrl,
 			   uint32_t command, uint32_t addr_offset)
 {
-	int i;
 	uint16_t reg;
 
-	/* TODO: Support for multiple DIMMs. */
+	/* TODO: Support for multiple DIMMs.
+	 * This requires that one read32() is performed
+	 * for each RAM bank/row.
+	 */
 
 	/* Configure the RAM command. */
 	reg = pci_read_config16(ctrl->d0, SDRAMC);
@@ -379,10 +432,20 @@
 
 	/* Read from (DIMM start address + addr_offset). */
 	read32(0 + addr_offset); // FIXME
+	read32(0x04000000 + addr_offset); // 64MB
+	/* Blindly probe for additional 256 MB
+	read32(0x08000000 + addr_offset); // 128MB
+	read32(0x0b000000 + addr_offset); // 192MB
+	*/
+	/* Probe the rest of (16 MB + 16 MB) + (16 MB + 16 MB)
+	read32(0x01000000 + addr_offset); // 16MB
+	read32(0x02000000 + addr_offset); // 32MB
+	read32(0x03000000 + addr_offset); // 48MB
+	*/
 }
 
 /*-----------------------------------------------------------------------------
-DIMM-independant configuration functions.
+DIMM-independent configuration functions.
 -----------------------------------------------------------------------------*/
 
 /**
@@ -400,9 +463,16 @@
 	for (i = 0; i < DIMM_SOCKETS; i++) {
 		value = spd_read_byte(ctrl->channel0[i], SPD_REFRESH);
 		if (value < 0)
+			/* Empty socket. */
 			continue;
-		reg = (reg & 0xf8) | refresh_rate_map[(value & 0x7f)];
 
+		/* Effectively, the refresh values are found in {0x00,...,0x05}.
+		 * A wider mask in stead of (value & 0x07) would exceed the defined
+		 * length of refresh_rate_map[].
+		 */
+		reg = (reg & 0xf8) | refresh_rate_map[(value & 0x07)]; 
+		/* reg = (reg & 0xf8) | refresh_rate_map[(value & 0x7f)]; */
+
 		PRINT_DEBUG("    Enabling refresh (DRAMC = 0x");
 		PRINT_DEBUG_HEX8(reg);
 		PRINT_DEBUG(") for DIMM ");
@@ -414,6 +484,123 @@
 }
 
 /*-----------------------------------------------------------------------------
+ * Calculation of row sizes.
+ *---------------------------------------------------------------------------*/
+
+#if SPD_UNIFIED_SIZE
+/**
+ * Calculate the sizes of sdram rows.
+ *
+ * @param unsigned char
+ */
+static uint16_t sdram_find_rowsize(unsigned char d)
+{
+	/* Presumption: The value 'd' has either one or two bits set.
+	 * With only one bit set, the size is duplicated. The least significant
+	 * return byte contains the larger size as reported by SPD. */
+
+	/* Start with the least significant bit. */
+	char mask = 1;
+
+	/* Exclude empty card reads (value 0xff).
+	 * The looping will halt since value 0x00 is now excluded. */
+	if ( d == 0x00 || d == 0xff )
+		return 0;
+
+	while (1) /* Loop exits at the first bit set. */
+	{
+		if ( d & mask ) /* Is size 2^(mask+1) MB present? */
+		{
+			/* The looping from least significant bit upwards,
+			 * ensures that (d & ~mask) represent a larger size,
+			 * should the value be non-zero at all. */
+			if ( d & ~mask )
+				/* A mixed size DIMM card has been found. */
+				return ((d & mask) << 8) | (d & ~mask);
+			/* The non-mixed size card demands a duplicate size. */
+			return (d << 8) | d;
+		}
+		/* Shift the mask in order to detect the next larger size. */
+		mask <<= 1;
+	}
+}
+#else
+
+/**
+ * Calculate the smallest size of an sdram row.
+ *
+ * @param unsigned char
+ */
+static unsigned char sdram_find_low_rowsize(unsigned char d)
+{
+	unsigned char mask = 1;
+
+	if ( d == 0x00 || d == 0xff )
+		return 0;
+
+	while (1)
+	{
+		if ( d & mask )
+			/* The smallest size is always found first. */
+			return mask;
+		/* Try the next larger size. */
+		mask <<= 1;
+	}
+}
+
+/**
+ * Calculate the largest size of an sdram row.
+ *
+ * @param unsigned char
+ */
+static unsigned char sdram_find_high_rowsize(unsigned char d)
+{
+	unsigned char mask = 1;
+
+	if ( d == 0x00 || d == 0xff )
+		return 0;
+
+	while (1)
+	{
+		if ( d & mask )
+		{
+			if ( d & ~mask )
+				/* This is indeed a mixed-size sdram card, as the
+				 * larger size is being triggered. */
+				return d & ~mask;
+			/* Either a single sided card, or the banks are symmetrical. */
+			return mask;
+		}
+		mask <<= 1;
+	}
+}
+#endif /* SPD_UNIFIED_SIZE */
+
+#if COMMON_DRB_UPDATE
+static uint8_t update_drbx(uint8_t reg, uint8_t channel, uint8_t drb, uint8_t data)
+{
+	uint8_t d = data;
+#if SPD_UNIFIED_SIZE
+	d += (sdram_find_rowsize(spd_read_byte(channel,31)) >> 1) & 0x007f;
+	pci_write_config8(reg, drb, d);
+
+	/* Is DIMM# a double sided memory device. */
+	if ( spd_read_byte(channel, 5) == 0x02 )
+		d += sdram_find_rowsize(spd_read_byte(channel,31)) >> 9;
+#else
+	d += sdram_find_low_rowsize(spd_read_byte(channel,31)) >> 1;
+	pci_write_config8(reg, drb, d);
+
+	/* Is DIMM# a double sided memory device. */
+	if ( spd_read_byte(channel, 5) == 0x02 )
+		d += sdram_find_high_rowsize(spd_read_byte(channel,31)) >> 1;
+#endif /* SPD_UNIFIED_SIZE */
+	pci_write_config8(reg, drb + 1, d);
+	return d;
+}
+#endif
+
+/*-----------------------------------------------------------------------------
 Public interface.
 -----------------------------------------------------------------------------*/
 
@@ -439,11 +626,13 @@
 		reg |= register_values[i + 2] & ~(register_values[i + 1]);
 		pci_write_config8(ctrl->d0, 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
 	}
 }
 
@@ -454,8 +643,13 @@
  */
 static void sdram_set_spd_registers(const struct mem_controller *ctrl)
 {
-	/* TODO: Don't hardcode the values here, get info via SPD. */
+	/* Use only a minimal amount of scratch data in order to
+	 * save precious internal register space under romcc. */
+	uint8_t data;
 
+	/* TODO: Don't hardcode the values here, get info via SPD.
+	 * Implemented for DRB#, */
+
 	/* 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
@@ -469,34 +663,275 @@
 	pci_write_config8(ctrl->d0, PAM5, 0x33);
 	pci_write_config8(ctrl->d0, PAM6, 0x33);
 
-	/* TODO: Set DRB0-DRB7. */
-	/* Currently this is hardcoded to one 64 MB DIMM in slot 0. */
-	pci_write_config8(ctrl->d0, DRB0, 0x08);
-	pci_write_config8(ctrl->d0, DRB1, 0x08);
-	pci_write_config8(ctrl->d0, DRB2, 0x08);
-	pci_write_config8(ctrl->d0, DRB3, 0x08);
-	pci_write_config8(ctrl->d0, DRB4, 0x08);
-	pci_write_config8(ctrl->d0, DRB5, 0x08);
-	pci_write_config8(ctrl->d0, DRB6, 0x08);
-	pci_write_config8(ctrl->d0, DRB7, 0x08);
+	/* Set DRB0-DRB7 dynamically. */
+#if 0
+		print_debug("Density at 0x00: 0x");
+		print_debug_hex8(spd_read_byte(ctrl->channel0[0],31));
+		print_debug(", rows 0x");
+		print_debug_hex8(spd_read_byte(ctrl->channel0[0],5));
+		print_debug("\r\n");
+#elif 0
+		PRINT_DEBUG("Density at 0x00: 0x");
+		PRINT_DEBUG_HEX8(spd_read_byte(ctrl->channel0[0],31));
+		PRINT_DEBUG(", rows 0x");
+		PRINT_DEBUG_HEX8(spd_read_byte(ctrl->channel0[0],5));
+		PRINT_DEBUG("\r\n");
+#endif
 
+	/* The first bank of DIMM0 initializes the size,
+	 * and all other produce a cumulative counter. */
+#if COMMON_DRB_UPDATE
+	data = update_drbx(ctrl->d0, ctrl->channel0[0], DRB0, 0);
+#else
+
+#  if SPD_UNIFIED_SIZE
+	print_debug("SPD size DIMM0: 0x");
+	print_debug_hex16(sdram_find_rowsize(spd_read_byte(ctrl->channel0[0],31)));
+	print_debug("\r\n");
+	data = (uint8_t) (sdram_find_rowsize(spd_read_byte(ctrl->channel0[0],31)) >> 1) & 0x007f;
+	//data = 0x08;
+	pci_write_config8(ctrl->d0, DRB0, data);
+
+	/* Is DIMM0 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[0], 5) == 0x02 )
+		data += (uint8_t) sdram_find_rowsize(spd_read_byte(ctrl->channel0[0],31)) >> 9;
+		//data += 0x08;
+#  else
+	data = sdram_find_low_rowsize(spd_read_byte(ctrl->channel0[0],31)) >> 1;
+	pci_write_config8(ctrl->d0, DRB0, data);
+
+	/* Is DIMM0 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[0], 5) == 0x02 )
+		data += sdram_find_high_rowsize(spd_read_byte(ctrl->channel0[0],31)) >> 1;
+#  endif /* SPD_UNIFIED_SIZE */
+	pci_write_config8(ctrl->d0, DRB1, data);
+
+#endif /* COMMON_DRB_UPDATE */
+
+#if 0
+		print_debug("Density at 0x01: 0x");
+		print_debug_hex8(spd_read_byte(ctrl->channel0[1],31));
+		print_debug(", rows 0x");
+		print_debug_hex8(spd_read_byte(ctrl->channel0[1],5));
+		print_debug("\r\n");
+#elif 0
+		PRINT_DEBUG("Density at 0x01: 0x");
+		PRINT_DEBUG_HEX8(spd_read_byte(ctrl->channel0[1],31));
+		PRINT_DEBUG(", rows 0x");
+		PRINT_DEBUG_HEX8(spd_read_byte(ctrl->channel0[1],5));
+		PRINT_DEBUG("\r\n");
+#endif
+
+	/* Probe DIMM1. */
+#if COMMON_DRB_UPDATE
+	data = update_drbx(ctrl->d0, ctrl->channel0[1], DRB2, data);
+#else
+
+#if SPD_UNIFIED_SIZE
+	print_debug("SPD size DIMM1: 0x");
+	print_debug_hex16(sdram_find_rowsize(spd_read_byte(ctrl->channel0[1],31)));
+	print_debug("\r\n");
+	data += (uint8_t) (sdram_find_rowsize(spd_read_byte(ctrl->channel0[1],31)) >> 1) & 0x007f;
+	//data += 0x08;
+	pci_write_config8(ctrl->d0, DRB2, data);
+
+	/* Is DIMM1 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[1], 5) == 0x02 )
+		data += (uint8_t) sdram_find_rowsize(spd_read_byte(ctrl->channel0[1],31)) >> 9;
+		//data += 0x08;
+#else
+	data += sdram_find_low_rowsize(spd_read_byte(ctrl->channel0[1],31)) >> 1;
+	pci_write_config8(ctrl->d0, DRB2, data);
+
+	/* Is DIMM1 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[1], 5) == 0x02 )
+		data += sdram_find_high_rowsize(spd_read_byte(ctrl->channel0[1],31)) >> 1;
+#endif /* SPD_UNIFIED_SIZE */
+	pci_write_config8(ctrl->d0, DRB3, data);
+
+#endif /* COMMON_DRB_UPDATE */
+
+#if USE_DIMM2
+	/* Do an active detection also for DIMM2. */
+	#if 0
+		print_debug("Density at 0x02: 0x");
+		print_debug_hex8(spd_read_byte(ctrl->channel0[2],31));
+		print_debug("\r\n");
+		print_debug(" rows: 0x");
+		print_debug_hex16(sdram_find_rowsize(spd_read_byte(ctrl->channel0[2],31)));
+		print_debug("\r\n");
+	#else
+		PRINT_DEBUG("Density at 0x02: 0x");
+		PRINT_DEBUG_HEX8(spd_read_byte(ctrl->channel0[2],31));
+		PRINT_DEBUG("\r\n");
+		PRINT_DEBUG(" rows: 0x");
+		PRINT_DEBUG_HEX16(sdram_find_rowsize(spd_read_byte(ctrl->channel0[2],31)));
+		PRINT_DEBUG("\r\n");
+	#endif
+
+	/* Now probe for DIMM2. */
+#if COMMON_DRB_UPDATE
+	data = update_drbx(ctrl->d0, ctrl->channel0[2], DRB4, data);
+#else
+# if SPD_UNIFIED_SIZE
+	data += (sdram_find_rowsize(spd_read_byte(ctrl->channel0[2],31)) & 0x00ff) >> 1;
+	pci_write_config8(ctrl->d0, DRB4, data);
+
+	/* Is DIMM2 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[2], 5) == 0x02 )
+		data += sdram_find_rowsize(spd_read_byte(ctrl->channel0[2],31)) >> 9;
+# else
+	data += sdram_find_low_rowsize(spd_read_byte(ctrl->channel0[2],31)) >> 1;
+	pci_write_config8(ctrl->d0, DRB4, data);
+
+	/* Is DIMM2 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[2], 5) == 0x02 )
+		data += sdram_find_high_rowsize(spd_read_byte(ctrl->channel0[2],31)) >> 1;
+# endif /* SPD_UNIFIED_SIZE */
+	pci_write_config8(ctrl->d0, DRB5, data);
+#endif /* COMMON_DRB_UPDATE */
+
+#else
+	/* No detection for DIMM2. Just continue using the cumulative counter.*/
+	pci_write_config8(ctrl->d0, DRB4, data);
+	pci_write_config8(ctrl->d0, DRB5, data);
+#endif /* USE_DIMM2 */
+
+#if USE_DIMM3
+	/* Probe for DIMM3. */
+		PRINT_DEBUG("Density at 0x03: 0x");
+		PRINT_DEBUG_HEX16(sdram_find_rowsize(spd_read_byte(ctrl->channel0[3],31)));
+		PRINT_DEBUG("\r\n");
+#if COMMON_DRB_UPDATE
+	data = update_drbx(ctrl->d0, ctrl->channel0[3], DRB6, data);
+#else
+# if SPD_UNIFIED_SIZE
+	data += (sdram_find_rowsize(spd_read_byte(ctrl->channel0[3],31)) & 0x00ff) >> 1;
+	pci_write_config8(ctrl->d0, DRB6, data);
+
+	/* Is DIMM3 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[3], 5) == 0x02 )
+		data += sdram_find_rowsize(spd_read_byte(ctrl->channel0[3],31)) >> 9;
+# else
+	data += sdram_find_low_rowsize(spd_read_byte(ctrl->channel0[3],31)) >> 1;
+	pci_write_config8(ctrl->d0, DRB6, data);
+
+	/* Is DIMM3 a double sided memory device. */
+	if ( spd_read_byte(ctrl->channel0[3], 5) == 0x02 )
+		data += sdram_find_high_rowsize(spd_read_byte(ctrl->channel0[3],31)) >> 1;
+# endif /* SPD_UNIFIED_SIZE */
+	pci_write_config8(ctrl->d0, DRB7, data);
+#endif /* COMMON_DRB_UPDATE */
+
+#else
+	/* No detection for DIMM3. Just continue using the cumulative counter.*/
+	pci_write_config8(ctrl->d0, DRB6, data);
+	pci_write_config8(ctrl->d0, DRB7, data);
+#endif /* USE_DIMM3 */
+
 	/* TODO: Set DRAMC. Don't enable refresh for now. */
-	pci_write_config8(ctrl->d0, DRAMC, 0x08);
+	pci_write_config8(ctrl->d0, DRAMC, 0x08); /* SDRAM installed */
 
 	/* TODO: Set RPS. */
-	pci_write_config16(ctrl->d0, RPS, 0x0001);
+	/* pci_write_config16(ctrl->d0, RPS, 0x0001); */
+#if FIXED_PAGESIZE_4KB
+	/* Fixed page size 4 kB.
+	 * Setting page size of an unused bank seems harmless
+	 * in practice, so all eight rows are of size 4 kB. */
+	pci_write_config16(ctrl->d0, RPS, 0x5555);
+#else
+	/* Probed page sizes.
+	 * Memory usage is critical for the fallback image.
+	 * The factor 5 takes care of double sided DIMMs,
+	 * but only in the symmetrical case.
+	 * Setting value as for double sided cards, where
+	 * only a single row exists, seems harmless in practice.
+	 * Legend:	8 columns -> 2 kB -> 0b00
+	 * 			9 columns -> 4 kB -> 0b01
+	 *			10 columns -> 8 kB -> 0b10.
+	 */
 
+	/* DIMM0 */
+	/* This calculation provide correct flags for {8,9,10} columns. */
+	data = 5 * (spd_read_byte(ctrl->channel0[0], 4) - 8);
+
+	/* If the read value was 0xff for empty socket, it needs correction. */
+	if ( data == 0xd3 )
+	{
+		print_debug("DIMM0 empty\r\n");
+		data = 0;
+	};
+	/* Must loose the case of eight address lines before shifting
+	 * a nubble upwards. Therefore the additional factor '16'. */
+	/* DIMM1 */
+	if (spd_read_byte(ctrl->channel0[1], 4) != -1)
+		/* Used socket */
+		data |= 16 * 5 * (spd_read_byte(ctrl->channel0[1], 4) - 8);
+	else
+		/* Empty; no additional flags in RPS. */
+		print_debug("DIMM1 empty\r\n");
+
+	pci_write_config8(ctrl->d0, RPS, data);
+	print_debug("RPS DIMM0/1: 0x");
+	print_debug_hex8(data);
+	print_debug("\r\n");
+
+#  if USE_DIMM2 || USE_DIMM3
+	/* DIMM2 */
+	if (spd_read_byte(ctrl->channel0[2], 4) != -1)
+		data = 5 * (spd_read_byte(ctrl->channel0[2], 4) - 8);
+	else
+		data = 0;
+	/* DIMM3 */
+	if (spd_read_byte(ctrl->channel0[3], 4) != -1)
+		data |= 16 * 5 * (spd_read_byte(ctrl->channel0[3], 4) - 8);
+
+	pci_write_config8(ctrl->d0, RPS + 1, data);
+	print_debug("RPS DIMM2/3: 0x");
+	print_debug_hex8(data);
+	print_debug("\r\n");
+#else
+	/* Mark page size 2 kB in the unused slots. */
+	pci_write_config8(ctrl->d0, RPS + 1, 0x00);
+
+#  endif /* USE_DIMM2 || USE_DIMM3 */
+
+#endif /* FIXED_PAGESIZE_4KB */
+
 	/* TODO: Set SDRAMC. */
 	// pci_write_config16(ctrl->d0, SDRAMC, 0x010f); // FIXME?
-	pci_write_config16(ctrl->d0, SDRAMC, 0x0003); // FIXME?
+	pci_write_config16(ctrl->d0, SDRAMC, 0x0103); // Leadoff delay + 3 DCLK
 
 	/* TODO: Set PGPOL. */
+	/* Devices are supposed to contain two banks, until an
+	 * SPD probe says they provide four banks. */
+	data = 0x00;
+
+	if ( spd_read_byte(ctrl->channel0[0], 17) == 0x04 )
+		data |= 0x03;
+	if ( spd_read_byte(ctrl->channel0[1], 17) == 0x04 )
+		data |= (0x03 << 2);
+#if USE_DIMM2 || USE_DIMM3
+	if ( spd_read_byte(ctrl->channel0[2], 17) == 0x04 )
+		data |= (0x03 << 4);
+#endif
+#if USE_DIMM3
+	if ( spd_read_byte(ctrl->channel0[3], 17) == 0x04 )
+		data |= (0x03 << 6);
+#endif
+	/* The value (data << 8) now produces correct bank flags for PGPOL. */
 	// pci_write_config16(ctrl->d0, PGPOL, 0x0107);
-	pci_write_config16(ctrl->d0, PGPOL, 0x0123);
+	//pci_write_config16(ctrl->d0, PGPOL, 0x0123);
+	/* The value 0x06 (idle 16 clock periods) is experimental. */
+	pci_write_config16(ctrl->d0, PGPOL, 0x06 | (data << 8));
 
 	/* TODO: Set NBXCFG. */
 	// pci_write_config32(ctrl->d0, NBXCFG, 0x0100220c); // FIXME?
-	pci_write_config32(ctrl->d0, NBXCFG, 0xff00800c);
+	/* Interpretation:	'ff' possibly populated rows,
+	 * 					'80' uni-processor, 100 MHz, no AGP, no-ECC,
+	 * 					'04' no USWC, maximum in-order queue.
+	 * Experimental */
+	pci_write_config32(ctrl->d0, NBXCFG, 0xff008004);
 
 	/* TODO: Set PMCR? */
 	// pci_write_config8(ctrl->d0, PMCR, 0x14);
@@ -505,7 +940,11 @@
 	/* TODO? */
 	pci_write_config8(ctrl->d0, PCI_LATENCY_TIMER, 0x40);
 	pci_write_config8(ctrl->d0, DRAMT, 0x03);
-	pci_write_config8(ctrl->d0, MBSC, 0x03);
+	//pci_write_config8(ctrl->d0, MBSC, 0x03);
+	//pci_write_config32(ctrl->d0, MBSC, 0x0003c003);
+	/* Experimental */
+	pci_write_config32(ctrl->d0, MBSC, 0x00000003);
+	//pci_write_config32(ctrl->d0, MBSC, 0xffffffff);
 	pci_write_config8(ctrl->d0, SCRR, 0x38);
 }
 
@@ -558,3 +997,5 @@
 	PRINT_DEBUG("Northbridge following SDRAM init:\r\n");
 	DUMPNORTH();
 }
+
+/* vi: set ai sw=4 ts=4 */
