[coreboot] [PATCH] v3: CN700 initram support

Carl-Daniel Hailfinger c-d.hailfinger.devel.2006 at gmx.net
Tue Oct 14 12:32:18 CEST 2008


On 14.10.2008 06:21, Corey Osgood wrote:
> See attached. Thanks Carl-Daniel for the explanation on floats in CAR, hope
> you don't mind that I've quoted you ;)
>   

Sure, no problem. I feel honored.

> Add ram init support for the Via CN700 to v3. Note that this isn't based on
> current v2 support, but rather an older version I was working on that used too
> many registers. It will be ported to v2 (eventually).
>
> Signed-off-by: Corey Osgood <corey.osgood at gmail.com>
>   

A few minor nitpicks, if you fix them, the patch is
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006 at gmx.net>

Great stuff!

My personal preference is to have a space between "if" and "(", but
that's a personal choice and not part of the to-be-fixed nitpicks. We do
have a mixture of styles in v3 for that.


> Index: Kconfig
> ===================================================================
> --- Kconfig	(revision 923)
> +++ Kconfig	(working copy)
> @@ -92,6 +92,8 @@
>  	boolean
>  config NORTHBRIDGE_INTEL_I440BXEMULATION
>  	boolean
> +config NORTHBRIDGE_VIA_CN700
> +	boolean
>  
>  # Southbridges:
>  config SOUTHBRIDGE_AMD_CS5536
> Index: northbridge/via/cn700/initram.c
> ===================================================================
> --- northbridge/via/cn700/initram.c	(revision 0)
> +++ northbridge/via/cn700/initram.c	(revision 0)
> @@ -0,0 +1,752 @@
> +/*
> + * This file is part of the coreboot project.
> + *
> + * Copyright (C) 2007-2008 Corey Osgood <corey.osgood at 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
> + * 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 <types.h>
> +#include <lib.h>
> +#include <console.h>
> +#include <device/pci.h>
> +#include <legacy.h>
> +#include <io.h>
> +#include <device/pci_ids.h>
> +#include <config.h>
> +#include <spd.h>
> +#include "cn700.h"
> +
> +/* NOTE: We can't use floats in CAR, even though gcc will allow it. "The 
> + * problem is that many x86 CPUs have cache bypasses for x87/MMX/SSE/whatever
> + * instructions and that would cause them not to be filled from CAR. At least 
> + * that's my understanding from reading a few too many of these databooks."
> + * -Carl-Daniel Hailfinger
> + */
> +
> +/* TODO: This only supports DDR2 memory, but CN700 also supports DDR */
> +
> +/* Is this correct? */
> +static void read32(u32 addr)
> +{
> +	u32 val;
> +	volatile unsigned long *x;
> +	x = (void *)addr;
> +	val = *x;
> +}
>   

Can't you use readl() instead?

