[LinuxBIOS] i82830 raminit.c

Joseph Smith joe at smittys.pointclark.net
Wed Jul 4 22:45:02 CEST 2007


Ok, I think I am ready to test my i82830 raminit.c. I started this  
from the i82810 code, but I want it to be able to configure an  
assymetric dimm. If anyone has a few minutes can you check this out,  
and give me some feedback??

Thanks - Joe
-------------- next part --------------
/*
 * This file is part of the LinuxBIOS project.
 *
 * Originally written for the i82810 by:
 * Copyright (C) 2007 Uwe Hermann <uwe at hermann-uwe.de>
 * Copyright (C) 2007 Corey Osgood <corey at slightlyhackish.com>
 *
 * Modified for the i82830 by:
 * Copyright (C) 2007 Joseph Smith <joe at smittys.pointclark.net>
 *
 * 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
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

#include <spd.h>
#include <sdram_mode.h>
#include <delay.h>
#include "i82830.h"

/*-----------------------------------------------------------------------------
Macros and definitions.
-----------------------------------------------------------------------------*/

/* Uncomment this to enable debugging output. */
// #define DEBUG_RAM_SETUP 1

/* Debugging macros. */
#if defined(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)
#define PRINT_DEBUG_HEX32(x)	print_debug_hex32(x)
#define DUMPNORTH()		dump_pci_device(PCI_DEV(0, 0, 0))
#else
#define PRINT_DEBUG(x)
#define PRINT_DEBUG_HEX8(x)
#define PRINT_DEBUG_HEX16(x)
#define PRINT_DEBUG_HEX32(x)
#define DUMPNORTH()
#endif

/* DRC[10:8] - Refresh Mode Select (RMS).
 * 0x1 for Refresh interval 15.6 us for 133MHz
 * 0x2 for Refresh interval 7.8 us for 133MHz
 * 0x7 /* Refresh interval 128 Clocks. (Fast Refresh Mode)
 */
#define RAM_COMMAND_REFRESH    0x1

/* DRC[6:4] - SDRAM Mode Select (SMS). */
#define RAM_COMMAND_NOP		 0x1
#define RAM_COMMAND_PRECHARGE	 0x2
#define RAM_COMMAND_MRS		 0x3
#define RAM_COMMAND_CBR		 0x6
#define RAM_COMMAND_NORMAL	 0x7

/*-----------------------------------------------------------------------------
SDRAM configuration functions.
-----------------------------------------------------------------------------*/

/**
 * Send the specified RAM command to all DIMMs.
 *
 * @param Memory controller
 * @param TODO
 * @param TODO
 */
static void do_ram_command(const struct mem_controller *ctrl, uint32_t command, uint32_t addr_offset, uint32_t row_offset) {

	uint8_t reg;

	/* TODO: Support for multiple DIMMs. */

	/* Configure the RAM command. */
	reg = pci_read_config32(ctrl->d0, DRC);
	reg &= 0xdffff88f;		/* Clear bits 29, 10-8, 6-4. */
	reg |= command << 4;
      /* If RAM_COMMAND_NORMAL set the refresh mode and IC bit. */
	if (command == RAM_COMMAND_NORMAL) {
	reg |= ((RAM_COMMAND_REFRESH << 8) | (1 << 29));
      }
	pci_write_config32(ctrl->d0, DRC, reg);

	/* RAM_COMMAND_NORMAL affects only the memory controller and
	   doesn't need to be "sent" to the DIMMs. */
	/* if (command == RAM_COMMAND_NORMAL) return; */

	PRINT_DEBUG("    Sending RAM command 0x");
	PRINT_DEBUG_HEX32(reg);
	PRINT_DEBUG(" to 0x");
	PRINT_DEBUG_HEX32(0 + addr_offset);	// FIXME
	PRINT_DEBUG("\r\n");

	/* Read from (DIMM start address + addr_offset). */
	read32(0 + addr_offset);	//first offset is always 0
	read32(row_offset + addr_offset);
}

