/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2007-2009 coresystems GmbH
 *
 * 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; version 2 of the License.
 *
 * 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 <spd.h>
#include <spd_ddr2.h>
#include <sdram_mode.h>
#include <delay.h>
#include "cx700_registers.h"

// #define DEBUG_RAM_SETUP 1

/* Debugging macros. */
#if defined(DEBUG_RAM_SETUP)
#define PRINTK_DEBUG(x...)      printk_debug(x)
#else
#define PRINTK_DEBUG(x...)
#endif

#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

#define HOSTCTRL		PCI_DEV(0, 0, 2)
#define MEMCTRL			PCI_DEV(0, 0, 3)

#define DDRII_666	0x5
#define DDRII_533	0x4
#define DDRII_400	0x3
#define DDRII_333	0x2
#define DDRII_266	0x1
#define DDRII_200	0x0

#define OHM_150 1

#ifdef	MEM_WIDTH_32BIT_MODE
#define SDRAM1X_RA_14		30
#define SDRAM1X_RA_13		29
#define SDRAM1X_RA_12		28
#define SDRAM1X_RA_12_8bk	26
#define	SDRAM1X_CA_12		15
#define	SDRAM1X_CA_11		14
#define	SDRAM1X_CA_09		11
#define	SDRAM1X_CA_09_8bk	11
#define	SDRAM1X_BA1		13
#define	SDRAM1X_BA2_8bk		14
#define	SDRAM1X_BA1_8bk		13
#else
#define	SDRAM1X_RA_14		31
#define	SDRAM1X_RA_13		30
#define	SDRAM1X_RA_12		29
#define	SDRAM1X_RA_12_8bk	27
#define SDRAM1X_CA_12		16
#define SDRAM1X_CA_11		15
#define SDRAM1X_CA_09		12
#define SDRAM1X_CA_09_8bk	12
#define SDRAM1X_BA1		14
#define SDRAM1X_BA2_8bk		15
#define SDRAM1X_BA1_8bk		14
#endif

#define	MA_Column	0x06
#define	MA_Bank		0x08
#define	MA_Row		0x30
#define	MA_4_Bank	0x00
#define	MA_8_Bank	0x08
#define	MA_12_Row	0x00
#define	MA_13_Row	0x10
#define	MA_14_Row	0x20
#define	MA_15_Row	0x30
#define	MA_9_Column	0x00
#define	MA_10_Column	0x02
#define	MA_11_Column	0x04
#define	MA_12_Column	0x06

#define	GET_SPD(i, val, tmp, reg)								\
	do{											\
		val = 0;									\
		tmp = 0;									\
		for(i = 0; i < 2; i++)	{							\
			if(pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (i << 1)))) {	\
				tmp = get_spd_data(ctrl, i, reg);				\
				if(tmp > val)							\
					val = tmp;						\
			}									\
		}										\
	} while ( 0 )

#define REGISTERPRESET(bus,dev,fun,bdfspec) \
	{ u8 i, reg; \
		for (i=0; i<(sizeof((bdfspec))/sizeof(struct regmask)); i++) { \
			printk_debug("Writing bus " #bus " dev " #dev " fun " #fun " register "); \
			printk_debug("%02x", (bdfspec)[i].reg); \
			printk_debug("\n"); \
			reg = pci_read_config8(PCI_DEV((bus), (dev), (fun)), (bdfspec)[i].reg); \
			reg &= (bdfspec)[i].mask; \
			reg |= (bdfspec)[i].val; \
			pci_write_config8(PCI_DEV((bus), (dev), (fun)), (bdfspec)[i].reg, reg); \
		} \
	}


static void do_ram_command(const struct mem_controller *ctrl, u8 command)
{
	u8 reg;

	reg = pci_read_config8(MEMCTRL, 0x6b);
	reg &= 0xf8;		/* Clear bits 2-0. */
	reg |= command;
	pci_write_config8(MEMCTRL, 0x6b, reg);

	PRINTK_DEBUG("    Sending RAM command 0x%02x\n", reg);
}

// TODO factor out to another file
static void c7_cpu_setup(const struct mem_controller *ctrl)
{
	u8 size, i;
	size = sizeof(Reg_Val) / sizeof(Reg_Val[0]);
	for (i = 0; i < size; i += 2)
		pci_write_config8(HOSTCTRL, Reg_Val[i], Reg_Val[i + 1]);
}

static void ddr_detect(const struct mem_controller *ctrl)
{
	/* FIXME: Only supports 2 ranks per DIMM */
	u8 val, rsize, dimm;
	u8 nrank = 0;
	u8 ndimm = 0;
	u8 rmap = 0;
	for (dimm = 0; dimm < DIMM_SOCKETS; dimm++) {
		val = get_spd_data(ctrl, dimm, 0);
		if ((val == 0x80) || (val == 0xff)) {
			ndimm++;
			rsize = get_spd_data(ctrl, dimm, SPD_RANK_SIZE);
			/* unit is 128M */
			rsize = (rsize << 3) | (rsize >> 5);
			val =
			    get_spd_data(ctrl, dimm,
					 SPD_MOD_ATTRIB_RANK) & SPD_MOD_ATTRIB_RANK_NUM_MASK;
			switch (val) {
			case 1:
				pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_1 + (dimm << 1)),
						  rsize);
				rmap |= (1 << ((dimm << 1) + 1));
				nrank++;
			case 0:
				pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + (dimm << 1)),
						  rsize);
				rmap |= (1 << (dimm << 1));
				nrank++;
			}
		}
	}
	pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DIMM_NUM, ndimm);
	pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM, nrank);
	pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_MAP, rmap);
}

