/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2017 Patrick Rudolph <siro@das-labor.org>
 * Copyright (C) 2017 Arthur Heymans <arthur@aheymans.xyz>
 *
 * 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.
 */

/**
 * @file ddr2.c
 *
 * \brief Utilities for decoding DDR2 SPDs
 */

#include <console/console.h>
#include <device/device.h>
#include <device/dram/ddr2.h>
#include <lib.h>
#include <string.h>

/*==============================================================================
 * = DDR2 SPD decoding helpers
 *----------------------------------------------------------------------------*/

/**
 * \brief Checks if the DIMM is Registered based on byte[20] of the SPD
 *
 * Tells if the DIMM type is registered or not.
 *
 * @param type DIMM type. This is byte[20] of the SPD.
 */
int spd_dimm_is_registered_ddr2(enum spd_dimm_type type)
{
	if ((type == SPD_DIMM_TYPE_RDIMM)
			|| (type == SPD_DIMM_TYPE_72B_SO_RDIMM)
			|| (type == SPD_DIMM_TYPE_MINI_RDIMM))
		return 1;

	return 0;
}

/**
 * \brief Calculate the checksum of a DDR2 SPD unique identifier
 *
 * @param spd pointer to raw SPD data
 * @param len length of data in SPD
 *
 * @return the checksum of SPD data bytes 63, or 0 when spd data is truncated.
 */
u8 spd_ddr2_calc_checksum(u8 *spd, int len)
{
	int i;
	u8 c = 0;

	if (len < 63)
		/* Not enough bytes available to get the checksum */
		return 0;

	for (i = 0; i < 63; i++)
		c += spd[i];

	return c;
}

/**
 * \brief Return size of SPD.
 *
 * Returns size of SPD. Usually 128 Byte.
 */
u32 spd_decode_spd_size_ddr2(u8 byte0)
{
	return MIN(byte0, SPD_SIZE_MAX_DDR2);
}

/**
 * \brief Return size of eeprom.
 *
 * Returns size of eeprom. Usually 256 Byte.
 */
u32 spd_decode_eeprom_size_ddr2(u8 byte1)
{
	if (!byte1)
		return 0;

	if (byte1 > 0x0e)
		return 0x3fff;

	return 1 << byte1;
}

/**
 * \brief Return index of MSB set
 *
 * Returns the index fof MSB set.
 */
u8 spd_get_msbs(u8 c)
{
	return log2(c);
}

/**
 * \brief Decode SPD tck cycle time
 *
 * Decodes a raw SPD data from a DDR2 DIMM.
 * Returns cycle time in 1/256th ns.
 */
static int spd_decode_tck_time(u32 *tck, u8 c)
{
	u8 high, low;

	high = c >> 4;

	switch (c & 0xf) {
	case 0xa:
		low = 25;
		break;
	case 0xb:
		low = 33;
		break;
	case 0xc:
		low = 66;
		break;
	case 0xd:
		low = 75;
		break;
	case 0xe:
	case 0xf:
		printk(BIOS_WARNING, "Invalid tck setting. "
			"lower nibble is 0x%x\n", c & 0xf);
		return CB_ERR;
	default:
		low = (c & 0xf) * 10;
	}

	*tck = ((high * 100 + low) << 8) / 100;
	return CB_SUCCESS;
}

/**
 * \brief Decode SPD bcd style timings
 *
 * Decodes a raw SPD data from a DDR2 DIMM.
 * Returns cycle time in 1/256th ns.
 */
static int spd_decode_bcd_time(u32 *bcd, u8 c)
{
	u8 high, low;

	high = c >> 4;
	low = c & 0xf;
	if (high >= 10 || low >= 10)
		return CB_ERR;

	*bcd = ((high * 10 + low) << 8) / 100;
	return CB_SUCCESS;
}

/**
 * \brief Decode SPD tRP, tRRP cycle time
 *
 * Decodes a raw SPD data from a DDR2 DIMM.
 * Returns cycle time in 1/256th ns.
 */
