#define HAVE_STRING_SUPPORT          0
#define HAVE_CAST_SUPPORT            1
#define HAVE_STATIC_ARRAY_SUPPORT    1
#define HAVE_POINTER_SUPPORT         1
#define HAVE_MACRO_ARG_SUPPORT       0

void outb(unsigned char value, unsigned short port)
{
	__builtin_outb(value, port);
}

void outw(unsigned short value, unsigned short port)
{
	__builtin_outw(value, port);
}

void outl(unsigned int value, unsigned short port)
{
	__builtin_outl(value, port);
}

unsigned char inb(unsigned short port)
{
	return __builtin_inb(port);
}

unsigned char inw(unsigned short port)
{
	return __builtin_inw(port);
}

unsigned char inl(unsigned short port)
{
	return __builtin_inl(port);
}

static unsigned int config_cmd(unsigned char bus, unsigned devfn, unsigned where)
{
	return 0x80000000 | (bus << 16) | (devfn << 8) | (where & ~3);
}

static unsigned char pcibios_read_config_byte(
	unsigned char bus, unsigned devfn, unsigned where)
{
	outl(config_cmd(bus, devfn, where), 0xCF8);
	return inb(0xCFC + (where & 3));
}

static unsigned short pcibios_read_config_word(
	unsigned char bus, unsigned devfn, unsigned where)
{
	outl(config_cmd(bus, devfn, where), 0xCF8);
	return inw(0xCFC + (where & 2));
}

static unsigned int pcibios_read_config_dword(
	unsigned char bus, unsigned devfn, unsigned where)
{
	outl(config_cmd(bus, devfn, where), 0xCF8);
	return inl(0xCFC);
}


static void pcibios_write_config_byte(
	unsigned char bus, unsigned devfn, unsigned where, unsigned char value)
{
	outl(config_cmd(bus, devfn, where), 0xCF8);
	outb(value, 0xCFC + (where & 3));
}

static void pcibios_write_config_word(
	unsigned char bus, unsigned devfn, unsigned where, unsigned short value)
{
	outl(config_cmd(bus, devfn, where), 0xCF8);
	outw(value, 0xCFC + (where & 2));
}

static void pcibios_write_config_dword(
	unsigned char bus, unsigned devfn, unsigned where, unsigned int value)
{
	outl(config_cmd(bus, devfn, where), 0xCF8);
	outl(value, 0xCFC);
}

/* Base Address */
#ifndef CONFIG_TTYS0_BASE
#define CONFIG_TTYS0_BASE 0x3f8
#endif

#ifndef CONFIG_TTYS0_BAUD
#define CONFIG_TTYS0_BAUD 115200
#endif

#if ((115200%CONFIG_TTYS0_BAUD) != 0)
#error Bad ttys0 baud rate
#endif

#define CONFIG_TTYS0_DIV	(115200/CONFIG_TTYS0_BAUD)

/* Line Control Settings */
#ifndef CONFIG_TTYS0_LCS
/* Set 8bit, 1 stop bit, no parity */
#define CONFIG_TTYS0_LCS	0x3
#endif

#define UART_LCS	CONFIG_TTYS0_LCS

/* Data */
#define UART_RBR 0x00
#define UART_TBR 0x00

/* Control */
#define UART_IER 0x01
#define UART_IIR 0x02
#define UART_FCR 0x02
#define UART_LCR 0x03
#define UART_MCR 0x04
#define UART_DLL 0x00
#define UART_DLM 0x01

/* Status */
#define UART_LSR 0x05
#define UART_MSR 0x06
#define UART_SCR 0x07

int uart_can_tx_byte(void)
{
	return inb(CONFIG_TTYS0_BASE + UART_LSR) & 0x20;
}

void uart_wait_to_tx_byte(void)
{
	while(!uart_can_tx_byte())
		;
}

void uart_wait_until_sent(void)
{
	while(!(inb(CONFIG_TTYS0_BASE + UART_LSR) & 0x40))
		;
}

void uart_tx_byte(unsigned char data)
{
	uart_wait_to_tx_byte();
	outb(data, CONFIG_TTYS0_BASE + UART_TBR);
	/* Make certain the data clears the fifos */
	uart_wait_until_sent();
}

void uart_init(void)
{
	/* disable interrupts */
	outb(0x0, CONFIG_TTYS0_BASE + UART_IER);
	/* enable fifo's */
	outb(0x01, CONFIG_TTYS0_BASE + UART_FCR);
	/* Set Baud Rate Divisor to 12 ==> 115200 Baud */
	outb(0x80 | UART_LCS, CONFIG_TTYS0_BASE + UART_LCR);
	outb(CONFIG_TTYS0_DIV & 0xFF,   CONFIG_TTYS0_BASE + UART_DLL);
	outb((CONFIG_TTYS0_DIV >> 8) & 0xFF,    CONFIG_TTYS0_BASE + UART_DLM);
	outb(UART_LCS, CONFIG_TTYS0_BASE + UART_LCR);
}