static void sdram_set_safe_values(const struct mem_controller *ctrl)
{
	/* The purpose of this function is to set initial values for the dram
	 * size and timings. It will be replaced with the SPD based function
	 * once the RAM commands are working with these values.
	 */
	u8 regs, val, t, dimm;
	u32 spds, tmp;

	regs = pci_read_config8(MEMCTRL, 0x6c);
	if (regs & (1 << 6))
		printk_debug("DDR2 Detected.\n");
	else
		die("ERROR: DDR1 memory detected but not supported by coreboot.\n");

	/* Enable DDR2 */
	regs |= (1 << 7);
	pci_write_config8(MEMCTRL, 0x6c, regs);

	/* SPD 5 # of ranks */
	pci_write_config8(MEMCTRL, 0x6d, 0xc0);

	/**********************************************/
	/*          Set DRAM Freq (DDR2 533)          */
	/**********************************************/
	/* SPD 9 SDRAM Cycle Time */
	GET_SPD(dimm, spds, regs, 9);

	printk_debug("\nDDRII ");
	if (spds <= 0x3d) {
		printk_debug("533");
		val = DDRII_533;
		t = 38;
	} else if (spds <= 0x50) {
		printk_debug("400");
		val = DDRII_400;
		t = 50;
	} else if (spds <= 0x60) {
		printk_debug("333");
		val = DDRII_333;
		t = 60;
	} else if (spds <= 0x75) {
		printk_debug("266");
		val = DDRII_266;
		t = 75;
	} else {
		printk_debug("200");
		val = DDRII_200;
		t = 100;
	}
	/* To store DDRII frequence */
	pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ, val);

	/* Manual reset and adjust DLL when DRAM change frequency 
	 * This is a necessary sequence.
	 */
	udelay(2000);
	regs = pci_read_config8(MEMCTRL, 0x90);
	regs |= 0x7;
	pci_write_config8(MEMCTRL, 0x90, regs);
	udelay(2000);
	regs = pci_read_config8(MEMCTRL, 0x90);
	regs &= ~0x7;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x90, regs);
	udelay(2000);
	regs = pci_read_config8(MEMCTRL, 0x6b);
	regs |= 0xc0;
	regs &= ~0x10;
	pci_write_config8(MEMCTRL, 0x6b, regs);
	udelay(1);
	regs |= 0x10;
	pci_write_config8(MEMCTRL, 0x6b, regs);
	udelay(1);
	regs &= ~0xc0;
	pci_write_config8(MEMCTRL, 0x6b, regs);
	regs = pci_read_config8(MEMCTRL, 0x6f);
	regs |= 0x1;
	pci_write_config8(MEMCTRL, 0x6f, regs);

	/**********************************************/
	/*      Set DRAM Timing Setting (DDR2 533)    */
	/**********************************************/
	/* SPD 9 18 23 25 CAS Latency NB3DRAM_REG62[2:0] */
	/* Read SPD byte 18 CAS Latency */
	GET_SPD(dimm, spds, regs, SPD_CAS_LAT);
	printk_debug("\nCAS Supported ");
	if (spds & SPD_CAS_LAT_2)
		printk_debug("2 ");
	if (spds & SPD_CAS_LAT_3)
		printk_debug("3 ");
	if (spds & SPD_CAS_LAT_4)
		printk_debug("4 ");
	if (spds & SPD_CAS_LAT_5)
		printk_debug("5 ");
	if (spds & SPD_CAS_LAT_6)
		printk_debug("6");

	/* We don't consider CAS = 6, because CX700 doesn't support it */
	printk_debug("\n CAS:");
	if (spds & SPD_CAS_LAT_5) {
		printk_debug("Starting at CL5");
		val = 0x3;
		/* See whether we can improve it */
		GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_1);
		if ((spds & SPD_CAS_LAT_4) && (tmp < 0x50)) {
			printk_debug("\n... going to CL4");
			val = 0x2;
		}
		GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_2);
		if ((spds & SPD_CAS_LAT_3) && (tmp < 0x50)) {
			printk_debug("\n... going to CL3");
			val = 0x1;
		}
	} else {
		printk_debug("Starting at CL4");
		val = 0x2;
		GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_1);
		if ((spds & SPD_CAS_LAT_3) && (tmp < 0x50)) {
			printk_debug("\n... going to CL3");
			val = 0x1;
		}
		GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_2);
		if ((spds & SPD_CAS_LAT_2) && (tmp < 0x50)) {
			printk_debug("\n... going to CL2");
			val = 0x0;
		}
	}
	regs = pci_read_config8(MEMCTRL, 0x62);
	regs &= ~0x7;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x62, regs);

	/* SPD 27 Trp NB3DRAM_REG64[3:2] */
	GET_SPD(dimm, spds, regs, SPD_TRP);
	printk_debug("\nTrp %d", spds);
	spds >>= 2;
	for (val = 2; val <= 5; val++) {
		if (spds <= (val * t / 10)) {
			val = val - 2;
			break;
		}
	}
	val <<= 2;
	regs = pci_read_config8(MEMCTRL, 0x64);
	regs &= ~0xc;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x64, regs);

	/* SPD 29 Trcd NB3DRAM_REG64[7:6] */
	GET_SPD(dimm, spds, regs, SPD_TRCD);
	printk_debug("\nTrcd %d", spds);
	spds >>= 2;
	for (val = 2; val <= 5; val++) {
		if (spds <= (val * t / 10)) {
			val = val - 2;
			break;
		}
	}
	val <<= 6;
	regs = pci_read_config8(MEMCTRL, 0x64);
	regs &= ~0xc0;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x64, regs);

	/* SPD 30 Tras NB3DRAM_REG62[7:4] */
	GET_SPD(dimm, spds, regs, SPD_TRAS);
	printk_debug("\nTras %d", spds);
	for (val = 5; val <= 20; val++) {
		if (spds <= (val * t / 10)) {
			val = val - 5;
			break;
		}
	}
	val <<= 4;
	regs = pci_read_config8(MEMCTRL, 0x62);
	regs &= ~0xf0;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x62, regs);

	/* SPD 42 SPD 40 Trfc NB3DRAM_REG61[5:0] */
	GET_SPD(dimm, spds, regs, SPD_TRFC);
	printk_debug("\nTrfc %d", spds);
	tmp = spds;
	GET_SPD(dimm, spds, regs, SPD_EX_TRC_TRFC);
	if (spds & 0x1)
		tmp += 256;
	if (spds & 0xe)
		tmp++;
	for (val = 8; val <= 71; val++) {
		if (tmp <= (val * t / 10)) {
			val = val - 8;
			break;
		}
	}
	regs = pci_read_config8(MEMCTRL, 0x61);
	regs &= ~0x3f;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x61, regs);

	/* SPD 28 Trrd NB3DRAM_REG63[7:6] */
	GET_SPD(dimm, spds, regs, SPD_TRRD);
	for (val = 2; val <= 5; val++) {
		if (spds <= (val * t / 10)) {
			val = val - 2;
			break;
		}
	}
	val <<= 6;
	printk_debug("\nTrrd val = 0x%x", val);
	regs = pci_read_config8(MEMCTRL, 0x63);
	regs &= ~0xc0;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x63, regs);

	/* SPD 36 Twr NB3DRAM_REG61[7:6] */
	GET_SPD(dimm, spds, regs, SPD_TWR);
	for (val = 2; val <= 5; val++) {
		if (spds <= (val * t / 10)) {
			val = val - 2;
			break;
		}
	}
	val <<= 6;
	printk_debug("\nTwr val = 0x%x", val);

	regs = pci_read_config8(MEMCTRL, 0x61);
	regs &= ~0xc0;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x61, regs);

	/* SPD 37 Twtr NB3DRAM_REG63[1] */
	GET_SPD(dimm, spds, regs, SPD_TWTR);
	spds >>= 2;
	printk_debug("\nTwtr 0x%x", spds);
	if (spds <= (t * 2 / 10))
		val = 0;
	else
		val = 1;
	val <<= 1;
	printk_debug("\nTwtr val = 0x%x", val);

	regs = pci_read_config8(MEMCTRL, 0x63);
	regs &= ~0x2;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x63, regs);

	/* SPD 38 Trtp NB3DRAM_REG63[3] */
	GET_SPD(dimm, spds, regs, SPD_TRTP);
	spds >>= 2;
	printk_debug("\nTrtp 0x%x", spds);
	if (spds <= (t * 2 / 10))
		val = 0;
	else
		val = 1;
	val <<= 3;
	printk_debug("\nTrtp val = 0x%x", val);

	regs = pci_read_config8(MEMCTRL, 0x63);
	regs &= ~0x8;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x63, regs);

	/**********************************************/
	/*           Set DRAM DRDY Setting            */
	/**********************************************/
	/* Write slowest value to register */
	tmp = sizeof(Host_Reg_Val) / sizeof(Host_Reg_Val[0]);
	for (val = 0; val < tmp; val += 2)
		pci_write_config8(HOSTCTRL, Host_Reg_Val[val], Host_Reg_Val[val + 1]);

	/* F2_RX51[7]=0, disable DRDY timing */
	regs = pci_read_config8(HOSTCTRL, 0x51);
	regs &= ~0x80;
	pci_write_config8(HOSTCTRL, 0x51, regs);

	/**********************************************/
	/*           Set DRAM BurstLength             */
	/**********************************************/
	regs = pci_read_config8(MEMCTRL, 0x6c);
	for (dimm = 0; dimm < DIMM_SOCKETS; dimm++) {
		if (pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (dimm << 1)))) {
			spds = get_spd_data(ctrl, dimm, 16);
			if (!(spds & 0x8))
				break;
		}
	}
	if (dimm == 2)
		regs |= 0x8;
	pci_write_config8(MEMCTRL, 0x6c, regs);
	val = pci_read_config8(HOSTCTRL, 0x54);
	val &= ~0x10;
	if (dimm == 2)
		val |= 0x10;
	pci_write_config8(HOSTCTRL, 0x54, val);

	/**********************************************/
	/*          Set DRAM Driving Setting          */
	/**********************************************/
	/* DRAM Timing ODT */
	tmp = sizeof(Dram_Driving_ODT_CTRL) / sizeof(Dram_Driving_ODT_CTRL[0]);
	for (val = 0; val < tmp; val += 2)
		pci_write_config8(MEMCTRL, Dram_Driving_ODT_CTRL[val],
				  Dram_Driving_ODT_CTRL[val + 1]);

	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
	val = pci_read_config8(MEMCTRL, 0xd5);
	val &= ~0xaa;
	switch (regs) {
	case 3:
	case 2:
		val |= 0xa0;
		break;
	default:
		val |= 0x80;
	}
	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DIMM_NUM);
	if (regs == 1)
		val |= 0xa;
	pci_write_config8(MEMCTRL, 0xd5, val);

	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DIMM_NUM);
	val = pci_read_config8(MEMCTRL, 0xd6);
	val &= ~0x2;
	if (regs == 1)
		val |= 0x2;
	pci_write_config8(MEMCTRL, 0xd6, val);

	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_MAP);
	tmp = sizeof(ODT_TBL) / sizeof(ODT_TBL[0]);
	for (val = 0; val < tmp; val += 3) {
		if (regs == ODT_TBL[val]) {
			pci_write_config8(MEMCTRL, 0xd8, ODT_TBL[val + 1]);
			/* Store DRAM & NB ODT setting in d0f4_Rxd8 */
			pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT, ODT_TBL[val + 2]);
			break;
		}
	}

	pci_write_config8(MEMCTRL, 0xd9, 0x0a);
	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
	regs--;
	regs = regs << 1;
	pci_write_config8(MEMCTRL, 0xe0, DQS_DQ_TBL[regs++]);
	pci_write_config8(MEMCTRL, 0xe2, DQS_DQ_TBL[regs]);

	/* DRAM Timing CS */
	pci_write_config8(MEMCTRL, 0xe4, 0x66);

	/* DRAM Timing MAA */
	val = 0;
	for (dimm = 0; dimm < DIMM_SOCKETS; dimm++) {
		if (pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (dimm << 1)))) {
			spds = get_spd_data(ctrl, dimm, SPD_PRI_WIDTH);
			spds = 64 / spds;
			if (pci_read_config8
			    (PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (dimm << 1) + 1)))
				spds = spds << 1;
			val += spds;
		}
	}
	printk_debug("\nchip #%d", val);
	if (val > 18)
		regs = 0xdb;
	else
		regs = 0x86;
	pci_write_config8(MEMCTRL, 0xe8, regs);

	/* DRAM Timing MAB */
	pci_write_config8(MEMCTRL, 0xe9, 0x0);

	/* DRAM Timing DCLK VT8454C always 0x66 */
	pci_write_config8(MEMCTRL, 0xe6, 0xaa);

	/**********************************************/
	/*            Set DRAM Duty Control           */
	/**********************************************/
	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
	switch (regs) {
	case 1:
	case 2:		/* 1~2 rank */
		val = 0;
		break;
	case 3:
	case 4:		/* 3~4 rank */
		regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
		if (regs == DDRII_533)
			val = 4;
		else		/* DDRII-400 */
			val = 0;
		break;
	}
	regs = 0xec;
	for (t = 0; t < 4; t++) {
		pci_write_config8(MEMCTRL, regs, Duty_Control_DDR2[val]);
		regs++;
		val++;
	}

	/**********************************************/
	/*            Set DRAM Clock Control          */
	/**********************************************/
	/* Write Data Phase */
	val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
	regs = pci_read_config8(MEMCTRL, 0x75);
	regs &= 0xf0;
	switch (val) {
	case DDRII_533:
		pci_write_config8(MEMCTRL, 0x74, 0x07);
		regs |= 0x7;
		break;
	case DDRII_400:
	default:
		pci_write_config8(MEMCTRL, 0x74, 0x05);
		regs |= 0x5;
		break;
	}
	pci_write_config8(MEMCTRL, 0x75, regs);
	pci_write_config8(MEMCTRL, 0x76, 0x80);

	/* Clock Phase Control for FeedBack Mode */
	regs = pci_read_config8(MEMCTRL, 0x90);
