summaryrefslogtreecommitdiff
path: root/src/northbridge/intel/i855
diff options
context:
space:
mode:
authorAndreas Schultz <aschultz@tpip.net>2010-08-30 16:19:04 +0000
committerStefan Reinauer <stepan@openbios.org>2010-08-30 16:19:04 +0000
commitb6b29dbbb9126f80b592f7856cd09882c231e745 (patch)
tree526e95c50cc66c276cfc5eca7279195a5410a485 /src/northbridge/intel/i855
parent1bab1fb839c59594b8dd0e48d35c15353266d6ad (diff)
downloadcoreboot-b6b29dbbb9126f80b592f7856cd09882c231e745.tar.xz
Rework i855GM/i855GME support
Signed-off-by: Andreas Schultz <aschultz@tpip.net> Acked-by: Stefan Reinauer <stepan@coresystems.de> --- src/northbridge/intel/i855/Kconfig | 30 + src/northbridge/intel/i855/i855.h | 76 +++ src/northbridge/intel/i855/northbridge.c | 21 + src/northbridge/intel/i855/raminit.c | 1036 +++++++++++++++++++++++++----- src/northbridge/intel/i855/raminit.h | 14 +- 5 files changed, 1002 insertions(+), 175 deletions(-) create mode 100644 src/northbridge/intel/i855/i855.h git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5751 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/intel/i855')
-rw-r--r--src/northbridge/intel/i855/Kconfig30
-rw-r--r--src/northbridge/intel/i855/northbridge.c21
-rw-r--r--src/northbridge/intel/i855/raminit.c1036
-rw-r--r--src/northbridge/intel/i855/raminit.h14
4 files changed, 926 insertions, 175 deletions
diff --git a/src/northbridge/intel/i855/Kconfig b/src/northbridge/intel/i855/Kconfig
index 3d3443a2c9..f5c2890e91 100644
--- a/src/northbridge/intel/i855/Kconfig
+++ b/src/northbridge/intel/i855/Kconfig
@@ -1,3 +1,33 @@
config NORTHBRIDGE_INTEL_I855
bool
+ select HAVE_DEBUG_RAM_SETUP
+choice
+ prompt "Onboard graphics"
+ default I855_VIDEO_MB_8MB
+ depends on NORTHBRIDGE_INTEL_I855
+
+config I855_VIDEO_MB_OFF
+ bool "Disabled, 0KB"
+config I855_VIDEO_MB_1MB
+ bool "Enabled, 1MB"
+config I855_VIDEO_MB_4MB
+ bool "Enabled, 4MB"
+config I855_VIDEO_MB_8MB
+ bool "Enabled, 8MB"
+config I855_VIDEO_MB_16MB
+ bool "Enabled, 16MB"
+config I855_VIDEO_MB_32MB
+ bool "Enabled, 32MB"
+
+endchoice
+
+config VIDEO_MB
+ int
+ default 0 if I855_VIDEO_MB_OFF
+ default 1 if I855_VIDEO_MB_1MB
+ default 4 if I855_VIDEO_MB_4MB
+ default 8 if I855_VIDEO_MB_8MB
+ default 16 if I855_VIDEO_MB_16MB
+ default 32 if I855_VIDEO_MB_32MB
+ depends on NORTHBRIDGE_INTEL_I855
diff --git a/src/northbridge/intel/i855/northbridge.c b/src/northbridge/intel/i855/northbridge.c
index 77d1564672..220f7220ce 100644
--- a/src/northbridge/intel/i855/northbridge.c
+++ b/src/northbridge/intel/i855/northbridge.c
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <device/device.h>
#include <device/pci.h>
+#include <device/pci_ids.h>
#include <stdlib.h>
#include <string.h>
#include <bitops.h>
@@ -32,6 +33,26 @@
#include <cpu/cpu.h>
#include "chip.h"
+static void northbridge_init(device_t dev)
+{
+ printk(BIOS_SPEW, "Northbridge init\n");
+}
+
+static struct device_operations northbridge_operations = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = northbridge_init,
+ .enable = 0,
+ .ops_pci = 0,
+};
+
+static const struct pci_driver northbridge_driver __pci_driver = {
+ .ops = &northbridge_operations,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x3580,
+};
+
static void ram_resource(device_t dev, unsigned long index,
unsigned long basek, unsigned long sizek)
{
diff --git a/src/northbridge/intel/i855/raminit.c b/src/northbridge/intel/i855/raminit.c
index 386eda10bb..e611d8e62b 100644
--- a/src/northbridge/intel/i855/raminit.c
+++ b/src/northbridge/intel/i855/raminit.c
@@ -18,256 +18,452 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <assert.h>
+#include <spd.h>
#include <sdram_mode.h>
+#include <stdlib.h>
#include <delay.h>
+#include "i855.h"
-#define dumpnorth() dump_pci_device(PCI_DEV(0, 0, 1))
-#define VG85X_MODE (SDRAM_BURST_4 | SDRAM_BURST_INTERLEAVED | SDRAM_CAS_2_5)
+#define VALIDATE_DIMM_COMPATIBILITY
-/**
- * Set only what I need until it works, then make it figure things out on boot
- * assumes only one dimm is populated
- */
+/* Debugging macros. */
+#if CONFIG_DEBUG_RAM_SETUP
+#define PRINTK_DEBUG(x...) printk(BIOS_DEBUG, x)
+#define DUMPNORTH() dump_pci_device(NORTHBRIDGE_MMC)
+#else
+#define PRINTK_DEBUG(x...)
+#define DUMPNORTH()
+#endif
-static void sdram_set_registers(const struct mem_controller *ctrl)
+#define delay() udelay(200)
+
+#define VG85X_MODE (SDRAM_BURST_4 | SDRAM_BURST_INTERLEAVED | SDRAM_CAS_2_5)
+
+/* DRC[10:8] - Refresh Mode Select (RMS).
+ * 0x0 for Refresh Disabled (Self Refresh)
+ * 0x1 for Refresh interval 15.6 us for 133MHz
+ * 0x2 for Refresh interval 7.8 us for 133MHz
+ * 0x7 for Refresh interval 64 Clocks. (Fast Refresh Mode)
+ */
+#define RAM_COMMAND_REFRESH 0x1
+
+/* DRC[6:4] - SDRAM Mode Select (SMS). */
+#define RAM_COMMAND_SELF_REFRESH 0x0
+#define RAM_COMMAND_NOP 0x1
+#define RAM_COMMAND_PRECHARGE 0x2
+#define RAM_COMMAND_MRS 0x3
+#define RAM_COMMAND_EMRS 0x4
+#define RAM_COMMAND_CBR 0x6
+#define RAM_COMMAND_NORMAL 0x7
+
+/* DRC[29] - Initialization Complete (IC). */
+#define RAM_COMMAND_IC 0x1
+
+struct dimm_size {
+ unsigned int side1;
+ unsigned int side2;
+};
+
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/* DEFINITIONS */
+/**********************************************************************************/
+
+static const uint32_t refresh_frequency[] = {
+ /* Relative frequency (array value) of each E7501 Refresh Mode Select
+ * (RMS) value (array index)
+ * 0 == least frequent refresh (longest interval between refreshes)
+ * [0] disabled -> 0
+ * [1] 15.6 usec -> 2
+ * [2] 7.8 usec -> 3
+ * [3] 64 usec -> 1
+ * [4] reserved -> 0
+ * [5] reserved -> 0
+ * [6] reserved -> 0
+ * [7] 64 clocks -> 4
+ */
+ 0, 2, 3, 1, 0, 0, 0, 4
+};
+
+static const uint32_t refresh_rate_map[] = {
+ /* Map the JEDEC spd refresh rates (array index) to i855 Refresh Mode
+ * Select values (array value)
+ * These are all the rates defined by JESD21-C Appendix D, Rev. 1.0
+ * The i855 supports only 15.6 us (1), 7.8 us (2) and
+ * 64 clock (481 ns) (7) refresh.
+ * [0] == 15.625 us -> 15.6 us
+ * [1] == 3.9 us -> 481 ns
+ * [2] == 7.8 us -> 7.8 us
+ * [3] == 31.3 us -> 15.6 us
+ * [4] == 62.5 us -> 15.6 us
+ * [5] == 125 us -> 15.6 us
+ */
+ 1, 7, 2, 1, 1, 1
+};
+
+#define MAX_SPD_REFRESH_RATE ((sizeof(refresh_rate_map) / sizeof(uint32_t)) - 1)
+
+/*-----------------------------------------------------------------------------
+SPD functions.
+-----------------------------------------------------------------------------*/
+
+static void die_on_spd_error(int spd_return_value)
{
- /*
- print_debug("Before configuration:\n");
- dump_pci_devices();
- */
+ if (spd_return_value < 0)
+ PRINTK_DEBUG("Error reading SPD info: got %d\n", spd_return_value);
+/*
+ if (spd_return_value < 0)
+ die("Error reading SPD info\n");
+*/
}
-static void spd_set_row_attributes(const struct mem_controller *ctrl)
+//----------------------------------------------------------------------------------
+// Function: sdram_spd_get_page_size
+// Parameters: dimm_socket_address - SMBus address of DIMM socket to interrogate
+// Return Value: struct dimm_size - log2(page size) for each side of the DIMM.
+// Description: Calculate the page size for each physical bank of the DIMM:
+// log2(page size) = (# columns) + log2(data width)
+//
+// NOTE: page size is the total number of data bits in a row.
+//
+static struct dimm_size sdram_spd_get_page_size(uint16_t dimm_socket_address)
{
- uint16_t dra_reg;
+ uint16_t module_data_width;
+ int value;
+ struct dimm_size pgsz;
- dra_reg = 0x7733;
- pci_write_config16(ctrl->d0, 0x50, dra_reg);
-}
+ pgsz.side1 = 0;
+ pgsz.side2 = 0;
-static void spd_set_dram_controller_mode(const struct mem_controller *ctrl)
-{
- uint32_t drc_reg;
+ // Side 1
+ value = spd_read_byte(dimm_socket_address, SPD_NUM_COLUMNS);
+ die_on_spd_error(value);
- /* drc_reg = 0x00009101; */
- drc_reg = 0x00009901;
- pci_write_config32(ctrl->d0, 0x70, drc_reg);
-}
+ pgsz.side1 = value & 0xf; // # columns in bank 1
-static void spd_set_dram_timing(const struct mem_controller *ctrl)
-{
- uint32_t drt_reg;
+ /* Get the module data width and convert it to a power of two */
+ value = spd_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_MSB);
+ die_on_spd_error(value);
- drt_reg = 0x2a004405;
- pci_write_config32(ctrl->d0, 0x60, drt_reg);
-}
+ module_data_width = (value & 0xff) << 8;
-static void spd_set_dram_size(const struct mem_controller *ctrl)
-{
- uint32_t drb_reg;
+ value = spd_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_LSB);
+ die_on_spd_error(value);
- drb_reg = 0x20202010;
- pci_write_config32(ctrl->d0, 0x40, drb_reg);
-}
+ module_data_width |= (value & 0xff);
-static void spd_set_dram_pwr_management(const struct mem_controller *ctrl)
-{
- uint32_t pwrmg_reg;
+ pgsz.side1 += log2(module_data_width);
- pwrmg_reg = 0x10f10430;
- pci_write_config32(ctrl->d0, 0x68, pwrmg_reg);
+ /* side two */
+ value = spd_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS);
+ die_on_spd_error(value);
+
+/*
+ if (value > 2)
+ die("Bad SPD value\n");
+*/
+ if (value > 2)
+ PRINTK_DEBUG("Bad SPD value\n");
+
+ if (value == 2) {
+ pgsz.side2 = pgsz.side1; // Assume symmetric banks until we know differently
+ value = spd_read_byte(dimm_socket_address, SPD_NUM_COLUMNS);
+ die_on_spd_error(value);
+
+ if ((value & 0xf0) != 0) {
+ // Asymmetric banks
+ pgsz.side2 -= value & 0xf; /* Subtract out columns on side 1 */
+ pgsz.side2 += (value >> 4) & 0xf; /* Add in columns on side 2 */
+ }
+ }
+
+ return pgsz;
}
-static void spd_set_dram_throttle_control(const struct mem_controller *ctrl)
+//----------------------------------------------------------------------------------
+// Function: sdram_spd_get_width
+// Parameters: dimm_socket_address - SMBus address of DIMM socket to interrogate
+// Return Value: dimm_size - width in bits of each DIMM side's DRAMs.
+// Description: Read the width in bits of each DIMM side's DRAMs via SPD.
+// (i.e. 4, 8, 16)
+//
+static struct dimm_size sdram_spd_get_width(uint16_t dimm_socket_address)
{
- uint32_t dtc_reg;
+ int value;
+ struct dimm_size width;
+
+ width.side1 = 0;
+ width.side2 = 0;
+
+ value = spd_read_byte(dimm_socket_address, SPD_PRIMARY_SDRAM_WIDTH);
+ die_on_spd_error(value);
+
+ width.side1 = value & 0x7f; // Mask off bank 2 flag
+
+ if (value & 0x80) {
+ width.side2 = width.side1 << 1; // Bank 2 exists and is double-width
+ } else {
+ // If bank 2 exists, it's the same width as bank 1
+ value = spd_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS);
+ die_on_spd_error(value);
+
+#ifdef ROMCC_IF_BUG_FIXED
+ if (value == 2)
+ width.side2 = width.side1;
+#else
+ switch (value) {
+ case 2:
+ width.side2 = width.side1;
+ break;
+
+ default:
+ break;
+ }
+#endif
+ }
- dtc_reg = 0x300aa2ff;
- pci_write_config32(ctrl->d0, 0xa0, dtc_reg);
+ return width;
}
-#define delay() udelay(200)
-
-/* if ram still doesn't work do this function */
-static void spd_set_undocumented_registers(const struct mem_controller *ctrl)
+//----------------------------------------------------------------------------------
+// Function: spd_get_dimm_size
+// Parameters: dimm_socket_address - SMBus address of DIMM socket to interrogate
+// Return Value: dimm_size - log2(number of bits) for each side of the DIMM
+// Description: Calculate the log base 2 size in bits of both DIMM sides.
+// log2(# bits) = (# columns) + log2(data width) +
+// (# rows) + log2(banks per SDRAM)
+//
+// Note that it might be easier to use SPD byte 31 here, it has the
+// DIMM size as a multiple of 4MB. The way we do it now we can size
+// both sides of an asymmetric dimm.
+//
+static struct dimm_size spd_get_dimm_size(unsigned dimm)
{
- /* 0:0.0 */
- /*
- pci_write_config32(PCI_DEV(0, 0, 0), 0x10, 0xe0000008);
- pci_write_config32(PCI_DEV(0, 0, 0), 0x2c, 0x35808086);
- pci_write_config32(PCI_DEV(0, 0, 0), 0x48, 0xfec10000);
- pci_write_config32(PCI_DEV(0, 0, 0), 0x50, 0x00440100);
+ int value;
- pci_write_config32(PCI_DEV(0, 0, 0), 0x58, 0x11111000);
+ // Start with log2(page size)
+ struct dimm_size sz = sdram_spd_get_page_size(dimm);
- pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0002);
- */
- pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0044);
- /*
- pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0000);
- */
- pci_write_config32(PCI_DEV(0, 0, 0), 0x58, 0x33333000);
- pci_write_config32(PCI_DEV(0, 0, 0), 0x5c, 0x33333333);
- /*
- pci_write_config32(PCI_DEV(0, 0, 0), 0x60, 0x0000390a);
- pci_write_config32(PCI_DEV(0, 0, 0), 0x74, 0x02006056);
- pci_write_config32(PCI_DEV(0, 0, 0), 0x78, 0x00800001);
- */
- pci_write_config32(PCI_DEV(0, 0, 0), 0xa8, 0x00000001);
+ if (sz.side1 > 0) {
+ value = spd_read_byte(dimm, SPD_NUM_ROWS);
+ die_on_spd_error(value);
- pci_write_config32(PCI_DEV(0, 0, 0), 0xbc, 0x00001020);
- /*
- pci_write_config32(PCI_DEV(0, 0, 0), 0xfc, 0x00000109);
- */
+ sz.side1 += value & 0xf;
- /* 0:0.1 */
- pci_write_config32(ctrl->d0, 0x74, 0x00000001);
- pci_write_config32(ctrl->d0, 0x78, 0x001fe974);
- pci_write_config32(ctrl->d0, 0x80, 0x00af0039);
- pci_write_config32(ctrl->d0, 0x84, 0x0000033c);
- pci_write_config32(ctrl->d0, 0x88, 0x00000010);
- pci_write_config32(ctrl->d0, 0x98, 0xde5a868c);
- pci_write_config32(ctrl->d0, 0x9c, 0x404e0046);
- pci_write_config32(ctrl->d0, 0xa8, 0x00020e1a);
- pci_write_config32(ctrl->d0, 0xb4, 0x0044cdac);
- pci_write_config32(ctrl->d0, 0xb8, 0x000055d4);
- pci_write_config32(ctrl->d0, 0xbc, 0x024acd38);
- pci_write_config32(ctrl->d0, 0xc0, 0x00000003);
-
- /* 0:0.3 */
- /*
- pci_write_config32(PCI_DEV(0, 0, 3), 0x2c, 0x35858086);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x44, 0x11110000);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x48, 0x09614a3c);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x4c, 0x4b09604a);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x50, 0x00000962);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x5c, 0x0b023331);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x6c, 0x0000402e);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x78, 0xe7c70f7f);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x7c, 0x0284007f);
- pci_write_config32(PCI_DEV(0, 0, 3), 0x84, 0x000000ef);
- */
+ if (sz.side2 > 0) {
+ // Double-sided DIMM
+ if (value & 0xF0)
+ sz.side2 += value >> 4; // Asymmetric
+ else
+ sz.side2 += value; // Symmetric
+ }
- /*
- pci_write_config16(PCI_DEV(0, 0, 3), 0xc0, 0x0200);
- pci_write_config16(PCI_DEV(0, 0, 3), 0xc0, 0x0400);
- */
+ value = spd_read_byte(dimm, SPD_NUM_BANKS_PER_SDRAM);
+ die_on_spd_error(value);
- /*
- pci_write_config32(PCI_DEV(0, 0, 3), 0xc4, 0x00000000);
- pci_write_config32(PCI_DEV(0, 0, 3), 0xd8, 0xff00c308);
- pci_write_config32(PCI_DEV(0, 0, 3), 0xdc, 0x00000025);
- pci_write_config32(PCI_DEV(0, 0, 3), 0xe0, 0x001f002a);
- pci_write_config32(PCI_DEV(0, 0, 3), 0xe4, 0x009f0098);
- pci_write_config32(PCI_DEV(0, 0, 3), 0xec, 0x00000400);
- pci_write_config32(PCI_DEV(0, 0, 3), 0xf0, 0xc0000000);
- */
-}
+ value = log2(value);
+ sz.side1 += value;
+ if (sz.side2 > 0)
+ sz.side2 += value;
+ }
-static void sdram_set_spd_registers(const struct mem_controller *ctrl)
-{
- spd_set_row_attributes(ctrl);
- spd_set_dram_controller_mode(ctrl);
- spd_set_dram_timing(ctrl);
- spd_set_dram_size(ctrl);
- spd_set_dram_pwr_management(ctrl);
- spd_set_dram_throttle_control(ctrl);
- spd_set_undocumented_registers(ctrl);
+ return sz;
}
-static void ram_command(const struct mem_controller *ctrl,
- uint8_t command,
- uint32_t addr)
+//----------------------------------------------------------------------------------
+// Function: spd_get_supported_dimms
+// Parameters: ctrl - PCI addresses of memory controller functions, and
+// SMBus addresses of DIMM slots on the mainboard
+// Return Value: uint8_t - a bitmask indicating which sockets contain a compatible DIMM.
+// Description: Scan for compatible DIMMs.
+//
+static uint8_t spd_get_supported_dimms(const struct mem_controller *ctrl)
{
- uint32_t drc_reg;
+ int i;
+ uint8_t dimm_mask = 0;
+
+ for (i = 0; i < DIMM_SOCKETS; i++) {
+ uint16_t dimm = ctrl->channel0[i];
+
+#ifdef VALIDATE_DIMM_COMPATIBILITY
+ struct dimm_size page_size;
+ struct dimm_size sdram_width;
+#endif
+ int spd_value;
+
+ if (dimm == 0)
+ continue; // No such socket on this mainboard
+
+ if (spd_read_byte(dimm, SPD_MEMORY_TYPE) != SPD_MEMORY_TYPE_SDRAM_DDR)
+ continue;
+
+#ifdef VALIDATE_DIMM_COMPATIBILITY
+ if ((spd_value = spd_read_byte(dimm, SPD_MODULE_VOLTAGE)) != SPD_VOLTAGE_SSTL2) {
+ PRINTK_DEBUG("Skipping DIMM with unsupported voltage: %02x\n", spd_value);
+ continue; // Unsupported voltage
+ }
+
+/*
+ // E7501 does not support unregistered DIMMs
+ spd_value = spd_read_byte(dimm, SPD_MODULE_ATTRIBUTES);
+ if (!(spd_value & MODULE_REGISTERED) || (spd_value < 0)) {
+ PRINTK_DEBUG("Skipping unregistered DIMM: %02x\n", spd_value);
+ continue;
+ }
+*/
+
+ page_size = sdram_spd_get_page_size(dimm);
+ sdram_width = sdram_spd_get_width(dimm);
+
+ // Validate DIMM page size
+ // The i855 only supports page sizes of 4, 8, 16 KB per channel
+ // NOTE: 4 KB = 32 Kb = 2^15
+ // 16 KB = 128 Kb = 2^17
+
+ if ((page_size.side1 < 15) || (page_size.side1 > 17)) {
+ PRINTK_DEBUG("Skipping DIMM with unsupported page size: %d\n", page_size.side1);
+ continue;
+ }
+
+ // If DIMM is double-sided, verify side2 page size
+ if (page_size.side2 != 0) {
+ if ((page_size.side2 < 15) || (page_size.side2 > 17)) {
+ PRINTK_DEBUG("Skipping DIMM with unsupported page size: %d\n", page_size.side2);
+ continue;
+ }
+ }
+ // Validate SDRAM width
+ // The i855 only supports x8 and x16 devices
+ if ((sdram_width.side1 != 8) && (sdram_width.side1 != 16)) {
+ PRINTK_DEBUG("Skipping DIMM with unsupported width: %d\n", sdram_width.side2);
+ continue;
+ }
+
+ // If DIMM is double-sided, verify side2 width
+ if (sdram_width.side2 != 0) {
+ if ((sdram_width.side2 != 8)
+ && (sdram_width.side2 != 16)) {
+ PRINTK_DEBUG("Skipping DIMM with unsupported width: %d\n", sdram_width.side2);
+ continue;
+ }
+ }
+#endif
+ // Made it through all the checks, this DIMM is usable
+ dimm_mask |= (1 << i);
+ }
- drc_reg = pci_read_config32(ctrl->d0, 0x70);
- drc_reg &= ~(7 << 4);
- drc_reg |= (command << 4);
- pci_write_config8(ctrl->d0, 0x70, drc_reg);
- read32(addr);
+ return dimm_mask;
}
-static void ram_command_mrs(const struct mem_controller *ctrl,
- uint8_t command,
- uint32_t mode,
- uint32_t addr)
+/*-----------------------------------------------------------------------------
+DIMM-initialization functions.
+-----------------------------------------------------------------------------*/
+static void do_ram_command(uint8_t command, uint16_t jedec_mode_bits)
{
- uint32_t drc_reg;
- uint32_t adjusted_mode;
-
- drc_reg = pci_read_config32(ctrl->d0, 0x70);
- drc_reg &= ~(7 << 4);
- drc_reg |= (command << 4);
- pci_write_config8(ctrl->d0, 0x70, drc_reg);
- /* Host address lines [13:3] map to DIMM address lines [11, 9:0] */
- adjusted_mode = ((mode & 0x800) << (13 - 11)) | ((mode & 0x3ff) << (12 - 9));
- print_debug("Setting mode: ");
- print_debug_hex32(adjusted_mode + addr);
- print_debug("\n");
- read32(adjusted_mode + addr);
+ int i;
+ u32 reg32;
+ uint8_t dimm_start_32M_multiple = 0;
+ uint16_t i855_mode_bits = jedec_mode_bits;
+
+ /* Configure the RAM command. */
+ reg32 = pci_read_config32(NORTHBRIDGE_MMC, DRC);
+ reg32 &= ~(7 << 4);
+ reg32 |= (command << 4);
+ PRINTK_DEBUG(" Sending RAM command 0x%08x\n", reg32);
+ pci_write_config32(NORTHBRIDGE_MMC, DRC, reg32);
+
+ // RAM_COMMAND_NORMAL is an exception.
+ // It affects only the memory controller and does not need to be "sent" to the DIMMs.
+
+ if (command != RAM_COMMAND_NORMAL) {
+
+ // Send the command to all DIMMs by accessing a memory location within each
+ // NOTE: for mode select commands, some of the location address bits
+ // are part of the command
+
+ // Map JEDEC mode bits to i855
+ if (command == RAM_COMMAND_MRS || command == RAM_COMMAND_EMRS) {
+ /* Host address lines [13:3] map to DIMM address lines [11, 9:0] */
+ i855_mode_bits = ((jedec_mode_bits & 0x800) << (13 - 11)) | ((jedec_mode_bits & 0x3ff) << (12 - 9));
+ }
+
+ for (i = 0; i < (DIMM_SOCKETS * 2); ++i) {
+ uint8_t dimm_end_32M_multiple = pci_read_config8(NORTHBRIDGE_MMC, DRB + i);
+ if (dimm_end_32M_multiple > dimm_start_32M_multiple) {
+
+ uint32_t dimm_start_address = dimm_start_32M_multiple << 25;
+ PRINTK_DEBUG(" Sending RAM command to 0x%08x\n", dimm_start_address + i855_mode_bits);
+ read32(dimm_start_address + i855_mode_bits);
+
+ // Set the start of the next DIMM
+ dimm_start_32M_multiple = dimm_end_32M_multiple;
+ }
+ }
+ }
}
static void set_initialize_complete(const struct mem_controller *ctrl)
{
uint32_t drc_reg;
- drc_reg = pci_read_config32(ctrl->d0, 0x70);
+ drc_reg = pci_read_config32(NORTHBRIDGE_MMC, DRC);
drc_reg |= (1 << 29);
- pci_write_config32(ctrl->d0, 0x70, drc_reg);
+ pci_write_config32(NORTHBRIDGE_MMC, DRC, drc_reg);
}
static void sdram_enable(int controllers, const struct mem_controller *ctrl)
{
int i;
- uint32_t rank1 = (1 << 30) / 2;
+
print_debug("Ram enable 1\n");
delay();
delay();
- print_debug("Ram enable 2\n");
- ram_command(ctrl, 1, 0);
- ram_command(ctrl, 1, rank1);
+ /* NOP command */
+ PRINTK_DEBUG(" NOP\n");
+ do_ram_command(RAM_COMMAND_NOP, 0);
+ delay();
delay();
delay();
- print_debug("Ram enable 3\n");
- ram_command(ctrl, 2, 0);
- ram_command(ctrl, 2, rank1);
+ /* Pre-charge all banks (at least 200 us after NOP) */
+ PRINTK_DEBUG(" Pre-charging all banks\n");
+ do_ram_command(RAM_COMMAND_PRECHARGE, 0);
+ delay();
delay();
delay();
print_debug("Ram enable 4\n");
- ram_command_mrs(ctrl, 4, SDRAM_EXTMODE_DLL_ENABLE, 0);
- ram_command_mrs(ctrl, 4, SDRAM_EXTMODE_DLL_ENABLE, rank1);
+ do_ram_command(RAM_COMMAND_EMRS, SDRAM_EXTMODE_DLL_ENABLE);
+ delay();
delay();
delay();
print_debug("Ram enable 5\n");
- ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_DLL_RESET, 0);
- ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_DLL_RESET, rank1);
+ do_ram_command(RAM_COMMAND_MRS, VG85X_MODE | SDRAM_MODE_DLL_RESET);
print_debug("Ram enable 6\n");
- ram_command(ctrl, 2, 0);
- ram_command(ctrl, 2, rank1);
+ do_ram_command(RAM_COMMAND_PRECHARGE, 0);
+ delay();
delay();
delay();
- print_debug("Ram enable 7\n");
+ /* 8 CBR refreshes (Auto Refresh) */
+ PRINTK_DEBUG(" 8 CBR refreshes\n");
for(i = 0; i < 8; i++) {
- ram_command(ctrl, 6, 0);
- ram_command(ctrl, 6, rank1);
+ do_ram_command(RAM_COMMAND_CBR, 0);
+ delay();
delay();
delay();
}
print_debug("Ram enable 8\n");
- ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_NORMAL, 0);
- ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_NORMAL, rank1);
+ do_ram_command(RAM_COMMAND_MRS, VG85X_MODE | SDRAM_MODE_NORMAL);
- print_debug("Ram enable 9\n");
- ram_command(ctrl, 7, 0);
- ram_command(ctrl, 7, rank1);
+ /* Set GME-M Mode Select bits back to NORMAL operation mode */
+ PRINTK_DEBUG(" Normal operation mode\n");
+ do_ram_command(RAM_COMMAND_NORMAL, 0);
+ delay();
delay();
delay();
@@ -277,6 +473,8 @@ static void sdram_enable(int controllers, const struct mem_controller *ctrl)
delay();
delay();
delay();
+ delay();
+ delay();
print_debug("After configuration:\n");
/* dump_pci_devices(); */
@@ -287,3 +485,497 @@ static void sdram_enable(int controllers, const struct mem_controller *ctrl)
ram_check(0x100000, 0x40000000);
*/
}
+
+/*-----------------------------------------------------------------------------
+DIMM-independant configuration functions.
+-----------------------------------------------------------------------------*/
+
+/**
+ * Set only what I need until it works, then make it figure things out on boot
+ * assumes only one dimm is populated
+ */
+
+static void sdram_set_registers(const struct mem_controller *ctrl)
+{
+ /*
+ print_debug("Before configuration:\n");
+ dump_pci_devices();
+ */
+}
+
+static void spd_set_row_attributes(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+ int i;
+ uint16_t row_attributes = 0;
+
+ for (i = 0; i < DIMM_SOCKETS; i++) {
+ uint16_t dimm = ctrl->channel0[i];
+ struct dimm_size page_size;
+ struct dimm_size sdram_width;
+
+ if (!(dimm_mask & (1 << i))) {
+ row_attributes |= 0x77 << (i << 3);
+ continue; // This DIMM not usable
+ }
+
+ // Get the relevant parameters via SPD
+ page_size = sdram_spd_get_page_size(dimm);
+ sdram_width = sdram_spd_get_width(dimm);
+
+ // Update the DRAM Row Attributes.
+ // Page size is encoded as log2(page size in bits) - log2(2 KB) or 4 KB == 1, 8 KB == 3, 16KB == 3
+ // NOTE: 2 KB = 16 Kb = 2^14
+ row_attributes |= (page_size.side1 - 14) << (i << 3); // Side 1 of each DIMM is an EVEN row
+
+ if (sdram_width.side2 > 0)
+ row_attributes |= (page_size.side2 - 14) << ((i << 3) + 4); // Side 2 is ODD
+ else
+ row_attributes |= 7 << ((i << 3) + 4);
+ /* go to the next DIMM */
+ }
+
+ PRINTK_DEBUG("DRA: %04x\n", row_attributes);
+
+ /* Write the new row attributes register */
+ pci_write_config16(NORTHBRIDGE_MMC, DRA, row_attributes);
+}
+
+static void spd_set_dram_controller_mode(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+ int i;
+
+ // Initial settings
+ u32 controller_mode = pci_read_config32(NORTHBRIDGE_MMC, DRC);
+ u32 system_refresh_mode = (controller_mode >> 7) & 7;
+
+ controller_mode |= (1 << 20); // ECC
+ controller_mode |= (1 << 15); // RAS lockout
+ controller_mode |= (1 << 12); // Address Tri-state enable (ADRTRIEN), FIXME: how is this detected?????
+ controller_mode |= (2 << 10); // FIXME: Undocumented, really needed?????
+
+ for (i = 0; i < DIMM_SOCKETS; i++) {
+ uint16_t dimm = ctrl->channel0[i];
+ uint32_t dimm_refresh_mode;
+ int value;
+ u8 tRCD, tRP;
+
+ if (!(dimm_mask & (1 << i))) {
+ continue; // This DIMM not usable
+ }
+
+ // Disable ECC mode if any one of the DIMMs does not support ECC
+ value = spd_read_byte(dimm, SPD_DIMM_CONFIG_TYPE);
+ die_on_spd_error(value);
+ if (value != ERROR_SCHEME_ECC)
+ controller_mode &= ~(3 << 20);
+
+ value = spd_read_byte(dimm, SPD_REFRESH);
+ die_on_spd_error(value);
+ value &= 0x7f; // Mask off self-refresh bit
+ if (value > MAX_SPD_REFRESH_RATE) {
+ print_err("unsupported refresh rate\n");
+ continue;
+ }
+ // Get the appropriate i855 refresh mode for this DIMM
+ dimm_refresh_mode = refresh_rate_map[value];
+ if (dimm_refresh_mode > 7) {
+ print_err("unsupported refresh rate\n");
+ continue;
+ }
+ // If this DIMM requires more frequent refresh than others,
+ // update the system setting
+ if (refresh_frequency[dimm_refresh_mode] >
+ refresh_frequency[system_refresh_mode])
+ system_refresh_mode = dimm_refresh_mode;
+
+ /* FIXME: is this correct? */
+ tRCD = spd_read_byte(dimm, SPD_tRCD);
+ tRP = spd_read_byte(dimm, SPD_tRP);
+ if (tRCD != tRP) {
+ PRINTK_DEBUG(" Disabling RAS lockouk due to tRCD (%d) != tRP (%d)\n", tRCD, tRP);
+ controller_mode &= ~(1 << 15);
+ }
+
+ /* go to the next DIMM */
+ }
+
+ controller_mode &= ~(7 << 7);
+ controller_mode |= (system_refresh_mode << 7);
+ PRINTK_DEBUG("DRC: %08x\n", controller_mode);
+
+ pci_write_config32(NORTHBRIDGE_MMC, DRC, controller_mode);
+}
+
+static void spd_set_dram_timing(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+ int i;
+ u32 dram_timing;
+
+ // CAS# latency bitmasks in SPD_ACCEPTABLE_CAS_LATENCIES format
+ // NOTE: i82822 supports only 2.0 and 2.5
+ uint32_t system_compatible_cas_latencies = SPD_CAS_LATENCY_2_0 | SPD_CAS_LATENCY_2_5;
+ uint8_t slowest_row_precharge = 0;
+ uint8_t slowest_ras_cas_delay = 0;
+ uint8_t slowest_active_to_precharge_delay = 0;
+
+ for (i = 0; i < DIMM_SOCKETS; i++) {
+ uint16_t dimm = ctrl->channel0[i];
+ int value;
+ uint32_t current_cas_latency;
+ uint32_t dimm_compatible_cas_latencies;
+ if (!(dimm_mask & (1 << i)))
+ continue; // This DIMM not usable
+
+ value = spd_read_byte(dimm, SPD_ACCEPTABLE_CAS_LATENCIES);
+ PRINTK_DEBUG("SPD_ACCEPTABLE_CAS_LATENCIES: %d\n", value);
+ die_on_spd_error(value);
+
+ dimm_compatible_cas_latencies = value & 0x7f; // Start with all supported by DIMM
+ PRINTK_DEBUG("dimm_compatible_cas_latencies #1: %d\n", dimm_compatible_cas_latencies);
+
+ current_cas_latency = 1 << log2(dimm_compatible_cas_latencies); // Max supported by DIMM
+ PRINTK_DEBUG("current_cas_latency: %d\n", current_cas_latency);
+
+ // Can we support the highest CAS# latency?
+ value = spd_read_byte(dimm, SPD_MIN_CYCLE_TIME_AT_CAS_MAX);
+ die_on_spd_error(value);
+ PRINTK_DEBUG("SPD_MIN_CYCLE_TIME_AT_CAS_MAX: %d.%d\n", value >> 4, value & 0xf);
+
+ // NOTE: At 133 MHz, 1 clock == 7.52 ns
+ if (value > 0x75) {
+ // Our bus is too fast for this CAS# latency
+ // Remove it from the bitmask of those supported by the DIMM that are compatible
+ dimm_compatible_cas_latencies &= ~current_cas_latency;
+ PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies);
+ }
+ // Can we support the next-highest CAS# latency (max - 0.5)?
+
+ current_cas_latency >>= 1;
+ if (current_cas_latency != 0) {
+ value = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_2ND);
+ die_on_spd_error(value);
+ PRINTK_DEBUG("SPD_SDRAM_CYCLE_TIME_2ND: %d.%d\n", value >> 4, value & 0xf);
+ if (value > 0x75) {
+ dimm_compatible_cas_latencies &= ~current_cas_latency;
+ PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies);
+ }
+ }
+ // Can we support the next-highest CAS# latency (max - 1.0)?
+ current_cas_latency >>= 1;
+ if (current_cas_latency != 0) {
+ value = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_3RD);
+ PRINTK_DEBUG("SPD_SDRAM_CYCLE_TIME_3RD: %d.%d\n", value >> 4, value & 0xf);
+ die_on_spd_error(value);
+ if (value > 0x75) {
+ dimm_compatible_cas_latencies &= ~current_cas_latency;
+ PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies);
+ }
+ }
+ // Restrict the system to CAS# latencies compatible with this DIMM
+ system_compatible_cas_latencies &= dimm_compatible_cas_latencies;
+
+ value = spd_read_byte(dimm, SPD_MIN_ROW_PRECHARGE_TIME);
+ die_on_spd_error(value);
+ if (value > slowest_row_precharge)
+ slowest_row_precharge = value;
+
+ value = spd_read_byte(dimm, SPD_MIN_RAS_TO_CAS_DELAY);
+ die_on_spd_error(value);
+ if (value > slowest_ras_cas_delay)
+ slowest_ras_cas_delay = value;
+
+ value = spd_read_byte(dimm, SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY);
+ die_on_spd_error(value);
+ if (value > slowest_active_to_precharge_delay)
+ slowest_active_to_precharge_delay = value;
+
+ /* go to the next DIMM */
+ }
+ PRINTK_DEBUG("CAS latency: %d\n", system_compatible_cas_latencies);
+
+ dram_timing = pci_read_config32(NORTHBRIDGE_MMC, DRT);
+ dram_timing &= ~(DRT_CAS_MASK | DRT_TRP_MASK | DRT_RCD_MASK);
+ PRINTK_DEBUG("DRT: %08x\n", dram_timing);
+
+ if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_0) {
+ dram_timing |= DRT_CAS_2_0;
+ } else if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_5) {
+ dram_timing |= DRT_CAS_2_5;
+ } else
+ die("No CAS# latencies compatible with all DIMMs!!\n");
+
+ uint32_t current_cas_latency = dram_timing & DRT_CAS_MASK;
+
+ /* tRP */
+
+ PRINTK_DEBUG("slowest_row_precharge: %d.%d\n", slowest_row_precharge >> 2, slowest_row_precharge & 0x3);
+ // i855 supports only 2, 3 or 4 clocks for tRP
+ if (slowest_row_precharge > ((30 << 2)))
+ die("unsupported DIMM tRP"); // > 30.0 ns: 5 or more clocks
+ else if (slowest_row_precharge > ((22 << 2) | (2 << 0)))
+ dram_timing |= DRT_TRP_4; // > 22.5 ns: 4 or more clocks
+ else if (slowest_row_precharge > (15 << 2))
+ dram_timing |= DRT_TRP_3; // > 15.0 ns: 3 clocks
+ else
+ dram_timing |= DRT_TRP_2; // <= 15.0 ns: 2 clocks
+
+ /* tRCD */
+
+ PRINTK_DEBUG("slowest_ras_cas_delay: %d.%d\n", slowest_ras_cas_delay >> 2, slowest_ras_cas_delay & 0x3);
+ // i855 supports only 2, 3 or 4 clocks for tRCD
+ if (slowest_ras_cas_delay > ((30 << 2)))
+ die("unsupported DIMM tRCD"); // > 30.0 ns: 5 or more clocks
+ else if (slowest_ras_cas_delay > ((22 << 2) | (2 << 0)))
+ dram_timing |= DRT_RCD_4; // > 22.5 ns: 4 or more clocks
+ else if (slowest_ras_cas_delay > (15 << 2))
+ dram_timing |= DRT_RCD_3; // > 15.0 ns: 3 clocks
+ else
+ dram_timing |= DRT_RCD_2; // <= 15.0 ns: 2 clocks
+
+ /* tRAS, min */
+
+ PRINTK_DEBUG("slowest_active_to_precharge_delay: %d\n", slowest_active_to_precharge_delay);
+ // i855 supports only 5, 6, 7 or 8 clocks for tRAS
+ // 5 clocks ~= 37.6 ns, 6 clocks ~= 45.1 ns, 7 clocks ~= 52.6 ns, 8 clocks ~= 60.1 ns
+ if (slowest_active_to_precharge_delay > 60)
+ die("unsupported DIMM tRAS"); // > 52 ns: 8 or more clocks
+ else if (slowest_active_to_precharge_delay > 52)
+ dram_timing |= DRT_TRAS_MIN_8; // 46-52 ns: 7 clocks
+ else if (slowest_active_to_precharge_delay > 45)
+ dram_timing |= DRT_TRAS_MIN_7; // 46-52 ns: 7 clocks
+ else if (slowest_active_to_precharge_delay > 37)
+ dram_timing |= DRT_TRAS_MIN_6; // 38-45 ns: 6 clocks
+ else
+ dram_timing |= DRT_TRAS_MIN_5; // < 38 ns: 5 clocks
+
+ /* FIXME: guess work starts here...
+ *
+ * Intel refers to DQ turn-arround values for back to calculate the values,
+ * but i have no idea what this means
+ */
+
+ /*
+ * Back to Back Read-Write command spaceing (DDR, different Rows/Bank)
+ */
+ /* Set to a 3 clock back to back read to write turn around.
+ * 2 is a good delay if the CAS latency is 2.0 */
+ dram_timing &= ~(3 << 28);
+ if (current_cas_latency == DRT_CAS_2_0)
+ dram_timing |= (2 << 28); // 2 clocks
+ else
+ dram_timing |= (1 << 28); // 3 clocks
+
+ /*
+ * Back to Back Read-Write command spaceing (DDR, same or different Rows/Bank)
+ */
+ dram_timing &= ~(3 << 26);
+ if (current_cas_latency == DRT_CAS_2_0)
+ dram_timing |= (2 << 26); // 5 clocks
+ else
+ dram_timing |= (1 << 26); // 6 clocks
+
+ /*
+ * Back To Back Read-Read commands spacing (DDR, different Rows):
+ */
+ dram_timing &= ~(1 << 25);
+ dram_timing |= (1 << 25); // 3 clocks
+
+ PRINTK_DEBUG("DRT: %08x\n", dram_timing);
+ pci_write_config32(NORTHBRIDGE_MMC, DRT, dram_timing);
+}
+
+static void spd_set_dram_size(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+ int i;
+ int total_dram = 0;
+ uint32_t drb_reg = 0;
+
+ for (i = 0; i < DIMM_SOCKETS; i++) {
+ uint16_t dimm = ctrl->channel0[i];
+ struct dimm_size sz;
+
+ if (!(dimm_mask & (1 << i))) {
+ /* fill values even for not present DIMMs */
+ drb_reg |= (total_dram << (i * 16));
+ drb_reg |= (total_dram << ((i * 16) + 8));
+
+ continue; // This DIMM not usable
+ }
+ sz = spd_get_dimm_size(dimm);
+
+ total_dram += (1 << (sz.side1 - 28));
+ drb_reg |= (total_dram << (i * 16));
+
+ total_dram += (1 << (sz.side2 - 28));
+ drb_reg |= (total_dram << ((i * 16) + 8));
+ }
+ PRINTK_DEBUG("DRB: %08x\n", drb_reg);
+ pci_write_config32(NORTHBRIDGE_MMC, DRB, drb_reg);
+}
+
+
+static void spd_set_dram_pwr_management(const struct mem_controller *ctrl)
+{
+ uint32_t pwrmg_reg;
+
+ pwrmg_reg = 0x10f10430;
+ pci_write_config32(NORTHBRIDGE_MMC, PWRMG, pwrmg_reg);
+}
+
+static void spd_set_dram_throttle_control(const struct mem_controller *ctrl)
+{
+ uint32_t dtc_reg = 0;
+
+ /* DDR SDRAM Throttle Mode (TMODE):
+ * 0011 = Both Rank and GMCH Thermal Sensor based throttling is enabled. When the external SO-
+ * DIMM Thermal Sensor is Tripped DDR SDRAM Throttling begins based on the setting in RTT
+ */
+ dtc_reg |= (3 << 28);
+
+ /* Read Counter Based Power Throttle Control (RCTC):
+ * 0 = 85%
+ */
+ dtc_reg |= (0 << 24);
+
+ /* Write Counter Based Power Throttle Control (WCTC):
+ * 0 = 85%
+ */
+ dtc_reg |= (0 << 20);
+
+ /* Read Thermal Based Power Throttle Control (RTTC):
+ * 0xA = 20%
+ */
+ dtc_reg |= (0xA << 16);
+
+ /* Write Thermal Based Power Throttle Control (WTTC):
+ * 0xA = 20%
+ */
+ dtc_reg |= (0xA << 12);
+
+ /* Counter Based Throttle Lock (CTLOCK): */
+ dtc_reg |= (0 << 11);
+
+ /* Thermal Throttle Lock (TTLOCK): */
+ dtc_reg |= (0 << 10);
+
+ /* Thermal Power Throttle Control fields Enable: */
+ dtc_reg |= (1 << 9);
+
+ /* High Priority Stream Throttling Enable: */
+ dtc_reg |= (0 << 8);
+
+ /* Global DDR SDRAM Sampling Window (GDSW): */
+ dtc_reg |= 0xff;
+ PRINTK_DEBUG("DTC: %08x\n", dtc_reg);
+ pci_write_config32(NORTHBRIDGE_MMC, DTC, dtc_reg);
+}
+
+static void spd_update(const struct mem_controller *ctrl, u8 reg, u32 new_value)
+{
+ u32 value1 = pci_read_config32(ctrl->d0, reg);
+ pci_write_config32(ctrl->d0, reg, new_value);
+ u32 value2 = pci_read_config32(ctrl->d0, reg);
+ PRINTK_DEBUG("update reg %02x, old: %08x, new: %08x, read back: %08x\n", reg, value1, new_value, value2);
+}
+
+/* if ram still doesn't work do this function */
+static void spd_set_undocumented_registers(const struct mem_controller *ctrl)
+{
+ spd_update(ctrl, 0x74, 0x00000001);
+ spd_update(ctrl, 0x78, 0x001fe974);
+ spd_update(ctrl, 0x80, 0x00af0039);
+ spd_update(ctrl, 0x84, 0x0000033c);
+ spd_update(ctrl, 0x88, 0x00000010);
+
+ spd_update(ctrl, 0xc0, 0x00000003);
+}
+
+static void northbridge_set_registers(void)
+{
+ u16 value;
+ int video_memory = 0;
+
+ printk(BIOS_DEBUG, "Setting initial Northbridge registers....\n");
+
+ /* Set the value for Fixed DRAM Hole Control Register */
+ pci_write_config8(NORTHBRIDGE, FDHC, 0x00);
+
+ /* Set the value for Programable Attribute Map Registers
+ * Ideally, this should be R/W for as many ranges as possible.
+ */
+ pci_write_config8(NORTHBRIDGE, PAM0, 0x30);
+ pci_write_config8(NORTHBRIDGE, PAM1, 0x33);
+ pci_write_config8(NORTHBRIDGE, PAM2, 0x33);
+ pci_write_config8(NORTHBRIDGE, PAM3, 0x33);
+ pci_write_config8(NORTHBRIDGE, PAM4, 0x33);
+ pci_write_config8(NORTHBRIDGE, PAM5, 0x33);
+ pci_write_config8(NORTHBRIDGE, PAM6, 0x33);
+
+ /* Set the value for System Management RAM Control Register */
+ pci_write_config8(NORTHBRIDGE, SMRAM, 0x02);
+
+ /* Set the value for GMCH Control Register #1 */
+ switch (CONFIG_VIDEO_MB) {
+ case 1: /* 1M of memory */
+ video_memory = 0x1;
+ break;
+ case 4: /* 4M of memory */
+ video_memory = 0x2;
+ break;
+ case 8: /* 8M of memory */
+ video_memory = 0x3;
+ break;
+ case 16: /* 16M of memory */
+ video_memory = 0x4;
+ break;
+ case 32: /* 32M of memory */
+ video_memory = 0x5;
+ break;
+ default: /* No memory */
+ pci_write_config16(NORTHBRIDGE, GMC, pci_read_config16(NORTHBRIDGE, GMC) | 1);
+ video_memory = 0x0;
+ }
+
+ value = pci_read_config16(NORTHBRIDGE, GGC);
+ value |= video_memory << 4;
+ if (video_memory == 0) {
+ value &= ~(1 < 1);
+ } else
+ value |= (1 < 1);
+ pci_write_config16(NORTHBRIDGE, GGC, value);
+
+ /* AGPCMD: disable AGP, Data-Rate: 1x */
+ pci_write_config32(NORTHBRIDGE, AGPCMD, 0x00000001);
+
+ pci_write_config8(NORTHBRIDGE, AMTT, 0x20);
+ pci_write_config8(NORTHBRIDGE, LPTT, 0x10);
+
+ printk(BIOS_DEBUG, "Initial Northbridge registers have been set.\n");
+}
+
+static void sdram_set_spd_registers(const struct mem_controller *ctrl)
+{
+ uint8_t dimm_mask;
+
+ PRINTK_DEBUG("Reading SPD data...\n");
+
+ dimm_mask = spd_get_supported_dimms(ctrl);
+
+ if (dimm_mask == 0) {
+ print_debug("No usable memory for this controller\n");
+ } else {
+ PRINTK_DEBUG("DIMM MASK: %02x\n", dimm_mask);
+
+ spd_set_row_attributes(ctrl, dimm_mask);
+ spd_set_dram_controller_mode(ctrl, dimm_mask);
+ spd_set_dram_timing(ctrl, dimm_mask);
+ spd_set_dram_size(ctrl, dimm_mask);
+ spd_set_dram_pwr_management(ctrl);
+ spd_set_dram_throttle_control(ctrl);
+ spd_set_undocumented_registers(ctrl);
+ }
+
+ /* Setup Initial Northbridge Registers */
+ northbridge_set_registers();
+}
+
diff --git a/src/northbridge/intel/i855/raminit.h b/src/northbridge/intel/i855/raminit.h
index dbd0be6927..1f1b34d14b 100644
--- a/src/northbridge/intel/i855/raminit.h
+++ b/src/northbridge/intel/i855/raminit.h
@@ -18,11 +18,19 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef RAMINIT_H
-#define RAMINIT_H
+#ifndef NORTHBRIDGE_INTEL_I855_RAMINIT_H
+#define NORTHBRIDGE_INTEL_I855_RAMINIT_H
+/* i855 Northbridge PCI device */
+#define NORTHBRIDGE PCI_DEV(0, 0, 0)
+#define NORTHBRIDGE_MMC PCI_DEV(0, 0, 1)
+
+/* The i855 supports max. 2 dual-sided SO-DIMMs. */
#define DIMM_SOCKETS 2
+/* DIMM0 is at 0x50, DIMM1 is at 0x51. */
+#define DIMM_SPD_BASE 0x50
+
struct mem_controller {
device_t d0;
uint16_t channel0[DIMM_SOCKETS];
@@ -31,4 +39,4 @@ struct mem_controller {
void sdram_initialize(int controllers, const struct mem_controller *ctrl);
-#endif /* RAMINIT_H */
+#endif /* NORTHBRIDGE_INTEL_I855_RAMINIT_H */