void __console_tx_char(unsigned char byte)
{
	uart_tx_byte(byte);
}
void __console_tx_nibble(unsigned nibble)
{
	unsigned char digit;
	digit = nibble + '0';
	if (digit > '9') {
		digit += 39;
	}
	__console_tx_char(digit);
}
void __console_tx_hex8(unsigned char byte)
{
	__console_tx_nibble(byte >> 4);
	__console_tx_nibble(byte & 0x0f);
}

void __console_tx_hex32(unsigned char value)
{
	__console_tx_nibble((value >> 28) & 0x0f);
	__console_tx_nibble((value >> 24) & 0x0f);
	__console_tx_nibble((value >> 20) & 0x0f);
	__console_tx_nibble((value >> 16) & 0x0f);
	__console_tx_nibble((value >> 12) & 0x0f);
	__console_tx_nibble((value >>  8) & 0x0f);
	__console_tx_nibble((value >>  4) & 0x0f);
	__console_tx_nibble(value & 0x0f);
}

#if HAVE_STRING_SUPPORT
void __console_tx_string(char *str)
{
	unsigned char ch;
	while((ch = *str++) != '\0') {
		__console_tx_char(ch);
	}
}
#else
void __console_tx_string(char *str)
{
}
#endif


void print_emerg_char(unsigned char byte) { __console_tx_char(byte); }
void print_emerg_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_emerg_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_emerg(char *str) { __console_tx_string(str); }

void print_alert_char(unsigned char byte) { __console_tx_char(byte); }
void print_alert_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_alert_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_alert(char *str) { __console_tx_string(str); }

void print_crit_char(unsigned char byte) { __console_tx_char(byte); }
void print_crit_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_crit_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_crit(char *str) { __console_tx_string(str); }

void print_err_char(unsigned char byte) { __console_tx_char(byte); }
void print_err_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_err_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_err(char *str) { __console_tx_string(str); }

void print_warning_char(unsigned char byte) { __console_tx_char(byte); }
void print_warning_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_warning_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_warning(char *str) { __console_tx_string(str); }

void print_notice_char(unsigned char byte) { __console_tx_char(byte); }
void print_notice_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_notice_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_notice(char *str) { __console_tx_string(str); }

void print_info_char(unsigned char byte) { __console_tx_char(byte); }
void print_info_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_info_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_info(char *str) { __console_tx_string(str); }

void print_debug_char(unsigned char byte) { __console_tx_char(byte); }
void print_debug_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_debug_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_debug(char *str) { __console_tx_string(str); }

void print_spew_char(unsigned char byte) { __console_tx_char(byte); }
void print_spew_hex8(unsigned char value) { __console_tx_hex8(value); }
void print_spew_hex32(unsigned int value) { __console_tx_hex32(value); }
void print_spew(char *str) { __console_tx_string(str); }

#define PIIX4_DEVFN 0x90
#define SMBUS_MEM_DEVICE_START 0x50
#define SMBUS_MEM_DEVICE_END 0x53
#define SMBUS_MEM_DEVICE_INC 1


#define PM_BUS 0
#define PM_DEVFN (PIIX4_DEVFN+3)

#define SMBUS_IO_BASE 0x1000
#define SMBHSTSTAT 0
#define SMBHSTCTL  2
#define SMBHSTCMD  3
#define SMBHSTADD  4
#define SMBHSTDAT0 5
#define SMBHSTDAT1 6
#define SMBBLKDAT  7

void smbus_enable(void)
{
	/* iobase addr */
	pcibios_write_config_dword(PM_BUS, PM_DEVFN, 0x90, SMBUS_IO_BASE | 1);
	/* smbus enable */
	pcibios_write_config_byte(PM_BUS, PM_DEVFN, 0xd2, (0x4 << 1) | 1);
	/* iospace enable */
	pcibios_write_config_word(PM_BUS, PM_DEVFN, 0x4, 1);
}

void smbus_setup(void)
{
	outb(0, SMBUS_IO_BASE + SMBHSTSTAT);
}

static void smbus_wait_until_ready(void)
{
	while((inb(SMBUS_IO_BASE + SMBHSTSTAT) & 1) == 1) {
		/* nop */
	}
}

static void smbus_wait_until_done(void)
{
	unsigned char byte;
	do {
		byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
	}while((byte &1) == 1);
	while( (byte & ~1) == 0) {
		byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
	}
}