//      regs |= 0x80;
	pci_write_config8(MEMCTRL, 0x90, regs);

	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
	switch (regs) {
	case DDRII_533:
		regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
		if (regs == 1)
			val = 0;
		else
			val = 3;
		break;
	case DDRII_400:
	default:
		val = 6;
		break;
	}
	regs = pci_read_config8(MEMCTRL, 0x91);
	regs &= ~0xc0;
	regs |= 0x80;
	pci_write_config8(MEMCTRL, 0x91, regs);
	regs = 0x91;
	for (t = 0; t < 3; t++) {
		dimm = pci_read_config8(MEMCTRL, regs);
		dimm &= ~0x7;
		dimm |= ChA_Clk_Phase_DDR2_Table[val];
		pci_write_config8(MEMCTRL, regs, dimm);
		regs++;
		val++;
	}

	pci_write_config8(MEMCTRL, 0x97, 0x12);
	pci_write_config8(MEMCTRL, 0x98, 0x33);

	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_0);
	val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_2);
	if (regs && val)
		pci_write_config8(MEMCTRL, 0x9d, 0x00);
	else
		pci_write_config8(MEMCTRL, 0x9d, 0x0f);

	tmp = sizeof(DQ_DQS_Table) / sizeof(DQ_DQS_Table[0]);
	for (val = 0; val < tmp; val += 2)
		pci_write_config8(MEMCTRL, DQ_DQS_Table[val], DQ_DQS_Table[val + 1]);
	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
	if (regs == DDRII_533)
		pci_write_config8(MEMCTRL, 0x7b, 0xa0);
	else
		pci_write_config8(MEMCTRL, 0x7b, 0x10);

	/***************************************************/
	/*  Set necessary register before DRAM initialize  */
	/***************************************************/
	tmp = sizeof(Mem_Reg_Init) / sizeof(Mem_Reg_Init[0]);
	for (val = 0; val < tmp; val += 3) {
		regs = pci_read_config8(MEMCTRL, Mem_Reg_Init[val]);
		regs &= Mem_Reg_Init[val + 1];
		regs |= Mem_Reg_Init[val + 2];
		pci_write_config8(MEMCTRL, Mem_Reg_Init[val], regs);
	}
	regs = pci_read_config8(HOSTCTRL, 0x51);
	regs &= 0xbf;		// Clear bit 6 Disable Read Around Write
	pci_write_config8(HOSTCTRL, 0x51, regs);

	regs = pci_read_config8(HOSTCTRL, 0x54);
	t = regs >> 5;
	val = pci_read_config8(HOSTCTRL, 0x57);
	dimm = val >> 5;
	if (t == dimm)
		t = 0x0;
	else
		t = 0x1;
	regs &= ~0x1;
	regs |= t;
	val &= ~0x1;
	val |= t;
	pci_write_config8(HOSTCTRL, 0x57, val);

	regs = pci_read_config8(HOSTCTRL, 0x51);
	regs |= t;
	pci_write_config8(HOSTCTRL, 0x51, regs);

	regs = pci_read_config8(MEMCTRL, 0x90);
	regs &= 0x7;
	val = 0;
	if (regs < 0x2)
		val = 0x80;
	regs = pci_read_config8(MEMCTRL, 0x76);
	regs &= 0x80;
	regs |= val;
	pci_write_config8(MEMCTRL, 0x76, regs);

	regs = pci_read_config8(MEMCTRL, 0x6f);
	regs |= 0x10;
	pci_write_config8(MEMCTRL, 0x6f, regs);

	/***************************************************/
	/*    Find suitable DQS value for ChA and ChB      */
	/***************************************************/
	// Set DQS output delay for Channel A
	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
	val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
	switch (regs) {
	case DDRII_533:
		if (val < 2)
			val = 0;
		else
			val = 2;
		break;
	case DDRII_400:
	default:
		if (val < 2)
			val = 4;
		else
			val = 6;
		break;
	}
	for (t = 0; t < 2; t++)
		pci_write_config8(MEMCTRL, (0x70 + t), DQSOChA_DDR2_Driving_Table[val + t]);
	// Set DQS output delay for Channel B
	pci_write_config8(MEMCTRL, 0x72, 0x0);

	regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_0);
	val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_2);
	if (regs && val)
		pci_write_config8(MEMCTRL, 0x73, 0xfd);
	else
		pci_write_config8(MEMCTRL, 0x73, 0x01);
}