static u32 spd_decode_quarter_time(u8 c)
{
	u8 high, low;

	high = c >> 2;
	low = 25 * (c & 0x3);

	return ((high * 100 + low) << 8) / 100;
}

/**
 * \brief Decode SPD tRR time
 *
 * Decodes a raw SPD data from a DDR2 DIMM.
 * Returns cycle time in 1/256th us.
 */
static int spd_decode_tRR_time(u32 *tRR, u8 c)
{
	switch (c & ~0x80) {
	default:
		printk(BIOS_WARNING, "Invalid tRR value 0x%x\n", c);
		return CB_ERR;
	case 0x0:
		*tRR = 15625 << 8;
		break;
	case 0x1:
		*tRR = 15625 << 6;
		break;
	case 0x2:
		*tRR = 15625 << 7;
		break;
	case 0x3:
		*tRR = 15625 << 9;
		break;
	case 0x4:
		*tRR = 15625 << 10;
		break;
	case 0x5:
		*tRR = 15625 << 11;
		break;
	}
	return CB_SUCCESS;
}

/**
 * \brief Decode SPD tRC,tRFC time
 *
 * Decodes a raw SPD data from a DDR2 DIMM.
 * Returns cycle time in 1/256th us.
 */
static void spd_decode_tRCtRFC_time(u8 *spd_40_41_42, u32 *tRC, u32 *tRFC)
{
	u8 b40, b41, b42;

	b40 = spd_40_41_42[0];
	b41 = spd_40_41_42[1];
	b42 = spd_40_41_42[2];

	*tRC = b41 * 100;
	*tRFC = b42 * 100;

	if (b40 & 0x01)
		*tRFC += 256 * 100;

	switch ((b40 >> 1) & 0x07) {
	case 1:
		*tRFC += 25;
		break;
	case 2:
		*tRFC += 33;
		break;
	case 3:
		*tRFC += 50;
		break;
	case 4:
		*tRFC += 66;
		break;
	case 5:
		*tRFC += 75;
		break;
	default:
		break;
	}

	switch ((b40 >> 4) & 0x07) {
	case 1:
		*tRC += 25;
		break;
	case 2:
		*tRC += 33;
		break;
	case 3:
		*tRC += 50;
		break;
	case 4:
		*tRC += 66;
		break;
	case 5:
		*tRC += 75;
		break;
	default:
		break;
	}

	/* Convert to 1/256th us */
	*tRC = (*tRC << 8) / 100;
	*tRFC = (*tRFC << 8) / 100;
}

/**
 * \brief Decode the raw SPD data
 *
 * Decodes a raw SPD data from a DDR2 DIMM, and organizes it into a
 * @ref dimm_attr structure. The SPD data must first be read in a contiguous
 * array, and passed to this function.
 *
 * @param dimm pointer to @ref dimm_attr structure where the decoded data is to
 *        be stored
 * @param spd array of raw data previously read from the SPD.
 *
 * @return @ref spd_status enumerator
 *         SPD_STATUS_OK -- decoding was successful
 *         SPD_STATUS_INVALID -- invalid SPD or not a DDR2 SPD
 *         SPD_STATUS_CRC_ERROR -- CRC did not verify
 *         SPD_STATUS_INVALID_FIELD -- A field with an invalid value was
 *             detected.
 */