int smbus_read_byte(unsigned device, unsigned address)
{
	unsigned char host_status_register;
	unsigned char byte;
	int result;

	smbus_wait_until_ready();

	/* setup transaction */
	/* disable interrupts */
	outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL);
	/* set the device I'm talking too */
	outb(((device & 0x7f) << 1) | 1, SMBUS_IO_BASE + SMBHSTADD);
	/* set the command/address... */
	outb(address & 0xFF, SMBUS_IO_BASE + SMBHSTCMD);
	/* set up for a byte data read */
	outb((inb(SMBUS_IO_BASE + SMBHSTCTL) & 0xE3) | (0x2 << 2), SMBUS_IO_BASE + SMBHSTCTL);

	/* clear any lingering errors, so the transaction will run */
	outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);

	/* clear the data byte...*/
	outb(0, SMBUS_IO_BASE + SMBHSTDAT0);

	/* start the command */
	outb((inb(SMBUS_IO_BASE + SMBHSTCTL) | 0x40), SMBUS_IO_BASE + SMBHSTCTL);

	/* poll for transaction completion */
	smbus_wait_until_done();

	host_status_register = inb(SMBUS_IO_BASE + SMBHSTSTAT);

	/* read results of transaction */
	byte = inb(SMBUS_IO_BASE + SMBHSTDAT0);

	result = byte;
	if (host_status_register != 0x02) {
		result = -1;
	}
	return result;
}

#define I440GX_BUS 0
#define I440GX_DEVFN ((0x00 << 3) + 0)

#define USE_ECC 0

#define CAS_LATENCY 3

	/* CAS latency 2 */
#if (CAS_LATENCY == 2)
#define CAS_NB 0x17
	/*
	 * 7 == 0111
	 * 1 == 0001
	 */
#define CAS_MODE 0x2a
	/*
	 * a == 1010
	 * 2 == 0010
	 */
#endif

	/* CAS latency 3 */
#if (CAS_LATENCY == 3)
#define CAS_NB 0x13
	/*
	 * 3 == 0011
	 * 1 == 0001
	 */
#define CAS_MODE 0x3a
	/*
	 * a == 1010
	 * 3 == 0011
	 */
#endif

#ifndef CAS_NB
#error "Nothing defined"
#endif

/* Default values for config registers */

static void set_nbxcfg(void)
{
	/* NBXCFG 0x50 - 0x53 */
	/* f == 1111
	 * 0 == 0000
	 * 0 == 0000
	 * 0 == 0000
	 * 0 == 0000
	 * 1 == 0001
	 * 8 == 1000
	 * c == 1100
	 * SDRAM Row without ECC:
	 * row 0 == 1 No ECC
	 * row 1 == 1 No ECC
	 * row 2 == 1 No ECC
	 * row 3 == 1 No ECC
	 * row 4 == 1 No ECC
	 * row 5 == 1 No ECC
	 * row 6 == 1 No ECC
	 * row 7 == 1 No ECC
	 * Host Bus Fast Data Ready Enable == 0 Disabled
	 * IDSEL_REDIRECT == 0 (430TX compatibility disable?)
	 * WSC# Hanshake Disable == 0 enable (Use External IOAPIC)
	 * Host/DRAM Frequence == 00 100Mhz
	 * AGP to PCI Access Enable == 0 Disable
	 * PCI Agent to Aperture Access Disable == 0 Enable (Ignored)
	 * Aperture Access Global Enable == 0 Disable
	 * DRAM Data Integrity Mode == 11 (Error Checking/Correction)
	 * ECC Diagnostic Mode Enable == 0 Not Enabled
	 * MDA present == 0 Not Present
	 * USWC Write Post During During I/O Bridge Access Enable == 1 Enabled
	 * In Order Queue Depth (IQD) (RO) == ??
	 */
	pcibios_write_config_dword(I440GX_BUS, I440GX_DEVFN, 0x50, 0xff00000c);
}

static void set_dramc(void)
{
	/* 0 == 0000
	 * 8 == 1000
	 * Not registered SDRAM
	 * refresh disabled
	 */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x57, 0x8);
}

static void set_pam(void)
{
	/* PAM - Programmable Attribute Map Registers */
	/* Ideally we want to enable all of these as DRAM and teach
	 * linux it is o.k. to use them...
	 */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x59, 0x00);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x5a, 0x00);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x5b, 0x00);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x5d, 0x00);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x5e, 0x00);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x5f, 0x00);
}

static void set_drb(void)
{
	/* DRB - DRAM Row Boundary Registers */
	/* Conservative setting 8MB of ram on first DIMM... */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x60, 0x01);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x61, 0x01);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x62, 0x01);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x63, 0x01);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x64, 0x01);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x65, 0x01);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x66, 0x01);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x67, 0x01);
}