static void sdram_set_registers(const struct mem_controller *ctrl)
{
	c7_cpu_setup(ctrl);
	ddr_detect(ctrl);
	sdram_set_safe_values(ctrl);
}

static void step_20_21(const struct mem_controller *ctrl)
{
	u8 val;

	// Step 20
	udelay(200);

	val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT);
	if (val & DDR2_ODT_150ohm)
		read32(0x102200);
	else
		read32(0x102020);

	/* Step 21. Normal operation */
	print_spew("RAM Enable 5: Normal operation\n");
	do_ram_command(ctrl, RAM_COMMAND_NORMAL);
	udelay(3);
}

static void step_2_19(const struct mem_controller *ctrl)
{
	u32 i;
	u8 val;

	//  Step 2
	val = pci_read_config8(MEMCTRL, 0x69);
	val &= ~0x03;
	pci_write_config8(MEMCTRL, 0x69, val);

	/* Step 3 Apply NOP. */
	print_spew("RAM Enable 1: Apply NOP\n");
	do_ram_command(ctrl, RAM_COMMAND_NOP);

	udelay(15);

	// Step 4
	print_spew("SEND: ");
	read32(0);
	print_spew("OK\n");

	// Step 5
	udelay(400);

	/* 6. Precharge all. Wait tRP. */
	print_spew("RAM Enable 2: Precharge all\n");
	do_ram_command(ctrl, RAM_COMMAND_PRECHARGE);

	// Step 7
	print_spew("SEND: ");
	read32(0);
	print_spew("OK\n");

	/* Step 8. Mode register set. */
	print_spew("RAM Enable 4: Mode register set\n");
	do_ram_command(ctrl, RAM_COMMAND_MRS);	//enable dll

	// Step 9
	print_spew("SEND: ");

	val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT);
	if (val & DDR2_ODT_150ohm)
		read32(0x102200);	//DDR2_ODT_150ohm
	else
		read32(0x102020);
	print_spew("OK\n");

	// Step 10
	print_spew("SEND: ");
	read32(0x800);
	print_spew("OK\n");

	/* Step 11. Precharge all. Wait tRP. */
	print_spew("RAM Enable 2: Precharge all\n");
	do_ram_command(ctrl, RAM_COMMAND_PRECHARGE);

	// Step 12
	print_spew("SEND: ");
	read32(0x0);
	print_spew("OK\n");

	/* Step 13. Perform 8 refresh cycles. Wait tRC each time. */
	print_spew("RAM Enable 3: CBR\n");
	do_ram_command(ctrl, RAM_COMMAND_CBR);

	/* JEDEC says only twice, do 8 times for posterity */
	// Step 16: Repeat Step 14 and 15 another 7 times
	for (i = 0; i < 8; i++) {
		// Step 14
		read32(0);
		print_spew(".");

		// Step 15
		udelay(100);
	}

	/* Step 17. Mode register set. Wait 200us. */
	print_spew("\nRAM Enable 4: Mode register set\n");

	//safe value for now, BL=8, WR=4, CAS=4
	do_ram_command(ctrl, RAM_COMMAND_MRS);
	udelay(200);

	/* Use Single Chanel temporarily */
	val = pci_read_config8(MEMCTRL, 0x6c);
	if (val & 0x8) {	/* Burst Length = 8 */
		val = pci_read_config8(MEMCTRL, 0x62);
		val &= 0x7;
		i = DDR2_MRS_table[4 + val];
	} else {
		val = pci_read_config8(MEMCTRL, 0x62);
		val &= 0x7;
		i = DDR2_MRS_table[val];
	}

	// Step 18
	val = pci_read_config8(MEMCTRL, 0x61);
	val = val >> 6;
	i |= DDR2_Twr_table[val];
	read32(i);

	printk_debug("MRS = %08x\n", i);

	udelay(15);

	// Step 19
	val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT);
	if (val & DDR2_ODT_150ohm)
		read32(0x103e00);	//EMRS OCD Default
	else
		read32(0x103c20);
}