int spd_decode_ddr2(struct dimm_attr_st *dimm, u8 spd[SPD_SIZE_MAX_DDR2])
{
	u8 spd_size, cl, reg8;
	u16 eeprom_size;
	int ret = SPD_STATUS_OK;

	memset(dimm, 0, sizeof(*dimm));

	spd_size = spd_decode_spd_size_ddr2(spd[0]);
	eeprom_size = spd_decode_eeprom_size_ddr2(spd[1]);

	printram("EEPROM with 0x%04x bytes\n", eeprom_size);
	printram("SPD contains 0x%02x bytes\n", spd_size);

	if (spd_size < 64 || eeprom_size < 64) {
		printk(BIOS_WARNING, "ERROR: SPD to small\n");
		dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
		return SPD_STATUS_INVALID;
	}

	if (spd_ddr2_calc_checksum(spd, spd_size) != spd[63]) {
		printk(BIOS_WARNING, "ERROR: SPD checksum error\n");
		dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
		return SPD_STATUS_CRC_ERROR;
	}

	reg8 = spd[62];
	if ((reg8 & 0xf0) != 0x10) {
		printk(BIOS_WARNING,
			"ERROR: Unsupported SPD revision %01x.%01x\n",
			reg8 >> 4, reg8 & 0xf);
		dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
		return SPD_STATUS_INVALID;
	}
	dimm->rev = reg8;
	printram("  Revision           : %01x.%01x\n", dimm->rev >> 4, dimm->rev & 0xf);

	reg8 = spd[2];
	printram("  Type               : 0x%02x\n", reg8);
	if (reg8 != 0x08) {
		printk(BIOS_WARNING, "ERROR: Unsupported SPD type %x\n", reg8);
		dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
		return SPD_STATUS_INVALID;
	}
	dimm->dram_type = SPD_MEMORY_TYPE_SDRAM_DDR2;

	dimm->row_bits = spd[3];
	printram("  Rows               : %u\n", dimm->row_bits);
	if ((dimm->row_bits > 31) ||
	    ((dimm->row_bits > 15) && (dimm->rev < 0x13))) {
		printk(BIOS_WARNING,
			"SPD decode: invalid number of memory rows\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	dimm->col_bits = spd[4];
	printram("  Columns            : %u\n", dimm->col_bits);
	if (dimm->col_bits > 15) {
		printk(BIOS_WARNING,
			"SPD decode: invalid number of memory columns\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	dimm->ranks = (spd[5] & 0x7) + 1;
	printram("  Ranks              : %u\n", dimm->ranks);

	dimm->mod_width = spd[6];
	printram("  Module data width  : x%u\n", dimm->mod_width);
	if (!dimm->mod_width) {
		printk(BIOS_WARNING, "SPD decode: invalid module data width\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	dimm->width = spd[13];
	printram("  SDRAM width        : x%u\n", dimm->width);
	if (!dimm->width) {
		printk(BIOS_WARNING, "SPD decode: invalid SDRAM width\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	dimm->banks = spd[17];
	printram("  Banks              : %u\n", dimm->banks);
	if (!dimm->banks) {
		printk(BIOS_WARNING,
			"SPD decode: invalid module banks count\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	switch (spd[8]) {
	case 0:
		dimm->flags.operable_5_00V = 1;
		printram("  Voltage            : 5.0V\n");
		break;
	case 1:
		dimm->flags.operable_3_33V = 1;
		printram("  Voltage            : 3.3V\n");
		break;
	case 2:
		dimm->flags.operable_1_50V = 1;
		printram("  Voltage            : 1.5V\n");
		break;
	case 3:
		dimm->flags.operable_3_33V = 1;
		printram("  Voltage            : 3.3V\n");
		break;
	case 4:
		dimm->flags.operable_2_50V = 1;
		printram("  Voltage            : 2.5V\n");
		break;
	case 5:
		dimm->flags.operable_1_80V = 1;
		printram("  Voltage            : 1.8V\n");
		break;
	default:
		printk(BIOS_WARNING, "SPD decode: unknown voltage level.\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	dimm->cas_supported = spd[18];
	if ((dimm->cas_supported & 0x3) || !dimm->cas_supported) {
		printk(BIOS_WARNING,
			"SPD decode: invalid CAS support advertised.\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}
	printram("  Supported CAS mask : 0x%x\n", dimm->cas_supported);

	if ((dimm->rev < 0x13) && (dimm->cas_supported & 0x80)) {
		printk(BIOS_WARNING,
			"SPD decode: invalid CAS support advertised.\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}
	if ((dimm->rev < 0x12) && (dimm->cas_supported & 0x40)) {
		printk(BIOS_WARNING,
			"SPD decode: invalid CAS support advertised.\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	/* CL=X */
	cl = spd_get_msbs(dimm->cas_supported);

	/* SDRAM Cycle time at Maximum Supported CAS Latency (CL), CL=X */
	if (spd_decode_tck_time(&dimm->cycle_time[cl], spd[9]) != CB_SUCCESS) {
		printk(BIOS_WARNING,
			"SPD decode: invalid min tCL for CAS%d\n", cl);
		ret = SPD_STATUS_INVALID_FIELD;
	}
	/* SDRAM Access from Clock */
	if (spd_decode_bcd_time(&dimm->access_time[cl], spd[10])
			!= CB_SUCCESS) {
		printk(BIOS_WARNING,
			"SPD decode: invalid min tAC for CAS%d\n", cl);
		ret = SPD_STATUS_INVALID_FIELD;
	}

	if (dimm->cas_supported & (1 << (cl - 1))) {
		/* Minimum Clock Cycle at CLX-1 */
		if (spd_decode_tck_time(&dimm->cycle_time[cl - 1], spd[23])
				!= CB_SUCCESS) {
			printk(BIOS_WARNING,
				"SPD decode: invalid min tCL for CAS%d\n",
				cl - 1);
			ret = SPD_STATUS_INVALID_FIELD;
		}
		/* Maximum Data Access Time (tAC) from Clock at CLX-1 */
		if (spd_decode_bcd_time(&dimm->access_time[cl - 1], spd[24])
				!= CB_SUCCESS) {
			printk(BIOS_WARNING,
				"SPD decode: invalid min tAC for CAS%d\n",
				cl - 1);
			ret = SPD_STATUS_INVALID_FIELD;
		}
	}
	if (dimm->cas_supported & (1 << (cl - 2))) {
		/* Minimum Clock Cycle at CLX-2 */
		if (spd_decode_tck_time(&dimm->cycle_time[cl - 2], spd[25])
				!= CB_SUCCESS) {
			printk(BIOS_WARNING,
				"SPD decode: invalid min tCL for CAS%d\n",
				cl - 2);
			ret = SPD_STATUS_INVALID_FIELD;
		}
		/* Maximum Data Access Time (tAC) from Clock at CLX-2 */
		if (spd_decode_bcd_time(&dimm->access_time[cl - 2], spd[26])
				!= CB_SUCCESS) {
			printk(BIOS_WARNING,
				"SPD decode: invalid min tAC for CAS%d\n",
				cl - 2);
			ret = SPD_STATUS_INVALID_FIELD;
		}
	}

	reg8 = (spd[31] >> 5) | (spd[31] << 3);
	if (!reg8) {
		printk(BIOS_WARNING,
			"SPD decode: invalid rank density.\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}

	/* Rank density */
	dimm->ranksize_mb = 128 * reg8;
	/* Module density */
	dimm->size_mb = dimm->ranksize_mb * dimm->ranks;
	if (dimm->size_mb < 1024)
		printram("  Capacity           : %u MB\n", dimm->size_mb);
	else
		printram("  Capacity           : %u GB\n", dimm->size_mb >> 10);

	/* SDRAM Maximum Cycle Time (tCKmax) */
	if (spd_decode_bcd_time(&dimm->tCK, spd[43]) != CB_SUCCESS) {
		printk(BIOS_WARNING, "SPD decode: invalid Max tCK\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}
	/* Minimum Write Recovery Time (tWRmin) */
	dimm->tWR = spd_decode_quarter_time(spd[36]);
	/* Minimum RAS# to CAS# Delay Time (tRCDmin) */
	dimm->tRCD = spd_decode_quarter_time(spd[29]);
	/* Minimum Row Active to Row Active Delay Time (tRRDmin) */
	dimm->tRRD = spd_decode_quarter_time(spd[28]);
	/* Minimum Row Precharge Delay Time (tRPmin) */
	dimm->tRP = spd_decode_quarter_time(spd[27]);
	/* Minimum Active to Precharge Delay Time (tRASmin) */
	dimm->tRAS = spd[30] << 8;
	/* Minimum Active to Active/Refresh Delay Time (tRCmin) */
	/* Minimum Refresh Recovery Delay Time (tRFCmin) */
	spd_decode_tRCtRFC_time(&spd[40], &dimm->tRC, &dimm->tRFC);
	/* Minimum Internal Write to Read Command Delay Time (tWTRmin) */
	dimm->tWTR = spd_decode_quarter_time(spd[37]);
	/* Minimum Internal Read to Precharge Command Delay Time (tRTPmin) */
	dimm->tRTP = spd_decode_quarter_time(spd[38]);
	/* Data Input Setup Time Before Strobe */
	if (spd_decode_bcd_time(&dimm->tDS, spd[34]) != CB_SUCCESS) {
		printk(BIOS_WARNING, "SPD decode: invalid tDS\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}
	/* Data Input Hold Time After Strobe */
	if (spd_decode_bcd_time(&dimm->tDH, spd[35]) != CB_SUCCESS) {
		printk(BIOS_WARNING, "SPD decode: invalid tDH\n");
		ret = SPD_STATUS_INVALID_FIELD;
	}
	/* SDRAM Device DQS-DQ Skew for DQS and associated DQ signals */
	dimm->tDQSQ = (spd[44] << 8) / 100;
	/* SDRAM Device Maximum Read Data Hold Skew Factor */
	dimm->tQHS = (spd[45] << 8) / 100;
	/* PLL Relock Time in us */
	dimm->tPLL = spd[46] << 8;
	/* Refresh rate in us */
	if (spd_decode_tRR_time(&dimm->tRR, spd[12]) != CB_SUCCESS)
		ret = SPD_STATUS_INVALID_FIELD;
	dimm->flags.self_refresh = (spd[12] >> 7) & 1;
	printram("The assembly supports self refresh: %s\n",
		 dimm->flags.self_refresh ? "true" : "false");

	/* Number of PLLs on DIMM */
	if (dimm->rev >= 0x11)
		dimm->plls = (spd[21] >> 2) & 0x3;

	/* SDRAM Thermal and Refresh Options */
	printram("  General features   :");
	if ((dimm->rev >= 0x12) && (spd[22] & 0x04)) {
		dimm->flags.pasr = 1;
		printram(" PASR");
	}
	if ((dimm->rev >= 0x12) && (spd[22] & 0x02)) {
		dimm->flags.terminate_50ohms = 1;
		printram(" 50Ohm");
	}
	if (spd[22] & 0x01) {
		dimm->flags.weak_driver = 1;
		printram(" WEAK_DRIVER");
	}
	printram("\n");

	/* SDRAM Supported Burst length */
	printram("  Burst length       :");
	if (spd[16] & 0x06) {
		dimm->flags.bl8 = 1;
		printram(" BL8");
	}
	if (spd[22] & 0x04) {
		dimm->flags.bl4 = 1;
		printram(" BL4");
	}
	printram("\n");

	dimm->dimm_type = spd[20] & SPD_DIMM_TYPE_MASK;
	printram("  Dimm type          : %x\n", dimm->dimm_type);

	dimm->flags.is_ecc = !!(spd[11] & 0x3);
	printram("  ECC support        : %x\n", dimm->flags.is_ecc);

	dimm->flags.stacked = !!(spd[5] & 0x10);
	printram("  Package            : %s\n",
	    dimm->flags.stacked ? "stack" : "planar");

	if (spd_size > 71) {
		memcpy(&dimm->manufacturer_id, &spd[64], 4);
		printram("  Manufacturer ID    : %x\n", dimm->manufacturer_id);
	}

	if (spd_size > 90) {
		dimm->part_number[16] = 0;
		memcpy(dimm->part_number, &spd[73], 16);
		printram("  Part number        : %s\n", dimm->part_number);
	}

	if (spd_size > 94) {
		dimm->year = spd[93] + 2000;
		dimm->weeks = spd[94];
		printram("  Date               : %d week %d\n", dimm->year, dimm->weeks);
	}

	if (spd_size > 98) {
		memcpy(&dimm->serial, &spd[95], 4);
		printram("  Serial number      : 0x%08x\n", dimm->serial);
	}
	return ret;
}

/*
 * The information printed below has a more informational character, and is not
 * necessarily tied in to RAM init debugging. Hence, we stop using printram(),
 * and use the standard printk()'s below.
 */

static void print_ns(const char *msg, u32 val)
{
	u32 mant, fp;
	mant = val / 256;
	fp = (val % 256) * 1000 / 256;

	printk(BIOS_INFO, "%s%3u.%.3u ns\n", msg, mant, fp);
}

static void print_us(const char *msg, u32 val)
{
	u32 mant, fp;
	mant = val / 256;
	fp = (val % 256) * 1000 / 256;

	printk(BIOS_INFO, "%s%3u.%.3u us\n", msg, mant, fp);
}

/**
* \brief Print the info in DIMM
*
* Print info about the DIMM. Useful to use when CONFIG_DEBUG_RAM_SETUP is
* selected, or for a purely informative output.
*
* @param dimm pointer to already decoded @ref dimm_attr structure
*/
void dram_print_spd_ddr2(const struct dimm_attr_st *dimm)
{
	char buf[32];
	int i;

	printk(BIOS_INFO, "  Row    addr bits  : %u\n", dimm->row_bits);
	printk(BIOS_INFO, "  Column addr bits  : %u\n", dimm->col_bits);
	printk(BIOS_INFO, "  Number of ranks   : %u\n", dimm->ranks);
	printk(BIOS_INFO, "  DIMM Capacity     : %u MB\n", dimm->size_mb);
	printk(BIOS_INFO, "  Width             : x%u\n", dimm->width);
	printk(BIOS_INFO, "  Banks             : %u\n", dimm->banks);

	/* CAS Latencies Supported */
	printk(BIOS_INFO, "  CAS latencies     :");
	for (i = 2; i < 8; i++) {
		if (dimm->cas_supported & (1 << i))
			printk(BIOS_INFO, " %u", i);
	}
	printk(BIOS_INFO, "\n");

	for (i = 2; i < 8; i++) {
		if (!(dimm->cas_supported & (1 << i)))
			continue;

		strcpy(buf, "  tCK at CLx        : ");
		/* Simple snprintf replacement */
		buf[11] = '0' + i;
		print_ns(buf, dimm->cycle_time[i]);

		strcpy(buf, "  tAC at CLx        : ");
		/* Simple snprintf replacement */
		buf[11] = '0' + i;
		print_ns(buf, dimm->access_time[i]);
	}
	print_ns("  tCKmax            : ", dimm->tCK);
	print_ns("  tWRmin            : ", dimm->tWR);
	print_ns("  tRCDmin           : ", dimm->tRCD);
	print_ns("  tRRDmin           : ", dimm->tRRD);
	print_ns("  tRPmin            : ", dimm->tRP);
	print_ns("  tRASmin           : ", dimm->tRAS);
	print_ns("  tRCmin            : ", dimm->tRC);
	print_ns("  tRFCmin           : ", dimm->tRFC);
	print_ns("  tWTRmin           : ", dimm->tWTR);
	print_ns("  tRTPmin           : ", dimm->tRTP);
	print_ns("  tDS               : ", dimm->tDS);
	print_ns("  tDH               : ", dimm->tDH);
	print_ns("  tDQSQmax          : ", dimm->tDQSQ);
	print_ns("  tQHSmax           : ", dimm->tQHS);
	print_us("  tPLL              : ", dimm->tPLL);
	print_us("  tRR               : ", dimm->tRR);
}

void normalize_tck(u32 *tclk)
{
	if (*tclk <= TCK_800MHZ) {
		*tclk = TCK_800MHZ;
	} else if (*tclk <= TCK_666MHZ) {
		*tclk = TCK_666MHZ;
	} else if (*tclk <= TCK_533MHZ) {
		*tclk = TCK_533MHZ;
	} else if (*tclk <= TCK_400MHZ) {
		*tclk = TCK_400MHZ;
	} else if (*tclk <= TCK_333MHZ) {
		*tclk = TCK_333MHZ;
	} else if (*tclk <= TCK_266MHZ) {
		*tclk = TCK_266MHZ;
	} else if (*tclk <= TCK_200MHZ) {
		*tclk = TCK_200MHZ;
	} else {
		*tclk = 0;
		printk(BIOS_ERR, "Too slow common tCLK found\n");
	}
}