static void set_fdhc(void)
{
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x68, 0x00);
}
static void set_mbsc(void)
{
	/* MBSC - Memory Buffer Strength Control */
	/* 00c00003e820
	 * [47:44] 0 == 0000
	 * [43:40] 0 == 0000
	 * [39:36] c == 1100
	 * [35:32] 0 == 0000
	 * [31:28] 0 == 0000
	 * [27:24] 0 == 0000
	 * [23:20] 0 == 0000
	 * [19:16] 3 == 0011
	 * [15:12] e == 1110
	 * [11: 8] 8 == 1000
	 * [ 7: 4] 2 == 0010
	 * [ 3: 0] 0 == 0000
	 * MAA[14:0]#, WEA#, SRASA#, SCASA# Buffer Strengths  ==  3x
	 * MAB[14,13,10,12:11,9:0]#, WEB#, SRASB#, SCASB# Buffer Strengths == 3x
	 * MD[63:0]# Buffer Strength Control 2 == 3x
	 * MD[63:0]# Buffer Strength Control 1 == 3x
	 * MECC[7:0] Buffer Strength Control 2 == 3x
	 * MECC[7:0] Buffer Strength Control 1 == 3x
	 * CSB7# Buffer Strength == 3x
	 * CSA7# Buffer Strength == 3x
	 * CSB6# Buffer Strength == 3x
	 * CSA6# Buffer Strength == 3x
	 * CSA5#/CSB5# Buffer Strength == 2x
	 * CSA4#/CSB4# Buffer Strength == 2x
	 * CSA3#/CSB3# Buffer Strength == 2x
	 * CSA2#/CSB2# Buffer Strength == 2x
	 * CSA1#/CSB1# Buffer Strength == 2x
	 * CSA0#/CSB0# Buffer Strength == 2x
	 * DQMA5 Buffer Strength == 2x
	 * DQMA1 Buffer Strength == 3x
	 * DQMB5 Buffer Strength == 2x
	 * DQMB1 Buffer Strength == 2x
	 * DQMA[7:6,4:2,0] Buffer Strength == 3x
	 * GCKE Buffer Strength == 1x
	 * FENA Buffer Strength == 3x
	 */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x69, 0xB3);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x6a, 0xee);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x6b, 0xff);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x6c, 0xff);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x6d, 0xff);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x6e, 0x03);
}

static void set_smram(void)
{
	/* 0x72 SMRAM */
	/* 1 == 0001
	 * a == 1010
	 * SMM Compatible base segment == 010 (Hardcoded value)
	 */
}

static void set_esramc(void)
{
	/* 0x73 ESMRAMC */
}

static void set_rps(void)
{
	/* RPS - Row Page Size Register */
	/* 0x0055
	 * [15:12] 0 == 0000
	 * [11: 8] 0 == 0000
	 * [ 7: 4] 5 == 0101
	 * [ 3: 0] 5 == 0101
	 * DRB[0] == 4KB
	 * DRB[1] == 4KB
	 * DRB[2] == 4KB
	 * DRB[3] == 4KB
	 * DRB[4] == 2KB
	 * DRB[5] == 2KB
	 * DRB[6] == 2KB
	 * DRB[7] == 2KB
	 */
	pcibios_write_config_word(I440GX_BUS, I440GX_DEVFN, 0x74, 0x5555);
}

static void set_sdramc(void)
{
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x76, CAS_NB);
}

static void set_pgpol(void)
{
	/* PGPOL - Paging Policy Register */
	/* 0xff07
	 * [15:12] f == 1111
	 * [11: 8] f == 1111
	 * [ 7: 4] 0 == 0000
	 * [ 3: 0] 7 == 0111
	 * row0 == 4banks
	 * row1 == 4banks
	 * row2 == 4banks
	 * row3 == 4banks
	 * row4 == 4banks
	 * row5 == 4banks
	 * row6 == 4banks
	 * row7 == 4banks
	 * Dram Idle Timer (DIT) == 32 clocks
	 */
	pcibios_write_config_word(I440GX_BUS, I440GX_DEVFN, 0x78, 0xff07);
}

static void set_mbfs(void)
{
	/* MBFS - Memory Buffer Frequencey Select Register */
	/* 0xffff7f
	 * [23:20] f == 1111
	 * [19:16] f == 1111
	 * [15:12] f == 1111
	 * [11: 8] f == 1111
	 * [ 7: 4] 7 == 0111
	 * [ 3: 0] f == 1111
	 * MAA[14:0], WEA#, SRASA#, SCASA# == 100Mhz Buffers Enabled
	 * MAB[14,13,10,12:11,9:0], WEB#, SRASB#, SCASB# == 100Mhz Buffers Enabled
	 * MD[63:0] Control 2 == 100 Mhz Buffer Enable
	 * MD[63:0] Control 1 == 100 Mhz B
	 * MECC[7:0] Control 2 == 100 Mhz B
	 *
	 */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xca, 0xff);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xcb, 0xff);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xcc, 0x7f);
}

static void set_dwtc(void)
{
	/* DWTC - DRAM Write Thermal Throttle Control */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe0, 0xb4);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe1, 0xbe);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe2, 0xff);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe3, 0xd7);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe4, 0x97);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe5, 0x3e);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe6, 0x00);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe7, 0x80);
}

static void set_drtc(void)
{
	/* DRTC - DRAM Read Thermal Throttle Control */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe8, 0x2c);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xe9, 0xd3);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xea, 0xf7);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xeb, 0xcf);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xec, 0x9d);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xed, 0x3e);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xee, 0x00);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xef, 0x00);
}