static void sdram_set_vr(const struct mem_controller *ctrl, u8 num)
{
	u8 reg, val;
	val = 0x54 + (num >> 1);
	reg = pci_read_config8(MEMCTRL, val);
	reg &= (0xf << (4 * (num & 0x1)));
	reg |= (((0x8 | num) << 4) >> (4 * (num & 0x1)));
	pci_write_config8(MEMCTRL, val, reg);
}
static void sdram_ending_addr(const struct mem_controller *ctrl, u8 num)
{
	u8 reg, val;
	/* Set Ending Address */
	val = 0x40 + num;
	reg = pci_read_config8(MEMCTRL, val);
	reg += 0x10;
	pci_write_config8(MEMCTRL, val, reg);
	/* Set Beginning Address */
	val = 0x48 + num;
	pci_write_config8(MEMCTRL, val, 0x0);
}

static void sdram_clear_vr_addr(const struct mem_controller *ctrl, u8 num)
{
	u8 reg, val;
	val = 0x54 + (num >> 1);
	reg = pci_read_config8(MEMCTRL, val);
	reg = ~(0x80 >> (4 * (num & 0x1)));
	pci_write_config8(MEMCTRL, val, reg);
	val = 0x40 + num;
	reg = pci_read_config8(MEMCTRL, val);
	reg -= 0x10;
	pci_write_config8(MEMCTRL, val, reg);
	val = 0x48 + num;
	pci_write_config8(MEMCTRL, val, 0x0);
}

/* Perform sizing DRAM by dynamic method */
static void sdram_calc_size(const struct mem_controller *ctrl, u8 num)
{
	u8 ca, ra, ba, reg;
	ba = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_FLAGS);
	if (ba == 8) {
		write8(0, 0x0d);
		ra = read8(0);
		write8((1 << SDRAM1X_RA_12_8bk), 0x0c);
		ra = read8(0);

		write8(0, 0x0a);
		ca = read8(0);
		write8((1 << SDRAM1X_CA_09_8bk), 0x0c);
		ca = read8(0);

		write8(0, 0x03);
		ba = read8(0);
		write8((1 << SDRAM1X_BA2_8bk), 0x02);
		ba = read8(0);
		write8((1 << SDRAM1X_BA1_8bk), 0x01);
		ba = read8(0);
	} else {
		write8(0, 0x0f);
		ra = read8(0);
		write8((1 << SDRAM1X_RA_14), 0x0e);
		ra = read8(0);
		write8((1 << SDRAM1X_RA_13), 0x0d);
		ra = read8(0);
		write8((1 << SDRAM1X_RA_12), 0x0c);
		ra = read8(0);

		write8(0, 0x0c);
		ca = read8(0);
		write8((1 << SDRAM1X_CA_12), 0x0b);
		ca = read8(0);
		write8((1 << SDRAM1X_CA_11), 0x0a);
		ca = read8(0);
		write8((1 << SDRAM1X_CA_09), 0x09);
		ca = read8(0);

		write8(0, 0x02);
		ba = read8(0);
		write8((1 << SDRAM1X_BA1), 0x01);
		ba = read8(0);
	}

	if (ra < 10 || ra > 15)
		die("bad RA");
	if (ca < 8 || ca > 12)
		die("bad CA");
	if (ba < 1 || ba > 3)
		die("bad BA");

	/* Calculate MA type save to scratch register */
	reg = 0;

	switch (ra) {
	case 12:
		reg |= MA_12_Row;
		break;
	case 13:
		reg |= MA_13_Row;
		break;
	case 14:
		reg |= MA_14_Row;
		break;
	default:
		reg |= MA_15_Row;
	}

	switch (ca) {
	case 9:
		reg |= MA_9_Column;
		break;
	case 10:
		reg |= MA_10_Column;
		break;
	case 11:
		reg |= MA_11_Column;
		break;
	default:
		reg |= MA_12_Column;
	}

	switch (ba) {
	case 3:
		reg |= MA_8_Bank;
		break;
	default:
		reg |= MA_4_Bank;
	}

	pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_MA_REG + num), reg);

	if (ra >= 13)
		pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_256M_BIT, 1);

	/* Calculate rank size save to scratch register */
	ra = ra + ca + ba + 3 - 26;	/* 1 unit = 64M */
	ra = 1 << ra;
	pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_SIZE_REG + num), ra);
}