> +
> +static void do_ram_command(struct board_info *dev, u8 command, u32 addr_offset)
> +{
> +	u8 reg8;
> +	int i, j;
> +
> +	reg8 = pci_conf1_read_config8(dev->d0f3, DRAM_MISC_CTL);
> +	reg8 &= 0xf8;		/* Clear bits 2-0. */
> +	reg8 |= command;
> +	pci_conf1_write_config8(dev->d0f3, DRAM_MISC_CTL, reg8);
> +
> +	printk(BIOS_SPEW, "   Sending RAM command ");
> +	switch(command) {
> +		case RAM_COMMAND_NORMAL: printk(BIOS_SPEW, "'Normal' to 0x%x",
> +					addr_offset);
>   

Missing line break after :

> +			break;
> +		case RAM_COMMAND_NOP: printk(BIOS_SPEW, "'No-op' to 0x%x",
> +					addr_offset);
>   

Same here.

> +			break;
> +		case RAM_COMMAND_PRECHARGE: printk(BIOS_SPEW, 
> +					"'Precharge' to 0x%x\n", addr_offset);
>   

Same here.

> +			break;
> +		case RAM_COMMAND_MRS: 
> +			printk(BIOS_SPEW, "'MRS' to 0x%x", addr_offset);
> +			if(addr_offset == 0x12000)
> +			{
> +				read32(0x800);
> +			} else {
> +				read32(0x121c20);
> +				read32(0x120020);
> +			}
> +			break;
>   

The logic above may be correct, but I have no way to figure it out.
Could you maybe use symbolic constants for 0x12000, 0x800, 0x121c20,
01120020?

> +		case RAM_COMMAND_CBR:
> +			for(j = 0; j < 8; j++) {
> +				read32((reg8 << 26));
> +				udelay(100);
> +			}
> +			printk(BIOS_SPEW, "'CBR' to 0x%x", addr_offset);
> +			break;
> +	};
> +
> +	/* NOTE: Dual-sided and multi-dimm ready */
> +	read32(0 + addr_offset);
> +	for(i = 0; i < (ARRAY_SIZE(dev->spd_channel0) * 2); i++) {
> +		reg8 = pci_conf1_read_config8(dev->d0f3, RANK0_START + i);
> +		if(reg8) {
> +			read32((reg8 << 26) + addr_offset);
> +			printk(BIOS_SPEW, ", 0x%x", (reg8 << 26) + addr_offset);
> +			if(command == RAM_COMMAND_MRS)
> +			{
> +				if(addr_offset == 0x12000)
> +				{
> +					read32((reg8 << 26) + 0x800);
> +				} else {
> +					read32((reg8 << 26) + 0x121c20);
> +					read32((reg8 << 26) + 0x120020);
>   

Same magic values as above.

> +				}
> +			} else if(command == RAM_COMMAND_CBR)
> +			{
> +				for(j = 0; j < 8; j++) {
> +					read32((reg8 << 26));
> +					udelay(100);
> +				}
> +			}
> +		}
> +	}
> +	printk(BIOS_SPEW, "\n");
> +}
> +
> +/**
> + * Configure the bus between the cpu and the northbridge. This might be able to 
> + * be moved to post-ram code in the future. For the most part, these registers
> + * should not be messed around with. These are too complex to explain short of
> + * copying the datasheets into the comments, but most of these values are from
> + * the BIOS Porting Guide, so they should work on any board. If they don't,
> + * try the values from your factory BIOS.
>   

Hm. Good leading explanation. If you could add a pointer to which data
sheet sections mainly have this info, it would be nice. Your choice.
The inline comments are really readable and I like them very much.

> + *
> + * TODO: Changing the DRAM frequency doesn't work (hard lockup)
> + *
> + * @param dev The northbridge's CPU Host Interface (D0F2)
> + */
> +void c7_cpu_setup(u32 dev)
> +{
> +	/* Host bus interface registers (D0F2 0x50-0x67) */
> +	/* Request phase control */
> +	pci_conf1_write_config8(dev, 0x50, 0x88);
> +	/* CPU Interface Control */
> +	pci_conf1_write_config8(dev, 0x51, 0x7a);
> +	pci_conf1_write_config8(dev, 0x52, 0x6f);
> +	/* Arbitration */
> +	pci_conf1_write_config8(dev, 0x53, 0x88);
> +	/* Miscellaneous Control */
> +	pci_conf1_write_config8(dev, 0x54, 0x1e);
> +	pci_conf1_write_config8(dev, 0x55, 0x16);
> +	/* Write Policy */
> +	pci_conf1_write_config8(dev, 0x56, 0x01);
> +	/* Miscellaneous Control */
> +	/* DRAM Operating Frequency (bits 7:5)
> +	 *      000 : 100MHz    001 : 133MHz
> +	 *      010 : 166MHz    011 : 200MHz
> +	 *      100 : 266MHz    101 : 333MHz
> +	 *      110/111 : Reserved
> +	 */
> +	/* CPU Miscellaneous Control */
> +	pci_conf1_write_config8(dev, 0x59, 0x44);
> +	/* Write Policy */
> +	pci_conf1_write_config8(dev, 0x5d, 0xb2);
> +	/* Bandwidth Timer */
> +	pci_conf1_write_config8(dev, 0x5e, 0x88);
> +	/* CPU Miscellaneous Control */
> +	pci_conf1_write_config8(dev, 0x5f, 0xc7);
> +
> +	/* Line DRDY# Timing Control */
> +	pci_conf1_write_config8(dev, 0x60, 0xff);
> +	pci_conf1_write_config8(dev, 0x61, 0xff);
> +	pci_conf1_write_config8(dev, 0x62, 0x0f);
> +	/* QW DRDY# Timing Control */
> +	pci_conf1_write_config8(dev, 0x63, 0xff);
> +	pci_conf1_write_config8(dev, 0x64, 0xff);
> +	pci_conf1_write_config8(dev, 0x65, 0x0f);
> +	/* Read Line Burst DRDY# Timing Control */
> +	pci_conf1_write_config8(dev, 0x66, 0xff);
> +	pci_conf1_write_config8(dev, 0x67, 0x30);
> +
> +	/* Host Bus I/O Circuit (see datasheet) */
> +	/* Host Address Pullup/down Driving */
> +	pci_conf1_write_config8(dev, 0x70, 0x11);
> +	pci_conf1_write_config8(dev, 0x71, 0x11);
> +	pci_conf1_write_config8(dev, 0x72, 0x11);
> +	pci_conf1_write_config8(dev, 0x73, 0x11);
> +	/* Miscellaneous Control */
> +	pci_conf1_write_config8(dev, 0x74, 0x35);
> +	/* AGTL+ I/O Circuit */
> +	pci_conf1_write_config8(dev, 0x75, 0x28);
> +	/* AGTL+ Compensation Status */
> +	pci_conf1_write_config8(dev, 0x76, 0x74);
> +	/* AGTL+ Auto Compensation Offest */
> +	pci_conf1_write_config8(dev, 0x77, 0x00);
> +	/* Host FSB CKG Control */
> +	pci_conf1_write_config8(dev, 0x78, 0x0a);
> +	/* Address/Address Clock Output Delay Control */
> +	pci_conf1_write_config8(dev, 0x79, 0xaa);
> +	/* Address Strobe Input Delay Control */
> +	pci_conf1_write_config8(dev, 0x7a, 0x24);
> +	/* Address CKG Rising/Falling Time Control */
> +	pci_conf1_write_config8(dev, 0x7b, 0xaa);
> +	/* Address CKG Clock Rising/Falling Time Control */
> +	pci_conf1_write_config8(dev, 0x7c, 0x00);
> +	/* Undefined (can't remember why I did this) */
> +	pci_conf1_write_config8(dev, 0x7d, 0x6d);
> +
> +	pci_conf1_write_config8(dev, 0x7e, 0x00);
> +	pci_conf1_write_config8(dev, 0x7f, 0x00);
> +	pci_conf1_write_config8(dev, 0x80, 0x1b);
> +	pci_conf1_write_config8(dev, 0x81, 0x0a);
> +	pci_conf1_write_config8(dev, 0x82, 0x0a);
> +	pci_conf1_write_config8(dev, 0x83, 0x0a);
> +}
> +
> +/**
> + * Set up various ram and other control registers statically. Some of these may 
> + * not be needed, other should be done with spd info, but that's a project for
> + * the future
> + */
> +void sdram_set_registers(struct board_info *dev)
> +{
> +	/* DQ/DQS Strength Control */
> +	pci_conf1_write_config8(dev->d0f3, 0xd0, 0x88);
> +	pci_conf1_write_config8(dev->d0f3, 0xd1, 0x8b);
> +	pci_conf1_write_config8(dev->d0f3, 0xd2, 0x89);
> +	/* SMM and APIC Decoding */
> +	//pci_conf1_write_config8(dev->d0f3, 0x86, 0x2d);
>   

Any reason why this is commented out? I'd like to avoid committing dead
code without explanations. If you don't know the reason and just copied
that part of the code, please point (in the comments) to the original
file with that problem.

> +	pci_conf1_write_config8(dev->d0f3, 0x86, (1 << 4));
> +	pci_conf1_write_config8(dev->d0f7, 0xe6, (1 << 4));
> +	
> +	/* Driving selection */
> +	/* DQ / DQS ODT Driving and Range Select */
> +	pci_conf1_write_config8(dev->d0f3, 0xd5, 0x8a);
> +	/* Memory Pads Driving and Range Select */
> +	pci_conf1_write_config8(dev->d0f3, 0xd6, 0xaa);
> +	/* DRAM Driving – Group DQS */
> +	pci_conf1_write_config8(dev->d0f3, 0xe0, 0xee);
> +	/* DRAM Driving – Group DQ (DQ, MPD, DQM) */
> +	pci_conf1_write_config8(dev->d0f3, 0xe2, 0xac);//ba
> +	/* DRAM Driving – Group CS */
> +	pci_conf1_write_config8(dev->d0f3, 0xe4, 0x66);
> +	/* DRAM Driving – Group MA */
> +	pci_conf1_write_config8(dev->d0f3, 0xe8, 0x86);
> +	/* DRAM Driving – Group MCLK */
> +	pci_conf1_write_config8(dev->d0f3, 0xe6, 0xaa);
> +
> +	/* ODT (some are set with driving select above) */
> +	/* Memory Pad ODT Pullup / Pulldown Control */
> +	pci_conf1_write_config8(dev->d0f3, 0xd4, 0x0a);
> +	/* Memory Ranks ODT Lookup Table */
> +	pci_conf1_write_config8(dev->d0f3, 0xd8, 0x00);//was 1
> +	/* Compensation Control */
> +	pci_conf1_write_config8(dev->d0f3, 0xd3, 0x89);//enable auto compensation
> +	
> +	/* MCLKO Phase Control */
> +	pci_conf1_write_config8(dev->d0f3, 0x91, 0x02);
> +	/* CS/CKE Clock Phase Control */
> +	pci_conf1_write_config8(dev->d0f3, 0x92, 0x06);
> +	/* SCMD/MA Clock Phase Control */
> +	pci_conf1_write_config8(dev->d0f3, 0x93, 0x07);
> +	/* Channel A DQS Input Capture Range Control */
> +	pci_conf1_write_config8(dev->d0f3, 0x78, 0x83);
> +	/* DQS Input Capture Range Control */
> +	/* Set in accordance with the BIOS update note */
> +	pci_conf1_write_config8(dev->d0f3, 0x7a, 0x00);
> +	/* DQS Input Delay Offset Control */
> +	pci_conf1_write_config8(dev->d0f3, 0x7c, 0x00);
> +	/* SDRAM ODT Control */
> +	pci_conf1_write_config8(dev->d0f3, 0xda, 0x80);
> +	/* DQ/DQS CKG Output Delay Control - I */
> +	pci_conf1_write_config8(dev->d0f3, 0xdc, 0xff);
> +	/* DQ/DQS CKG Output Delay Control - II */
> +	pci_conf1_write_config8(dev->d0f3, 0xdd, 0xff);
> +	/* DQS / DQ CKG Duty Cycle Control */
> +	pci_conf1_write_config8(dev->d0f3, 0xec, 0x88);
> +	/* MCLK Output Duty Control */
> +	pci_conf1_write_config8(dev->d0f3, 0xee, 0x00);
> +	pci_conf1_write_config8(dev->d0f3, 0xed, 0x10);
> +	/* DQS CKG Input Delay Control */
> +	pci_conf1_write_config8(dev->d0f3, 0xef, 0x10);
> +
> +	pci_conf1_write_config8(dev->d0f3, 0x77, 0x9d);
> +	pci_conf1_write_config8(dev->d0f3, 0x79, 0x83);
> +	pci_conf1_write_config16(dev->d0f3, 0x88, 0x0020);
> +	
> +	//pci_conf1_write_config8(dev->d0f4, 0xa7, 0x80);
>   

Same here.

> +
> +	/* VLink Control */
> +	pci_conf1_write_config8(dev->d0f7, 0xb0, 0x05);
> +	pci_conf1_write_config8(dev->d0f7, 0xb1, 0x01);
> +	
> +	/* Memory base */
> +	pci_conf1_write_config16(dev->d1f0, 0x20, 0xfb00);
> +	/* Memory limit */
> +	pci_conf1_write_config16(dev->d1f0, 0x22, 0xfcf0);
> +	/* Prefetch memory base */
> +	pci_conf1_write_config16(dev->d1f0, 0x24, 0xf400);
> +	/* Prefetch memory limit */
> +	pci_conf1_write_config16(dev->d1f0, 0x26, 0xf7f0);
> +	/* PCI to PCI bridge control */
> +	pci_conf1_write_config16(dev->d1f0, 0x3e, 0x0008);
> +	
> +	/* CPU to PCI flow control 1 */
> +	pci_conf1_write_config8(dev->d1f0, 0x40, 0x83);
> +	pci_conf1_write_config8(dev->d1f0, 0x41, 0xc3);//clear reset error, set to 43
> +	pci_conf1_write_config8(dev->d1f0, 0x42, 0xe2);
> +	pci_conf1_write_config8(dev->d1f0, 0x43, 0x44);
> +	pci_conf1_write_config8(dev->d1f0, 0x44, 0x34);
> +	pci_conf1_write_config8(dev->d1f0, 0x45, 0x72);
> +
> +	/* Disable cross bank/multi page mode */
> +	pci_conf1_write_config8(dev->d0f3, DDR_PAGE_CTL, 0x80);
> +	pci_conf1_write_config8(dev->d0f3, DRAM_REFRESH_COUNTER, 0x00);
> +
> +	/* Set WR=5 and RFC */
> +	//pci_conf1_write_config8(dev->d0f3, 0x61, 0x94);//c7
>   

And here.

> +	/* Set CAS=5 */
> +	//pci_conf1_write_config8(dev->d0f3, 0x62, 0x7a);//af
> +	//pci_conf1_write_config8(dev->d0f3, 0x63, 0x00);//ca
> +	//pci_conf1_write_config8(dev->d0f3, 0x64, 0x88);
>   

And here.

> +	/* Set to DDR2 sdram, BL=8 (0xc8, 0xc0 for bl=4) */
> +	pci_conf1_write_config8(dev->d0f3, 0x6c, 0xc8);//c8
> +	/* Allow manual dll reset */
> +	pci_conf1_write_config8(dev->d0f3, 0x6b, 0x10);
> +	
> +	pci_conf1_write_config8(dev->d0f3, 0x6e, 0x89);
> +	pci_conf1_write_config8(dev->d0f3, 0x67, 0x50);
> +	pci_conf1_write_config8(dev->d0f3, 0x65, 0xd9);
> +	
> +	/* Zero the MA map field */
> +	pci_conf1_write_config16(dev->d0f3, 0x50, 0x0);
> +
> +	/* BA0-2 Selection. Don't mess with */
> +	pci_conf1_write_config8(dev->d0f3, 0x52, 0x33);
> +	pci_conf1_write_config8(dev->d0f3, 0x53, 0x3f);
> +	
> +	/* Disable bank interleaving. */
> +	pci_conf1_write_config32(dev->d0f3, 0x58, 0x00000000);
> +	pci_conf1_write_config8(dev->d0f3, 0x88, 0x08);
> +
> +	/* Some DQS control stuffs */
> +	pci_conf1_write_config8(dev->d0f3, 0x74, 0x04);
> +	pci_conf1_write_config8(dev->d0f3, 0x75, 0x04);
> +	pci_conf1_write_config8(dev->d0f3, 0x76, 0x00);
> +	
> +	/* Thanks to Urbez Santana Roma for this */
> +	pci_conf1_write_config8(dev->d1f0, 0x19, 0x1);
> +	pci_conf1_write_config8(dev->d1f0, 0x1a, 0x1);
> +}
> +
> +
> +
>   