static void set_pmcr(void)
{
	/* PMCR -- BIOS sets 0x90 into it.
	 * 0x10 is REQUIRED.
	 * we have never used it. So why did this ever work?
	 */
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x7a, 0x90);

}
void sdram_set_registers(void)
{
	set_nbxcfg();
	set_dramc();
	set_pam();
	set_drb();
	set_fdhc();
	set_mbsc();
	set_smram();
	set_esramc();
	set_rps();
	set_sdramc();
	set_pgpol();
	set_mbfs();
	set_dwtc();
	set_drtc();
	set_pmcr();
}

int log2(int value)
{
	/* __builtin_bsr is a exactly equivalent to the x86 machine
	 * instruction with the exception that it returns -1
	 * when the value presented to it is zero.
	 * Otherwise __builtin_bsr returns the zero based index of
	 * the highest bit set.
	 */
	return __builtin_bsr(value);
}


static void spd_set_drb(void)
{
	/*
	 * Effects:	Uses serial presence detect to set the
	 *              DRB registers which holds the ending memory address assigned
	 *              to each DIMM.
	 */
	unsigned end_of_memory;
	unsigned device;
	unsigned drb_reg;

	end_of_memory = 0; /* in multiples of 8MiB */
	device = SMBUS_MEM_DEVICE_START;
	drb_reg = 0x60;
	while (device <= SMBUS_MEM_DEVICE_END) {
		unsigned side1_bits, side2_bits;
		int byte, byte2;

		side1_bits = side2_bits = -1;

		/* rows */
		byte = smbus_read_byte(device, 3);
		if (byte >= 0) {
			side1_bits += byte & 0xf;

			/* columns */
			byte = smbus_read_byte(device, 4);
			side1_bits += byte & 0xf;

			/* banks */
			byte = smbus_read_byte(device, 17);
			side1_bits += log2(byte);

			/* Get the moduel data width and convert it to a power of two */
			/* low byte */
			byte = smbus_read_byte(device, 6);

			/* high byte */
			byte2 = smbus_read_byte(device, 7);
#if HAVE_CAST_SUPPORT
			side1_bits += log2((((unsigned long)byte2 << 8)| byte));
#else
			side1_bits += log2((byte2 << 8) | byte);
#endif

			/* now I have the ram size in bits as a power of two (less 1) */
			/* Make it mulitples of 8MB */
			side1_bits -= 25;

			/* side two */

			/* number of physical banks */
			byte = smbus_read_byte(device, 5);
			if (byte > 1) {
				/* for now only handle the symmetrical case */
				side2_bits = side1_bits;
			}
		}

		/* Compute the end address for the DRB register */
		/* Only process dimms < 2GB (2^8 * 8MB) */
		if (side1_bits < 8) {
			end_of_memory += (1 << side1_bits);
		}
#if HAVE_STRING_SUPPORT
		print_debug("end_of_memory: "); print_debug_hex32(end_of_memory); print_debug("\n");
#endif
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, drb_reg, end_of_memory);

		if (side2_bits < 8 ) {
			end_of_memory += (1 << side2_bits);
		}
#if HAVE_STRING_SUPPORT
		print_debug("end_of_memory: "); print_debug_hex32(end_of_memory); print_debug("\n");
#endif
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, drb_reg +1, end_of_memory);

		drb_reg += 2;
		device += SMBUS_MEM_DEVICE_INC;
	}
}

void sdram_no_memory(void)
{
#if HAVE_STRING_SUPPORT
	print_err("No memory!!\n");
#endif
	while(1) ;
}

static void spd_set_dramc(void)
{
	/*
	 * Effects:	Uses serial presence detect to set the
	 *              DRAMC register, which records if ram is registerd or not,
	 *              and controls the refresh rate.
	 *              The refresh rate is not set here, as memory refresh
	 *              cannot be enbaled until after memory is initialized.
	 *		see spd_enable_refresh.
	 */
	/* auto detect if ram is registered or not. */
	/* The DRAMC register also contorls the refresh rate but we can't
         * set that here because we must leave refresh disabled.
	 * see:	spd_enable_refresh
	 */
	/* Find the first dimm and assume the rest are the same */
	/* FIXME Check for illegal/unsupported ram configurations and abort */
	unsigned device;
	int byte;
	unsigned dramc;
	byte = -1;
	device = SMBUS_MEM_DEVICE_START;

	while ((byte < 0) && (device <= SMBUS_MEM_DEVICE_END)) {
		byte = smbus_read_byte(device, 21);
		device += SMBUS_MEM_DEVICE_INC;
	}
	if (byte < 0) {
		/* We couldn't find anything we must have no memory */
		sdram_no_memory();
	}
	dramc = 0x8;
	if ((byte & 0x12) != 0) {
		/* this is a registered part.
		 * observation: for register parts, BIOS zeros (!)
		 * registers CA-CC. This has an undocumented meaning.
		 */
		/* But it does make sense the oppisite of registered
		 * sdram is buffered and 0xca - 0xcc control the buffers.
		 * Clearing them aparently disables them.
		 */
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xca, 0);
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xcb, 0);
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0xcc, 0);
		dramc = 0x10;
	}
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x57, dramc);
}