static void sdram_enable(const struct mem_controller *ctrl)
{
	u8 reg8;
	u8 val, i;
	device_t dev;
	u8 dl, dh;
	u32 quot;

	/* Init Present Bank */
	val = sizeof(Init_Rank_Reg_Table) / sizeof(Init_Rank_Reg_Table[0]);
	for (i = 0; i < val; i++)
		pci_write_config8(MEMCTRL, Init_Rank_Reg_Table[i], 0x0);

	/* Init other banks */
	for (i = 0; i < 4; i++) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (reg8) {
			sdram_set_vr(ctrl, i);
			sdram_ending_addr(ctrl, i);
			step_2_19(ctrl);
			step_20_21(ctrl);
			sdram_clear_vr_addr(ctrl, i);
		}
	}

#ifdef MEM_WIDTH_32BIT_MODE
	/****************************************************************/
	/*                      Set Dram 32bit Mode                     */
	/****************************************************************/
	reg8 = pci_read_config8(MEMCTRL, 0x6c);
	reg8 |= 0x20;
	pci_write_config(MEMCTRL, 0x6c, reg8);
#endif

	/****************************************************************/
	/* Find the DQSI Low/High bound and save it to Scratch register */
	/****************************************************************/
	for (dl = 0; dl < 0x3f; dl += 2) {
		reg8 = dl & 0x3f;
		reg8 |= 0x80;	/* Set Manual Mode */
		pci_write_config8(MEMCTRL, 0x77, reg8);
		for (i = 0; i < 4; i++) {
			reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
			if (reg8) {
				sdram_set_vr(ctrl, i);
				sdram_ending_addr(ctrl, i);
				write32(0, 0x55555555);
				write32(4, 0x55555555);
				udelay(15);
				if (read32(0) != 0x55555555)
					break;
				if (read32(4) != 0x55555555)
					break;
				write32(0, 0xaaaaaaaa);
				write32(4, 0xaaaaaaaa);
				udelay(15);
				if (read32(0) != 0xaaaaaaaa)
					break;
				if (read32(4) != 0xaaaaaaaa)
					break;
				sdram_clear_vr_addr(ctrl, i);
			}
		}
		if (i == 4)
			break;
		else
			sdram_clear_vr_addr(ctrl, i);
	}
	printk_debug("\nDQSI Low %08x", dl);
	for (dh = dl; dh < 0x3f; dh += 2) {
		reg8 = dh & 0x3f;
		reg8 |= 0x80;	/* Set Manual Mode */
		pci_write_config8(MEMCTRL, 0x77, reg8);
		for (i = 0; i < 4; i++) {
			reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
			if (reg8) {
				sdram_set_vr(ctrl, i);
				sdram_ending_addr(ctrl, i);

				write32(0, 0x55555555);
				write32(4, 0x55555555);
				udelay(15);
				if (read32(0) != 0x55555555)
					break;
				if (read32(4) != 0x55555555)
					break;
				write32(0, 0xaaaaaaaa);
				write32(4, 0xaaaaaaaa);
				udelay(15);
				if (read32(0) != 0xaaaaaaaa)
					break;
				if (read32(4) != 0xaaaaaaaa)
					break;
				sdram_clear_vr_addr(ctrl, i);
			}
		}
		if (i != 4) {
			sdram_clear_vr_addr(ctrl, i);
			break;
		}
	}
	printk_debug("\nDQSI High %02x", dh);
	pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_CHA_DQSI_LOW_REG, dl);
	pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_CHA_DQSI_HIGH_REG, dh);
	reg8 = pci_read_config8(MEMCTRL, 0X90) & 0X7;
	val = DQSI_Rate_Table[reg8];
	quot = dh - dl;
	quot = quot * val;
	quot >>= 4;
	val = quot + dl;
	pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_ChA_DQSI_REG, val);
	reg8 = val & 0x3f;
	reg8 |= 0x80;
	pci_write_config8(MEMCTRL, 0x77, reg8);

	/****************************************************************/
	/*     Find out the lowest Bank Interleave and Set Register     */
	/****************************************************************/
#if 0
	//TODO
	reg8 = pci_read_config8(MEMCTRL, 0x69);
	reg8 &= ~0xc0;
	reg8 |= 0x80;		//8 banks
	pci_write_config8(MEMCTRL, 0x69, reg8);