Two empty lines.

> +/**
> + * Check the detected timing against timings supported by the CN700, and
> + * adjust as necessary.
> + *
> + * @param val The value to be checked.
> + * @param min Minimum value supported by the CN700.
> + * @param max Maximum value supported by the CN700.
> + * @return ret The checked and/or modified value.
> + */
> +static u16 check_timing(u16 val, u16 min, u16 max)
> +{
> +	int ret;
> +	if(val < min)
> +		ret = min;
> +	else if(val > max)
> +		ret = max;
> +	else
> +		ret = val;
> +	return ret;
> +}
> +
> +/**
> + * Set the DRAM size registers
> + *
> + * @param dev: struct containing spd addresses and pci device locations
> + * @param i: Which DRAM Rank to be sizing
> + */ 
> +static int do_ram_size(struct board_info *dev, int i)
> +{
> +	u8 spd_data, j, reg8 = 0;
> +
> +	/* DRAM Bank Size */
> +	spd_data = spd_read_byte(dev->spd_channel0[i],
> +					SPD_DENSITY_OF_EACH_ROW_ON_MODULE);
> +
> +	/* The nibbles of the bytes are flipped between 
> +	 * SPD data and what Via requires */
> +	if(!spd_data || spd_data == 0xff) {
> +		printk(BIOS_DEBUG, "No memory in slot %d\n", i);
> +		return 0;
> +	}
> +	else if(spd_data >= 0x10)
> +		spd_data = spd_data >> 4;
> +	else
> +		spd_data = spd_data << 4;
>   

Hm. Are you trying to just flip the nibbles or are you selectively
picking one nibble? For the former, try this:
spd_data = (spd_data >> 4) | (spd_data << 4) & 0xff;

> +
> +	printk(BIOS_DEBUG, "Found %dMB module in slot %d\n", (int)(spd_data << 6), i);
> +
> +	/* Set up the DRAM registers */
> +	if(i)
> +	{
> +		printk(BIOS_DEBUG, "Searching for the top of the previous module\n");
> +		/* 0x40-0x47 are top addresses, 0x49-0x4f are base addressses */
> +		for(j = 1; !reg8 && j < (i * 2); j++) {
> +			reg8 = pci_conf1_read_config8(dev->d0f3, 0x49 + (i * 2) - j);
> +			printk(BIOS_DEBUG, ".");
> +		}
> +		printk(BIOS_DEBUG, "\nDone, previous module size is %dMB\n", (int)reg8);
> +	}
> +
> +	pci_conf1_write_config8(dev->d0f3, RANK0_START + (i * 2), reg8);
> +	pci_conf1_write_config8(dev->d0f3, RANK0_END + (i * 2), spd_data + reg8);
> +
> +	/* Set TOUD. It's easier to keep bumping 
> +	 * this up then to figure it out later */
> +	printk(BIOS_DEBUG, "Setting Top of Usable DRAM to %dMB\n", 
> +				(int)((spd_data + reg8) << 6));
> +
> +	pci_conf1_write_config8(dev->d0f3, 0x85, (spd_data + reg8) << 2);
> +	pci_conf1_write_config8(dev->d0f7, 0xe5, (spd_data + reg8) << 2);
> +
> +	/* Just a mockup, needs a detection routine */
>   

Please add a TODO/FIXME comment or even a #warning.

> +	/* if(dual sided) {
> +		pci_conf1_write_config8(dev->d0f3, 0x49 + (i * 2) + 1,
> +					spd_data + reg8);
> +		pci_conf1_write_config8(dev->d0f3, 0x40 + (i * 2) + 1,
> +					(spd_data * 2) + reg8);
> +	} */
> +
> +	/* Enable the rank, and map it to the virtual rank of the same number. */
> +	reg8 = 0x80;
> +	reg8 |= ((i * 2) << 4);
> +	/* if(dual sided) reg8 |= (1 << 4) | ((i * 2) + 1); */
>   

Same here.

> +	pci_conf1_write_config8(dev->d0f3, 0x54 + i, reg8);
> +
> +	return 1;
> +}
> +
> +static void do_twr_trfc(struct board_info *dev, int i, int ram_cycle)
> +{
> +	/* Transform spd byte 40's tRFC value into a decimal */
> +	static const int byte_40_tRFC[6] = {0, 25, 33, 50, 66, 75 };
>   

You could define this outside the function and save stack space.

> +
> +	u8 reg8, j;
> +	u16 spd_data;
> +
> +	printk(BIOS_SPEW, "Calculating tWR and tRFC\n");
> +	/* tWR (bits 7:6) */
> +	/* JEDEC spec allows for decimal values, but coreboot doesn't.
> +	 * Convert the decimal value to an int (done more below). */
> +	reg8 = spd_read_byte(dev->spd_channel0[i], SPD_tWR);
> +	spd_data = (((reg8 & ~0x3) >> 2) * 100);
> +	spd_data |= ((reg8 & 0x3) * 25);
> +	spd_data = spd_data / (ram_cycle * 100);
> +	spd_data = check_timing(spd_data, 2, 5);
> +	reg8 = pci_conf1_read_config8(dev->d0f3, 0x61);
> +	if((spd_data - 2) > (reg8 >> 6)) {
> +		reg8 &= ~(0x3 << 6);
> +		reg8 |= ((spd_data - 2) << 6);
> +	}
> +
> +	/* tRFC */
> +	spd_data = (spd_read_byte(dev->spd_channel0[i], SPD_tRFC) * 100);
> +	j = spd_read_byte(dev->spd_channel0[i], 40);
> +	if(j & 1)
> +		spd_data += (256 * 100);
> +	j = (j >> 1) & 0x7;
> +	spd_data |= byte_40_tRFC[j];
> +	spd_data = spd_data / (ram_cycle * 100);
> +	spd_data = check_timing(spd_data, 8, 71);
> +	if((spd_data - 8) > (reg8 & 0x3f)) {
> +		reg8 &= ~0x3f;
> +		reg8 |= (spd_data - 8);
> +	}
> +	pci_conf1_write_config8(dev->d0f3, 0x61, reg8);
> +}
> +
> +static void do_tras_cas(struct board_info *dev, int i, int ram_cycle)
> +{
> +	u8 reg8;
> +	int spd_data, j;
> +	
> +	printk(BIOS_SPEW, "Calculating tRAS and CAS\n");
> +
> +	/* tRAS */
> +	spd_data = spd_read_byte(dev->spd_channel0[i], 30);
> +	spd_data = (spd_data / ram_cycle);
> +	spd_data = check_timing(spd_data, 5, 20);
> +
> +	reg8 = pci_conf1_read_config8(dev->d0f3, 0x62);
> +	if((spd_data - 10) > (reg8 >> 4)) {
> +		reg8 &= 0x0f;
> +		reg8 |= ((spd_data -10) << 4);
> +	}
> +
> +	/* CAS Latency */
> +	spd_data = spd_read_byte(dev->spd_channel0[i],
> +			SPD_ACCEPTABLE_CAS_LATENCIES);
> +
> +	j = 2;
> +	while(!((spd_data >> j) & 1)) {
> +		j++;
> +	}
> +	/* j should now be the CAS latency, 
> +	 * in T for the module's rated speed */
> +	j = check_timing(j, 2, 5);
> +	if((j - 2) > (reg8 & 0x7)) {
> +		reg8 &= ~0x7;
> +		reg8 |= j;
> +	}
> +	pci_conf1_write_config8(dev->d0f3, 0x62, reg8);
> +}
> +
> +static void do_trrd_trtp_twtr(struct board_info *dev, int i, int ram_cycle)
> +{
> +	u8 reg8, j;
> +	u16 spd_data;
> +
> +	printk(BIOS_SPEW, "Calculating tRRD, tRTP, and tWTR\n");
> +	/* tRRD */
> +	reg8 = pci_conf1_read_config8(dev->d0f3, 0x63);
> +	j = spd_read_byte(dev->spd_channel0[i], SPD_tRRD);
> +	spd_data = ((j >> 2) * 100);
> +	spd_data |= ((j & 0x3) * 25);
> +	spd_data = spd_data / (ram_cycle * 100);
> +	spd_data = check_timing(spd_data, 2, 5);
> +	if((spd_data - 2) > (reg8 >> 6)) {
> +		reg8 &= 0x3f;
> +		reg8 |= (spd_data - 2) << 6;
> +	}
> +
> +	/* tRTP */
> +	j = spd_read_byte(dev->spd_channel0[i], SPD_tRTP);
> +	spd_data = ((j >> 2) * 100);
> +	spd_data |= ((j & 0x3) * 25);
> +	spd_data = spd_data / (ram_cycle * 100);
> +	spd_data = check_timing(spd_data, 2, 3);
> +	if(spd_data - 2) {
> +		reg8 |= 0x8;
> +	}
> +
> +	/* tWTR */
> +	j = spd_read_byte(dev->spd_channel0[i], SPD_tWTR);
> +	spd_data = ((j >> 2) * 100);
> +	spd_data |= ((j & 0x3) * 25);
> +	spd_data = spd_data / (ram_cycle * 100);
> +	spd_data = check_timing(spd_data, 2, 3);
> +	if(spd_data - 2) {
> +		reg8 |= 0x2;
> +	}
> +	pci_conf1_write_config8(dev->d0f3, 0x63, reg8);
> +}
> +
> +
> +static void do_trcd_trp(struct board_info *dev, int i, int ram_cycle)
> +{
> +	u8 reg8, j;
> +	u16 spd_data;
> +
> +	printk(BIOS_SPEW, "Calculating tRCD and tRP\n");
> +
> +	/* tRCD */
> +	reg8 = pci_conf1_read_config8(dev->d0f3, 0x64);
> +	j = spd_read_byte(dev->spd_channel0[i], SPD_tRCD);
> +	spd_data = ((j >> 2) * 100);
> +	spd_data |= ((j & 0x3) * 25);
> +	spd_data = spd_data / (ram_cycle * 100);
> +	spd_data = check_timing(spd_data, 2, 5);
> +	if((spd_data - 2) > (reg8 >> 6)) {
> +		reg8 &= 0x3f;
> +		reg8 |= (spd_data - 2) << 6;
> +	}
> +
> +	/* CKE Minimum Pulse */
> +	reg8 |= 1 << 4; /* 0 = 2T, 1 = 3T */
> +
> +	/* tRP (datasheet calls this tPR) */
> +	j = spd_read_byte(dev->spd_channel0[i], SPD_tRP);
> +	spd_data = ((j >> 2) * 100);
> +	spd_data |= ((j & 0x3) * 25);
> +	spd_data = spd_data / (ram_cycle * 100);
> +	spd_data = check_timing(spd_data, 2, 5);
> +	if((spd_data - 2) > ((reg8 >> 2) & 0x3)) {
> +		reg8 &= ~(0x3 << 2);
> +		reg8 |= (spd_data - 2) << 2;
> +	}
> +
> +	/* Exit powerdown/active powerdown to any command delay */
> +	reg8 |= 0x1; /* 0 = 2T, 1 = 3T */
> +	pci_conf1_write_config8(dev->d0f3, 0x64, reg8);
> +}
> +
> +static void do_ma_map(struct board_info *dev, int i)
> +{
> +	/* The MA Map bits need to be shifted all around */
> +	static const int ma_map_shift[4] = {4, 0, 12, 8};
> +	u8 reg8, spd_data;
> +
> +	printk(BIOS_SPEW, "calculating MA map\n");
> +	/* Last but not least, MA Map Type */
> +	spd_data = spd_read_byte(dev->spd_channel0[i], SPD_NUM_DIMM_BANKS);
> +	spd_data &= 0x7;
> +	spd_data = check_timing(spd_data, 2, 3);
> +	if(spd_data - 2)
> +		reg8 = 4;
> +	else
> +		reg8 = 0;
> +	spd_data = spd_read_byte(dev->spd_channel0[i], SPD_NUM_COLUMNS);
> +	reg8 += (spd_data - 9);
> +	
> +	spd_data = pci_conf1_read_config16(dev->d0f3, 0x50);
> +	spd_data &= ~(0xf << ma_map_shift[i]);
> +	spd_data |= (reg8 << (ma_map_shift[i] + 1));
> +
> +	pci_conf1_write_config16(dev->d0f3, 0x50, spd_data);
> +	printk(BIOS_SPEW, "Setting Memory Address Map type to 0x%x\n", reg8);
> +}
> +
> +/**
> + * Set up dram size according to spd data. Eventually, DRAM timings should be 
> + * done in a similar manner.
> + *
> + * NOTE: Via datasheets contradict themselves. Some places specify 8 ranks (4 banks),
> + * other places (on the same page) say 4 ranks. This code should be able to handle
> + * either situation.
> + *
> + * TODO: Double sided dimm support, the old code was broken. This is why odd-numbered
> + * ranks are ignored.
> + *
> + * @param dev The northbridge devices and spd addresses.
> + */
> +void sdram_set_spd_registers(struct board_info *dev)
> +{
> +	int i, ram_cycle;
> +	//int spd_data;
>   

Unused variable. If you don't plan to do anything with it, please kill it.

> +	u8 reg8;
> +
> +	/* Table to transform the ram speed register value into a MHz value */ 
> +	/* TODO: multiply table by 10 to get 7.5ns timing accurately
> +	 * without using floating points */
> +	static const int ram_speeds[6] = {10, 8 /*7.5*/, 6, 5, 3};
> +	
> +	/* f(MHz) = 1 / (T(ns) * 1000) */
> +
> +	/* ram_cycle is the cycle time of the ram, in ns */
> +	reg8 = (pci_conf1_read_config8(dev->d0f2, 0x57) >> 5);
> +	ram_cycle = ram_speeds[reg8];
> +
> +	for(i = 0; i < ARRAY_SIZE(dev->spd_channel0); i++)
> +	{
> +		printk(BIOS_SPEW, "Configuring DRAM Bank %d\n", i);
> +		
> +		/* If there's no ram, just continue to the next bank */
> +		if(do_ram_size(dev, i))
> +		{
> +			do_twr_trfc(dev, i, ram_cycle);
> +			do_tras_cas(dev, i, ram_cycle);
> +			do_trrd_trtp_twtr(dev, i, ram_cycle);
> +			do_trcd_trp(dev, i, ram_cycle);
> +			do_ma_map(dev, i);
> +		} /* if() */
> +	} /* for() loop */
>   

The two end-of-control-structure comments above are superfluous.

> +}
> +
> +/**
> + * Use the DRAM timing values previously plugged into the CN700 registers to
> + * calculate an MRS value that matches Via's values, defined in the CN700 Bios
> + * Porting Guide. Somehow these conform to JEDEC spec, but I'm not sure how.
> + *
> + * CAS	BL	WR=2	WR=3	WR=4	WR=5
> + *  2	4	001150	100150	101150	002150
> + *  2	8	001158	100158	101158	002158
> + *  3	4	0011d0	1001d0	1011d0	0021d0
> + *  3	8	0011d8	1001d8	1011d8	0021d8
> + *  4	4	001250	100250	101250	002250
> + *  4	8	001258	100258	101258	002258
> + *  5	4	0012d0	1002d0	1012d0	0022d0
> + *  5	8	0012d8	1002d8	1012d8	0022d8
> +**/
> +static u32 find_ddr2_mrs_value(struct board_info *dev)
> +{
> +	static const int CASLength[4] = {0x150, 0x1d0, 0x250, 0x2d0};
> +	static const int WR[4] = {0x1000, 0x100000, 0x101000, 0x2000};
>   

Define outside the function?

> +
> +	u8 reg8;
> +	u32 mrs = 0x0;
> +
> +	reg8 = pci_conf1_read_config8(dev->d0f3, 0x6c);
> +	reg8 &= 8;
> +	mrs |= reg8;
> +
> +	reg8 = pci_conf1_read_config8(dev->d0f3, 0x62) & 0x3;
> +	mrs |= CASLength[reg8];
> +
> +	reg8 = pci_conf1_read_config8(dev->d0f3, 0x61) >> 6;
> +	mrs |= WR[reg8];
> +
> +	printk(BIOS_SPEW, "Mode Register Set (MRS) value is 0x%x\n", mrs);
> +
> +	return mrs;
> +}
> +
> +void ddr2_sdram_enable(struct board_info *dev)
> +{
> +	u32 reg32;
> +
> +	/* 1. Apply NOP. */
> +	printk(BIOS_SPEW, "RAM Enable 1: Apply NOP\n");
> +	do_ram_command(dev, RAM_COMMAND_NOP, 0);
> +	udelay(200);
> +
> +	/* 2. Precharge all. */
> +	printk(BIOS_SPEW, "RAM Enable 2: Precharge all\n");
> +	do_ram_command(dev, RAM_COMMAND_PRECHARGE, 0);
> +
> +	/* 3. Mode register set. */
> +	printk(BIOS_SPEW, "RAM Enable 3: Mode register set\n");
> +	/* Note: 1 in bit 20 of all MRS commands is thanks to Urbez Santana Roma
> +	 * and is necessary for his Kingston ram */
> +	do_ram_command(dev, RAM_COMMAND_MRS, 0x12000);//enable dll
> +	do_ram_command(dev, RAM_COMMAND_MRS, 0x800);//reset dll
> +	
> +	/* 4. Precharge all again. */
> +	printk(BIOS_SPEW, "RAM Enable 4: Precharge all\n");
> +	do_ram_command(dev, RAM_COMMAND_PRECHARGE, 0);
> +	
> +	/* 5. Perform 8 refresh cycles. Wait tRC each time. */
> +	printk(BIOS_SPEW, "RAM Enable 5: CBR\n");
> +	do_ram_command(dev, RAM_COMMAND_CBR, 0);
> +
> +	/* 6. Mode register set. */
> +	printk(BIOS_SPEW, "RAM Enable 6: Mode register set\n");
> +	reg32 = find_ddr2_mrs_value(dev);
> +	do_ram_command(dev, RAM_COMMAND_MRS, reg32);
> +	
> +	/* 8. Normal operation */
> +	printk(BIOS_SPEW, "RAM Enable 7: Normal operation\n");
> +	do_ram_command(dev, RAM_COMMAND_NORMAL, 0);
> +	do_ram_command(dev, RAM_COMMAND_NORMAL, 0x30);
> +	
> +	/* Enable refresh. 
> +	 * TODO: Needs to be dynamic, 
> +	 * doesn't matter until ram speed change works */
> +	pci_conf1_write_config8(dev->d0f3, DRAM_REFRESH_COUNTER, 0x32);
> +	
> +	/* DQS Tuning: testing on a couple different boards has shown this is
> +	 * static, or close enough that it can be. Which is good, because the
> +	 * tuning function used too many registers. */
> +	pci_conf1_write_config8(dev->d0f3, CH_A_DQS_OUTPUT_DELAY, 0x00);
> +	pci_conf1_write_config8(dev->d0f3, CH_A_MD_OUTPUT_DELAY, 0x03);
> +
> +	/* Enable VGA device with no memory, add memory later. We need this
> +	 * here to enable the actual device, otherwise it won't show up until
> +	 * later and CB will have a fit. */
> +	pci_conf1_write_config16(dev->d0f3, 0xa0, (1 << 15));
> +	pci_conf1_write_config16(dev->d0f3, 0xa4, 0x0010);
> +}
> Index: northbridge/via/cn700/Makefile
> ===================================================================
> --- northbridge/via/cn700/Makefile	(revision 0)
> +++ northbridge/via/cn700/Makefile	(revision 0)
> @@ -0,0 +1,25 @@
> +##
> +## This file is part of the coreboot project.
> +##
> +## Copyright (C) 2008 Corey Osgood <corey.osgood at 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
> +## 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
> +##
> +
> +ifeq ($(CONFIG_NORTHBRIDGE_VIA_CN700),y)
> +
> +STAGE2_CHIPSET_SRC += 	
> +
> +endif
> Index: northbridge/via/cn700/cn700.h
> ===================================================================
> --- northbridge/via/cn700/cn700.h	(revision 0)
> +++ northbridge/via/cn700/cn700.h	(revision 0)
> @@ -0,0 +1,61 @@
> +/*
> + * This file is part of the coreboot project.
> + *
> + * Copyright (C) 2008 Corey Osgood <corey.osgood at 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
> + * 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
> + */
> +
>   