static void spd_enable_refresh(void)
{
	/*
	 * Effects:	Uses serial presence detect to set the
	 *              refresh rate in the DRAMC register.
	 *		see spd_set_dramc for the other values.
	 * FIXME:	Check for illegal/unsupported ram configurations and abort
	 */
#if HAVE_STATIC_ARRAY_SUPPORT
	static const unsigned char refresh_rates[] = {
		0x01, /* Normal        15.625 us -> 15.6 us */
		0x05, /* Reduced(.25X) 3.9 us    -> 7.8 us */
		0x05, /* Reduced(.5X)  7.8 us    -> 7.8 us */
		0x02, /* Extended(2x)  31.3 us   -> 31.2 us */
		0x03, /* Extended(4x)  62.5 us   -> 62.4 us */
		0x04, /* Extended(8x)  125 us    -> 124.8 us */
	};
#endif
	/* Find the first dimm and assume the rest are the same */
	int status;
	int byte;
	unsigned device;
	unsigned refresh_rate;
	byte = -1;
	status = -1;
	device = SMBUS_MEM_DEVICE_START;
	while ((byte < 0) && (device <= SMBUS_MEM_DEVICE_END)) {
		byte = smbus_read_byte(device, 12);
		device += SMBUS_MEM_DEVICE_INC;
	}
	if (byte < 0) {
		/* We couldn't find anything we must have no memory */
		sdram_no_memory();
	}
	byte &= 0x7f;
	/* Default refresh rate be conservative */
	refresh_rate = 5;
	/* see if the ram refresh is a supported one */
	if (byte < 6) {
#if HAVE_STATIC_ARRAY_SUPPORT
		refresh_rate = refresh_rates[byte];
#endif
	}
	byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x57);
	byte &= 0xf8;
	byte |= refresh_rate;
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x57, byte);
}

static void spd_set_sdramc(void)
{
	return;
}

static void spd_set_rps(void)
{
	/*
	 * Effects:	Uses serial presence detect to set the row size
	 *		on a given DIMM
	 * FIXME:	Check for illegal/unsupported ram configurations and abort
	 */
	/* The RPS register holds the size of a ``page'' of DRAM on each DIMM */
	unsigned page_sizes;
	unsigned index;
	unsigned device;
	unsigned char dramc;
	/* default all page sizes to 2KB */
	page_sizes = 0;
	index = 0;
	device = SMBUS_MEM_DEVICE_START;
	for(; device <= SMBUS_MEM_DEVICE_END; index += 4, device += SMBUS_MEM_DEVICE_INC) {
		unsigned int status;
		unsigned int byte;
		int page_size;

		byte = smbus_read_byte(device, 3);
		if (byte < 0) continue;

		/* I now have the row page size as a power of 2 */
		page_size = byte & 0xf;
		/* make it in multiples of 2Kb */
		page_size -= 11;

		if (page_size <= 0) continue;

		/* FIXME: do something with page sizes greather than 8KB!! */
		page_sizes |= (page_size << index);

		/* side two */
		byte = smbus_read_byte(device, 5);
		if (byte <= 1)  continue;

		/* For now only handle the symmetrical case */
		page_sizes |= (page_size << (index +2));
	}
	/* next block is for Ron's attempt to get registered to work. */
	/* we have just verified that we have to have this code. It appears that
	 * the registered SDRAMs do indeed set the RPS wrong. sheesh.
	 */
	/* at this point, page_sizes holds the RPS for all ram.
	 * we have verified that for registered DRAM the values are
	 * 1/2 the size they should be. So we test for registered
	 * and then double the sizes if needed.
	 */

	dramc = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x57);
	if (dramc & 0x10) {
		/* registered */

		/* BIOS makes weird page size for registered! */
		/* what we have found is you need to set the EVEN banks to
		 * twice the size. Fortunately there is a very easy way to
		 * do this. First, read the WORD value of register 0x74.
		 */
		page_sizes += 0x1111;
	}

	pcibios_write_config_word(I440GX_BUS, I440GX_DEVFN, 0x74, page_sizes);
}