#endif
	dl = 2;
	for (i = 0; i < 4; i++) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (reg8) {
			reg8 = get_spd_data(ctrl, (i >> 1), 17);
			sdram_set_vr(ctrl, i);
			sdram_ending_addr(ctrl, i);
			if (reg8 == 4) {
				write8(0, 0x02);
				val = read8(0);
				write8((1 << SDRAM1X_BA1), 0x01);
				val = read8(0);
			} else {
				write8(0, 0x03);
				val = read8(0);
				write8((1 << SDRAM1X_BA2_8bk), 0x02);
				val = read8(0);
				write8((1 << SDRAM1X_BA1_8bk), 0x01);
				val = read8(0);
			}
			if (val < dl)
				dl = val;
			sdram_clear_vr_addr(ctrl, i);
		}
	}
	dl <<= 6;
	reg8 = pci_read_config8(MEMCTRL, 0x69);
	reg8 &= ~0xc0;
	reg8 |= dl;
	pci_write_config8(MEMCTRL, 0x69, reg8);

	/****************************************************************/
	/*               DRAM Sizing and Fill MA type                   */
	/****************************************************************/
	for (i = 0; i < 4; i++) {
		val = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (val) {
			reg8 = get_spd_data(ctrl, (i >> 1), 17);
			pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_FLAGS, reg8);
			if (reg8 == 4) {
				/* Use MA Type 3 for DRAM sizing */
				reg8 = pci_read_config8(MEMCTRL, 0x50);
				reg8 &= 0x11;
				reg8 |= 0x66;
				pci_write_config8(MEMCTRL, 0x50, reg8);
				pci_write_config8(MEMCTRL, 0x51, reg8);
			} else {
				/* Use MA Type 5 for DRAM sizing */
				reg8 = pci_read_config8(MEMCTRL, 0x50);
				reg8 &= 0x11;
				reg8 |= 0xaa;
				pci_write_config8(MEMCTRL, 0x50, reg8);
				pci_write_config8(MEMCTRL, 0x51, reg8);
				reg8 = pci_read_config8(MEMCTRL, 0x53);
				reg8 &= 0x0f;
				reg8 |= 0x90;
				pci_write_config8(MEMCTRL, 0x53, reg8);
			}
			sdram_set_vr(ctrl, i);
			val = 0x40 + i;
			reg8 = pci_read_config8(MEMCTRL, val);
			/* max size 3G for new MA table */
			reg8 += 0x30;
			pci_write_config8(MEMCTRL, val, reg8);
			/* Set Beginning Address */
			val = 0x48 + i;
			pci_write_config8(MEMCTRL, val, 0x0);

			sdram_calc_size(ctrl, i);

			/* Clear */
			val = 0x54 + (i >> 1);
			reg8 = pci_read_config8(MEMCTRL, val);
			reg8 = ~(0x80 >> (4 * (i & 0x1)));
			pci_write_config8(MEMCTRL, val, reg8);
			val = 0x40 + i;
			reg8 = pci_read_config8(MEMCTRL, val);
			reg8 -= 0x30;
			pci_write_config8(MEMCTRL, val, reg8);
			val = 0x48 + i;
			pci_write_config8(MEMCTRL, val, 0x0);

		}
	}
	/* Clear MA Type */
	reg8 = pci_read_config8(MEMCTRL, 0x50);
	reg8 &= 0x11;
	pci_write_config8(MEMCTRL, 0x50, reg8);
	pci_write_config8(MEMCTRL, 0x51, reg8);
	reg8 = pci_read_config8(MEMCTRL, 0x6b);
	reg8 &= ~0x08;
	pci_write_config8(MEMCTRL, 0x6b, reg8);

	/****************************************************************/
	/*             DRAM re-initialize for burst length              */
	/****************************************************************/
	for (i = 0; i < 4; i++) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (reg8) {
			sdram_set_vr(ctrl, i);
			sdram_ending_addr(ctrl, i);
			step_2_19(ctrl);
			step_20_21(ctrl);
			sdram_clear_vr_addr(ctrl, i);
		}
	}

	/****************************************************************/
	/*                    Set the MA Type                           */
	/****************************************************************/
	reg8 = pci_read_config8(MEMCTRL, 0x50);
	reg8 &= 0x11;
	pci_write_config8(MEMCTRL, 0x50, reg8);

	reg8 = pci_read_config8(MEMCTRL, 0x51);
	reg8 &= 0x11;
	pci_write_config8(MEMCTRL, 0x51, reg8);

	reg8 = pci_read_config8(MEMCTRL, 0x6b);
	reg8 &= ~0x08;
	pci_write_config8(MEMCTRL, 0x6b, reg8);

	for (i = 0; i < 4; i += 2) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (reg8) {
			reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_MA_REG + i));
			reg8 &= (MA_Bank + MA_Column);
			val = pci_read_config8(MEMCTRL, 0x50);
			if (i == 0) {
				reg8 <<= 4;
				val &= 0x1f;
			} else
				val &= 0xf1;
			val |= reg8;
			pci_write_config8(MEMCTRL, 0x50, val);
		}
	}

	/****************************************************************/
	/*                 Set Start and Ending Address                 */
	/****************************************************************/
	dl = 0;			/* Begin Address */
	dh = 0;			/* Ending Address */
	for (i = 0; i < 4; i++) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (reg8) {
			reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_SIZE_REG + i));
			if (reg8 == 0)
				continue;
			dh += reg8;
			pci_write_config8(MEMCTRL, (0x40 + i), dh);
			pci_write_config8(MEMCTRL, (0x48 + i), dl);
			dl = dh;
		}
	}
	dh <<= 2;
	// F7_Rx57 Ending address mirror register
	pci_write_config8(PCI_DEV(0, 0, 7), 0x57, dh);
	dev = pci_locate_device(PCI_ID(0x1106, 0x324e), 0);
	pci_write_config8(dev, 0x57, dh);
	// LOW TOP Address
	pci_write_config8(MEMCTRL, 0x88, dh);
	pci_write_config8(MEMCTRL, 0x85, dh);
	// also program vlink mirror
	pci_write_config8(PCI_DEV(0, 0, 7), 0xe5, dh);

	/****************************************************************/
	/*            Set Physical to Virtual Rank mapping              */
	/****************************************************************/
	pci_write_config32(MEMCTRL, 0x54, 0x0);
	for (i = 0; i < 4; i++) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (reg8) {
			reg8 = pci_read_config8(MEMCTRL, (0x54 + (i >> 1)));
			if (i & 0x1) {	/* Odd Rank */
				reg8 &= 0xf0;
				reg8 |= (0x8 | i);
			} else {	/* Even Rank */

				reg8 &= 0x0f;
				reg8 |= ((0x8 | i) << 4);
			}
			pci_write_config8(MEMCTRL, (0x54 + (i >> 1)), reg8);
		}
	}

	/****************************************************************/
	/*                   Set DRAM Refresh Counter                   */
	/****************************************************************/
	val = pci_read_config8(MEMCTRL, 0X90) & 0X7;
	val <<= 1;
	reg8 = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_256M_BIT);
	if (reg8)
		val++;
	pci_write_config8(MEMCTRL, 0x6a, REFC_Table[val]);

	/****************************************************************/
	/*  Chipset Performance UP and other setting after DRAM Sizing  */
	/****************************************************************/
	/* Dram Registers */
	val = sizeof(Dram_Table) / sizeof(Dram_Table[0]);
	for (i = 0; i < val; i += 3) {
		reg8 = pci_read_config8(MEMCTRL, Dram_Table[i]);
		reg8 &= Dram_Table[i + 1];
		reg8 |= Dram_Table[i + 2];
		pci_write_config8(MEMCTRL, Dram_Table[i], reg8);
	}

	/* Host Registers */
	val = sizeof(Host_Table) / sizeof(Host_Table[0]);
	for (i = 0; i < val; i += 3) {
		reg8 = pci_read_config8(HOSTCTRL, Host_Table[i]);
		reg8 &= Host_Table[i + 1];
		reg8 |= Host_Table[i + 2];
		pci_write_config8(HOSTCTRL, Host_Table[i], reg8);
	}

	/* PM Registers */