/*-----------------------------------------------------------------------------
DIMM-independant configuration functions.
-----------------------------------------------------------------------------*/

static unsigned int spd_detect_dimms(const struct mem_controller *ctrl)
{
	unsigned dimm_mask;
	int i;
	dimm_mask = 0;
	for(i = 0; i < DIMM_SOCKETS; i++) {
		int byte;
		unsigned device;
		device = ctrl->channel0[i];
		if (device) {
			byte = spd_read_byte(device, 2);  /* Type */
			if (byte == 4) {
				dimm_mask |= (1 << i);
			}
		}
		device = ctrl->channel1[i];
		if (device) {
			byte = spd_read_byte(device, 2);
			if (byte == 4) {
				dimm_mask |= (1 << (i + DIMM_SOCKETS));
			}
		}
	}
	return dimm_mask;
}

struct dimm_size {
	unsigned long side1;
	unsigned long side2;
};

static struct dimm_size spd_get_dimm_size(unsigned device)
{
	/* Calculate the log base 2 size of a DIMM in bits */
	struct dimm_size sz;
	int value, low;
	sz.side1 = 0;
	sz.side2 = 0;

	/* test for sdram */
	value = spd_read_byte(device, 2);      /* type */
      if (value < 0) goto hw_err;
	if (value != 4) {
		print_debug("SPD2 DIMM Is Not Compatable\r\n");
		goto val_err;
	}

	/* test for PC133 (i830 only supports PC133) */
	value = spd_read_byte(device, 9);      /* cycle time */
      if (value < 0) goto hw_err;
	if (value != 75) {
		print_debug("SPD9 DIMM Is Not PC133 Compatable\r\n");
		goto val_err;
	}
	value = 0;
	value = spd_read_byte(device, 10);     /* access time */
      if (value < 0) goto hw_err;
	if (value != 54) {
		print_debug("SPD10 DIMM Is Not PC133 Compatable\r\n");
		goto val_err;
	}

	/* Note it might be easier to use 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 assymetric dimm.
	 */
	value = spd_read_byte(device, 3);	/* rows */
	if (value < 0) goto hw_err;
	if ((value & 0xf) == 0) {
		print_debug("SPD3 Error With Rows\r\n");
		goto val_err;
	}
	sz.side1 += value & 0xf;

	value = spd_read_byte(device, 4);	/* columns */
	if (value < 0) goto hw_err;
	if ((value & 0xf) == 0) {
		print_debug("SPD4 Error With Columns\r\n");
		goto val_err;
	}
	sz.side1 += value & 0xf;

	value = spd_read_byte(device, 17);	/* banks */
	if (value < 0) goto hw_err;
	if ((value & 0xff) == 0) {
		print_debug("SPD17 Error With Banks\r\n");
		goto val_err;
	}
	sz.side1 += log2(value & 0xff);

	/* Get the module data width and convert it to a power of two */
	value = spd_read_byte(device, 7);	/* (high byte) */
	if (value < 0) goto hw_err;
	value &= 0xff;
	value <<= 8;
	
	low = spd_read_byte(device, 6);	/* (low byte) */
	if (low < 0) goto hw_err;
	value = value | (low & 0xff);
	if ((value != 72) && (value != 64)) {
		print_debug("SPD6 Error With Data Width\r\n");
		goto val_err;
	}
	sz.side1 += log2(value);

	/* side 2 */
	value = spd_read_byte(device, 5);	/* number of physical banks */
	if (value < 0) goto hw_err;
	value &= 7;
	if (value == 1) goto out;
	if (value != 2) {
		print_debug("SPD5 Error With Physical Banks\r\n");
		goto val_err;
	}

	/* Start with the symmetrical case */
	sz.side2 = sz.side1;