static void spd_set_pgpol(void)
{
	/*
	 * Effects:	Uses serial presence detect to set the number of banks
	 *		on a given DIMM
	 * FIXME:	Check for illegal/unsupported ram configurations and abort
	 */
	/* The PGPOL register stores the number of logical banks per DIMM,
	 * and number of clocks the DRAM controller waits in the idle
	 * state.
	 */
	unsigned device;
	unsigned bank_sizes;
	unsigned bank;
	unsigned reg;
	/* default all bank counts 2 */
	bank_sizes = 0;
	bank = 0;
	device = SMBUS_MEM_DEVICE_START;
	for(; device <= SMBUS_MEM_DEVICE_END;
	    bank += 2, device += SMBUS_MEM_DEVICE_INC) {
		int byte;

		/* logical banks */
		byte = smbus_read_byte(device, 17);
		if (byte < 0) continue;
		if (byte < 4) continue;
		bank_sizes |= (1 << bank);

		/* side 2 */
		/* Number of physical banks */
		byte  = smbus_read_byte(device, 5);
		if (byte <= 1) continue;
		/* for now only handle the symmetrical case */
		bank_sizes |= (1 << (bank +1));
	}
	reg = bank_sizes << 8;
	reg |= 0x7; /* 32 clocks idle time */
	pcibios_write_config_word(I440GX_BUS, I440GX_DEVFN, 0x78, reg);
}

static void spd_set_nbxcfg(void)
{
	/*
	 * Effects:	Uses serial presence detect to set the
	 *              ECC support flags in the NBXCFG register
	 * FIXME:	Check for illegal/unsupported ram configurations and abort
	 */
	unsigned reg;
	unsigned index;
	unsigned device;

	/* Say all dimms have no ECC support */
	reg = 0xff;
	index = 0;

	device = SMBUS_MEM_DEVICE_START;
	for(; device <= SMBUS_MEM_DEVICE_END; index += 2, device += SMBUS_MEM_DEVICE_INC) {
		int byte;

		byte = smbus_read_byte(device, 11);
		if (byte < 0) continue;
#if !USE_ECC
		byte = 0; /* Disable ECC */
#endif
		/* 0 == None, 1 == Parity, 2 == ECC */
		if (byte != 2) continue;
		reg ^= (1 << index);

		/* side two */
		/* number of physical banks */
		byte = smbus_read_byte(device, 5);
		if (byte <= 1) continue;
		/* There is only the symmetrical case */
		reg ^= (1 << (index +1));
	}
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x53, reg);
	/* Now see if reg is 0xff.  If it is we are done.  If not,
	 * we need to set 0x18 into regster 0x50.l
	 * we will do this in two steps, first or in 0x80 to 0x50.b,
	 * then or in 0x1 to 0x51.b
	 */
#if HAVE_STRING_SUPPORT
	print_debug("spd_set_nbxcfg reg="); print_debug_hex8(reg); print_debug("\n");
#endif
	if (reg != 0xff) {
		unsigned char byte;
		byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x50);
		byte |= 0x80;
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x50, byte);
		byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x51);
		byte |= 1;
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x51, byte);
		/* try this.
		 * We should be setting bit 2 in register 76 and we're not
		 * technically we should see if CL=2 for the ram,
		 * but registered is so screwed up that it's kind of a lost
		 * cause.
		 */
		byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x76);
		byte |= 4;
		pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x76, byte);
#if HAVE_STRING_SUPPORT
		print_debug("spd_set_nbxcfg 0x76.b="); print_debug_hex8(byte); print_debug("\n");
#endif
	}
}

void sdram_set_spd_registers(void)
{
	spd_set_drb();
	spd_set_dramc();
	spd_set_rps();
	spd_set_sdramc();
	spd_set_pgpol();
	spd_set_nbxcfg();
}

void sdram_first_normal_reference(void)
{
	return;
}

void sdram_special_finishup(void)
{
	return;
}

static void set_ram_command(unsigned command)
{
	unsigned char byte;
	command &= 0x7;
	byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x76);
	byte &= 0x1f;
	byte |= (command << 5);
	pcibios_write_config_byte(I440GX_BUS, I440GX_DEVFN, 0x76, byte);
#if HAVE_STRING_SUPPORT
	print_debug("set_ram_command 0x76.b="); print_debug_hex8(byte); print_debug("\n");
#endif
}

#define RAM_COMMAND_NONE	0x0
#define RAM_COMMAND_NOOP	0x1
#define RAM_COMMAND_PRECHARGE	0x2
#define RAM_COMMAND_MRS		0x3
#define RAM_COMMAND_CBR		0x4

void sdram_set_command_none(void)
{
	set_ram_command(RAM_COMMAND_NONE);
}
void sdram_set_command_noop(void)
{
	set_ram_command(RAM_COMMAND_NOOP);
}
void sdram_set_command_precharge(void)
{
	set_ram_command(RAM_COMMAND_PRECHARGE);
}

static unsigned long dimm_base(int n)
{
	unsigned char byte;
	unsigned long result;
	if (n == 0) {
		return 0;
	}

	byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x60 + (n - 1));
	result = byte;
	result <<= 23;
	return result;
}