#ifdef SETUP_PM_REGISTERS
	val = sizeof(PM_Table) / sizeof(PM_Table[0]);
	for (i = 0; i < val; i += 3) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), PM_Table[i]);
		reg8 &= PM_Table[i + 1];
		reg8 |= PM_Table[i + 2];
		pci_write_config8(PCI_DEV(0, 0, 4), PM_Table[i], reg8);
	}
#endif
	pci_write_config8(HOSTCTRL, 0x5d, 0xb2);

	/****************************************************************/
	/*              UMA registers for N-series projects             */
	/****************************************************************/

	/* Manual setting frame buffer bank */
	for (i = 0; i < 4; i++) {
		reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
		if (reg8)
			val = i;
	}
	pci_write_config8(MEMCTRL, 0xb0, val);
	reg8 = 0x40;		// Frame buffer size 64M
	reg8 |= 0x80;		// VGA Enable
	reg8 |= 0x0a;		// A[31:28] = 1010b
	pci_write_config8(MEMCTRL, 0xa1, reg8);

#ifdef ECC
	// Clear Ecc
	outl(0x80000180, 0xcf8);
	outb(0xff, 0xcfc);
	// Enable Ecc
	outl(0x80000188, 0xcf8);
	outb(0xcf, 0xcfc);

	reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xa5);
	reg8 |= 0x10;
	pci_write_config8(PCI_DEV(0, 0, 0), 0xa5, reg8);

	reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0x91);
	reg8 |= 0x20;
	pci_write_config8(PCI_DEV(0, 0, 0), 0x91, reg8);
#endif

	static const struct regmask {
		u8 reg;
		u8 mask;
		u8 val;
	} b0d1f0[] = {
		{ 0x40, 0x00, 0x8b}, 
		{ 0x41, 0x80, 0x43},
		{ 0x42, 0x00, 0x62},
		{ 0x43, 0x00, 0x44},
		{ 0x44, 0x00, 0x34},
		{ 0x45, 0x00, 0x72}
	}, b0d0f3[] = {
		{ 0x53, 0xf0, 0x0f},
		{ 0x60, 0x00, 0x03},
		{ 0x65, 0x00, 0xd9},
		{ 0x66, 0x00, 0x80},
		{ 0x67, 0x00, 0x00},
		{ 0x68, 0x00, 0x01},
		{ 0x69, 0xe0, 0x03},
		{ 0x6b, 0x00, 0x10},
		{ 0x6c, 0xc1, 0x08},
		{ 0x6e, 0x00, 0x89},
		{ 0x6f, 0x00, 0x51},
		{ 0x75, ~0x40, 0x40},
		{ 0x76, 0x8f, 0x00},
		{ 0x7b, 0x00, 0xa0},
		{ 0x86, 0x01, 0x24},
		{ 0x86, 0x04, 0x29},
		{ 0x8c, 0x00, 0x00},
		{ 0x8d, 0x00, 0x00},
		{ 0x95, ~0x40, 0x00},
		{ 0xa2, 0x00, 0x44},
		{ 0xb1, 0x00, 0xaa}
	}, b0d0f0[] = {
		{ 0x4d, 0x00, 0x24},
		{ 0x4f, 0x00, 0x01},
		{ 0xbc, 0x00, 0x21},
		{ 0xbe, 0x00, 0x00},
		{ 0xbf, 0x7f, 0x80}
	}, b0d17f0[] = {
		{ 0x40, ~0x01, 0x01},		// enable timer/counter shadow registers
		{ 0x67, ~0x03, 0x01},
		{ 0x5b, ~0x01, 0x00},
		{ 0x8d, ~0x02, 0x02},
		{ 0x97, ~0x80, 0x00},
		{ 0xd2, ~0x18, 0x00},
		{ 0xe2, ~0x36, 0x06},
		{ 0xe4, ~0x80, 0x00},
		{ 0xe5, 0x00, 0x40},
		{ 0xe6, 0x00, 0x20},
		{ 0xe7, ~0xd0, 0xc0},
		{ 0xec, ~0x08, 0x00}
	}, b0d17f7[] = {
		{ 0x4e, ~0x80, 0x80},
		{ 0x4f, ~(1 << 6), 1 << 6 },	/* PG_CX700: 14.1.1 enable P2P Bridge Header for External PCI Bus */
		{ 0x74, ~0x00, 0x04},		/* PG_CX700: 14.1.2 APIC FSB directly up to snmic, not on pci */
		{ 0x7c, ~0x00, 0x02},		/* PG_CX700: 14.1.1 APIC FSB directly up to snmic, not on pci */
		{ 0xe6, 0x0, 0x04}		// MSI post
	}, b0d19f0[] = {	/* P2PE */
		{ 0x42, ~0x08, 0x08},		// Disable HD Audio,
		{ 0x40, ~0xc0, 0x80}		// 14.1.3.1.1 of the PG: extended cfg mode for pcie. enable capability, but don't activate
	}, b0d0f2[] = {
		{ 0x50, ~0x40, 0x88},
		{ 0x51, 0x80, 0x7b},
		{ 0x52, 0x90, 0x6f},
		{ 0x53, 0x00, 0x88},
		{ 0x54, 0xe4, 0x16},
		{ 0x55, 0xf2, 0x04},
		{ 0x56, 0x0f, 0x00},
		{ 0x57, ~0x04, 0x00},
		{ 0x5d, 0x00, 0xb2},
		{ 0x5e, 0x00, 0x88},
		{ 0x5f, 0x00, 0xc7},
		{ 0x5c, 0x00, 0x01}
	};

	REGISTERPRESET(0, 0, 0, b0d0f0);
	REGISTERPRESET(0, 0, 2, b0d0f2);
	REGISTERPRESET(0, 0, 3, b0d0f3);
	REGISTERPRESET(0, 1, 0, b0d1f0);
	REGISTERPRESET(0, 17, 0, b0d17f0);
	REGISTERPRESET(0, 17, 7, b0d17f7);
	REGISTERPRESET(0, 19, 0, b0d19f0);
}
