/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2006 Indrek Kruusa <indrek.kruusa@artecdesign.ee>
 * Copyright (C) 2006 Ronald G. Minnich <rminnich@gmail.com>
 * Copyright (C) 2007 Advanced Micro Devices, Inc.
 *
 * 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.
 */

/* SetDelayControl */
#include "cpu/x86/msr.h"


/**
 * Delay Control Settings table from AMD (MCP 0x4C00000F).
 */
static const msrinit_t delay_msr_table[] = {
	{CPU_BC_MSS_ARRAY_CTL0, {.hi = 0x00000000, .lo = 0x2814D352}},
	{CPU_BC_MSS_ARRAY_CTL1, {.hi = 0x00000000, .lo = 0x1068334D}},
	{CPU_BC_MSS_ARRAY_CTL2, {.hi = 0x00000106, .lo = 0x83104104}},
};

static const struct delay_controls {
	u8 dimms;
	u8 devices;
	u32 slow_hi;
	u32 slow_low;
	u32 fast_hi;
	u32 fast_low;
} delay_control_table[] = {
	/* DIMMs Devs Slow (<=333MHz)            Fast (>334MHz) */
	{   1,     4, 0x0837100FF, 0x056960004,  0x0827100FF, 0x056960004 },
	{   1,     8, 0x0837100AA, 0x056960004,  0x0827100AA, 0x056960004 },
	{   1,    16, 0x0837100AA, 0x056960004,  0x082710055, 0x056960004 },
	{   2,     8, 0x0837100A5, 0x056960004,  0x082710000, 0x056960004 },
	{   2,    16, 0x0937100A5, 0x056960004,  0x0C27100A5, 0x056960004 },
	{   2,    20, 0x0B37100A5, 0x056960004,  0x0B27100A5, 0x056960004 },
	{   2,    24, 0x0B37100A5, 0x056960004,  0x0B27100A5, 0x056960004 },
	{   2,    32, 0x0B37100A5, 0x056960004,  0x0B2710000, 0x056960004 },
};

/*
 * Bit 55 (disable SDCLK 1,3,5) should be set if there is a single DIMM
 * in slot 0, but it should be clear for all 2 DIMM settings and if a
 * single DIMM is in slot 1. Bits 54:52 should always be set to '111'.
 *
 * Settings for single DIMM and no VTT termination (like DB800 platform)
 * 0xF2F100FF 0x56960004
 * -------------------------------------
 * ADDR/CTL have 22 ohm series R
 * DQ/DQM/DQS have 33 ohm series R
 */

/**
 * This is Black Magic DRAM timing juju[1].
 *
 * DRAM delay depends on CPU clock, memory bus clock, memory bus loading,
 * memory bus termination, your middle initial (ha! caught you!), GeodeLink
 * clock rate, and DRAM timing specifications.
 *
 * From this the code computes a number which is "known to work". No,
 * hardware is not an exact science. And, finally, if an FS2 (JTAG debugger)
 * is hooked up, then just don't do anything. This code was written by a master
 * of the Dark Arts at AMD and should not be modified in any way.
 *
 * [1] (http://www.thefreedictionary.com/juju)
 *
 * @param dimm0 The SMBus address of DIMM 0 (mainboard dependent).
 * @param dimm1 The SMBus address of DIMM 1 (mainboard dependent).
 * @param terminated The bus is terminated. (mainboard dependent).
 */
static void SetDelayControl(u8 dimm0, u8 dimm1, int terminated)
{
	u32 glspeed;
	u8 spdbyte0, spdbyte1, dimms, i;
	msr_t msr;

	glspeed = GeodeLinkSpeed();

	/* Fix delay controls for DM and IM arrays. */
	for (i = 0; i < ARRAY_SIZE(delay_msr_table); i++)
		wrmsr(delay_msr_table[i].index, delay_msr_table[i].msr);

	msr = rdmsr(GLCP_FIFOCTL);
	msr.hi = 0x00000005;
	wrmsr(GLCP_FIFOCTL, msr);

	/* Enable setting. */
	msr.hi = 0;
	msr.lo = 0x00000001;
	wrmsr(CPU_BC_MSS_ARRAY_CTL_ENA, msr);

	/* Debug Delay Control setup check.
	 * Leave it alone if it has been setup. FS2 or something is here.
	 */
	msr = rdmsr(GLCP_DELAY_CONTROLS);
	if (msr.lo & ~(DELAY_LOWER_STATUS_MASK))
		return;

	/* Delay Controls based on DIMM loading. UGH!
	 * Number of devices = module width (SPD 6) / device width (SPD 13)
	 *                     * physical banks (SPD 5)
	 *
	 * Note: We only support a module width of 64.
	 */
	dimms = 0;
	spdbyte0 = spd_read_byte(dimm0, SPD_PRIMARY_SDRAM_WIDTH);
	if (spdbyte0 != 0xFF) {
		dimms++;
		spdbyte0 = (u8)64 / spdbyte0 *
			   (u8)(spd_read_byte(dimm0, SPD_NUM_DIMM_BANKS));
	} else {
		spdbyte0 = 0;
	}

	spdbyte1 = spd_read_byte(dimm1, SPD_PRIMARY_SDRAM_WIDTH);
	if (spdbyte1 != 0xFF) {
		dimms++;
		spdbyte1 = (u8)64 / spdbyte1 *
			   (u8)(spd_read_byte(dimm1, SPD_NUM_DIMM_BANKS));
	} else {
		spdbyte1 = 0;
	}

	/* Zero GLCP_DELAY_CONTROLS MSR */
	msr.hi = msr.lo = 0;

	/* Save some power, disable clock to second DIMM if it is empty. */
	if (spdbyte1 == 0)
		msr.hi |= DELAY_UPPER_DISABLE_CLK135;

	spdbyte0 += spdbyte1;

	if ((dimms == 1) && (terminated == DRAM_TERMINATED)) {
		msr.hi = 0xF2F100FF;
		msr.lo = 0x56960004;
	} else for (i = 0; i < ARRAY_SIZE(delay_control_table); i++) {
		if ((dimms == delay_control_table[i].dimms) &&
		    (spdbyte0 <= delay_control_table[i].devices)) {
			if (glspeed < 334) {
				msr.hi |= delay_control_table[i].slow_hi;
				msr.lo |= delay_control_table[i].slow_low;
			} else {
				msr.hi |= delay_control_table[i].fast_hi;
				msr.lo |= delay_control_table[i].fast_low;
			}
			break;
		}
	}
	wrmsr(GLCP_DELAY_CONTROLS, msr);
}