static void dimms_read(unsigned long offset)
{
	int i;
	for(i = 0; i < 8; i++) {
		unsigned long dummy;
		unsigned long addr;
		unsigned long next_base;

		next_base = dimm_base(i +1);
		addr =  dimm_base(i);
		if (addr == next_base) {
			continue;
		}
		addr += offset;
#if HAVE_STRING_SUPPORT
		print_debug("Reading ");
		print_debug_hex32(addr);
		print_debug("\n");
#endif
#if HAVE_POINTER_SUPPORT
#if HAVE_MACRO_ARG_SUPPORT
		dummy = RAM(unsigned long, addr);
#else
		dummy = *((volatile unsigned long *)(addr));
#endif
#endif
#if HAVE_STRING_SUPPORT
		print_debug("Reading ");
		print_debug_hex32(addr ^ 0xddf8);
		print_debug("\n");
#endif
#if HAVE_POINTER_SUPPORT
#if HAVE_MACRO_ARG_SUPPORT
		dummy = RAM(unsigned long, addr ^ 0xdff8);
#else
		dummy = *((volatile unsigned long *)(addr ^ 0xdff8));
#endif
#endif
#if HAVE_STRING_SUPPORT
		print_debug("Read ");
		print_debug_hex32(addr);
		print_debug_hex32(addr ^ 0xddf8);
		print_debug("\n");
#endif
	}
}

void sdram_set_command_cbr(void)
{
	set_ram_command(RAM_COMMAND_CBR);
}

void sdram_assert_command(void)
{
	dimms_read(0x400);
}

void sdram_set_mode_register(void)
{
	unsigned char byte;
	unsigned cas_mode;
	set_ram_command(RAM_COMMAND_MRS);
	byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x76);
	cas_mode = byte & 0x4;
	cas_mode ^= 4;
	cas_mode <<= 2;
	cas_mode |= 0x2a;
	cas_mode <<= 3;
	dimms_read(cas_mode);
}

void sdram_enable_refresh(void)
{
	spd_enable_refresh();
}


unsigned long sdram_get_ecc_size_bytes(void)
{
	unsigned char byte;
	unsigned long size;
	/* FIXME handle the no ram case. */
	/* Read the RAM SIZE */
	byte = pcibios_read_config_byte(I440GX_BUS, I440GX_DEVFN, 0x67);
	/* Convert it to bytes */
	size = byte;
	size <<= 23;
#if !USE_ECC
	size = 0;
#endif
	return size;
}

/* Dummy udelay code acting as a place holder... */
void udelay(int count)
{
	int i;
	i = 5;
}

void sdram_enable(void)
{
#if HAVE_STRING_SUPPORT
	print_debug("Ram Enable 1\n");
#endif

	/* noop command */
	sdram_set_command_noop();
	udelay(200);
	sdram_assert_command();

	/* Precharge all */
	sdram_set_command_precharge();
	sdram_assert_command();

	/* wait until the all banks idle state... */
#if HAVE_STRING_SUPPORT
	print_debug("Ram Enable 2\n");
#endif

	/* Now we need 8 AUTO REFRESH / CBR cycles to be performed */

	sdram_set_command_cbr();
	sdram_assert_command();
	sdram_assert_command();
	sdram_assert_command();
	sdram_assert_command();
	sdram_assert_command();
	sdram_assert_command();
	sdram_assert_command();
	sdram_assert_command();

#if HAVE_STRING_SUPPORT
	print_debug("Ram Enable 3\n");
#endif

	/* mode register set */
	sdram_set_mode_register();
	/* MAx[14:0] lines,
	 * MAx[2:0 ] 010 == burst mode of 4
	 * MAx[3:3 ] 1 == interleave wrap type
	 * MAx[4:4 ] == CAS# latency bit
	 * MAx[6:5 ] == 01
	 * MAx[12:7] ==	0
	 */

#if HAVE_STRING_SUPPORT
	print_debug("Ram Enable 4\n");
#endif

	/* normal operation */
	sdram_set_command_none();

#if HAVE_STRING_SUPPORT
	print_debug("Ram Enable 5\n");
#endif
}

/* Setup SDRAM */
void sdram_initialize(void)
{
#if HAVE_STRING_SUPPORT
	print_debug("Ram1\n");
#endif
	/* Set the registers we can set once to reasonable values */
	sdram_set_registers();

#if HAVE_STRING_SUPPORT
	print_debug("Ram2\n");
#endif
	/* Now setup those things we can auto detect */
	sdram_set_spd_registers();

#if HAVE_STRING_SUPPORT
	print_debug("Ram3\n");
#endif
	/* Now that everything is setup enable the SDRAM.
	 * Some chipsets do the work for use while on others
	 * we need to it by hand.
	 */
	sdram_enable();

#if HAVE_STRING_SUPPORT
	print_debug("Ram4\n");
#endif
	sdram_first_normal_reference();

#if HAVE_STRING_SUPPORT
	print_debug("Ram5\n");
#endif
	sdram_enable_refresh();
	sdram_special_finishup();

#if HAVE_STRING_SUPPORT
	print_debug("Ram6\n");
#endif
}