	value = spd_read_byte(device, 3);	/* rows */
	if (value < 0) goto hw_err;
	if ((value & 0xf0) == 0) goto out;	/* If symmetrical we are done */
	sz.side2 -= (value & 0x0f);		/* Subtract out rows on side 1 */
	sz.side2 += ((value >> 4) & 0x0f);	/* Add in rows on side 2 */

	value = spd_read_byte(device, 4);	/* columns */
	if (value < 0) goto hw_err;
	if ((value & 0xff) == 0) {
		print_debug("SPD4 Error With Side2 Rows\r\n");
		goto val_err;
	}
	sz.side2 -= (value & 0x0f);		/* Subtract out columns on side 1 */
	sz.side2 += ((value >> 4) & 0x0f);	/* Add in columsn on side 2 */
	goto out;

 val_err:
	die("Bad SPD value\r\n");

/* If an hw_error occurs report that I have no memory */
hw_err:
	sz.side1 = 0;
	sz.side2 = 0;
 out:
	return sz;

}

static long spd_set_ram_size(const struct mem_controller *ctrl, long dimm_mask)
{
	int i;
	int cum;
	
	for(i = cum = 0; i < DIMM_SOCKETS; i++) {
		struct dimm_size sz;
		if (dimm_mask & (1 << i)) {
			sz = spd_get_dimm_size(ctrl->channel0[i]);

			/* WISHLIST: would be nice to display it as decimal? */
			print_debug("DIMM is ");
			print_debug_hex8(sz.side1);
			print_debug(" On Side 1\r\n");
			print_debug("DIMM is ");
			print_debug_hex8(sz.side2);
			print_debug(" On Side 2\r\n");

			/* Set the row offset, in KBytes (should this be
			* Kbits?). Note that this offset is the start of the
			* next row.
			*/
			row_offset = ((sz.side1 + sz.side2) * 1024);

			if (sz.side1 < 29) {
				return -1; /* Report SPD error */
			}
			/* convert bits to multiples of 64MB */
			sz.side1 -= 29;
			cum += (1 << sz.side1);
			/* DRB = 0x60 */
			pci_write_config8(ctrl->d0, DRB + (i*2), cum);
			print_debug("DRB On Side 1 ");
			print_debug_hex8(cum);
			print_debug("\r\n");
			if( sz.side2 > 28) {
				sz.side2 -= 29;
				cum += (1 << sz.side2);
			}
			pci_write_config8(ctrl->d0, DRB+1 + (i*2), cum);
			print_debug("DRB On Side 2 ");
			print_debug_hex8(cum);
			print_debug("\r\n");
		}
		else {
			pci_write_config8(ctrl->d0, DRB + (i*2), cum);
			pci_write_config8(ctrl->d0, DRB+1 + (i*2), cum);
		}
	}
	return 0;
}

static void set_dram_timing(const struct mem_controller *ctrl) {

      /* Set the value for DRAM Timing Register */
      pci_write_config32(ctrl->d0, DRT, 0x00000010);
}

static void set_dram_buffer_strength(const struct mem_controller *ctrl) {

	/* 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
	 */
      /* Set the value for System Memory Buffer Strength Control Registers */
      pci_write_config32(ctrl->d0, BUFF_SC0, 0x0000491B);
      pci_write_config32(ctrl->d0, BUFF_SC1, 0x00009B49);
      pci_write_config32(ctrl->d0, BUFF_SC2, 0x0000FC9B);
      pci_write_config32(ctrl->d0, BUFF_SC3, 0x000014FC);
}

/*-----------------------------------------------------------------------------
Public interface.
-----------------------------------------------------------------------------*/

/**
 * TODO.
 *
 * @param Memory controller
 */