void cpuRegInit(int debug_clock_disable, u8 dimm0, u8 dimm1, int terminated)
{
	int msrnum;
	msr_t msr;

	/* Castle 2.0 BTM periodic sync period. */
	/*      [40:37] 1 sync record per 256 bytes */
	printk(BIOS_DEBUG, "Castle 2.0 BTM periodic sync period.\n");
	msrnum = CPU_PF_CONF;
	msr = rdmsr(msrnum);
	msr.hi |= (0x8 << 5);
	wrmsr(msrnum, msr);

	/*
	 * LX performance setting.
	 * Enable Quack for fewer re-RAS on the MC
	 */
	printk(BIOS_DEBUG, "Enable Quack for fewer re-RAS on the MC\n");
	msrnum = GLIU0_ARB;
	msr = rdmsr(msrnum);
	msr.hi &= ~ARB_UPPER_DACK_EN_SET;
	msr.hi |= ARB_UPPER_QUACK_EN_SET;
	wrmsr(msrnum, msr);

	msrnum = GLIU1_ARB;
	msr = rdmsr(msrnum);
	msr.hi &= ~ARB_UPPER_DACK_EN_SET;
	msr.hi |= ARB_UPPER_QUACK_EN_SET;
	wrmsr(msrnum, msr);

	/* GLIU port active enable, limit south pole masters
	 * (AES and PCI) to one outstanding transaction.
	 */
	printk(BIOS_DEBUG, " GLIU port active enable\n");
	msrnum = GLIU1_PORT_ACTIVE;
	msr = rdmsr(msrnum);
	msr.lo &= ~0x880;
	wrmsr(msrnum, msr);

	/* Set the Delay Control in GLCP */
	printk(BIOS_DEBUG, "Set the Delay Control in GLCP\n");
	SetDelayControl(dimm0, dimm1, terminated);

	/*  Enable RSDC */
	printk(BIOS_DEBUG, "Enable RSDC\n");
	msrnum = CPU_AC_SMM_CTL;
	msr = rdmsr(msrnum);
	msr.lo |= SMM_INST_EN_SET;
	wrmsr(msrnum, msr);

	/* FPU imprecise exceptions bit */
	printk(BIOS_DEBUG, "FPU imprecise exceptions bit\n");
	msrnum = CPU_FPU_MSR_MODE;
	msr = rdmsr(msrnum);
	msr.lo |= FPU_IE_SET;
	wrmsr(msrnum, msr);

	/* Power Savers (Do after BIST) */
	/* Enable Suspend on HLT & PAUSE instructions */
	printk(BIOS_DEBUG, "Enable Suspend on HLT & PAUSE instructions\n");
	msrnum = CPU_XC_CONFIG;
	msr = rdmsr(msrnum);
	msr.lo |= XC_CONFIG_SUSP_ON_HLT | XC_CONFIG_SUSP_ON_PAUSE;
	wrmsr(msrnum, msr);

	/* Enable SUSP and allow TSC to run in Suspend (keep speed detection happy) */
	printk(BIOS_DEBUG, "Enable SUSP and allow TSC to run in Suspend\n");
	msrnum = CPU_BC_CONF_0;
	msr = rdmsr(msrnum);
	msr.lo |= TSC_SUSP_SET | SUSP_EN_SET;
	msr.lo &= 0x0F0FFFFFF;
	msr.lo |= 0x002000000;	/* PBZ213: Set PAUSEDLY = 2 */
	wrmsr(msrnum, msr);

	/* Disable the debug clock to save power. */
	/* NOTE: leave it enabled for fs2 debug */
	if (debug_clock_disable && 0) {
		msrnum = GLCP_DBGCLKCTL;
		msr.hi = 0;
		msr.lo = 0;
		wrmsr(msrnum, msr);
	}

	/* Setup throttling delays to proper mode if it is ever enabled. */
	printk(BIOS_DEBUG, "Setup throttling delays to proper mode\n");
	msrnum = GLCP_TH_OD;
	msr.hi = 0;
	msr.lo = 0x00000603C;
	wrmsr(msrnum, msr);
	printk(BIOS_DEBUG, "Done cpuRegInit\n");
}