Needs multiple inclusion guard.
#ifndef CN700_H
#define CN700_H
code....
#endif

> +#include <types.h>
> + 
> +struct board_info {
> +	u32 d0f2, d0f3, d0f4, d0f7, d1f0;
> +	u16 spd_channel0[2];
> +};
> +
> +void c7_cpu_setup(u32);
> +void sdram_set_registers(struct board_info *);
> +void sdram_set_spd_registers(struct board_info *);
> +void ddr2_sdram_enable(struct board_info *);
> +
> +/* VGA stuff */
> +#define SR_INDEX	0x3c4
> +#define SR_DATA		0x3c5
> +#define CRTM_INDEX	0x3b4
> +#define CRTM_DATA	0x3b5
> +#define CRTC_INDEX	0x3d4
> +#define CRTC_DATA	0x3d5
> +
> +/* Memory Controller Registers */
> +#define RANK0_END		0x40
> +#define RANK1_END		0x41
> +#define RANK2_END		0x42
> +#define RANK3_END		0x43
> +#define RANK0_START		0x48
> +#define RANK1_START		0x49
> +#define RANK2_START		0x4a
> +#define RANK3_START		0x4b
> +#define DDR_PAGE_CTL		0x69
> +#define DRAM_REFRESH_COUNTER	0x6a
> +#define DRAM_MISC_CTL		0x6b
> +#define CH_A_DQS_OUTPUT_DELAY	0x70
> +#define CH_A_MD_OUTPUT_DELAY	0x71
> +
> +/* RAM Init Commands */
> +#define RAM_COMMAND_NORMAL	0x0
> +#define RAM_COMMAND_NOP		0x1
> +#define RAM_COMMAND_PRECHARGE	0x2
> +#define RAM_COMMAND_MRS		0x3
> +#define RAM_COMMAND_CBR		0x4
>
>
>   

Regards,
Carl-Daniel

-- 
http://www.hailfinger.org/





More information about the coreboot mailing list