static void sdram_set_registers(const struct mem_controller *ctrl)
{

	print_debug("Setting Initial Registers....\r\n");

      /* Set the value for PCI Command Register */
      pci_write_config16(ctrl->d0, PCICMD, 0x0006);

      /* Set the value for PCI Status Register */
      pci_write_config16(ctrl->d0, PCISTS, 0x0010);

      /* Set the value for Register Range Base Address Register */
      pci_write_config32(ctrl->d0, RRBAR, 0x00000000);

      /* Set the value for GMCH Control Register #0 */
      pci_write_config16(ctrl->d0, GCC0, 0xA072);

      /* Set the value for GMCH Control Register #1 */
      pci_write_config16(ctrl->d0, GCC1, 0x0000);

      /* Set the value for Fixed DRAM Hole Control Register */
      pci_write_config8(ctrl->d0, 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(ctrl->d0, PAM0, 0x30);
      pci_write_config8(ctrl->d0, PAM1, 0x33);
      pci_write_config8(ctrl->d0, PAM2, 0x33);
      pci_write_config8(ctrl->d0, PAM3, 0x33);
      pci_write_config8(ctrl->d0, PAM4, 0x33);
      pci_write_config8(ctrl->d0, PAM5, 0x33);
      pci_write_config8(ctrl->d0, PAM6, 0x33);

      /* Set the value for DRAM Row Attribute Registers */
      pci_write_config8(ctrl->d0, DRA, 0xFF);
      pci_write_config8(ctrl->d0, DRA1, 0xF1);

      /* Set the value for DRAM Throttling Control Register */
      pci_write_config32(ctrl->d0, DTC, 0x00000000);

      /* Set the value for System Management RAM Control Register */
      pci_write_config8(ctrl->d0, SMRAM, 0x02);

      /* Set the value for Extended System Management RAM Control Register */
      pci_write_config8(ctrl->d0, ESMRAMC, 0x38);

      print_debug("Initial registers have been set.\r\n");
}

/**
 * TODO.
 *
 * @param Memory controller
 */
static void sdram_set_spd_registers(const struct mem_controller *ctrl) {

	/* spd_set_dram_size() moved into sdram_enable() to prevent having
	 * to pass a variable between here and there.
	 */
	set_dram_buffer_strength(ctrl);

	set_dram_timing(ctrl);
}

/**
 * Enable SDRAM.
 *
 * @param Number of controllers
 * @param Memory controller
 */
static void sdram_enable(int controllers, const struct mem_controller *ctrl) {

	int i;

	/* Todo: this will currently work with either one dual sided or two
	 * single sided DIMMs. Needs to work with 2 dual sided DIMMs in the
	 * long run.
	 */
	long mask;
	uint32_t row_offset;

	mask = spd_detect_dimms(ctrl);
	spd_set_dram_size(ctrl, mask);

	/* 1. Apply NOP. */
	PRINT_DEBUG("RAM Enable 1: Apply NOP\r\n");
	do_ram_command(ctrl, RAM_COMMAND_NOP, 0, row_offset);
	udelay(200);

	/* 2. Precharge all. Wait tRP. */
	PRINT_DEBUG("RAM Enable 2: Precharge all\r\n");
	do_ram_command(ctrl, RAM_COMMAND_PRECHARGE, 0, row_offset);
	udelay(1);

	/* 3. Perform 8 refresh cycles. Wait tRC each time. */
	PRINT_DEBUG("RAM Enable 3: CBR\r\n");
	do_ram_command(ctrl, RAM_COMMAND_CBR, 0, row_offset);
	for (i = 0; i < 8; i++) {
		read32(0);
		read32(row_offset);
		udelay(1);
	}

	/* 4. Mode register set. Wait two memory cycles. */
	PRINT_DEBUG("RAM Enable 4: Mode register set\r\n");
	do_ram_command(ctrl, RAM_COMMAND_MRS, 0x1d0, row_offset);
	udelay(2);

	/* 5. Normal operation (enables refresh) */
	PRINT_DEBUG("RAM Enable 5: Normal operation\r\n");
	do_ram_command(ctrl, RAM_COMMAND_NORMAL, 0, row_offset);
	udelay(1);

	PRINT_DEBUG("Northbridge following SDRAM init:\r\n");
	DUMPNORTH();
}


More information about the coreboot mailing list