Alexandru Gagniuc (mr.nuke.me@gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1228
-gerrit
commit 901327ec7ecec94aba8d6ca6765b9f8da8495835 Author: Alexandru Gagniuc mr.nuke.me@gmail.com Date: Fri Jul 13 15:17:38 2012 -0500
NOTFORMERGE: VX900 early init (non-functional)
Just a backup push. Please ignore.
Change-Id: I7624944dbc05fbf3019897a116954d71dfda0031 Signed-off-by: Alexandru Gagniuc mr.nuke.me@gmail.com --- src/devices/dram/dram.h | 154 ++ src/devices/dram/dram_util.c | 297 +++ src/devices/smbus/early_smbus.c | 141 ++ src/devices/smbus/smbus.h | 99 + src/mainboard/via/epia-m850/.directory | 4 + src/mainboard/via/epia-m850/Kconfig | 47 + src/mainboard/via/epia-m850/Makefile.inc | 21 + src/mainboard/via/epia-m850/chip.h | 21 + src/mainboard/via/epia-m850/devicetree.cb | 62 + src/mainboard/via/epia-m850/mainboard.c | 25 + src/mainboard/via/epia-m850/romstage.c | 76 + src/northbridge/via/vx900/Kconfig | 23 + src/northbridge/via/vx900/Makefile.inc | 35 + src/northbridge/via/vx900/early_smbus.c | 191 ++ src/northbridge/via/vx900/early_vx900.h | 35 + src/northbridge/via/vx900/forgotten.c | 192 ++ src/northbridge/via/vx900/forgotten.h | 78 + src/northbridge/via/vx900/raminit.h | 77 + src/northbridge/via/vx900/raminit_ddr3.c | 1027 ++++++++++ .../via/vx900/raminit_ddr3_delay_calib.c | 2150 ++++++++++++++++++++ src/northbridge/via/vx900/romstrap.inc | 50 + src/northbridge/via/vx900/romstrap.lds | 27 + src/northbridge/via/vx900/vx900.h | 26 + 23 files changed, 4858 insertions(+), 0 deletions(-)
diff --git a/src/devices/dram/dram.h b/src/devices/dram/dram.h new file mode 100644 index 0000000..d79cc52 --- /dev/null +++ b/src/devices/dram/dram.h @@ -0,0 +1,154 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef DRAM_H +#define DRAM_H + +#include <stdint.h> + +/* DRAM type, byte 2 of spd */ +#define DRAM_TYPE_UNDEFINED 0x00 +#define DRAM_TYPE_FPM_DRAM 0x01 +#define DRAM_TYPE_EDO 0x02 +#define DRAM_TYPE_PIPELINED_NIBBLE 0x03 +#define DRAM_TYPE_SDRAM 0x04 +#define DRAM_TYPE_ROM 0x05 +#define DRAM_TYPE_DDR_SGRAM 0x06 +#define DRAM_TYPE_DDR 0x07 +#define DRAM_TYPE_DDR2 0x08 +#define DRAM_TYPE_DDR2_FBDIMM 0x09 +#define DRAM_TYPE_DDR2_FB_PROBE 0x0a +#define DRAM_TYPE_DDR3 0x0b + +/* Module type (byte 3, bits 3:0) of SPD */ +#define DIMM_TYPE_UNDEFINED 0 +#define DIMM_TYPE_RDIMM 1 +#define DIMM_TYPE_UDIMM 2 +#define DIMM_TYPE_SO_DIMM 3 +#define DIMM_TYPE_MICRO_DIMM 4 +#define DIMM_TYPE_MINI_RDIMM 5 +#define DIMM_TYPE_MINI_UDIMM 6 +#define DIMM_TYPE_MINI_CDIMM 7 +#define DIMM_TYPE_72B_SO_UDIMM 8 +#define DIMM_TYPE_72B_SO_RDIMM 9 +#define DIMM_TYPE_72B_SO_CDIMM 10 + +#if defined(CONFIG_DEBUG_RAM_SETUP) && (CONFIG_DEBUG_RAM_SETUP) +#define printram(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__) +#else +#define printram(x, ...) +#endif + +/* Different values for tCK, representing standard DDR3 frequencies + * As always, these values are in 1/256 ns units */ +#define TCK_1066MHZ 240 +#define TCK_800MHZ 320 +#define TCK_666MHZ 384 +#define TCK_533MHZ 480 +#define TCK_400MHZ 640 +#define TCK_333MHZ 768 +#define TCK_266MHZ 960 +#define TCK_200MHZ 1280 + +#define RAM_UNIT (1<<24) + +#include <stdint.h> + +/** + *\brief DIMM characteristics + * + * The characteristics of each DIMM, as presented by the SPD + */ +typedef struct dimm_attr_st { + u8 dram_type; + u16 cas_supported; + /* Number of ranks */ + u8 ranks; + /* Number or row address bits */ + u8 row_bits; + /* Number or column address bits */ + u8 col_bits; + /* Size of module in (1 << 24) bytes (16MB) */ + u16 size; + /* Latencies are in units of ns, scaled by x256 */ + u32 tCK; + u32 tAA; + u32 tWR; + u32 tRCD; + u32 tRRD; + u32 tRP; + u32 tRAS; + u32 tRC; + u32 tRFC; + u32 tWTR; + u32 tRTP; + u32 tFAW; + +} dimm_attr; + +typedef struct ramctr_timing_st { + u8 dram_type; + u16 cas_supported; + /* tLatencies are in units of ns, scaled by x256 */ + u32 tCK; + u32 tAA; + u32 tWR; + u32 tRCD; + u32 tRRD; + u32 tRP; + u32 tRAS; + u32 tRC; + u32 tRFC; + u32 tWTR; + u32 tRTP; + u32 tFAW; + /* Latencies in terms of clock cycles + * They are saved separately as they are needed for DRAM MRS commands*/ + u8 CAS; /* CAS read latency */ + u8 CWL; /* CAS write latency */ + u8 WR; /* write recovery time */ + +} ramctr_timing; + + +typedef u8 spd_raw_data[256]; + +/** + * \brief Decode the raw spd data + */ +void spd_decode_ddr3(dimm_attr *dimm, spd_raw_data spd_data); + +/** + * \brief Checks if the DIMM is Registered based on byte[3] of the spd + */ +int dimm_is_registered(u8 spd_byte3); + +/** + * \brief Print the info in dimm + */ +void dram_print_spd_ddr3(const dimm_attr *dimm); + +/** + * \brief Read double word from specified address + * + * Should be useful when doing an MRS to the DIMM + */ +u32 volatile_read(u32 addr); + +#endif /* DRAM_H */ diff --git a/src/devices/dram/dram_util.c b/src/devices/dram/dram_util.c new file mode 100644 index 0000000..75913e4 --- /dev/null +++ b/src/devices/dram/dram_util.c @@ -0,0 +1,297 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include "dram.h" +#include <console/console.h> +#include <device/device.h> + +u32 volatile_read(volatile u32 addr) +{ + volatile u32 result; + result = *(volatile u32*)addr; + return result; +} + +int dimm_is_registered(u8 spd_byte3) +{ + spd_byte3 &= 0x0f; + if( (spd_byte3 == DIMM_TYPE_RDIMM) + | (spd_byte3 == DIMM_TYPE_MINI_RDIMM) + | (spd_byte3 == DIMM_TYPE_72B_SO_RDIMM) ) + return 1; + + return 0; +} + +void spd_decode_ddr3(dimm_attr *dimm, spd_raw_data spd) +{ + int nCRC, i; + u16 crc, spd_crc; + u8 * ptr = spd; + u8 ftb_divisor; + u8 ftb_dividend; + u8 capacity_shift, bus_width, sdram_width; + u32 mtb; /* medium time base */ + /* Make sure that the spd dump is indeed from a DDR3 module */ + if(spd[2] != DRAM_TYPE_DDR3) + { + printram("Not a DDR3 SPD!\n"); + dimm->dram_type = DRAM_TYPE_UNDEFINED; + return; + } + dimm->dram_type = DRAM_TYPE_DDR3; + + /* Find the number of bytes covered by CRC */ + if(spd[0] & 0x80) { + nCRC = 117; + } else { + nCRC =126; + } + + /* Compute the CRC */ + crc = 0; + while (--nCRC >= 0) { + crc = crc ^ (int)*ptr++ << 8; + for (i = 0; i < 8; ++i) + if (crc & 0x8000) { + crc = crc << 1 ^ 0x1021; + } else { + crc = crc << 1; + } + } + /* Compare with the CRC in the SPD */ + spd_crc = (spd[127] << 8) + spd[126]; + /* Verify the CRC is correct */ + if(crc != spd_crc) + printram("SPD CRC failed!!!"); + + + unsigned int reg, val, param; + printram(" Revision: %x \n", spd[1] ); + printram(" Type : %x \n", spd[2] ); + printram(" Key : %x \n", spd[3] ); + + reg = spd[4]; + /* Number of memory banks */ + val = (reg >> 4) & 0x07; + if (val > 0x03) printram(" Invalid number of memory banks\n"); + param = 1 << (val + 3); + printram(" Banks : %u \n", param ); + /* SDRAM capacity */ + capacity_shift = reg & 0x0f; + if (capacity_shift > 0x06) printram(" Invalid module capacity\n"); + if (capacity_shift < 0x02) { + printram(" Capacity: %u Mb\n", 256 << capacity_shift); + } else { + printram(" Capacity: %u Gb\n", 1 << (capacity_shift - 2)); + } + + reg = spd[5]; + /* Row address bits */ + val = (reg >> 4) & 0x07; + if(val > 0x04) printram(" Invalid row address bits\n"); + dimm->row_bits = val + 12; + /* Column address bits */ + val = reg & 0x07; + if(val > 0x03) printram(" Invalid column address bits\n"); + dimm->col_bits = val + 9; + + /* Module nominal voltage */ + reg = spd[6]; + print_debug(" Supported voltages: "); + if(reg & (1<<2) ) print_debug("1.2V "); + if(reg & (1<<1) ) print_debug("1.35V "); + if( !(reg & (1<<0)) ) print_debug("1.5V "); + print_debug("\n"); + + /* Module organization */ + reg = spd[7]; + /* Number of ranks*/ + val = (reg >> 3) & 0x07; + if(val > 3) printram(" Invalid number of ranks\n"); + dimm->ranks = val + 1; + /* SDRAM device width */ + val = (reg & 0x07); + if(val > 3) printram(" Invalid SDRAM width\n"); + sdram_width = (4 << val); + printram(" SDRAM width : %u \n", sdram_width); + + /* Memory bus width */ + reg = spd[8]; + /* Bus extension */ + val = (reg >> 3) & 0x03; + if(val > 1) printram(" Invalid bus extension\n"); + printram(" Bus extension : %u bits\n", val?8:0); + /* Bus width */ + val = reg & 0x07; + if(val > 3) printram(" Invalid bus width\n"); + bus_width = 8 << val; + printram(" Bus width : %u \n", bus_width); + + /* We have all the info we need to compute the dimm size */ + /* Capacity is 256Mbit multiplied by the power of 2 specified in + * capacity_shift + * The rest is the JEDEC formula */ + /* I am certain it will fit in 16 bits + * Remember, It's in units of 2^24 bytes*/ + dimm->size = ( (1 << (capacity_shift + (25-24)) ) * bus_width + * dimm->ranks ) / sdram_width; + + /* Fine Timebase (FTB) Dividend/Divisor */ + /* Dividend */ + ftb_dividend = (spd[9] >> 4) & 0x0f; + /* Divisor */ + ftb_divisor = spd[9] & 0x0f; + + /* Medium Timebase = + * Medium Timebase (MTB) Dividend / + * Medium Timebase (MTB) Divisor */ + mtb = (((u32)spd[10]) << 8) / spd [11]; + + /* SDRAM Minimum Cycle Time (tCKmin) */ + dimm->tCK = spd[12] * mtb; + + /* CAS Latencies Supported */ + dimm->cas_supported = (spd[15] << 8) + spd[14]; + + /* Minimum CAS Latency Time (tAAmin) */ + dimm->tAA = spd[16] * mtb; + + /* Minimum Write Recovery Time (tWRmin) */ + dimm->tWR = spd[17] * mtb; + + /* Minimum RAS# to CAS# Delay Time (tRCDmin) */ + dimm->tRCD = spd[18] * mtb; + + /* Minimum Row Active to Row Active Delay Time (tRRDmin) */ + dimm->tRRD = spd[19] * mtb; + + /* Minimum Row Precharge Delay Time (tRPmin)*/ + dimm->tRP = spd[20] * mtb; + + /* Minimum Active to Precharge Delay Time (tRASmin) */ + dimm->tRAS = (((spd[21] & 0x0f) << 8) + spd[22]) * mtb; + + /* Minimum Active to Active/Refresh Delay Time (tRCmin) */ + dimm->tRC = (((spd[21] & 0xf0) << 4) + spd[23]) * mtb; + + /* Minimum Refresh Recovery Delay Time (tRFCmin) */ + dimm->tRFC = ((spd[25] << 8) + spd[24]) * mtb; + + /* Minimum Internal Write to Read Command Delay Time (tWTRmin) */ + dimm->tWTR = spd[26] * mtb; + + /* Minimum Internal Read to Precharge Command Delay Time (tRTPmin) */ + dimm->tRTP = spd[27] * mtb; + + /* Minimum Four Activate Window Delay Time (tFAWmin) */ + dimm->tFAW = (((spd[28] & 0x0f) << 8) + spd[29]) * mtb; + + /* SDRAM Optional Features */ + reg = spd[30]; + print_debug(" Optional features :"); + if(reg & 0x80) print_debug(" DLL-Off_mode"); + if(reg & 0x02) print_debug(" RZQ/7"); + if(reg & 0x01) print_debug(" RZQ/6"); + print_debug("\n"); + + /* SDRAM Thermal and Refresh Options */ + reg = spd[31]; + print_debug(" Thermal features :"); + if(reg & 0x80) print_debug(" PASR"); + if(reg & 0x08) print_debug(" ODTS"); + if(reg & 0x04) print_debug(" ASR"); + if(reg & 0x02) print_debug(" ext_temp_refresh"); + if(reg & 0x01) print_debug(" ext_temp_range"); + print_debug("\n"); + + /* Module Thermal Sensor */ + reg = spd[32]; + print_debug(" Thermal sensor : "); + if(reg & 0x80) print_debug("yes"); + else print_debug("no"); + print_debug("\n"); + + /* SDRAM Device Type */ + reg = spd[33]; + print_debug(" Standard SDRAM : "); + if(reg & 0x80) print_debug("no"); + else print_debug("yes"); + print_debug("\n"); + + /* Fine Offset for SDRAM Minimum Cycle Time (tCKmin) */ + //printram(" tCKmin FTB : %i \n", spd[34]); + /* Fine Offset for Minimum CAS Latency Time (tAAmin) */ + //printram(" tAAmin FTB : %i \n", spd[35]); + /* Fine Offset for Minimum RAS# to CAS# Delay Time (tRCDmin) */ + //printram(" tRCDmin FTB : %i \n", spd[36]); + /* Fine Offset for Minimum Row Precharge Delay Time (tRPmin) */ + //printram(" tRPmin FTB : %i \n", spd[37]); + /* Fine Offset for Minimum Active to Active/Refresh Delay Time (tRCmin) */ + //printram(" tRCmin FTB : %i \n", spd[38]); + + if(spd[60] & 0x01) + printram(" DIMM Address bits mirrorred!!!\n"); + +} + +static void print_ns(const char * msg, u32 val) +{ + u32 mant, fp; + mant = val/256; + fp = (val % 256) * 1000/256; + + printram("%s%3u.%.3u ns\n", msg, mant, fp); +} + +void dram_print_spd_ddr3(const dimm_attr *dimm) +{ + u16 val16; + int i; + + printram(" Row addr bits : %u \n", dimm->row_bits); + printram(" Column addr bits : %u \n", dimm->col_bits); + printram(" Number of ranks : %u \n", dimm->ranks); + printram(" DIMM Capacity : %u MB\n", dimm->size << 4); + + /* CAS Latencies Supported */ + val16 = dimm->cas_supported; + print_debug(" CAS latencies :"); + i = 0; + do{ + if(val16 & 1) printram(" %u", i + 4); + i++; + val16 >>= 1; + } while(val16); + print_debug("\n"); + + print_ns(" tCKmin : ", dimm->tCK); + print_ns(" tAAmin : ", dimm->tAA); + 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(" tFAWmin : ", dimm->tFAW); + +} diff --git a/src/devices/smbus/early_smbus.c b/src/devices/smbus/early_smbus.c new file mode 100644 index 0000000..4583aca --- /dev/null +++ b/src/devices/smbus/early_smbus.c @@ -0,0 +1,141 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +/** + * @file post_codes.h + * + * This file defines the implementations for the functions defined in smbus.h + * These are a generic SMBUS implementation, which should work with a majority + * of chipsets. + * They are marked weak so that they can be overridden by the chipset code if + * necessary. + */ + +#include "smbus.h" + +/** + * \brief Brief delay for SMBUS transactions + */ +__attribute__((weak)) +void __smbus_delay(void) +{ + inb(0x80); +} + +/** + * \brief Clear the SMBUS host status register + */ +__attribute__((weak)) +void __smbus_reset(u16 __smbus_io_base) +{ + outb(0xdf, SMBHSTSTAT); +} + +/** + * \brief Print an error, should it occur. If no error, just exit. + * + * @param host_status The data returned on the host status register after + * a transaction is processed. + * @param loops The number of times a transaction was attempted. + * @return 0 if no error occurred + * 1 if an error was detected + */ +__attribute__((weak)) +int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base) +{ + /* Check if there actually was an error. */ + if ((host_status == 0x00 || host_status == 0x40 || + host_status == 0x42) && (loops < SMBUS_TIMEOUT)) + return 0; + + if (loops >= SMBUS_TIMEOUT) + printsmbus("SMBus timeout\n"); + if (host_status & (1 << 4)) + printsmbus("Interrupt/SMI# was Failed Bus Transaction\n"); + if (host_status & (1 << 3)) + printsmbus("Bus error\n"); + if (host_status & (1 << 2)) + printsmbus("Device error\n"); + if (host_status & (1 << 1)) + printsmbus("Interrupt/SMI# completed successfully\n"); + if (host_status & (1 << 0)) + printsmbus("Host busy\n"); + return 1; +} + +/** + * \brief Checks if the SMBUS is currently busy with a transaction + */ +__attribute__((weak)) +int __smbus_is_busy(u16 __smbus_io_base) +{ + /* Check if bit 0 of the status register is 1 (busy) or 0 (ready) */ + return ( (inb(SMBHSTSTAT) & (1 << 0)) == 1); +} + +/** + * \brief Wait for the SMBUS to become ready to process a new transaction. + */ +__attribute__((weak)) +int __smbus_wait_until_ready(u16 __smbus_io_base) +{ + int loops; + + printsmbus("Waiting until SMBus ready\n"); + + /* Loop up to SMBUS_TIMEOUT times, waiting for bit 0 of the + * SMBus Host Status register to go to 0, indicating the operation + * was completed successfully. I don't remember why I did it this way, + * but I think it was because ROMCC was running low on registers */ + loops = 0; + while (__smbus_is_busy(__smbus_io_base) && loops < SMBUS_TIMEOUT) + ++loops; + + return __smbus_print_error(inb(SMBHSTSTAT), loops, __smbus_io_base); +} + +/** + * \brief Read a byte from the SMBUS. + * + * @param dimm The address location of the DIMM on the SMBus. + * @param offset The offset the data is located at. + */ +__attribute__((weak)) +u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base) +{ + u8 val; + + /* Initialize SMBUS sequence */ + __smbus_reset(__smbus_io_base); + /* Clear host data port. */ + outb(0x00, SMBHSTDAT0); + + __smbus_wait_until_ready(__smbus_io_base); + + /* Actual addr to reg format. */ + dimm = (dimm << 1); + dimm |= 1; /* read command */ + outb(dimm, SMBXMITADD); + outb(offset, SMBHSTCMD); + /* Start transaction, byte data read. */ + outb(0x48, SMBHSTCTL); + __smbus_wait_until_ready(__smbus_io_base); + + val = inb(SMBHSTDAT0); + return val; +} \ No newline at end of file diff --git a/src/devices/smbus/smbus.h b/src/devices/smbus/smbus.h new file mode 100644 index 0000000..820a0dd --- /dev/null +++ b/src/devices/smbus/smbus.h @@ -0,0 +1,99 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file post_codes.h + * + * This file defines the prototypes for several common SMBUS functions + * These functions are prefixed with __smbus_ so that they do not conflict with + * the dozens of similar (duplicated) implementations in many southbridges. + * + * As a last parameter, the SMBUS functions take a u16 value __smbus_io_base, + * which represents the base IO port for smbus transactions + */ + +#include <arch/io.h> + +/** + * \brief SMBUS IO ports in relation to the base IO port + */ +#define SMBHSTSTAT __smbus_io_base + 0x0 +#define SMBSLVSTAT __smbus_io_base + 0x1 +#define SMBHSTCTL __smbus_io_base + 0x2 +#define SMBHSTCMD __smbus_io_base + 0x3 +#define SMBXMITADD __smbus_io_base + 0x4 +#define SMBHSTDAT0 __smbus_io_base + 0x5 +#define SMBHSTDAT1 __smbus_io_base + 0x6 +#define SMBBLKDAT __smbus_io_base + 0x7 +#define SMBSLVCTL __smbus_io_base + 0x8 +#define SMBTRNSADD __smbus_io_base + 0x9 +#define SMBSLVDATA __smbus_io_base + 0xa + +#define SMBUS_TIMEOUT (100*1000*10) + +/** + * \brief printk macro for SMBUS debugging + */ +#if defined(CONFIG_DEBUG_SMBUS_SETUP) && (CONFIG_DEBUG_SMBUS_SETUP) +#define printsmbus(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__) +#else +#define printsmbus(x, ...) +#endif + +void __smbus_reset(u16 __smbus_io_base); +int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base); +int __smbus_is_busy(u16 __smbus_io_base); +int __smbus_wait_until_ready(u16 __smbus_io_base); +u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base); + +void __smbus_delay(void); + +#if defined(SMBUS_IO_BASE) && (SMBUS_IO_BASE != 0) + +__attribute__((always_inline, unused)) +static void smbus_reset(void) +{ + __smbus_reset(SMBUS_IO_BASE); +} + +__attribute__((always_inline, unused)) +static int smbus_is_busy(void) +{ + return __smbus_is_busy(SMBUS_IO_BASE); +} + +__attribute__((always_inline, unused)) +static int smbus_wait_until_ready(void) +{ + return __smbus_wait_until_ready(SMBUS_IO_BASE); +} + +__attribute__((always_inline, unused)) +static int smbus_print_error(u8 host_status, int loops) +{ + return __smbus_print_error(host_status, loops, SMBUS_IO_BASE); +} + +__attribute__((always_inline, unused)) +static u8 smbus_read_byte(u8 dimm, u8 offset) +{ + return __smbus_read_byte(dimm, offset, SMBUS_IO_BASE); +} + +#endif \ No newline at end of file diff --git a/src/mainboard/via/epia-m850/.directory b/src/mainboard/via/epia-m850/.directory new file mode 100644 index 0000000..8a30135 --- /dev/null +++ b/src/mainboard/via/epia-m850/.directory @@ -0,0 +1,4 @@ +[Dolphin] +ShowPreview=true +Timestamp=2011,8,1,4,33,6 +Version=2 diff --git a/src/mainboard/via/epia-m850/Kconfig b/src/mainboard/via/epia-m850/Kconfig new file mode 100644 index 0000000..ae46646 --- /dev/null +++ b/src/mainboard/via/epia-m850/Kconfig @@ -0,0 +1,47 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see http://www.gnu.org/licenses/. +## + +if BOARD_VIA_EPIA_M850 + +config BOARD_SPECIFIC_OPTIONS # dummy + def_bool y + select ARCH_X86 + select CPU_VIA_C7 + select NORTHBRIDGE_VIA_VX900 + select SUPERIO_FINTEK_F81865F + #select BOARD_HAS_FADT + #select HAVE_PIRQ_TABLE + #select HAVE_ACPI_TABLES + #select HAVE_OPTION_TABLE + select BOARD_ROMSIZE_KB_512 + #select RAMINIT_SYSINFO + +config MAINBOARD_DIR + string + default via/epia-m850 + +config MAINBOARD_PART_NUMBER + string + default "EPIA-M850" + +config IRQ_SLOT_COUNT + int + default 13 + +endif # BOARD_VIA_EPIA_M850 diff --git a/src/mainboard/via/epia-m850/Makefile.inc b/src/mainboard/via/epia-m850/Makefile.inc new file mode 100644 index 0000000..9dffc79 --- /dev/null +++ b/src/mainboard/via/epia-m850/Makefile.inc @@ -0,0 +1,21 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see http://www.gnu.org/licenses/. +## + +#romstage-y += ./../../../superio/fintek/f81865f/f81865f_early_serial.c + diff --git a/src/mainboard/via/epia-m850/chip.h b/src/mainboard/via/epia-m850/chip.h new file mode 100644 index 0000000..2ac4f12 --- /dev/null +++ b/src/mainboard/via/epia-m850/chip.h @@ -0,0 +1,21 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +extern struct chip_operations mainboard_ops; + +struct mainboard_config {}; diff --git a/src/mainboard/via/epia-m850/devicetree.cb b/src/mainboard/via/epia-m850/devicetree.cb new file mode 100644 index 0000000..34f2848 --- /dev/null +++ b/src/mainboard/via/epia-m850/devicetree.cb @@ -0,0 +1,62 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see http://www.gnu.org/licenses/. +## + +chip northbridge/via/vx900 # Northbridge + device lapic_cluster 0 on # APIC cluster + chip cpu/via/model_c7 # CPU ATOM (same as VIA C7) + device lapic 0 on end # APIC + end + end + device pci_domain 0 on + device pci 0.0 on end # AGP Bridge + device pci 0.1 on end # Error Reporting + device pci 0.2 on end # Host Bus Control + device pci 0.3 on end # Memory Controller + device pci 0.4 on end # Power Management + device pci 0.5 on end # Power Management + device pci 0.6 on end # Power Management + device pci 0.7 on end # V-Link Controller + device pci 1.0 on end # PCI Bridge + device pci 1.1 on end # Audio + device pci 3.0 on end # PCIE control + device pci 3.1 on end # PEX1 + device pci 3.2 on end # PEX2 + device pci 3.3 on end # PEX3 + device pci 3.4 on end # PEX4 + device pci f.0 on end # IDE/SATA + device pci 10.0 on end # USB 1.1 + device pci 10.1 on end # USB 1.1 + device pci 10.2 on end # USB 1.1 + device pci 10.3 on end # USB 1.1 + device pci 10.4 on end # USB 2.0 + device pci 11.0 on end # LPC + chip drivers/generic/generic # DIMM 0-0-0 + device i2c 50 on end + end + chip drivers/generic/generic # DIMM 0-0-1 + device i2c 51 on end + end + chip superio/fintek/f81865f # Super duper IO + end + device pci 11.7 on end # NB/SB control + device pci 13.0 on end # PCI Bridge + device pci 14.0 on end # HD Audio + end + +end diff --git a/src/mainboard/via/epia-m850/mainboard.c b/src/mainboard/via/epia-m850/mainboard.c new file mode 100644 index 0000000..572f7da --- /dev/null +++ b/src/mainboard/via/epia-m850/mainboard.c @@ -0,0 +1,25 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <device/device.h> +#include "chip.h" + +struct chip_operations mainboard_ops = { + CHIP_NAME("VIA EPIA-M850 Mainboard") +}; diff --git a/src/mainboard/via/epia-m850/romstage.c b/src/mainboard/via/epia-m850/romstage.c new file mode 100644 index 0000000..60dd1fb --- /dev/null +++ b/src/mainboard/via/epia-m850/romstage.c @@ -0,0 +1,76 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011-2012 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +/* + * Inspired from the EPIA-M700 + */ + +#include <stdint.h> +#include <device/pci_def.h> +#include <device/pci_ids.h> +#include <arch/io.h> +#include <device/pnp_def.h> +#include <arch/romcc_io.h> +#include <arch/hlt.h> +#include <console/console.h> +#include <lib.h> +#include "cpu/x86/bist.h" +#include <string.h> + +#include "northbridge/via/vx900/early_vx900.h" +#include "northbridge/via/vx900/raminit.h" +#include "superio/fintek/f81865f/f81865f_early_serial.c" + +#define SERIAL_DEV PNP_DEV(0x2e, 0) + +/* cache_as_ram.inc jumps to here. */ +void main(unsigned long bist) +{ + /* Enable multifunction bit for northbridge. + * This enables the PCI configuration spaces of D0F1 to D0F7 to be + * accessed */ + pci_write_config8(PCI_DEV(0, 0, 0), 0x4f, 0x01); + + f81865f_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE); + console_init(); + print_debug("Console initialized. \n"); + + /* Halt if there was a built-in self test failure. */ + report_bist_failure(bist); + + /* x86 cold boot I/O cmd. */ + enable_smbus(); + + /* If this works, then SMBUS is up and running */ + //dump_spd_data(); + + /* Now we can worry about raminit. + * This board only has DDR3, so no need to worry about which DRAM type + * to use */ + dimm_layout dimms = {{0x50, 0x51, SPD_END_LIST}}; + vx900_init_dram_ddr3(&dimms); + //ram_check(0, 640 * 1024); + //ram_check(1<<26, (1<<26) + 0x20); + ram_check(0, 0x80); + + print_debug("We passed RAM verify\n"); + + return; + +} diff --git a/src/northbridge/via/vx900/Kconfig b/src/northbridge/via/vx900/Kconfig new file mode 100644 index 0000000..60e7993 --- /dev/null +++ b/src/northbridge/via/vx900/Kconfig @@ -0,0 +1,23 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see http://www.gnu.org/licenses/. +## + +config NORTHBRIDGE_VIA_VX900 + bool + select HAVE_DEBUG_RAM_SETUP + select HAVE_DEBUG_SMBUS \ No newline at end of file diff --git a/src/northbridge/via/vx900/Makefile.inc b/src/northbridge/via/vx900/Makefile.inc new file mode 100644 index 0000000..ac04434 --- /dev/null +++ b/src/northbridge/via/vx900/Makefile.inc @@ -0,0 +1,35 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see http://www.gnu.org/licenses/. +## + +romstage-y += early_smbus.c +romstage-y += raminit_ddr3.c +romstage-y += raminit_ddr3_delay_calib.c +romstage-y += ./../../../devices/dram/dram_util.c +romstage-y += ./../../../devices/smbus/early_smbus.c +#romstage-y += ./../../../cpu/x86/lapic/apic_timer.c + +# Drivers for these devices already exist with the vx800 +# Use those instead of duplicating code + +driver-y += ./../vx800/northbridge.c +driver-y += ./../vx800/vga.c +driver-y += ./../vx800/lpc.c + +chipset_bootblock_inc += $(src)/northbridge/via/vx900/romstrap.inc +chipset_bootblock_lds += $(src)/northbridge/via/vx900/romstrap.lds diff --git a/src/northbridge/via/vx900/early_smbus.c b/src/northbridge/via/vx900/early_smbus.c new file mode 100644 index 0000000..aa6cec0 --- /dev/null +++ b/src/northbridge/via/vx900/early_smbus.c @@ -0,0 +1,191 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <device/pci_ids.h> +#include "early_vx900.h" + +#include <arch/io.h> +#include <arch/romcc_io.h> +#include <console/console.h> +#include <devices/dram/dram.h> + +__attribute__((unused)) +static void smbus_delays(int delays) +{ + while(delays--) __smbus_delay(); +} + + +/** + * Read a byte from the SMBus. + * + * @param dimm The address location of the DIMM on the SMBus. + * @param offset The offset the data is located at. + */ +u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base) +{ + u8 val; + + /* Initialize SMBUS sequence */ + smbus_reset(); + /* Clear host data port. */ + outb(0x00, SMBHSTDAT0); + + smbus_wait_until_ready(); + smbus_delays(50); + + /* Actual addr to reg format. */ + dimm = (dimm << 1); + dimm |= 1; /* read command */ + outb(dimm, SMBXMITADD); + outb(offset, SMBHSTCMD); + /* Start transaction, byte data read. */ + outb(0x48, SMBHSTCTL); + smbus_wait_until_ready(); + + val = inb(SMBHSTDAT0); + return val; +} + +void enable_smbus(void) +{ + device_t dev; + u8 reg8; + u16 __smbus_io_base = SMBUS_IO_BASE; + + /* Locate the Power Management control */ + dev = pci_locate_device(PCI_ID(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_VX900_LPC), 0); + + if (dev == PCI_DEV_INVALID) { + die("Power Managment Controller not found\n"); + } + + /* + * To use SMBus to manage devices on the system board, it is a must to + * enable SMBus function by setting + * PMU_RXD2[0] (SMBus Controller Enable) to 1. + * And set PMU_RXD0 and PMU_RXD1 (SMBus I/O Base) to an appropriate + * I/O port address, so that all registers in SMBus I/O port can be + * accessed. + */ + + reg8 = pci_read_config8(dev, 0xd2); + /* Enable SMBus controller */ + reg8 |= 1; + /* Set SMBUS clock from 128k source */ + reg8 |= 1<<2; + pci_write_config8(dev, 0xd2, reg8); + + reg8 = pci_read_config8(dev, 0x94); + /* SMBUS clock from divider of 14.318 MHz */ + reg8 &= ~(1<<7); + pci_write_config8(dev, 0x94, reg8); + + /* Set SMBus IO base */ + pci_write_config16(dev, 0xd0, SMBUS_IO_BASE); + + /* + * Initialize the SMBus sequence: + */ + /* Clear SMBus host status register */ + smbus_reset(); + /* Clear SMBus host data 0 register */ + outb(0x00, SMBHSTDAT0); + + /* Wait for SMBUS */ + smbus_wait_until_ready(); + +} + +void spd_read(u8 addr, spd_raw_data spd) +{ + u8 reg; + int i, regs; + reg = smbus_read_byte(addr, 2); + if(reg != 0x0b) + { + printk(BIOS_DEBUG, "SMBUS device %x not a DDR3 module\n", addr); + spd[2] = 0; + return; + } + + reg = smbus_read_byte(addr, 0); + reg &= 0xf; + if (reg == 0x3) { + regs = 256; + } else if (reg == 0x2) { + regs = 176; + } else if (reg == 0x1) { + regs = 128; + } else { + printk(BIOS_INFO, "No DIMM present at %x\n", addr); + spd[2] = 0; + return; + } + printk(BIOS_DEBUG, "SPD Data for DIMM %x \n", addr); + for (i = 0; i < regs; i++) { + reg = smbus_read_byte(addr, i); + //printk(BIOS_DEBUG, " Offset %u = 0x%x \n", i, reg ); + spd[i] = reg; + } +} + +void dump_spd_data(void) +{ + int dimm, offset, regs; + unsigned int reg; + spd_raw_data spd; + dimm_attr dimmx; + + for (dimm = 0x50; dimm < 0x52; dimm++) { + reg = smbus_read_byte(dimm, 2); + if(reg != 0x0b) + { + printk(BIOS_DEBUG, + "SMBUS device %x not a DDR3 module\n", dimm); + continue; + } + + reg = smbus_read_byte(dimm, 0); + reg &= 0xf; + if (reg == 0x3) { + regs = 256; + } else if (reg == 0x2) { + regs = 176; + } else if (reg == 0x1) { + regs = 128; + } else { + printk(BIOS_INFO, "No DIMM present at %x\n", dimm); + regs = 0; + continue; + } + printk(BIOS_DEBUG, "SPD Data for DIMM %x \n", dimm); + for (offset = 0; offset < regs; offset++) { + reg = smbus_read_byte(dimm, offset); + //printk(BIOS_DEBUG, " Offset %u = 0x%x \n", offset, reg ); + spd[offset] = reg; + } + + spd_decode_ddr3(&dimmx, spd); + dram_print_spd_ddr3(&dimmx); + + } +} + diff --git a/src/northbridge/via/vx900/early_vx900.h b/src/northbridge/via/vx900/early_vx900.h new file mode 100644 index 0000000..6b33077 --- /dev/null +++ b/src/northbridge/via/vx900/early_vx900.h @@ -0,0 +1,35 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef EARLY_VX900_H +#define EARLY_VX900_H + +#include <stdint.h> + +#define SMBUS_IO_BASE 0x500 +#include <devices/smbus/smbus.h> + + +#include "raminit.h" + +void enable_smbus(void); +void dump_spd_data(void); +void spd_read(u8 addr, spd_raw_data spd); + +#endif /* EARLY_VX900_H */ diff --git a/src/northbridge/via/vx900/forgotten.c b/src/northbridge/via/vx900/forgotten.c new file mode 100644 index 0000000..54cb88f --- /dev/null +++ b/src/northbridge/via/vx900/forgotten.c @@ -0,0 +1,192 @@ +#include "forgotten.h" +#include <../../dram/dram.h> + +u16 ddr3_get_mr0( + char precharge_pd, + u8 write_recovery, + char dll_reset, + char mode, + u8 cas, + char interleaved_burst, + u8 burst_lenght +){ + u32 cmd = 0; + if(precharge_pd) cmd |= (1 << 12); + /* Write recovery */ + cmd |= ( ((write_recovery - 4) & 0x7) << 9); + if(dll_reset) cmd |= (1 << 8); + if(mode) cmd |= (1<<7); + /* CAS latency) */ + cmd |= ( ((cas - 4) & 0x7) << 4); + if(interleaved_burst) cmd |= (1 << 3); + /* Burst lenght */ + cmd |= (burst_lenght & 0x3); + return cmd; +} + +u16 ddr3_get_mr1( + char q_off_disable, + char tdqs, + u8 rtt_nom, + char write_leveling, + u8 output_drive_strenght, + u8 additive_latency, + u8 dll_disable +){ + u32 cmd = 0; + if(q_off_disable) cmd |= (1 << 12); + if(tdqs) cmd |= (1 << 11); + /* rtt_nom */ + cmd |= ( ((rtt_nom & 4) << (9-2)) | ((rtt_nom & 2) << (6-1)) + | ((rtt_nom & 1) << 2)); + if(write_leveling) cmd |= (1 << 7); + /* output drive strenght */ + cmd |= ( ((output_drive_strenght & 2) << (5-1)) + | ((output_drive_strenght & 1) << 1) ); + /* Additive latency */ + cmd |= ((additive_latency & 0x3) << 3); + if(dll_disable) cmd |= (1 << 0); + return cmd; +} + +u16 ddr3_get_mr2( + u8 rtt_wr, + char extended_temp, + char auto_self_refresh, + u8 cas_write +){ + u32 cmd = 0; + /* Rtt_wr */ + cmd |= ( (rtt_wr & 0x3) << 9); + if(extended_temp) cmd |= (1 << 7); + if(auto_self_refresh) cmd |= (1 << 6); + /* CAS write latency */ + cmd |= ( ((cas_write - 5) & 0x7) << 3); + return cmd; +} + +u16 ddr3_get_mr3(char dataflow_from_mpr) +{ + u32 cmd = 0; + if(dataflow_from_mpr) cmd |= (1<<2); + return cmd; +} + +/* + * Translate the MRS command into the memory address corresponding to the + * command. This is based on the CPU address to memory address mapping described + * by the initial values of registers 0x52 and 0x53, so do not fuck with them + * until after the MRS commands have been sent to all ranks + */ + +u32 vx900_get_mrs_addr(u8 mrs_type, u16 cmd); + +u32 vx900_get_mrs_addr(u8 mrs_type, u16 cmd) +{ + u32 addr = 0; + /* A3 <-> MA0, A4 <-> MA1, ... A12 <-> MA9 */ + addr |= ((cmd &0x3ff)<< 3); + /* A20 <-> MA10 */ + addr |= (((cmd >> 10) & 0x1) << 20); + /* A13 <-> MA11, A14 <-> MA12 */ + addr |= (((cmd >> 11) & 0x3) << 13); + + /* Do not fuck with registers 0x52 and 0x53 if you want the following + * mappings to work for you: + * A17 <-> BA0, A18 <-> BA1, A19 <-> BA2 */ + addr |= ((mrs_type & 0x7) << 17); + return addr; +} +void vx900_dram_ddr3_init_rank(device_t mcu, const ramctr_timing *ctrl, + int rank); +void vx900_dram_ddr3_init_rank(device_t mcu, const ramctr_timing *ctrl, + int rank) +{ + u8 reg8; + u32 reg32, cmd, res, addr; + int i; + + printram("Initializing rank %u\n", rank); + + /* Step 06 - Set Fun3_RX6B[2:0] to 001b (NOP Command Enable). */ + reg8 = pci_read_config8(mcu, 0x6b); + reg8 &= ~(0x03); + reg8 |= (1<<0); + pci_write_config8(mcu, 0x6b, reg8); + /* Step 07 - Read a double word from any address of the DIMM. */ + reg32 = volatile_read(0x0); + udelay(10); + printram("We just read 0x%x\n", reg32); + /* Step 08 - Set Fun3_RX6B[2:0] to 011b (MSR Enable). */ + reg8 = pci_read_config8(mcu, 0x6b); + reg8 &= ~(0x03); + reg8 |= (3<<0); + pci_write_config8(mcu, 0x6b, reg8); + + /* Step 09 – Issue MR2 cycle. Read a double word from the address depended + * on DRAM’s Rtt_WR and CWL settings. + * (Check the byte 63 of SPD. If memory address lines of target physical + * rank is mirrored, MA3~MA8 and BA0~BA1 should be swapped.) */ + cmd = ddr3_get_mr2(DDR3_MR2_RTT_WR_OFF,0,0,ctrl->CWL); + addr = vx900_get_mrs_addr(2, cmd); + if(addr != 0x00040040){ + printram("MR2 needed at addr 0x%.8x, but done at 0x%.8x\n", 0x00040040, addr); + addr = 0x00040040; + } + res = volatile_read(addr); + udelay(10); + printram("MR2 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res); + + /* Step 10 – Issue MR3 cycle - set DRAM to normal operation mode. + */ + cmd = ddr3_get_mr3(0); + addr = vx900_get_mrs_addr(3, cmd); + res = volatile_read(addr); + udelay(10); + printram("MR3 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res); + + /* Step 11 –Issue MR1 cycle + * Set DRAM’s output driver impedance, and Rtt_Nom settings. + * * The DLL enable field, TDQS field, write leveling enable field, + * additive latency field, and Qoff field should be set to 0. + */ + cmd = ddr3_get_mr1(DDR3_MR1_QOFF_ENABLE, DDR3_MR1_TQDS_DISABLE, + DDR3_MR1_RTT_NOM_RZQ2, + DDR3_MR1_WRITE_LEVELING_DISABLE, DDR3_MR1_ODS_RZQ7, + DDR3_MR1_AL_DISABLE, DDR3_MR1_DLL_ENABLE); + addr = vx900_get_mrs_addr(1, cmd); + res = volatile_read(addr); + udelay(10); + printram("MR1 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res); + + /* Step 12 - Issue MR0 cycle. + * Set DRAM’s burst length, CAS latency and write recovery settings. + * * The read burst type field should be set to interleave. + * * The mode field should be set to normal mode. + * * The DLL reset field should be set to No. + * * The DLL control for precharge PD field should be set to Fast exit. + */ + cmd = ddr3_get_mr0(DDR3_MR0_PRECHARGE_FAST, ctrl->WR, + DDR3_MR0_DLL_RESET_NO, DDR3_MR0_MODE_NORMAL, + ctrl->CAS, DDR3_MR0_BURST_TYPE_INTERLEAVED, 1); + addr = vx900_get_mrs_addr(0, cmd); + if(addr != 0x000069c8){ + printram("MR0 needed at addr 0x%.8x, but done at 0x%.8x\n", 0x000069c8, addr); + addr = 0x000069c8; + } + res = volatile_read(addr); + printram("MR0 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res); + + /* Step 13 - Set Rx6B[2:0] to 110b (Long ZQ calibration command). */ + reg8 = pci_read_config8(mcu, 0x6b); + reg8 &= ~(0x03); + reg8 |= (3<<1); + pci_write_config8(mcu, 0x6b, reg8); + for(i = 0; i < 1000; i++) inb(0x80); + + + /* Step 14 - Read a double word from any address of the DIMM. */ + reg32 = volatile_read(0x0); + printram("We just read 0x%x\n", reg32); +} + diff --git a/src/northbridge/via/vx900/forgotten.h b/src/northbridge/via/vx900/forgotten.h new file mode 100644 index 0000000..43641f7 --- /dev/null +++ b/src/northbridge/via/vx900/forgotten.h @@ -0,0 +1,78 @@ +#ifndef REDUNDANT_H +#define REDUNDANT_H + +#define DDR3_MR0_PRECHARGE_SLOW 0 +#define DDR3_MR0_PRECHARGE_FAST 1 +#define DDR3_MR0_MODE_NORMAL 0 +#define DDR3_MR0_MODE_TEST 1 +#define DDR3_MR0_DLL_RESET_NO 0 +#define DDR3_MR0_DLL_RESET_YES 1 +#define DDR3_MR0_BURST_TYPE_SEQUENTIAL 0 +#define DDR3_MR0_BURST_TYPE_INTERLEAVED 1 +#define DDR3_MR0_BURST_LENGTH_FIXED_8 0 +#define DDR3_MR0_BURST_LENGTH_CHOP 1 +#define DDR3_MR0_BURST_LENGTH_FIXED_4 2 +/** + * \brief Get command address for a DDR3 MR0 command + */ +u16 ddr3_get_mr0( + char precharge_pd, + u8 write_recovery, + char dll_reset, + char mode, + u8 cas, + char interleaved_burst, + u8 burst_lenght +); + +#define DDR3_MR1_TQDS_DISABLE 0 +#define DDR3_MR1_TQDS_ENABLE 1 +#define DDR3_MR1_QOFF_ENABLE 0 +#define DDR3_MR1_QOFF_DISABLE 1 +#define DDR3_MR1_WRITE_LEVELING_DISABLE 0 +#define DDR3_MR1_WRITE_LEVELING_ENABLE 1 +#define DDR3_MR1_RTT_NOM_OFF 0 +#define DDR3_MR1_RTT_NOM_RZQ4 1 +#define DDR3_MR1_RTT_NOM_RZQ2 2 +#define DDR3_MR1_RTT_NOM_RZQ6 3 +#define DDR3_MR1_RTT_NOM_RZQ12 4 +#define DDR3_MR1_RTT_NOM_RZQ8 5 +#define DDR3_MR1_AL_DISABLE 0 +#define DDR3_MR1_AL_CL_MINUS_1 1 +#define DDR3_MR1_AL_CL_MINUS_2 2 +#define DDR3_MR1_ODS_RZQ6 0 +#define DDR3_MR1_ODS_RZQ7 1 +#define DDR3_MR1_DLL_ENABLE 0 +#define DDR3_MR1_DLL_DISABLE 1 +/** + * \brief Get command address for a DDR3 MR1 command + */ +u16 ddr3_get_mr1( + char q_off, + char tdqs, + u8 rtt_nom, + char write_leveling, + u8 output_drive_strenght, + u8 additive_latency, + u8 dll_disable +); + +#define DDR3_MR2_RTT_WR_OFF 0 +#define DDR3_MR2_RTT_WR_RZQ4 1 +#define DDR3_MR2_RTT_WR_RZQ2 2 +/** + * \brief Get command address for a DDR3 MR2 command + */ +u16 ddr3_get_mr2( + u8 rtt_wr, + char extended_temp, + char auto_self_refresh, + u8 cas_write +); + +/** + * \brief Get command address for a DDR3 MR3 command + */ +u16 ddr3_get_mr3(char dataflow_from_mpr); + +#endif /* REDUNDANT_H */ \ No newline at end of file diff --git a/src/northbridge/via/vx900/raminit.h b/src/northbridge/via/vx900/raminit.h new file mode 100644 index 0000000..5c606c6 --- /dev/null +++ b/src/northbridge/via/vx900/raminit.h @@ -0,0 +1,77 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef RAMINIT_VX900_H +#define RAMINIT_VX900_H + +#include <devices/dram/dram.h> + +/* The maximum number of DIMM slots that the VX900 supports */ +#define VX900_MAX_DIMM_SLOTS 2 + +#define VX900_MAX_MEM_RANKS 4 + + +#define SPD_END_LIST 0xff + +typedef struct dimm_layout_st +{ + /* The address of the DIMM on the SMBUS * + * 0xFF to terminate the array*/ + u8 spd_addr[VX900_MAX_DIMM_SLOTS + 1]; +} dimm_layout; + +typedef struct dimm_info_st +{ + dimm_attr dimm[VX900_MAX_DIMM_SLOTS]; +} dimm_info; + +typedef struct mem_rank_st { + u16 start_addr; + u16 end_addr; +}mem_rank; + +typedef struct rank_layout_st { + u32 phys_rank_size[VX900_MAX_MEM_RANKS]; + mem_rank virt[VX900_MAX_MEM_RANKS]; +} rank_layout; + +typedef struct pci_reg8_st { + u8 addr; + u8 val; +} pci_reg8; + +typedef u8 timing_dly[8]; + +typedef struct delay_range_st { + timing_dly low; + timing_dly avg; + timing_dly high; +} delay_range; + +typedef struct vx900_delay_calib_st { + delay_range rx_dqi_cr; + delay_range rx_dqsi; + delay_range tx_dqo; + delay_range tx_dqso; +} vx900_delay_calib; + +void vx900_init_dram_ddr3(const dimm_layout *dimms); + +#endif /* RAMINIT_VX900_H */ diff --git a/src/northbridge/via/vx900/raminit_ddr3.c b/src/northbridge/via/vx900/raminit_ddr3.c new file mode 100644 index 0000000..ea8513e --- /dev/null +++ b/src/northbridge/via/vx900/raminit_ddr3.c @@ -0,0 +1,1027 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011-2012 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <arch/io.h> +#include <arch/romcc_io.h> + +#include "early_vx900.h" +#include "raminit.h" + +#include <string.h> +#include <console/console.h> +#include <device/pci_ids.h> + +#include <delay.h> + +#define min(a,b) a<b?a:b +#define max(a,b) a>b?a:b + +#define MCU PCI_DEV(0, 0, 3) + + +/* FIXME: Really Alex, is this all you can come up with? */ +void udelay(unsigned usecs) +{ + int i; + for(i = 0; i < usecs; i++) + inb(0x80); +} + +/* Registers 0x78 -> 0x7f contain calibration the settings for DRAM IO timing + * The dataset in these registers is selected from 0x70. + * Once the correct dataset is selected the delays can be altered. + * mode refers to TxDQS, TxDQ, RxDQS, or RxCR + * setting refers to either manual, average, upper bound, or lower bound + */ +#define VX900_CALIB_TxDQSO 0 +#define VX900_CALIB_TxDQO 1 +#define VX900_CALIB_RxDQSI 2 +#define VX900_CALIB_RxDQI_CR 3 + +#define VX900_CALIB_AVERAGE 0 +#define VX900_CALIB_LOWER 1 +#define VX900_CALIB_UPPER 2 +#define VX900_CALIB_MANUAL 4 + +static void vx900_delay_calib_mode_select(u8 delay_type, u8 bound) +{ + /* Which calibration setting */ + u8 reg8 = (delay_type & 0x03) << 2; + /* Upper, lower, average, or manual setting */ + reg8 |= (bound & 0x03); + pci_write_config8(MCU, 0x70, reg8); +} + +static void vx900_read_0x78_0x7f(timing_dly dly) +{ + *((u32*) (&(dly[0]))) = pci_read_config32(MCU, 0x78); + *((u32*) (&(dly[4]))) = pci_read_config32(MCU, 0x7c); +} + +static void vx900_write_0x78_0x7f(const timing_dly dly) +{ + pci_write_config32(MCU, 0x78, *((u32*) (&(dly[0]))) ); + pci_write_config32(MCU, 0x7c, *((u32*) (&(dly[4]))) ); +} + +static void vx900_read_delay_range(delay_range *d_range, u8 mode) +{ + vx900_delay_calib_mode_select(mode, VX900_CALIB_LOWER); + vx900_read_0x78_0x7f(d_range->low); + vx900_delay_calib_mode_select(mode, VX900_CALIB_AVERAGE); + vx900_read_0x78_0x7f(d_range->avg); + vx900_delay_calib_mode_select(mode, VX900_CALIB_UPPER); + vx900_read_0x78_0x7f(d_range->high); +} + +static void dump_delay(const timing_dly dly) +{ + u8 i; + for(i = 0; i < 8; i ++) + { + printram(" %.2x", dly[i]); + } + printram("\n"); +} + +static void dump_delay_range(const delay_range d_range) +{ + printram("Lower limit: "); + dump_delay(d_range.low); + printram("Average: "); + dump_delay(d_range.avg); + printram("Upper limit: "); + dump_delay(d_range.high); +} + +/* These are some "safe" values that can be used for memory initialization. + * Some will stay untouched, and others will be overwritten later on + * YOU REALLY NEED THE DATASHEET TO UNDERSTAND THESE !!! */ +static pci_reg8 mcu_init_config[] = { + {0x40, 0x01}, /* Virtual rank 0 ending address = 64M - 1 */ + {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00}, /* Virtual Ranks ending */ + {0x48, 0x00}, /* Virtual rank 0 starting address = 0 */ + {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00}, /* Virtual Ranks beginning */ + {0x50, 0xd8}, /* Set ranks 0-3 to 11 col bits, 16 row bits */ + {0x52, 0x33}, /* Map BA0 to A17, BA1 to A18 */ + {0x53, 0x5b}, /* Map BA2 to A19, FIXME: check RA0/RA1 */ + /* Disable all virtual ranks */ + {0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00}, + /* Disable rank interleaving in ranks 0-3 */ + {0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00}, + {0x6c, 0xA0}, /* Memory type: DDR3, VDIMM: 1.5V, 64-bit DRAM */ + {0xc4, 0x80}, /* Enable 8 memory banks */ + {0xc6, 0x80}, /* Minimum latency from self-refresh. Bit [7] must be 1 */ + {0xc8, 0x80}, /* Enable automatic triggering of short ZQ calibration */ + {0x99, 0xf0}, /* Power Management and Bypass Reorder Queue */ + /* Enable differential DQS; MODT assertion values suggested in DS */ + {0x9e, 0xa1}, {0x9f, 0x51}, + /* DQ/DQM Duty Control - Do not put any extra delays*/ + {0xe9, 0x00}, {0xea, 0x00}, {0xeb, 0x00}, {0xec, 0x00}, + {0xed, 0x00}, {0xee, 0x00}, {0xef, 0x00}, + {0xfc, 0x00}, {0xfd, 0x00}, {0xfe, 0x00}, {0xff, 0x00}, + /* The following parameters we may or may not change */ + {0x61, 0x2e}, /* DRAMC Pipeline Control */ + {0x77, 0x10}, /* MDQS Output Control */ + + /* The following are parameters we'll most likely never change again */ + {0x60, 0xf4}, /* DRAM Pipeline Turn-Around Setting */ + {0x65, 0x49}, /* DRAM Arbitration Bandwidth Timer - I */ + {0x66, 0x80}, /* DRAM Queue / Arbitration */ + {0x69, 0xc6}, /* Bank Interleave Control */ + {0x6a, 0xfc}, /* DRAMC Request Reorder Control */ + {0x6e, 0x38}, /* Burst lenght: 8, burst-chop: enable */ + {0x73, 0x04}, /* Close All Pages Threshold */ + + /* The following need to be dynamically asserted */ + /* See: check_special_registers.c */ + {0x74, 0xa0}, /* Yes, same 0x74; add one more T */ + {0x76, 0x60}, /* Write Data Phase Control */ + +}; + +/* This table keeps the driving strength control setting that we can safely use + * doring initialization. */ +static pci_reg8 mcu_drv_ctrl_config[] = { + {0xd3, 0x01}, /* Enable auto-compensation circuit for ODT strength */ + {0xd4, 0x80}, /* Set internal ODT to dynamically turn on or off */ + {0xd6, 0x20}, /* Enable strong driving for MA and DRAM commands*/ + {0xd0, 0x88}, /* (ODT) Strength ?has effect? */ + {0xe0, 0x88}, /* DRAM Driving – Group DQS (MDQS) */ + {0xe1, 0x00}, /* Disable offset mode for driving strength control */ + {0xe2, 0x88}, /* DRAM Driving – Group DQ (MD, MDQM) */ + {0xe4, 0xcc}, /* DRAM Driving – Group CSA (MCS, MCKE, MODT) */ + {0xe8, 0x88}, /* DRAM Driving – Group MA (MA, MBA, MSRAS, MSCAS, MSWE)*/ + {0xe6, 0xff}, /* DRAM Driving – Group DCLK0 (DCLK[2:0] for DIMM0) */ + {0xe7, 0xff}, /* DRAM Driving – Group DCLK1 (DCLK[5:3] for DIMM1) */ + {0xe4, 0xcc}, /* DRAM Driving – Group CSA (MCS, MCKE, MODT)*/ + {0x91, 0x08}, /* MCLKO Output Phase Delay - I */ + {0x92, 0x08}, /* MCLKO Output Phase Delay - II */ + {0x93, 0x16}, /* CS/CKE Output Phase Delay */ + {0x95, 0x16}, /* SCMD/MA Output Phase Delay */ + {0x9b, 0x3f}, /* Memory Clock Output Enable */ +}; + +static void vx900_dram_write_init_config(void) +{ + size_t i; + for(i = 0; i < (sizeof(mcu_init_config)/sizeof(pci_reg8)); i++) + { + pci_write_config8(MCU, mcu_init_config[i].addr, + mcu_init_config[i].val); + } +} + +static void dram_find_spds_ddr3(const dimm_layout *addr, dimm_info *dimm) +{ + size_t i = 0; + int dimms = 0; + do { + spd_raw_data spd; + spd_read(addr->spd_addr[i], spd); + spd_decode_ddr3(&dimm->dimm[i], spd); + if(dimm->dimm[i].dram_type != DRAM_TYPE_DDR3) continue; + dimms++; + dram_print_spd_ddr3(&dimm->dimm[i]); + } while(addr->spd_addr[++i] != SPD_END_LIST + && i < VX900_MAX_DIMM_SLOTS); + + if(!dimms) + die("No DIMMs were found"); +} + +static void dram_find_common_params(const dimm_info *dimms, ramctr_timing *ctrl) +{ + size_t i, valid_dimms; + memset(ctrl, 0, sizeof(ramctr_timing)); + ctrl->cas_supported = 0xff; + valid_dimms = 0; + for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++) + { + const dimm_attr *dimm = &dimms->dimm[i]; + if(dimm->dram_type == DRAM_TYPE_UNDEFINED) continue; + valid_dimms++; + + if(valid_dimms == 1) { + /* First DIMM defines the type of DIMM */ + ctrl->dram_type = dimm->dram_type; + } else { + /* Check if we have mismatched DIMMs */ + if(ctrl->dram_type != dimm->dram_type) + die("Mismatched DIMM Types"); + } + /* Find all possible CAS combinations */ + ctrl->cas_supported &= dimm->cas_supported; + + /* Find the smallest common latencies supported by all DIMMs */ + ctrl->tCK = max(ctrl->tCK, dimm->tCK ); + ctrl->tAA = max(ctrl->tAA, dimm->tAA ); + ctrl->tWR = max(ctrl->tWR, dimm->tWR ); + ctrl->tRCD = max(ctrl->tRCD, dimm->tRCD); + ctrl->tRRD = max(ctrl->tRRD, dimm->tRRD); + ctrl->tRP = max(ctrl->tRP, dimm->tRP ); + ctrl->tRAS = max(ctrl->tRAS, dimm->tRAS); + ctrl->tRC = max(ctrl->tRC, dimm->tRC ); + ctrl->tRFC = max(ctrl->tRFC, dimm->tRFC); + ctrl->tWTR = max(ctrl->tWTR, dimm->tWTR); + ctrl->tRTP = max(ctrl->tRTP, dimm->tRTP); + ctrl->tFAW = max(ctrl->tFAW, dimm->tFAW); + + } + + if(!ctrl->cas_supported) die("Unsupported DIMM combination. " + "DIMMS do not support common CAS latency"); + if(!valid_dimms) die("No valid DIMMs found"); +} + +static void vx900_dram_phys_bank_range(const dimm_info *dimms, + rank_layout *ranks) +{ + size_t i; + //u8 reg8; + for(i = 0; i < VX900_MAX_DIMM_SLOTS; i ++) + { + if(dimms->dimm[i].dram_type == DRAM_TYPE_UNDEFINED) + continue; + u8 nranks = dimms->dimm[i].ranks; + if(nranks > 2) + die("Found DIMM with more than two ranks, which is not " + "supported by this chipset"); + u32 size = dimms->dimm[i].size; + if(nranks == 2) { + /* Each rank holds half the capacity of the DIMM */ + size >>= 1; + ranks->phys_rank_size[i<<1] = size; + ranks->phys_rank_size[(i<<1) | 1] = size; + } else { + /* Otherwise, everything is held in the first bank */ + ranks->phys_rank_size[i<<1] = size; + ranks->phys_rank_size[(i<<1) | 1] = 0;; + } + } +} + +static void vx900_dram_driving_ctrl(const dimm_info *dimm) +{ + size_t i, ndimms; + u8 val; + + /* For ODT range selection, datasheet recommends + * when 1 DIMM present: 60 Ohm + * when 2 DIMMs present: 120 Ohm */ + ndimms = 0; + for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++) { + if(dimm->dimm[i].dram_type == DRAM_TYPE_DDR3) ndimms++; + } + val = (ndimms > 1) ? 0x0 : 0x1; + pci_write_config8(MCU, 0xd5, val << 2); + + + /* FIXME: Assert dynamically based on dimm config */ + /* DRAM ODT Lookup Table*/ + pci_write_config8(MCU, 0x9c, 0xe4); + + for(i = 0; i < (sizeof(mcu_drv_ctrl_config)/sizeof(pci_reg8)); i++) + { + pci_write_config8(MCU, mcu_drv_ctrl_config[i].addr, + mcu_drv_ctrl_config[i].val); + } +} + +static void vx900_clear_vr_map(void) +{ + /* Disable all ranks */ + pci_write_config16(MCU, 0x54, 0x0000); +} + +static void vx900_pr_map_all_vr3(void) +{ + /* Enable all ranks and set them to VR3 */ + pci_write_config16(MCU, 0x54, 0xbbbb); +} +/* Map physical rank pr to virtual rank vr */ +static void vx900_map_pr_vr(u8 pr, u8 vr) +{ + pr &= 0x3; vr &= 0x3; + /* Enable rank (bit [3], and set the VR number bits [1:0] */ + u16 val = 0x8 | vr; + /* Now move the value to the appropriate PR */ + val <<= (pr * 4); + pci_mod_config16(MCU, 0x54, 0xf << (pr * 4), val); + printram("Mapping PR %u to VR %u\n", pr, vr); +} + +static void vx900_dram_range(ramctr_timing *ctrl, rank_layout *ranks) +{ + size_t i, vrank = 0; + u8 reg8; + u32 ramsize = 0; + vx900_clear_vr_map(); + for(i = 0; i < VX900_MAX_MEM_RANKS; i++) + { + u32 rank_size = ranks->phys_rank_size[i]; + if(!rank_size) continue; + ranks->virt[vrank].start_addr = ramsize; + ramsize += rank_size; + ranks->virt[vrank].end_addr = ramsize; + + /* Physical to virtual rank mapping */ + /* Rank memory range */ + reg8 = (ranks->virt[vrank].start_addr >> 2); + pci_write_config8(MCU, 0x48 + vrank, reg8); + reg8 = (ranks->virt[vrank].end_addr >> 2); + pci_write_config8(MCU, 0x40 + vrank, reg8); + + vx900_map_pr_vr(i, vrank); + + printram("Mapped Physical rank %u, to virtual rank %u\n" + " Start address: 0x%.8x000000\n" + " End address: 0x%.8x000000\n", + (int) i, (int) vrank, + ranks->virt[vrank].start_addr, + ranks->virt[vrank].end_addr); + + /* Move on to next virtual rank */ + vrank++; + } + + printram("Initialized %u virtual ranks, with a total size of %u MB\n", + (int) vrank, ramsize << 4); +} + +static u8 vx900_get_CWL(u8 CAS) +{ + /* Get CWL based on CAS using the following rule: + * _________________________________________ + * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T | + * CWL: | 5T | 5T | 5T | 6T | 6T | 7T | 7T | 8T | + */ + static const u8 cas_cwl_map[] = {5, 5, 5, 6, 6, 7, 7, 8}; + if(CAS > 11) return 8; /* FIXME: Should this be an error condition? */ + return cas_cwl_map[CAS - 4]; +} + +static void vx900_dram_timing(ramctr_timing *ctrl) +{ + /* Here we are calculating latencies, and writing them to the appropiate + * registers. Some registers do not take latencies from 0T, for example: + * CAS: 000 = 4T, 001 = 5T, 010 = 6T, etc + * In this example we subtract 4T from the result for CAS: (val - 4) + * The & 0x07 after (val - T0) just makes sure that, no matter what + * crazy thing may happen, we do not write outside the bits allocated + * in the register */ + u8 reg8, val, tFAW, tRRD; + u32 val32; + + /* Maximum supported DDR3 frequency is 533MHz (DDR3 1066) + * so make sure we cap it if we have faster DIMMs */ + if(ctrl->tCK < TCK_533MHZ) ctrl->tCK = TCK_533MHZ; + val32 = (1000 << 8) / ctrl->tCK; + printram("Selected DRAM frequency: %u MHz\n", val32); + + /* Now find the right DRAM frequency setting, + * and bring it to the closest JEDEC standard frequency */ + if(ctrl->tCK <= TCK_533MHZ) {val = 0x07; ctrl->tCK = TCK_533MHZ;} + else if(ctrl->tCK <= TCK_400MHZ) {val = 0x06; ctrl->tCK = TCK_400MHZ;} + else if(ctrl->tCK <= TCK_333MHZ) {val = 0x05; ctrl->tCK = TCK_333MHZ;} + else if(ctrl->tCK <= TCK_266MHZ) {val = 0x04; ctrl->tCK = TCK_266MHZ;} + + /* Find CAS and CWL latencies */ + val = (ctrl->tAA + ctrl->tCK -1) / ctrl->tCK; + printram("Minimum CAS latency : %uT\n", val); + /* Find lowest supported CAS latency that satisfies the minimum value */ + while( !((ctrl->cas_supported >> (val-4))&1) + && (ctrl->cas_supported >> (val-4))) { + val++; + } + /* Is CAS supported */ + if(!(ctrl->cas_supported & (1 << (val-4))) ) + printram("CAS not supported\n"); + printram("Selected CAS latency : %uT\n", val); + ctrl->CAS = val; + ctrl->CWL = vx900_get_CWL(ctrl->CAS); + printram("Selected CWL latency : %uT\n", ctrl->CWL); + /* Write CAS and CWL */ + reg8 = ( ((ctrl->CWL - 4) &0x07) << 4 ) | ((ctrl->CAS - 4) & 0x07); + pci_write_config8(MCU, 0xc0, reg8); + + /* Find tRCD */ + val = (ctrl->tRCD + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tRCD : %uT\n", val); + reg8 = ((val-4) & 0x7) << 4; + /* Find tRP */ + val = (ctrl->tRP + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tRP : %uT\n", val); + reg8 |= ((val-4) & 0x7); + pci_write_config8(MCU, 0xc1, reg8); + + /* Find tRAS */ + val = (ctrl->tRAS + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tRAS : %uT\n", val); + reg8 = ((val-15) & 0x7) << 4; + /* Find tWR */ + ctrl->WR = (ctrl->tWR + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tWR : %uT\n", ctrl->WR); + reg8 |= ((ctrl->WR-4) & 0x7); + pci_write_config8(MCU, 0xc2, reg8); + + /* Find tFAW */ + tFAW = (ctrl->tFAW + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tFAW : %uT\n", tFAW); + /* Find tRRD */ + tRRD = (ctrl->tRRD + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tRRD : %uT\n", tRRD); + val = tFAW - 4*tRRD; /* number of cycles above 4*tRRD */ + reg8 = ((val-0) & 0x7) << 4; + reg8 |= ((tRRD-2) & 0x7); + pci_write_config8(MCU, 0xc3, reg8); + + /* FIXME: When Rx61[5] is set, 1T is added to tRTP */ + /* Find tRTP */ + val = (ctrl->tRTP + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tRTP : %uT\n", val); + reg8 = ((val & 0x3) << 4); + /* Find tWTR */ + val = (ctrl->tWTR + ctrl->tCK -1) / ctrl->tCK; + printram("Selected tWTR : %uT\n", val); + reg8 |= ((val - 2) & 0x7); + pci_mod_config8(MCU, 0xc4, 0x3f, reg8); + + /* DRAM Timing for All Ranks - VI + * [7:6] CKE Assertion Minimum Pulse Width + * We probably don't want to mess with this just yet. + * [5:0] Refresh-to-Active or Refresh-to-Refresh (tRFC) + * tRFC = (30 + 2 * [5:0])T + * Since we previously set RxC4[7] + */ + reg8 = pci_read_config8(MCU, 0xc5); + val = (ctrl->tRFC + ctrl->tCK -1) / ctrl->tCK; + printram("Minimum tRFC : %uT\n", val); + if(val < 30) { + val = 0; + } else { + val = (val -30 + 1 ) / 2; + } + ; + printram("Selected tRFC : %uT\n", 30 + 2 * val); + reg8 |= (val & 0x3f); + pci_write_config8(MCU, 0xc5, reg8); + + /* Where does this go??? */ + val = (ctrl->tRC + ctrl->tCK -1) / ctrl->tCK; + printram("Required tRC : %uT\n", val); +} + +static void vx900_dram_freq(ramctr_timing *ctrl) +{ + u8 val; + + /* Program the DRAM frequency */ + + /* Step 1 - Reset the PLL */ + pci_mod_config8(MCU, 0x90, 0x00, 0x0f); + /* Wait at least 10 ns; VIA code delays by 640us */ + udelay(640); + + /* Step 2 - Set target frequency */ + if(ctrl->tCK <= TCK_533MHZ) {val = 0x07; ctrl->tCK = TCK_533MHZ;} + else if(ctrl->tCK <= TCK_400MHZ) {val = 0x06; ctrl->tCK = TCK_400MHZ;} + else if(ctrl->tCK <= TCK_333MHZ) {val = 0x05; ctrl->tCK = TCK_333MHZ;} + else /*ctrl->tCK <= TCK_266MHZ*/ {val = 0x04; ctrl->tCK = TCK_266MHZ;} + /* Restart the PLL with the desired frequency */ + pci_mod_config8(MCU, 0x90, 0x0f, val); + + /* Step 3 - Wait for PLL to stabilize */ + udelay(2000); + + /* Step 4 - Reset the DLL - Clear [7,4]*/ + pci_mod_config8(MCU, 0x6b, 0x90, 0x00); + udelay(2000); + + /* Step 5 - Enable the DLL - Set bits [7,4] to 01b*/ + pci_mod_config8(MCU, 0x6b, 0x00, 0x10); + udelay(2000); + + /* Step 6 - Start DLL Calibration - Set bit [7] */ + pci_mod_config8(MCU, 0x6b, 0x00, 0x80); + udelay(5); + + /* Step 7 - Finish DLL Calibration - Clear bit [7]*/ + pci_mod_config8(MCU, 0x6b, 0x80, 0x00); + + /* Step 8 - If we have registered DIMMs, we need to set bit[0] */ + if(dimm_is_registered(ctrl->dram_type)){ + printram("Enabling RDIMM support in memory controller\n"); + pci_mod_config8(MCU, 0x6c, 0x00, 0x01); + } +} + +/* The VX900 can send the MRS commands directly through hardware */ +void vx900_dram_ddr3_do_hw_mrs(u8 ma_swap, u8 rtt_nom, + u8 ods, u8 rtt_wr, u8 srt, u8 asr); +/*static*/ void vx900_dram_ddr3_do_hw_mrs(u8 ma_swap, u8 rtt_nom, + u8 ods, u8 rtt_wr, u8 srt, u8 asr) +{ + u8 reg8 = 0; + u32 reg32; + if(asr) reg8 |= (1 << 0); + if(srt) reg8 |= (1 << 1); + reg8 |= ((rtt_wr & 0x03) << 4); + pci_write_config8(MCU, 0xcd, reg8); + reg8 = 1; + if(ma_swap) reg8 |= (1 << 1); + reg8 |= ((ods & 0x03) << 2); + reg8 |= ((rtt_nom & 0x7) << 4); + pci_write_config8(MCU, 0xcc, reg8); + reg32=0; + while(pci_read_config8(MCU, 0xcc) & 1) reg32++; + printram(" Waited %u PCI cycles for HW MRS\n", reg32); +} +#include "forgotten.h" +#include "forgotten.c" + +static void vx900_dram_send_soft_mrs(u8 type, u16 cmd) +{ + u32 addr; + /* Set Fun3_RX6B[2:0] to 011b (MSR Enable). */ + pci_mod_config8(MCU, 0x6b, 0x07, (3<<0)); + /* Find the address corresponding to the MRS */ + addr = vx900_get_mrs_addr(type, cmd); + //printram("MRS CPU addr: %.8x\n", addr); + /* Execute the MRS */ + volatile_read(addr); + /* Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */ + pci_mod_config8(MCU, 0x6b, 0x07, 0x00); +} + + +static void vx900_dram_ddr3_dimm_init(const ramctr_timing *ctrl, + const rank_layout *ranks) +{ + size_t i; + + /* FIXME: Do we want 0x5b33, or 0x0b33 */ + /* Set BA[0/1/2] to [A17/18/19] */ + pci_write_config16(MCU, 0x52, 0x5b33); + + /* Step 01 - Set Fun3_Rx6E[5] to 1b to support burst length. */ + pci_mod_config8(MCU, 0x6e, 0, 1<<5); + /* Step 02 - Set Fun3_RX69[0] to 0b (Disable Multiple Page Mode). */ + pci_mod_config8(MCU, 0x69, (1<<0), 0x00); + /* And set [7:6] to 10b ?*/ + pci_write_config8(MCU, 0x69, 0x87); + + /* Step 03 - Set the target physical rank to virtual rank0 and other + * ranks to virtual rank3. */ + vx900_pr_map_all_vr3(); + + /* Step 04 - Set Fun3_Rx50 to D8h. */ + pci_write_config8(MCU, 0x50, 0xd8); + /* Step 05 - Set Fun3_RX6B[5] to 1b to de-assert RESET# and wait for at + * least 500 us. */ + pci_mod_config8(MCU, 0x6b, 0x00, (1<<5) ); + udelay(500); + + /* Step 6 -> 15 - Set the target physical rank to virtual rank 0 and + * other ranks to virtual rank 3. + * Repeat Step 6 to 14 for every rank present, then jump to Step 16. */ + for(i = 0; i < VX900_MAX_MEM_RANKS; i++) + { + if(ranks->phys_rank_size[i] == 0) continue; + printram("Initializing rank %lu\n", i); + + /* Set target physical rank to virtual rank 0 + * other ranks to virtual rank 3*/ + vx900_map_pr_vr(i, 0); + +#define USE_HW_INIT_SEQUENCE 0 +#if USE_HW_INIT_SEQUENCE + + /* FIXME: Is this needed on HW init? */ + pci_mod_config8(MCU, 0x6b, 0x07, 0x01); /* Enable NOP */ + volatile_read(0x0); /* Do NOP */ + pci_mod_config8(MCU, 0x6b, 0x07, 0x03); /* MSR Enable */ + + /* FIXME: Values dependent on DIMM setup + * This works for 1 DIMM + * See init_dram_by_rank.c and get_basic_information.c + * in the VIA provided code */ + const u8 rtt_nom = DDR3_MR1_RTT_NOM_RZQ2; + const u8 ods = DDR3_MR1_ODS_RZQ6; + const u8 rtt_wr = DDR3_MR2_RTT_WR_OFF; + + printram("Using Hardware method\n"); + /* FIXME: MA swap dependent on module */ + vx900_dram_ddr3_do_hw_mrs(0, rtt_nom, ods, rtt_wr, 0, 0); + + /* FIXME: Are these steps needed on HW init? */ + /* Send a ZQ calibration command */ + pci_mod_config8(MCU, 0x6b, 0x07, 0x06); + /* Read a double word from any address of the DIMM. */ + volatile_read(0x0); + /* Normal SDRAM Mode */ + pci_mod_config8(MCU, 0x6b, 0x07, 0x00); + +#else /* USE_HW_INIT_SEQUENCE */ + printram("Using software method\n"); + vx900_dram_ddr3_init_rank(MCU, ctrl, i); +#endif /* USE_HW_INIT_SEQUENCE */ + /* Step 15, set the rank to virtual rank 3*/ + vx900_map_pr_vr(i, 3); + } + + /* Step 16 – Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */ + pci_mod_config8(MCU, 0x6b, 0x07, 0x00); + + /* Set BA[0/1/2] to [A13/14/15] */ + pci_mod_config16(MCU, 0x52, 0x0fff, 0x0911); + + /* Step 17 – Set Fun3_Rx69[0] to 1b (Enable Multiple Page Mode). */ + pci_mod_config8(MCU, 0x69, 0x00, (1<<0) ); + + printram("DIMM initialization sequence complete\n"); +} + + +static void vx900_rx_capture_range_calib(void) +{ + u8 reg8; + u16 cmd; + const u32 cal_addr = 0x20; + + /* Set IO calibration address */ + pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0)); + /* Data pattern must be 0x00 for this calibration + * See paragraph describing Rx8e */ + pci_write_config8(MCU, 0x8e, 0x00); + + /* Precharge all */ + pci_mod_config8(MCU, 0x06b, 0x07, 0x02); + volatile_read(0x0); + udelay(1000); + + /* Enable read leveling: Set D0F3Rx71[7]=1 + * Set return data format: Clear D0F3Rx71[6]=0 */ + pci_mod_config8(MCU, 0x71, 0x40, 0x80); + + /* Put DRAM in read leveling mode */ + cmd = ddr3_get_mr3(1); + vx900_dram_send_soft_mrs(3, cmd); + + /* Data pattern must be 0x00 for this calibration + * See paragraph describing Rx8e */ + pci_write_config8(MCU, 0x8e, 0x00); + /* Trigger calibration */ + reg8 = 0xa0; + pci_write_config8(MCU, 0x71, reg8); + + /* Wait for it */ + while(pci_read_config8(MCU, 0x71) & 0x10) cmd++; + printram("RX capture range calibration took %u PCI cycles\n", cmd); + + /* Disable read leveling, and put dram in normal operation mode */ + cmd = ddr3_get_mr3(0); + vx900_dram_send_soft_mrs(3, cmd); + + /* Disable read leveling: Set D0F3Rx71[7]=0 */ + pci_mod_config8(MCU, 0x71, 1<<7, 0); +} + +static void vx900_rx_dqs_delay_calib(void) +{ + u16 cmd; + const u32 cal_addr = 0x30; + + /* Set IO calibration address */ + pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0)); + /* Data pattern must be 0x00 for this calibration + * See paragraph describing Rx8e */ + pci_write_config8(MCU, 0x8e, 0x00); + + /* Precharge all */ + pci_mod_config8(MCU, 0x06b, 0x07, 0x02); + volatile_read(0x0); + udelay(1000); + + /* Enable read leveling: Set D0F3Rx71[7]=1 + * Set return data format: Clear D0F3Rx71[6]=0 + * Set RxDQS to use calibration setting - Clear Rx71[2] and Rx71[0] */ + pci_mod_config8(MCU, 0x71, 0x45, 0x80); + + /* Put DRAM in read leveling mode (enable MPR flow) */ + cmd = ddr3_get_mr3(1); + vx900_dram_send_soft_mrs(3, cmd); + + /* From VIA code; Undocumented + * In theory this enables MODT[3:0] to be asserted*/ + pci_mod_config8(MCU, 0x9e, 0, 0x80); + + /* Trigger calibration: Set D0F3Rx71[1:0]=10b */ + pci_mod_config8(MCU, 0x71, 0x03, 0x02); + + /* Wait for calibration to complete */ + while( pci_read_config8(MCU, 0x71) & 0x02 ); + + /* Put DRAM in normal mode (disable MPR flow) */ + cmd = ddr3_get_mr3(0); + vx900_dram_send_soft_mrs(3, cmd); + + /* Disable read leveling: Set D0F3Rx71[7]=0 */ + pci_mod_config8(MCU, 0x71, 1<<7, 0); +} + +static void vx900_tx_dqs_trigger_calib(u8 pattern) +{ + u32 i; + /* Data pattern for calibration */ + pci_write_config8(MCU, 0x8e, pattern); + /* Trigger calibration */ + pci_mod_config8(MCU, 0x75, 0, 0x20); + /* Wait for calibration */ + i = 0; + while(pci_read_config8(MCU, 0x75) & 0x20) i++; + printram(" Tx DQS calib took %u PCI cycles\n", i); +} +static void vx900_tx_dqs_delay_calib(void) +{ + const u32 cal_addr = 0x00; + /* Set IO calibration address */ + pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0)); + /* Set circuit to use calibration results - Clear Rx75[0]*/ + pci_mod_config8(MCU, 0x75, 0x01, 0); + /* Run calibration with first data pattern*/ + vx900_tx_dqs_trigger_calib(0x5a); + /* Run again with different pattern */ + vx900_tx_dqs_trigger_calib(0xa5); +} + +static void vx900_tx_dq_delay_calib(void) +{ + int i = 0; + /* Data pattern for calibration */ + pci_write_config8(MCU, 0x8e, 0x5a); + /* Trigger calibration */ + pci_mod_config8(MCU, 0x75, 0, 0x02); + /* Wait for calibration */ + while(pci_read_config8(MCU, 0x75) & 0x02) i++; + printram("TX DQ calibration took %u PCI cycles\n", i); +} + +static void vx900_rxdqs_adjust(delay_range *dly) +{ + /* Adjust Rx DQS delay after calibration has been run. This is + * recommended by VIA, but no explanation was provided as to why */ + size_t i; + for(i = 0; i < 8; i++) + { + if(dly->low[i] < 3) + { + if(i == 2 || i== 4) dly->low[i] += 4; + else dly->avg[i] += 3; + + } + + if(dly->high[i] > 0x38) dly->avg[i] -= 6; + else if(dly->high[i] > 0x30) dly->avg[i] -= 4; + + if(dly->avg[i] > 0x20) dly->avg[i] = 0x20; + } + + /* Put Rx DQS delay into manual mode (Set Rx[2,0] to 01) */ + pci_mod_config8(MCU, 0x71, 0x05, 0x01); + /* Now write the new settings */ + vx900_write_0x78_0x7f(dly->avg); +} + +static void vx900_calibrate_delays(const ramctr_timing *ctrl, + const rank_layout *ranks) +{ + timing_dly dly; + size_t i; + u8 val; + vx900_delay_calib delay_cal; + memset(&delay_cal, 0, sizeof(delay_cal)); + printram("Starting delay calibration\n"); + + /* MD Input Data Push Timing Control; + * use values recommended in datasheet */ + if (ctrl->tCK <= TCK_533MHZ) val = 2; + else if (ctrl->tCK <= TCK_333MHZ) val = 1; + else val = 0; + val ++; /* FIXME: vendor BIOS sets this to 3 */ + pci_mod_config8(MCU, 0x74, (0x03 << 1), ((val & 0x03) << 1) ); + + /* FIXME: The vendor BIOS does this, but WHY? + * Early DQ/DQS for write cycles */ + pci_mod_config8(MCU, 0x76, (3<<2), 2<<2); + /* FIXME: The vendor BIOS increases the MD input delay - WHY ? */ + pci_mod_config8(MCU, 0xef, (3<<4), 3<<4); + /* FIXME: The vendor BIOS does this - WHY ?*/ + pci_write_config8(MCU, 0x77, 0x10); + /* FIXME: Vendor BIOS goes in with + * 8 page registers + * multiple page mode + * High Priority Refresh request + * -- WHY?*/ + pci_write_config8(MCU, 0x69, 0xc7); + + /* Set BA[0/1/2] to [A17/18/19] */ + pci_write_config16(MCU, 0x52, 0x5b33); + /* Disable Multiple Page Mode - Set Rx69[0] to 0 */ + pci_mod_config8(MCU, 0x69, (1<<0), 0x00); + + /* FIXME: we'll need to set up a loop and do the mapping there */ + vx900_map_pr_vr(2, 0); + + /* Run calibrations */if(1){ + vx900_rx_capture_range_calib(); + vx900_read_delay_range(&(delay_cal.rx_dqi_cr), + VX900_CALIB_RxDQI_CR); + dump_delay_range(delay_cal.rx_dqi_cr);} + + /*FIXME: Cheating with Rx CR setting + * We need to either use Rx CR calibration + * or set up a table for the calibration */ + dly[0] = 0x28; + dly[1] = 0x1c; + dly[2] = 0x28; + dly[3] = 0x28; + dly[4] = 0x2c; + dly[5] = 0x30; + dly[6] = 0x30; + dly[7] = 0x34; + printram("Cheat RxCR 78-7f with Rx71 at 0x%.2x\n", + pci_read_config8(MCU, 0x71)); + /* We need to put the setting on manual mode */ + pci_mod_config8(MCU, 0x71, 0, 0x10); + dump_delay(dly); + vx900_delay_calib_mode_select(VX900_CALIB_RxDQI_CR, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(dly); + + /************* RxDQS *************/ + vx900_rx_dqs_delay_calib(); + vx900_read_delay_range(&(delay_cal.rx_dqsi), VX900_CALIB_RxDQSI); + printram("RX DQS calibration results\n"); + dump_delay_range(delay_cal.rx_dqsi); + + vx900_rxdqs_adjust(&(delay_cal.rx_dqsi)); + + vx900_read_delay_range(&(delay_cal.rx_dqsi), VX900_CALIB_RxDQSI); + printram("RX DQS calibration results after adjustment\n"); + dump_delay_range(delay_cal.rx_dqsi); + + /* FIXME: Vendor BIOS does it again (enable auto-precharge) - WHY? */ + pci_write_config8(MCU, 0x69, 0xce); + + if(ctrl->tCK <= TCK_533MHZ){ + for( i = 0; i< 8; i++) dly[i] = 0x80; + pci_mod_config8(MCU, 0x75, 0x00, 0x01); /* manual Tx DQ DQS */ + vx900_delay_calib_mode_select(VX900_CALIB_TxDQO, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(dly); + vx900_delay_calib_mode_select(VX900_CALIB_TxDQSO, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(dly);} + /************* TxDQS *************/ + vx900_tx_dqs_delay_calib(); + + vx900_read_delay_range(&(delay_cal.tx_dqso), VX900_CALIB_TxDQSO); + printram("Tx DQS calibration results\n"); + dump_delay_range(delay_cal.tx_dqso); + /************* TxDQ *************/ + /* FIXME: not sure if multiple page mode should be enabled here + * Vendor BIOS does it*/ + pci_mod_config8(MCU, 0x69, 0 , 0x01); + + vx900_tx_dq_delay_calib(); + vx900_read_delay_range(&(delay_cal.tx_dqo), VX900_CALIB_TxDQO); + printram("TX DQ delay calibration results:\n"); + dump_delay_range(delay_cal.tx_dqo); + + /* write manual settings */ + pci_mod_config8(MCU, 0x75, 0, 0x01); + vx900_delay_calib_mode_select(VX900_CALIB_TxDQSO, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(delay_cal.tx_dqso.avg); + vx900_delay_calib_mode_select(VX900_CALIB_TxDQO, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(delay_cal.tx_dqo.avg); + + /* Set BA[0/1/2] to [A13/14/15] */ + pci_mod_config16(MCU, 0x52, 0x0fff, 0x0911); +} + +static void vx900_dram_set_refresh_counter(ramctr_timing *ctrl) +{ + u8 reg8; + /* Set DRAM refresh counter + * Based on a refresh counter of 0x61 at 400MHz */ + reg8 = (TCK_400MHZ * 0x61) / ctrl->tCK; + pci_write_config8(MCU, 0xc7, reg8); +} + +static void vx900_dram_write_final_config(ramctr_timing *ctrl) +{ + /* FIXME: These are quick cheats */ + pci_write_config8(MCU, 0x50, 0xa0); /* DRAM MA map */ + pci_write_config16(MCU, 0x52, 0x5911); /* Rank interleave */ + pci_write_config16(MCU, 0x54, 0x0800); /* Bank mapping */ + pci_write_config8(MCU, 0x69, 0xe7); + pci_write_config8(MCU, 0x72, 0x0f); + pci_write_config8(MCU, 0x97, 0xa4); + pci_write_config8(MCU, 0xd3, 0x00); +} + +static void print_debug_pci_dev(device_t dev) +{ + print_debug("PCI: "); + print_debug_hex8((dev >> 20) & 0xff); + print_debug_char(':'); + print_debug_hex8((dev >> 15) & 0x1f); + print_debug_char('.'); + print_debug_hex8((dev >> 12) & 7); +} + +static void dump_pci_device(device_t dev) +{ + int i; + print_debug_pci_dev(dev); + print_debug("\n"); + + for (i = 0; i <= 0xff; i++) { + unsigned char val; + if ((i & 0x0f) == 0) { + print_debug_hex8(i); + print_debug_char(':'); + } + + if ((i & 0x0f) == 0x08) { + print_debug(" |"); + } + + val = pci_read_config8(dev, i); + print_debug_char(' '); + print_debug_hex8(val); + + if ((i & 0x0f) == 0x0f) { + print_debug("\n"); + } + } +} + +void vx900_init_dram_ddr3(const dimm_layout *dimm_addr) +{ + dimm_info dimm_prop; + ramctr_timing ctrl_prop; + rank_layout ranks; + device_t mcu; + + /* Locate the Memory controller */ + mcu = pci_locate_device(PCI_ID(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_VX900_DRAM_CTR), 0); + + if (mcu == PCI_DEV_INVALID) { + die("Memory Controller not found\n"); + } + + dump_pci_device(mcu); + memset(&ranks, 0, sizeof(ranks)); + /* 1) Write some initial "safe" parameters */ + vx900_dram_write_init_config(); + /* 2) Get timing information from SPDs */ + dram_find_spds_ddr3(dimm_addr, &dimm_prop); + /* 3) Find lowest common denominator for all modules */ + dram_find_common_params(&dimm_prop, &ctrl_prop); + /* 4) Find the size of each memory rank */ + vx900_dram_phys_bank_range(&dimm_prop, &ranks); + /* 5) Set DRAM driving strength */ + vx900_dram_driving_ctrl(&dimm_prop); + /* 6) Set DRAM frequency and latencies */ + vx900_dram_timing(&ctrl_prop); + vx900_dram_freq(&ctrl_prop); + /* 7) Initialize the modules themselves */ + vx900_dram_ddr3_dimm_init(&ctrl_prop, &ranks); + /* 8) Calibrate receive and transmit delays */ + vx900_calibrate_delays(&ctrl_prop, &ranks); + /* 9) Set refresh counter based on DRAM frequency */ + vx900_dram_set_refresh_counter(&ctrl_prop); + /* 10) Enable Physical to Virtual Rank mapping */ + vx900_dram_range(&ctrl_prop, &ranks); + /* 99) Some final adjustments */ + vx900_dram_write_final_config(&ctrl_prop); + /* Take a dump */ + dump_pci_device(mcu); + +} diff --git a/src/northbridge/via/vx900/raminit_ddr3_delay_calib.c b/src/northbridge/via/vx900/raminit_ddr3_delay_calib.c new file mode 100644 index 0000000..a3f39dc --- /dev/null +++ b/src/northbridge/via/vx900/raminit_ddr3_delay_calib.c @@ -0,0 +1,2150 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 One Laptop per Child, Association, 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; 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 <stdint.h> +#include "raminit.h" +#include <stdint.h> +#include <arch/io.h> +#include <arch/romcc_io.h> +#include <string.h> +#include <console/console.h> +#include <device/pci_ids.h> +#include <console/console.h> + +#include "early_vx900.h" +#include "raminit.h" + + +typedef uint8_t BOOLEAN; +#define TRUE 1 +#define FALSE 0 + +/*=================================================================== + * Function : via_write_phys() + * Precondition : + * Input : addr + * value + * Output : void + * Purpose : + * Reference : None + * ===================================================================*/ + +static void via_write_phys(volatile uint32_t addr, volatile uint32_t value) +{ + volatile uint32_t *ptr; + ptr = (volatile uint32_t *)addr; + *ptr = (volatile uint32_t)value; +} + +/*=================================================================== + * Function : via_read_phys() + * Precondition : + * Input : addr + * Output : u32 + * Purpose : + * Reference : None + * ===================================================================*/ + +static uint32_t via_read_phys(volatile u32 addr) +{ + volatile uint32_t y; + y = *(volatile uint32_t *)addr; + return y; +} + +/*=================================================================== + * Function : DimmRead() + * Precondition : + * Input : x + * Output : u32 + * Purpose : + * Reference : None + * ===================================================================*/ +/* + * static uint32_t DimmRead(volatile uint32_t x) + * { + * volatile uint32_t y; + * y = *(volatile uint32_t *)x; + * + * return y; + } + */ +/*=================================================================== + * Function : dram_base_test() + * Precondition : this function used to verify memory + * Input : + * BaseAdd, + * length, + * mode + * Output : u32 + * Purpose :write into and read out to verify if dram is correct + * Reference : None + * ===================================================================*/ +#define STEPSPAN 0x1000 //the span when test memory in spare mode +#define TESTCOUNT 0x4 // the test count in each range when test memory in spare mode +#define TEST_PATTERN 0x5A5A5A5A //the test pattern + +typedef enum __DRAM_TEST_MODE{ + EXTENSIVE, + SPARE, + MAXMODE +} DRAM_TEST_MODE; + +static BOOLEAN dram_base_test(uint32_t base_addr, uint32_t len, + DRAM_TEST_MODE mode, uint8_t print_flag) +{ + uint32_t test_span; + uint32_t d32, addr, addr2; + uint8_t i, test_count; + + //decide the test mode is continous or step + if (mode == EXTENSIVE) { + //the test mode is continuos and must test each unit + test_span = 4; + test_count = 1; + } else if (mode == SPARE) { + // the test mode is step and test some unit + test_span = STEPSPAN; + test_count = TESTCOUNT; + } else { + printram("the test mode is error\n"); + return FALSE; + } + + //write each test unit the value with TEST_PATTERN + for (addr = base_addr; addr < base_addr + len; addr += test_span) { + for (i = 0; i < test_count; i++) + via_write_phys(addr + i * 4, TEST_PATTERN); + if (print_flag) { + if ((u32) addr % 0x10000000 == 0) { + printram("Write in Addr = 0x%.8x", addr); + printram("\n"); + } + } + } + + //compare each test unit with the value of TEST_PATTERN + //and write it with compliment of TEST_PATTERN + for (addr = base_addr; addr < base_addr + len; addr += test_span) { + for (i = 0; i < test_count; i++) { + d32 = via_read_phys(addr + i * 4); + via_write_phys(addr + i * 4, (uint32_t) (~TEST_PATTERN)); + if (d32 != TEST_PATTERN) { + addr2 = addr + i * 4; + printram("TEST_PATTERN ERROR !!!!! 0x%.8x", addr2); + printram(" : 0x%.8x", d32); + printram(" \n"); + return FALSE; + } + } + if (print_flag) { + if ((u32) addr % 0x10000000 == 0) { + printram("Write in Addr ="); + print_debug_hex32(addr); + printram("\n"); + } + } + } + + //compare each test unit with the value of ~TEST_PATTERN + for (addr = base_addr; addr < base_addr + len; addr += test_span) { + for (i = (u8) (test_count); i > 0; i--) { + d32 = via_read_phys(addr + (i - 1) * 4); + if (d32 != ~TEST_PATTERN) { + + print_debug("~TEST_PATTERN ERROR !!!!! "); + addr2 = addr + (i - 1) * 4; + print_debug_hex32(addr2); + printram(" : "); + print_debug_hex32(d32); + printram(" \n"); + return FALSE; + } + } + } + + return TRUE; +} + +/*=================================================================== + * Function : DumpRegisters() + * Precondition : + * Input : + * pPCIPPI, + * DevNum, + * FuncNum + * Output : Void + * Purpose : + * Reference : None + * ===================================================================*/ +#if 0 +static void dump_registers(uint8_t dev_num, uint8_t func_num) +{ + uint8_t i, j; + uint8_t d8; + + d8 = 0; + printk(BIOS_DEBUG, "Dev %01x Fun %01x:", dev_num, func_num); + printram + ("\n 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + printram + ("---------------------------------------------------\n"); + for (i = 0; i < 0x10; i++) + { + print_debug_hex8(i); + for (j = 0; j < 0x10; j++) + { + d8 =pci_read_config8(PCI_DEV(0, dev_num, func_num), i * 0x10 + j); + printram(" "); + print_debug_hex8(d8); + } + printram("\n"); + } + return; +} + +#endif +static uint8_t shift_to_mask_bit(uint8_t MaskBit, uint8_t ShiftData) +{ + uint8_t value, index, buffer=0; + + for (index=0; index<8; index++) + { + buffer = MaskBit; + if ((buffer >> index) & 0x01) + break; + } + value = ShiftData << index; + return value; +} + +//---------------------------------------------------------------------------- +//Ver Date Name Note +//---------------------------------------------------------------------------- +//R21 +// 1. 3410-21-LNA-06 Add define to support RDSIT calibration limitation. +//R32 +// 1. 3410-32-LNA-01 Add different raw cards support. +//R34 +//3410-34-LNA-03 Add raw card C support. If user populates raw card C DIMM, set the raw card type to raw card A. +//R36 +//3410-36-LNA-01 When detect Mezza Dimm, bios need to modify DRAM TRCD. +/*************************************************************************** + * Copyright (C) 2006 VIA Technologies, Inc. All Rights Reserved. + * + * Information in this file is the intellectual property of + * VIA Technologies, Inc., and may contains trade secrets that must be stored + * and viewed confidentially. + * + * + * Filename : DramInit.h + * Version : 0.1 + * Date : 20050401 + * Author : Bruce Cao + * + * Description : The header file of the DramInit.c + * Noted by Jason Zhao in 20060627. + * + * Others : None + ****************************************************************************/ +#ifndef __DRAMINIT_H_ +#define __DRAMINIT_H_ + + + +//#include "../vx900.h" +//#include "../vx900_cfg.h" + + +#define TRUE 1 +#define FALSE 0 + + +static void pci_modify_config8(device_t dev, unsigned int where, + u8 clr_mask, u8 set_mask) +{ + u8 reg8 = pci_read_config8(dev, where); + reg8 &= ~clr_mask; + reg8 |= set_mask; + pci_write_config8(dev, where, reg8); +} + + +//================================================================== +// DDR3 Code Start{ + //================================================================== + + +#define SPD_MEMORY_TYPE 2 ;/*Memory type FPM,EDO,SDRAM,DDR,DDR2, DDR3*/ +#define SPD_MODULE_TYPE 3 +#define SPD_DENSITY_BANKS 4 +#define SPD_ADDRESSING 5 +#define SPD_NOMINAL_VOLTAGE 6 +#define SPD_MODULE_ORG 7 +#define SPD_BUS_WIDTH 8 +#define SPD_FINE_TIMEBASE 9 +#define SPD_MEDIUM_TIMEBASE_DIVIDEND 10 +#define SPD_MEDIUM_TIMEBASE_DIVISOR 11 +#define SPD_MIN_CYCLE_TIME 12 +#define SPD_SUPPORTED_CL_LSB 14 +#define SPD_SUPPORTED_CL_MSB 15 +#define SPD_MIN_CL_TIME 16 +#define SPD_MIN_TWR 17 +#define SPD_MIN_TRCD 18 +#define SPD_MIN_TRRD 19 +#define SPD_MIN_TRP 20 +#define SPD_UPPER_NIBBLE_TRAS_TRC 21 +#define SPD_MIN_TRAS_LSB 22 +#define SPD_MIN_TRC_LSB 23 +#define SPD_MIN_TRFC_LSB 24 +#define SPD_MIN_TRFC_MSB 25 +#define SPD_MIN_TWTR 26 +#define SPD_MIN_TRTP 27 +#define SPD_UPPER_NIBBLE_TFAW 28 +#define SPD_MIN_TFAW 29 +#define SPD_OPTIONAL_FEATURE 30 +#define SPD_THERMAL_REFRESH 31 +#define SPD_THERMAL_SENSOR 32 +#define SPD_DEVICE_TYPE 33 +#define SPD_RANK1_ADDRMAPPING 63 + +// According to SPD byte 7 to define. +#define RAW_CARD_A 0x0A // 2R x16 +#define RAW_CARD_B 0x01 // 1R x8 +#define RAW_CARD_C 0x02 // 1R x16 +#define RAW_CARD_F 0x09 // 2R x8 +//3410-32-LNA-01-start +#define RAW_CARD_O 0x00 // dram controller can not support x4 DRAM chiop, so use x4 DRAM chip definition to define no dram +//card type combination of DIMM1:DIMM0 +#define RAW_CARD_AO ((RAW_CARD_A << 4) | RAW_CARD_O) +#define RAW_CARD_AA ((RAW_CARD_A << 4 )| RAW_CARD_A) +#define RAW_CARD_AB ((RAW_CARD_A << 4 )| RAW_CARD_B) +#define RAW_CARD_AF ((RAW_CARD_A << 4 )| RAW_CARD_F) +#define RAW_CARD_BO ((RAW_CARD_B << 4 )| RAW_CARD_O) +#define RAW_CARD_BA ((RAW_CARD_B << 4 )| RAW_CARD_A) +#define RAW_CARD_BB ((RAW_CARD_B << 4 )| RAW_CARD_B) +#define RAW_CARD_BF ((RAW_CARD_B << 4 )| RAW_CARD_F) +#define RAW_CARD_FO ((RAW_CARD_F << 4 )| RAW_CARD_O) +#define RAW_CARD_FA ((RAW_CARD_F << 4 )| RAW_CARD_A) +#define RAW_CARD_FB ((RAW_CARD_F << 4 )| RAW_CARD_B) +#define RAW_CARD_FF ((RAW_CARD_F << 4 )| RAW_CARD_F) +#define RAW_CARD_OO ((RAW_CARD_O << 4 )| RAW_CARD_O) +#define RAW_CARD_OA ((RAW_CARD_O << 4 )| RAW_CARD_A) +#define RAW_CARD_OB ((RAW_CARD_O << 4 )| RAW_CARD_B) +#define RAW_CARD_OF ((RAW_CARD_O << 4 )| RAW_CARD_F) + + +#define DRAMFREQ_533 0x04 +#define DRAMFREQ_667 0x05 +#define DRAMFREQ_800 0x06 +#define DRAMFREQ_1066 0x07 +#define DRAMFREQ_1333 0x08 + +#define DRAMCYCLE_800 2500 +#define DRAMCYCLE_1066 1875 +#define DRAMCYCLE_1333 1500 +#define DRAMCYCLE_1600 1250 + +#define CMD_2T 0x00 +#define CMD_1T 0x01 + +#define BL4 0x00 +#define BL8 0x01 + + +#define MAA_BITMAP 0x0F +#define MAB_BITMAP 0xF0 +#define CHA_BITMAP MAA_BITMAP +#define CHB_BITMAP MAB_BITMAP +#define ALL_BITMAP CHA_BITMAP + CHB_BITMAP +#define ALL_EVEN ((ALL_BITMAP) & 0x55) +#define CHA_EVEN_BANK ( CHA_BITMAP & 0x55 ) +#define CHB_EVEN_BANK ( CHB_BITMAP & 0x55 ) + + +#define NOTREGDIMM 0x00 +#define ISREGDIMM 0x01 + +#define RANK1_MAPPING_STANDARD 0 +#define RANK1_MAPPING_MIRRORED 1 + +//---------------------------------------// +// MSR setting // +//---------------------------------------// + + +//Typical MA mapping for a MRS command +//BA2 BA1 BA0 MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00 +//19 18 17 x x x 14 13 20 12 11 10 9 8 7 6 5 4 3 + +// DDR3 MR0 (BA1=0, BA0=0) +// MA[12] - DLL Control for Precharge PD +// MA[11:9] - TWR +// MA[8] - DLL Reset +// MA[7] - Mode +// MA[3] - Read Burst Type +// MA[6:4,2] - CAS LAtency +// MA[1:0] - BL +// BA1 BA0 +#define DDR3_MR0 (0 << 18) + (0 << 17) + +// MA12 +#define MR0_DDR3_DLLCONTROL (1 << 14) + +// MA11 MA10 MA9 +#define MR0_DDR3_WR5 (0 << 13) + (0 << 20) + (1 << 12) +#define MR0_DDR3_WR6 (0 << 13) + (1 << 20) + (0 << 12) +#define MR0_DDR3_WR7 (0 << 13) + (1 << 20) + (1 << 12) +#define MR0_DDR3_WR8 (1 << 13) + (0 << 20) + (0 << 12) +#define MR0_DDR3_WR10 (1 << 13) + (0 << 20) + (1 << 12) +#define MR0_DDR3_WR12 (1 << 13) + (1 << 20) + (0 << 12) + +// MA8 +#define MR0_DLL_RESET (1 << 11) + +// MA7 +#define MR0_TEST_MODE (1 << 10) + +// MA6 MA5 MA4 MA2 +#define MR0_DDR3_CL5 (0 << 9) + (0 << 8) + (1 << 7) + (0 << 5) +#define MR0_DDR3_CL6 (0 << 9) + (1 << 8) + (0 << 7) + (0 << 5) +#define MR0_DDR3_CL7 (0 << 9) + (1 << 8) + (1 << 7) + (0 << 5) +#define MR0_DDR3_CL8 (1 << 9) + (0 << 8) + (0 << 7) + (0 << 5) +#define MR0_DDR3_CL9 (1 << 9) + (0 << 8) + (1 << 7) + (0 << 5) +#define MR0_DDR3_CL10 (1 << 9) + (1 << 8) + (0 << 7) + (0 << 5) +#define MR0_DDR3_CL11 (1 << 9) + (1 << 8) + (1 << 7) + (0 << 5) + +// MA3 +#define MR0_DDR3_READ_BURST_INVL (1 << 6) + + +// MA1 MA0 +#define MR0_DDR3_BL8 (0 << 4) + (0 << 3) +#define MR0_DDR3_ON_THE_FLY (0 << 4) + (1 << 3) +#define MR0_DDR3_BL4 (1 << 4) + (0 << 3) + + +//Typical MA mapping for a MRS command +//BA2 BA1 BA0 MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00 +//19 18 17 x x x 14 13 20 12 11 10 9 8 7 6 5 4 3 + +// DDR3 MR1 (BA1=0, BA0=1) +// MA[12] - Qoff, output buffer enable/disable +// MA[11] - TDQS Enable +// MA[7] - Write Leveling enable +// MA[4:3] - Additive Latency +// MA[9,6,2] - Rtt_Nom +// MA[5,1] - Output Driver Impedance Control +// MA[0] - DLL Enable + +// BA1 BA0 +#define DDR3_MR1 (0 << 18) + (1 << 17) + +// MA12 +#define MR1_DDR3_QOFF_DIS (1 << 14) + +// MA11 +#define MR1_DDR3_TDQS_EN (1 << 13) + +// MA7 +#define MR1_DDR3_WLVL_EN (1 << 10) + +// MA4 MA3 +#define MR1_DDR3_AL_DIS (0 << 7) + (0 << 6) +#define MR1_DDR3_AL_CL1 (0 << 7) + (1 << 6) +#define MR1_DDR3_AL_CL2 (1 << 7) + (0 << 6) + + +// MA9 MA6 MA2 +#define MR1_DDR3_RTTNOM_DIS (0 << 12) + (0 << 9) + (0 << 5) +#define MR1_DDR3_RTTNOM_60OHM (0 << 12) + (0 << 9) + (1 << 5) +#define MR1_DDR3_RTTNOM_120OHM (0 << 12) + (1 << 9) + (0 << 5) +#define MR1_DDR3_RTTNOM_40OHM (0 << 12) + (1 << 9) + (1 << 5) +#define MR1_DDR3_RTTNOM_20OHM (1 << 12) + (0 << 9) + (0 << 5) +#define MR1_DDR3_RTTNOM_30OHM (1 << 12) + (0 << 9) + (1 << 5) + +// MA5 MA1 +#define MR1_DDR3_RZQ6 (0 << 8) + (0 << 4) +#define MR1_DDR3_RZQ7 (0 << 8) + (1 << 4) + +// MA0 +#define MR1_DDR3_DLL_EN (0 << 3) +#define MR1_DDR3_DLL_DIS (1 << 3) +//Typical MA mapping for a MRS command +//BA2 BA1 BA0 MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00 +//19 18 17 x x x 14 13 20 12 11 10 9 8 7 6 5 4 3 + +// DDR3 MR2 (BA1=1, BA0=0) +// MA[10:9] - Rtt_WR +// MA[7] - Self-Refresh Temprerature (SRT) Range +// MA[6] - Auto Self-Refresh (ASR) +// MA[5:3] - CAS Write Latency (CWL) +// MA[2:0] - Partial Array Self-Refresh (Optional) + +// BA1 BA0 +#define DDR3_MR2 (1 << 18) + (0 << 17) + +// MA10 MA9 +#define MR2_DDR3_RTTWR_DIS (0 << 20) + (0 << 12) +#define MR2_DDR3_RTTWR_60OHM (0 << 20) + (1 << 12) +#define MR2_DDR3_RTTWR_120OHM (1 << 20) + (0 << 12) + +// MA7 +#define MR2_DDR3_SRT_EXTENDED (1 << 10) + +// MA6 +#define MR2_DDR3_ASR_EN (1 << 9) + +// MA5 MA4 MA3 +#define MR2_DDR3_CWL_5 (0 << 8) + (0 << 7) + (0 << 6) +#define MR2_DDR3_CWL_6 (0 << 8) + (0 << 7) + (1 << 6) +#define MR2_DDR3_CWL_7 (0 << 8) + (1 << 7) + (0 << 6) +#define MR2_DDR3_CWL_8 (0 << 8) + (1 << 7) + (1 << 6) + +// MA2 MA1 MA0 +#define MR2_DDR3_PASR_FULL (0 << 5) + (0 << 4) + (0 << 3) +#define MR2_DDR3_PASR_HALFL (0 << 5) + (0 << 4) + (1 << 3) +#define MR2_DDR3_PASR_QUATERL (0 << 5) + (1 << 4) + (0 << 3) +#define MR2_DDR3_PASR_1_8_L (0 << 5) + (1 << 4) + (1 << 3) +#define MR2_DDR3_PASR_3_4 (1 << 5) + (0 << 4) + (0 << 3) +#define MR2_DDR3_PASR_HALFH (1 << 5) + (0 << 4) + (1 << 3) +#define MR2_DDR3_PASR_QUATEH (1 << 5) + (1 << 4) + (0 << 3) +#define MR2_DDR3_PASR_1_8_H (1 << 5) + (1 << 4) + (1 << 3) + + +//Typical MA mapping for a MRS command +//BA2 BA1 BA0 MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00 +//19 18 17 x x x 14 13 20 12 11 10 9 8 7 6 5 4 3 + +// DDR3 MR3 (BA1=1, BA0=1) +// MA[2] - MPR +// MA[1:0] - MPR location + +// BA1 BA0 +#define DDR3_MR3 (1 << 18) + (1 << 17) + +// MA2 +#define MR3_DDR3_MPR_EN (1 << 5) + +//Action Type for MR0~3 +#define MR0_INIT 0x00 + +#define MR1_INIT 0x00 +#define MR1_WLVL_UNDERCALPR 0x01 +#define MR1_WLVL_OTHERPR 0x02 + +#define MR2_INIT 0x00 +#define MR2_RTTWR_DIS 0x01 + +#define MR3_INIT 0x00 +#define MR3_MPR_ON 0x01 + + + + +#define CAL_FIRST_DIMM 0 +#define CAL_SECOND_DIMM 1 + +#define MODULE_TYPE_UDIMM 0x02 +#define MODULE_TYPE_SODIMM 0x03 + +#define IO_RDSIT 0 +#define IO_DQSI 1 +#define IO_DQO 2 +#define IO_DQSO 3 + +#define IOCTL_MANUAL 0 +#define IOCTL_LOW 1 +#define IOCTL_HIGH 2 +#define IOCTL_CENTER 3 + + + +typedef struct{ + uint8_t SI_Rdsit_Ref_Freq_1333; + uint8_t SI_Rdsit_Ref_Freq_1066; + uint8_t SI_Rdsit_Ref_Freq_0800; +}VIA_RDSIT_REF_DRAM; + + + + +#define CHIP_MAX_SOCKETS 2 +#define CHIP_MAX_RANKS CHIP_MAX_SOCKETS*2 +#define CHIP_MAX_CHANNELS 1 +#define CHANNEL_MAX_SOCKETS 2 +#define CHANNEL_MAX_RANKS CHANNEL_MAX_SOCKETS*2 +#define SPD_SIZE 128 + +typedef struct { + uint8_t SpdPresent; + uint8_t Buffer[SPD_SIZE]; +} VIA_MEMINIT_SPD_DATA; + +typedef struct { + uint8_t PR_CurrentSize; + uint8_t PR_VRank; + uint8_t PR_MergeRank; + uint8_t PR_MergeCount; + uint8_t PR_Dual_En; + uint8_t PR_Above4G; + uint8_t PR_DRAMWaste; +} VIA_VIRTUALRANK_INFO; + + +//;PR_Above4G, PR_DRAMWaste Action +//;0 , 0 this VR in Below 4G +//;0 , 1 this VR in Below 4G and waste +//;1 , 0 this VR in Above 4G +//;1 , 1 this VR in Above 4G and part or all Address >= 16G + +typedef struct { + uint8_t TotalSize_GreatThen2G; + uint8_t AllVR_LessThen2G; + uint8_t AllVR_GreatThen2G; + uint8_t TotalSizeOfPRsThatLessThen2G; + uint8_t PR_Size_Table; + uint8_t Virtual_Rank_Count ; + uint16_t Total_DRAM_Size; +} VIA_ABOVE4G_INFO; + +typedef struct{ + //Tx DQ + uint8_t TxDq_Lower_Bound; + uint8_t TxDq_Center; + uint8_t TxDq_Upper_Bound; + //Tx DQS + uint8_t TxDqs_Lower_Bound; + uint8_t TxDqs_Center; + uint8_t TxDqs_Upper_Bound; +}VIA_TX_IO_CALIBRATION_INFO; + +typedef struct { + uint8_t BA_Number; + uint8_t RA_Number; + uint8_t CA_Number; + uint8_t RankSize; //Unit:64MHz +} VIA_RANK_INFO; + +typedef struct { + uint8_t DQSI_LowBound; + uint8_t DQSI_HighBound; +} VIA_CHANNEL_INFO; + + +//======================================== +// define function prototype +//======================================== + +//void dram_init(NB_CONFIGURATION *nb_cfg, DRAM_CONFIGURATION *dram_cfg); + +//void dram_s3_init(NB_CONFIGURATION *nb_cfg, DRAM_CONFIGURATION *dram_cfg); + +//======================================== +// define macro +//======================================== + +//================================================================== +// DDR3 Code End} +//================================================================== + +#endif //__DRAMINIT_H_ + + +/* + * + * VISA 0.5 DRAM Module + * + */ +//---------------------------------------------------------------------------- +//Ver Date Name Note +//---------------------------------------------------------------------------- + + + +//#include "../via_io_lib.h" + +//#include "../vx900.h" +//#include "../vx900_cfg.h" +//#include "../stall.h" + +//#include "si_dram_tbl.h" + +//#include "dram_init.h" +//#include "dram_util.h" + +#define D0F3VR0EA 0x40 +#define D0F3VR1EA 0x41 +#define D0F3VR2EA 0x42 +#define D0F3VR3EA 0x43 +#define D0F3VR0BA 0x48 +#define D0F3VR1BA 0x49 +#define D0F3VR2BA 0x4A +#define D0F3VR3BA 0x4B + +#define D0F3MA 0x50 +#define D0F3INLVSEL 0x52 +#define D0F3INLVSEL_1 0x53 +#define D0F3PVRMAP 0x54 +#define D0F3PVRMAP_1 0x55 +#define D0F3PVRMAP_2 0x56 +#define D0F3PVRMAP_3 0x57 + +#define D0F3DPTA 0x60 +#define D0F3DPCTL 0x61 +#define D0F3REOPMCTL 0x63 +#define D0F3REOPMCTL_1 0x64 +#define D0F3DARBTIM 0x65 +#define D0F3DQUARB 0x66 +#define D0F3DIMCHSEL 0x67 +#define D0F3PGCTIM 0x68 +#define D0F3BAINLVCTL 0x69 +#define D0F3RQREOCTL 0x6a +#define D0F3DINITCTL 0x6b +#define D0F3DTYPE 0x6c +#define D0F3INORSERC 0x6c +#define D0F3DATBURCTL 0x6e + +#define D0F3IOTIMSEL 0x70 +#define D0F3DITIMCTL 0x71 +#define D0F3PGSCLSAL 0x73 +#define D0F3RDPHCTL 0x74 +#define D0F3DOTIMCTL 0x75 +#define D0F3WDPHCTL 0x76 +#define D0F3DQSOCTL 0x77 +#define D0F3DQG0IOTC 0x78 + +#define D0F3SEGFSHAD 0x83 +#define D0F3SMAPICDC 0x86 +#define D0F3WMGCTL 0x88 +#define D0F3DTSTMDC 0x8c +#define D0F3ADRIOCAL 0x8c +#define D0F3ADRIOCAL_1 0x8d +#define D0F3DQPIOCAL 0x8e + +#define D0F3DCLKOPMD 0x90 +#define D0F3RSELFREF2 0x98 +#define D0F3PMBYREOQ 0x99 +#define D0F3MCLKAEN 0x9b +#define D0F3ODTLTBL 0x9c +#define D0F3ODTCTL 0x9e +#define D0F3ODTCTL2 0x98 + +#define D0F3DTIMING5 0xc4 +#define D0F3DTIMING6 0xc5 +#define D0F3DTIMING7 0xc6 +#define D0F3REFCNT 0xc7 +#define D0F3DINIT1 0xcc + +#define DRAM_ATTRIBUTE rank_layout + +void io_timing_control(DRAM_ATTRIBUTE *dram_attr); + + +//================================================================== +// DDR3 Code Start{ +//================================================================== + + +#define TX_DQ_Clock_Address_H 0x10 +#define TX_DQ_Clock_Pattern 0x5A + +#define RX_Input_Enable_Clock_Address_H 0x20 +#define RX_Input_Enable_Clock_Pattern 0x5A + +#define RX_Input_DQS_Clock_Address_H 0x30 +#define RX_Input_DQS_Clock_Pattern 0x5A + +#define MCU PCI_DEV(0, 0, 3) + +#define D0F3IOTIMSEL_DMIOSEL3_0 0xf + +#define D0F3IOTIMSEL_DMIOSEL_MANUAL 0x0 +#define D0F3IOTIMSEL_DMIOSEL_CENTER 0x0 +#define D0F3IOTIMSEL_DMIOSEL_LOWER 0x1 +#define D0F3IOTIMSEL_DMIOSEL_UPPER 0x2 + +#define D0F3IOTIMSEL_DMIOSEL_DQSO (0<<2) +#define D0F3IOTIMSEL_DMIOSEL_DQO (1<<2) +#define D0F3IOTIMSEL_DMIOSEL_DQSI (2<<2) +#define D0F3IOTIMSEL_DMIOSEL_RDSIT (3<<2) + + +static void set_io_timing_si_register(uint8_t io_timing_mode, uint8_t reg_off, uint8_t nand_data, uint8_t or_data) +{ + uint8_t d8; + + switch (io_timing_mode) { + + case IO_RDSIT: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_RDSIT+D0F3IOTIMSEL_DMIOSEL_MANUAL); + break; + + case IO_DQO: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_MANUAL); + break; + + case IO_DQSO: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_MANUAL); + + case IO_DQSI: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSI+D0F3IOTIMSEL_DMIOSEL_MANUAL); + break; + + default: + break; + } + + + d8= pci_read_config8(MCU, reg_off); + d8 = (d8 &(~nand_data))|or_data; + + switch (io_timing_mode) + { + case IO_RDSIT: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_RDSIT+D0F3IOTIMSEL_DMIOSEL_MANUAL); + break; + + case IO_DQO: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_MANUAL); + break; + + case IO_DQSO: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_MANUAL); + break; + + case IO_DQSI: + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSI+D0F3IOTIMSEL_DMIOSEL_MANUAL); + break; + + default: + break; + } + + pci_write_config8(MCU, reg_off, d8); +} + +static void dram_ioctl(uint8_t io_timing_mode, uint8_t io_selection_mode, uint8_t is_write, uint8_t byte_i, uint8_t *d8) +{ + uint8_t or_data, and_data; + + or_data = 0; + and_data = 0; + switch(io_timing_mode) + { + case IO_RDSIT: + or_data = D0F3IOTIMSEL_DMIOSEL_RDSIT; + break; + + case IO_DQO: + or_data = D0F3IOTIMSEL_DMIOSEL_DQO; + break; + + case IO_DQSO: + or_data = D0F3IOTIMSEL_DMIOSEL_DQSO; + break; + + case IO_DQSI: + or_data = D0F3IOTIMSEL_DMIOSEL_DQSI; + break; + default: + break; + } + + switch(io_selection_mode) + { + case IOCTL_MANUAL: + or_data |= D0F3IOTIMSEL_DMIOSEL_MANUAL; + break; + + case IOCTL_CENTER: + or_data |= D0F3IOTIMSEL_DMIOSEL_CENTER; + break; + + case IOCTL_LOW: + or_data |= D0F3IOTIMSEL_DMIOSEL_LOWER; + break; + + case IOCTL_HIGH: + or_data |= D0F3IOTIMSEL_DMIOSEL_UPPER; + break; + + default: + break; + } + + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, or_data); + +#define D0F3DITIMCTL_RSETDSIT 0x10 +#define D0F3DITIMCTL_RSETDSIDLY 0x01 +#define D0F3DOTIMCTL_RSETTXCLK 0x01 + + + if (io_selection_mode == IOCTL_MANUAL) + { + switch(io_timing_mode) + { + case IO_RDSIT: + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIT, D0F3DITIMCTL_RSETDSIT); + break; + + case IO_DQO: + case IO_DQSO: + pci_modify_config8(MCU, D0F3DOTIMCTL, D0F3DOTIMCTL_RSETTXCLK, D0F3DOTIMCTL_RSETTXCLK); + break; + + case IO_DQSI: + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIDLY, D0F3DITIMCTL_RSETDSIDLY); + break; + + default: + break; + } + }else + { + switch(io_timing_mode) + { + case IO_RDSIT: + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIT, 0); + break; + case IO_DQO: + case IO_DQSO: + pci_modify_config8(MCU, D0F3DOTIMCTL, D0F3DOTIMCTL_RSETTXCLK, 0); + break; + + case IO_DQSI: + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIDLY, 0); + break; + + default: + break; + } + } + + if (is_write){ + pci_write_config8(MCU, (D0F3DQG0IOTC + byte_i), *d8); + }else + { + *d8 = pci_read_config8(MCU, (D0F3DQG0IOTC + byte_i)); + } +} + +static const u8 OutputTiming_Tbl[8][8]; +static const u8 OutputTiming_Tbl_Items = 0; +static const u8 ReadTiming_Tbl[8][8]; +static const u8 ReadTiming_Tbl_Items = 0; + +static const u8 SIDramMask = 0; +static const u8 SIDramAttr = 0; +static const u8 SIDramReg = 0; + +#define Normal 0 + +static void preset_si_normal_table(DRAM_ATTRIBUTE *dram_attr) +{ + + uint8_t reg_i, or_data, freq_i; + + uint8_t io_timing_mode = IO_DQO; + + freq_i = 0; + /*FIXME: + switch (dram_attr->DRAMFreq) { + case DRAMFREQ_800: freq_i = SIDramFreq800; break; + case DRAMFREQ_1066: freq_i = SIDramFreq1066; break; + case DRAMFREQ_1333: freq_i = SIDramFreq1333; break; + default: freq_i = SIDramFreq800; break; + } + */ + + for (reg_i=0; reg_i< OutputTiming_Tbl_Items; reg_i++) + { + + or_data = shift_to_mask_bit(OutputTiming_Tbl[reg_i][SIDramMask], OutputTiming_Tbl[reg_i][freq_i]); + switch (OutputTiming_Tbl[reg_i][SIDramAttr]) + { + case Normal: + set_io_timing_si_register( io_timing_mode, OutputTiming_Tbl[reg_i][SIDramReg], OutputTiming_Tbl[reg_i][SIDramMask], or_data); + break; + default: + break; + } + } + + io_timing_mode = IO_RDSIT; + for (reg_i=0; reg_i< ReadTiming_Tbl_Items; reg_i++) + { + or_data = shift_to_mask_bit(ReadTiming_Tbl[reg_i][SIDramMask], ReadTiming_Tbl[reg_i][freq_i]); + switch (ReadTiming_Tbl[reg_i][SIDramAttr]) + { + case Normal: + set_io_timing_si_register( io_timing_mode, ReadTiming_Tbl[reg_i][SIDramReg], ReadTiming_Tbl[reg_i][SIDramMask], or_data); + break; + default: + break; + } + } +} + + +static void preset_tx_dqo(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t i, d8; + if(1)//(dram_attr->DRAMFreq >= DRAMFREQ_1066) + { + for (i = 0; i < 8 ; i++) + { + //Set DQO Rx70-78 bit 7 = 1 when DRAMFReq_1066 + d8 = 0x80; //Set RDQWADJ //3410-24-LNA-DRAM + dram_ioctl( IO_DQSO, IOCTL_MANUAL, TRUE, i, &d8);//3410-24-LNA-DRAM + d8 = 0x80; //Set RDQWADJ + dram_ioctl( IO_DQO, IOCTL_MANUAL, TRUE, i, &d8); + } + } +} + +static void preset_rdswadvos(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t i, d8; + + if(1)//(dram_attr->DRAMFreq >= DRAMFREQ_1066) + { + for (i = 0; i < 8 ; i++) + { + //Set DQO Rx70-78 bit 7 = 1 when DRAMFReq_1066 + d8 = 0x80; + dram_ioctl( IO_DQSO, IOCTL_MANUAL, TRUE, i, &d8); + d8 = 0x80; + dram_ioctl( IO_DQO, IOCTL_MANUAL, TRUE, i, &d8); + } + } +} + +static void preset_for_lcu_hw(DRAM_ATTRIBUTE *dram_attr) +{ + //uint8_t Index, Value8; + preset_tx_dqo(dram_attr); +} + +#define D0F3DITIMCTL_RDLLDSIDLY 0x04 +static void rx_input_delay_save(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t i = 0; + uint8_t d8 = 0; + + //set Rx71[2] = 1 + pci_modify_config8(MCU, D0F3DITIMCTL, + D0F3DITIMCTL_RDLLDSIDLY, D0F3DITIMCTL_RDLLDSIDLY); + + for (i = 0; i < 8; i++){ + dram_ioctl( IO_DQSI, IOCTL_MANUAL, FALSE,i, &d8); + dram_ioctl( IO_DQSI, IOCTL_MANUAL, TRUE,i, &d8); + } + + //set Rx71[2] = 0 + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RDLLDSIDLY, 0); +} + +void set_vr_map(uint8_t dimm_i, uint8_t vr_i); +uint32_t get_ddr3_mr0(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type); +uint32_t get_ddr3_mr1(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type); +uint32_t get_ddr3_mr2(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type); +uint32_t get_ddr3_mr3(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type); +uint32_t rank1_address_mirror(uint32_t mrs_value); +void udelay(unsigned usecs); +static void rx_input_delay_hw(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t save_rx52,save_rx53,save_rxc7; + uint8_t i,tmp; + uint32_t buffer32=0, dummy_read_base=0x00; + uint32_t mrs_set; + + //Step1.Save Rx52, 53 and set Rx52=33, Rx53=5B before RDSIT CAL, NeedeCheck + save_rx52 = pci_read_config8(MCU, D0F3INLVSEL); + pci_write_config8(MCU, D0F3INLVSEL, 0x33); + + save_rx53 = pci_read_config8(MCU, D0F3INLVSEL_1); + pci_write_config8(MCU, D0F3INLVSEL_1, 0x5B); + + //save refresh counter value and disable it + save_rxc7 = pci_read_config8(MCU, D0F3REFCNT); + pci_write_config8(MCU, D0F3REFCNT, 0); + + //Step 2. Clear all VRank size + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + pci_write_config8(MCU, (D0F3VR0BA+i), 0); + pci_write_config8(MCU, (D0F3VR0EA+i), 0); + } + + // Step 3. Map ChA lowest physical rank to VR0 and ChB lowest physical rank to VR4 + //If DIMM exist, ChA, map to VR3; ChB, map to VR7 + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + set_vr_map(i, 0x03); + } + } + + //Map the lowest rank to VR0/VR4 + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + set_vr_map( i, 0x00); + pci_write_config8(MCU, D0F3VR0EA, 0x01); + break; + } + } + + +#define RDMTSA_31_24 0xff +#define D0F3DINITCTL_RSDM2_0 0x07 +#define D0F3DINITCTL_RSDM_NORMAL 0x00 +#define D0F3DINITCTL_RSDM_PREA 0x02 +#define D0F3DINITCTL_RSDM_MRS 0x03 + +#define D0F3DITIMCTL_RDMRLEN 0x80 +#define D0F3DITIMCTL_RDRLDQPAT 0x40 + //Step 4. Set IO calibration address + pci_modify_config8(MCU, D0F3ADRIOCAL_1 , RDMTSA_31_24, 0); + pci_modify_config8(MCU, D0F3ADRIOCAL , 0xF0, RX_Input_DQS_Clock_Address_H); + //Precharge all + pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_PREA); + buffer32 = *(uint32_t volatile *)(uint32_t)(dummy_read_base); // Dummy Read + udelay( 1000); //delay 1ms + + //Step 5. Set MRS mode + pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_MRS); + + //Step6.Enable read leveling and set return data format + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RDMRLEN|D0F3DITIMCTL_RDRLDQPAT, D0F3DITIMCTL_RDMRLEN); + + //Step 8. Trigger MRS3 with MPR enable + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + buffer32 = get_ddr3_mr3( dram_attr, i, MR3_MPR_ON); + mrs_set = buffer32; + //if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED) + //{ + // mrs_set = rank1_address_mirror(buffer32); + //} + buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set); // Dummy Read + outb(0x00, 0xed); + break; + } + } + + //Step9. Set Normal mode + pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_NORMAL); + +#define D0F3ODTCTL_RODTEN 0X80 +#define D0F3DITIMCTL_RSCNDSIDLY 0x02 + pci_modify_config8(MCU, D0F3ODTCTL, D0F3ODTCTL_RODTEN, D0F3ODTCTL_RODTEN); + + //Step10.Set D0F3Rx71[1:0]=10b + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSCNDSIDLY|D0F3DITIMCTL_RSETDSIDLY, D0F3DITIMCTL_RSCNDSIDLY); + + //Step 11. Check if calibration is done + do{ + tmp = 0xFF; + tmp = pci_read_config8(MCU, D0F3DITIMCTL); + tmp = tmp & D0F3DITIMCTL_RSCNDSIDLY; + }while(tmp != 0); + + //Step 12. Set MRS mode + pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_MRS); + + //Step 13. Trigger MRS3 with MPR disable + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + buffer32 = get_ddr3_mr3(dram_attr, i, MR3_INIT); + mrs_set = buffer32; + //if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED) + //{ + // mrs_set = rank1_address_mirror(buffer32); + //} + buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set); // Dummy Read + outb(0x00, 0xed); + break; + } + } + + //Step 14. Set Normal mode + pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_NORMAL); + + //Step 16. Set D0F3Rx71[7]=0 to disable read leveling + pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RDMRLEN, 0); + + //restore refresh counter value + pci_write_config8(MCU, D0F3REFCNT, save_rxc7); + + //Restore Rx52, 53 + pci_write_config8(MCU, D0F3INLVSEL, save_rx52); + pci_write_config8(MCU, D0F3INLVSEL_1, save_rx53); +} + +static void tx_dws_clock_hw(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t save_rx52, save_rx53, save_rxc7; + uint8_t i,tmp; + uint32_t buffer32=0,dummy_read_base=0x00; + uint32_t mrs_set; + uint8_t save_begin[CHIP_MAX_RANKS]; + uint8_t save_end[CHIP_MAX_RANKS]; + uint8_t save_rx9e,save_rx9c; + uint8_t save_rx54, save_rx55; + uint8_t cha_cal_rank_selected = 0; + uint8_t cal_rank_index = 0; + + //Step1.Save Rx52, 53 and set Rx52=33, Rx53=5B + save_rx52 = pci_read_config8(MCU, D0F3INLVSEL); + pci_write_config8(MCU, D0F3INLVSEL, 0x33); + + save_rx53 = pci_read_config8(MCU, D0F3INLVSEL_1); + pci_write_config8(MCU, D0F3INLVSEL_1, 0x5B); + + //save all VR begin/End addr, and clear them + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + save_begin[i] = pci_read_config8(MCU, (D0F3VR0BA+i)); + pci_write_config8(MCU, (D0F3VR0BA+i), 0); + save_end[i] = pci_read_config8(MCU, (D0F3VR0EA+i)); + pci_write_config8(MCU, (D0F3VR0EA+i), 0); + } + + //save ODT Control register and disable ODT control + save_rx9e = pci_read_config8(MCU, D0F3ODTCTL); + pci_write_config8(MCU, D0F3ODTCTL, (save_rx9e & (~D0F3ODTCTL_RODTEN))); + + //save ODT Lookup Table and set 11100100b for ChA + save_rx9c = pci_read_config8(MCU, D0F3ODTLTBL); + pci_write_config8(MCU, D0F3ODTLTBL, 0xE4); + + //save refresh counter value and disable it + save_rxc7 = pci_read_config8(MCU, D0F3REFCNT); + pci_write_config8(MCU, D0F3REFCNT, 0); + + //save Rx54-57 + save_rx54 = pci_read_config8(MCU, D0F3PVRMAP); + save_rx55 = pci_read_config8(MCU, D0F3PVRMAP_1); + +#define D0F3BAINLVCTL_RPGEN 0x01 + //Disable Page Mode and Disable Page Mode of multibanks + pci_modify_config8(MCU, D0F3BAINLVCTL, D0F3BAINLVCTL_RPGEN, 0); + + //set MRS mode + pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_MRS); + + // Step 3. Map ChA lowest physical rank to VR0 and ChB lowest physical rank to VR4 + //If DIMM exist, ChA, map to VR3; ChB, map to VR7 + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + set_vr_map( i, 0x03); + } + } + + //A Find the lowest rank existed on ChA and set MRS1 to (WLVL = enabled) and (Rtt_Nom = 120ohm) + //Set other rank existed to (WLVL = enabled) and (Qoff = disabled) + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + set_vr_map( i, 0x00); + pci_write_config8(MCU, D0F3VR0EA, 0x01); + if (cha_cal_rank_selected == 0) + { + //set MRS1 to (WLVL = enabled) and (Rtt_Nom = 120ohm) + buffer32 = get_ddr3_mr1( dram_attr, i, MR1_WLVL_UNDERCALPR); + mrs_set = buffer32; + //if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED) + //{ + // mrs_set = rank1_address_mirror(buffer32); + //} + buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set); // Dummy Read + outb(0x00, 0xed); + cal_rank_index = i; + cha_cal_rank_selected = 1; + }else{ + //set MRS1 to (WLVL = enabled) and (Qoff = disabled) + buffer32 = get_ddr3_mr1( dram_attr, i, MR1_WLVL_OTHERPR); + mrs_set = buffer32; + //if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED) + //{ + // mrs_set = rank1_address_mirror( buffer32); + //} + buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set); // Dummy Read + outb(0x00, 0xed); + } + //Map to VR3 and Clear End Address + set_vr_map( i, 0x03); + pci_write_config8(MCU, D0F3VR0EA, 0x00); + } + } + +#define D0F3ODTCTL_RDMWLPR_Offset 2 +#define D0F3ODTCTL_RDMWLPR1_0 0xc0 + +#define D0F3DOTIMCTL_RDMWLEN 0x04 + //Step 8. Set which physical rank is under write leveling + tmp = cal_rank_index << D0F3ODTCTL_RDMWLPR_Offset; + pci_modify_config8(MCU, D0F3ODTCTL, D0F3ODTCTL_RDMWLPR1_0, tmp); + + //Step 9. Start Write Leveling + pci_modify_config8(MCU, D0F3DOTIMCTL, D0F3DOTIMCTL_RDMWLEN, D0F3DOTIMCTL_RDMWLEN); + + //Step10.Check if Write Leveling is done + do{ + tmp = pci_read_config8(MCU, D0F3DOTIMCTL); + }while((tmp & D0F3DOTIMCTL_RDMWLEN)!=0); + + //Step11.Stop Write Leveling + pci_modify_config8(MCU, D0F3DOTIMCTL, D0F3DOTIMCTL_RDMWLEN, 0); + + //Step 17. Restore all existed ranks MR1 to normal value + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + set_vr_map( i, 0x00); + pci_write_config8(MCU, D0F3VR0EA, 0x01); + + //set MRS1 to normal value + buffer32 = get_ddr3_mr1( dram_attr, i, MR1_INIT); + mrs_set = buffer32; + //if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED) + //{ + // mrs_set = rank1_address_mirror( buffer32); + //} + buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set); // Dummy Read + outb(0x00, 0xed); + + //Map to VR3 and Clear End Address + set_vr_map( i, 0x03); + pci_write_config8(MCU, D0F3VR0EA, 0x00); + } + } + + //Step18.Assign PR number to VR number <-Tony_debug maybe remove Step 18 + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + set_vr_map( i, i); + } + + //Restore Rx54-57 + pci_write_config8(MCU, D0F3PVRMAP_1, save_rx55); + pci_write_config8(MCU, D0F3PVRMAP, save_rx54); + + //Step 19. Set Normal mode + pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_NORMAL); + + //Enable Page Mode and Enable Page Mode of multibanks + pci_modify_config8(MCU, D0F3BAINLVCTL, D0F3BAINLVCTL_RPGEN, D0F3BAINLVCTL_RPGEN + ); + + //Step 20. Restore auto refresh value + pci_write_config8(MCU, D0F3REFCNT, save_rxc7); + + //Step 22. Restore ChA ODT lookup table + pci_write_config8(MCU, D0F3ODTLTBL, save_rx9c); + + //Step 23. Restore ODT control + pci_write_config8(MCU, D0F3ODTCTL, save_rx9e); + + //Restore all ranks' begin/end address + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + pci_write_config8(MCU, (D0F3VR0BA+i), save_begin[i]); + pci_write_config8(MCU, (D0F3VR0EA+i), save_end[i]); + } + + //Restore Rx52, 53 + pci_write_config8(MCU, D0F3INLVSEL, save_rx52); + pci_write_config8(MCU, D0F3INLVSEL_1, save_rx53); +} + +static void tx_dqs_clock_lcuhw(DRAM_ATTRIBUTE *dram_attr, uint8_t dimm_no) //3410-32-LNA-01 +{ + uint8_t i,tmp; + uint8_t d8; + + //Step 2. Clear all VRank size + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + pci_write_config8(MCU, (D0F3VR0BA+i), 0); + pci_write_config8(MCU, (D0F3VR0EA+i), 0); + } + + + //If DIMM exist, ChA, map to VR3; ChB, map to VR7 + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + set_vr_map( i, 0x03); + } + } + + + //Step 3. Set the begin/end address of the lowest virthual rank of channel A/B to 0~256 + //Because we will assign PR number to VR number, so we could check the PR rank map + //to determine which VR we should program. + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + if(dimm_no == CAL_SECOND_DIMM) + { + pci_write_config8(MCU, D0F3PVRMAP_1, 0xB8); + pci_write_config8(MCU, D0F3PVRMAP, 0xBB); + }else //3410-32-LNA-01 + { + set_vr_map( i, 0x00); + } + pci_write_config8(MCU, D0F3VR0EA, 0x01); + break; + } + } + +#define D0F3BAINLVCTL_RNONPGMEN 0 +#define RDMTSA_23_20 0xf0 +#define D0F3DOTIMCTL_RSCNTXCLKS 0x20 + //Disable Page Mode and Disable Page Mode of multibanks, NeedCheck Why MUST use this??? + d8 = pci_read_config8(MCU, D0F3BAINLVCTL); + pci_modify_config8(MCU, D0F3BAINLVCTL, D0F3BAINLVCTL_RNONPGMEN + D0F3BAINLVCTL_RPGEN, D0F3BAINLVCTL_RNONPGMEN); + + //Step 3. Program F3Rx8E, set IO calibration data pattern + pci_write_config8(MCU, D0F3DQPIOCAL, 0x5A); + + //Step 4. Set F3Rx8D[7:0] and F3Rx8C[7:4] to the value + //which is different with that for the upper calibration + pci_modify_config8(MCU, D0F3ADRIOCAL_1,RDMTSA_31_24, 0); + pci_modify_config8(MCU, D0F3ADRIOCAL,RDMTSA_23_20, 0); + + //Step 6. Start Calibration, set auto mode for Internal Clock Phase for DQ/DQS output + pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSETTXCLK, 0); + + //Step 7. enable scan internal clock phase for DQS output + pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSCNTXCLKS, D0F3DOTIMCTL_RSCNTXCLKS); + + //Step 8. Check if calibration is done + do{ + tmp = pci_read_config8(MCU, D0F3DOTIMCTL); + }while((tmp & D0F3DOTIMCTL_RSCNTXCLKS) != 0); + + //second test use different pattern + //Step 3. Program F3Rx8E, set IO calibration data pattern + pci_write_config8(MCU, D0F3DQPIOCAL, 0xA5); + + //Step 7. enable scan internal clock phase for DQS output + pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSCNTXCLKS, D0F3DOTIMCTL_RSCNTXCLKS); + + //Step 8. Check if calibration is done + do{ + tmp = pci_read_config8(MCU, D0F3DOTIMCTL); + }while((tmp & D0F3DOTIMCTL_RSCNTXCLKS) != 0); + + //Enable Page Mode and Enable Page Mode of multibanks + pci_write_config8(MCU, D0F3BAINLVCTL,d8); + +} + +static void tx_dq_clock_hw(DRAM_ATTRIBUTE *dram_attr, uint8_t diomm_no) +{ + uint8_t i,tmp; + + //Step 2. Clear all VRank size + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + pci_write_config8(MCU, (D0F3VR0BA+i), 0); + pci_write_config8(MCU, (D0F3VR0EA+i), 0); + } + + //If DIMM exist, ChA, map to VR3; ChB, map to VR7 + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + set_vr_map( i, 0x03); + } + } + + //Step 3. Set the begin/end address of the lowest virthual rank of channel A/B to 0~256 + //Because we will assign PR number to VR number, so we could check the PR rank map + // to determine which VR we should program. + for(i = 0; i < CHIP_MAX_RANKS; i++) + { + if(dram_attr->phys_rank_size[i] != 0) + { + if(diomm_no == CAL_SECOND_DIMM) + { + set_vr_map( 2, 0x00); //set rank2 + }else + { + set_vr_map( i, 0x00); + } + pci_write_config8(MCU, D0F3VR0EA, 0x01); + break; + } + } + + //Step 4. Set IO calibration address + pci_modify_config8(MCU, D0F3ADRIOCAL_1,RDMTSA_31_24, 0); + pci_modify_config8(MCU, D0F3ADRIOCAL,RDMTSA_23_20, 0); + + //step5.Set IO calibration pattern + pci_write_config8(MCU, D0F3DQPIOCAL, 0x5A); + //set Rx75[0]=0 + pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSETTXCLK, 0); +#define D0F3DOTIMCTL_RSCNTXCLKD 0x02 + //Step 6. Enable scan internal clock phase for DQ output + pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSCNTXCLKD, D0F3DOTIMCTL_RSCNTXCLKD); + + //Step 7. Check if DQ output clock calibration is done + do{ + tmp = pci_read_config8(MCU, D0F3DOTIMCTL); + }while((tmp&D0F3DOTIMCTL_RSCNTXCLKD)!= 0x00); +} + + +static void rx_input_delay_cal_to_manual(DRAM_ATTRIBUTE *dram_attr, uint16_t * p) +{ + uint8_t i, d8, d81; + + + //for (i = 0; i < 8; i++){ + for (i = 0; i < 8; i++) + { + dram_ioctl(IO_DQSI,IOCTL_CENTER,FALSE,i,&d8); + + dram_ioctl(IO_DQSI,IOCTL_LOW,FALSE,i,&d81); + if(d81 < 3) + { + if(i == 0x02 || i == 0x04) + { + d8 += 0x04; + }else + { + d8 += 0x03; + } + } + //3410-25-LNA-01-end + + //3410-24-LNA-DRAM-end + //3410-31-LNA-03-start + dram_ioctl(IO_DQSI,IOCTL_HIGH,FALSE,i,&d81); + if (d81 > 0x38) + { + d8 -= 0x06; + }else if(d81 > 0x30) + { + d8 -= 0x04; + } + + if (d8 > 0x20) + { + d8 = 0x20; + } + + p[i] += d8;//3410-42-LNA-02 + } +} + +static void tx_dqs_clock_cal_to_manual(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t i,d8; + + for (i = 0; i < 8; i++) + { + dram_ioctl(IO_DQSO,IOCTL_CENTER, FALSE, i, &d8); + dram_ioctl(IO_DQSO,IOCTL_MANUAL, TRUE, i, &d8); + } +} + +static void tx_dq_clock_cal_to_mannual(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t i,d8; + + for (i = 0; i < 8; i++) + { + dram_ioctl(IO_DQO,IOCTL_CENTER, FALSE, i, &d8); + dram_ioctl(IO_DQO,IOCTL_MANUAL, TRUE, i, &d8); + } +} + + +#define DQSI_THRESHOLD 0x10 +//3410-30-LNA-02 #define DQSI_THRESHOLD_1066 0x20 //3410-21-LNA-04 +#define DQSI_THRESHOLD_1066 0x10 //3410-30-LNA-02 //for OC 10%+Vcore=1.00V +static BOOLEAN check_dqsi_range(DRAM_ATTRIBUTE *dram_attr) +{ + uint8_t i, low, high; + BOOLEAN flag; + + flag = TRUE; + //check DQSI range, if one of 8 bytes is less than 10h, return FALSE. + for(i = 8; i > 0; i--) + { + dram_ioctl(IO_DQSI,IOCTL_LOW,FALSE,i-1,&low); + dram_ioctl(IO_DQSI,IOCTL_HIGH,FALSE,i-1,&high); + + if(1)//(dram_attr->DRAMFreq == DRAMFREQ_1066) + { + if((high -low) < DQSI_THRESHOLD_1066) + { + flag = FALSE; + break; + } + }else { + if ((high - low) < DQSI_THRESHOLD) + { + flag = FALSE; + break; + } + } + } + + return flag; +} + +#define DQSO_THRESHOLD 0x12 +static BOOLEAN check_dqso_range(void) +{ + //check DQSO range, if one of 8 bytes is less than 18, set CF = 1 + uint8_t i, low, high; + BOOLEAN flag = TRUE; + + for(i = 0; i < 8; i++) + { + dram_ioctl(IO_DQSO,IOCTL_LOW,FALSE,i,&low); + if(low == 0x7F) + { + printram("DQS lower bound error\n"); + flag = FALSE; + break; + } + dram_ioctl(IO_DQSO,IOCTL_HIGH,FALSE,i,&high); + if((high - low) < DQSO_THRESHOLD) + { + printram("DQS range error at %u with " + "0x%.2x - 0x%.2x = 0x%.2x \n", i, high, low, high - low); + flag = FALSE; + break; + } + } + return flag; +} + + +#define LOW_BOUND_OFFSET 0x0-0x5 +#define HIGH_BOUND_OFFSET 0x5 +#define DQO_THRESHOLD 0x09 //3410-36-LNA-03 + +static BOOLEAN check_dqo_range(void) +{ + uint8_t i, low,high; + BOOLEAN flag; + + flag = TRUE; + //check DQSI range, if one of 8 bytes is less than 9h, return FALSE. + for(i = 8; i > 0; i--) + { + dram_ioctl(IO_DQO,IOCTL_LOW,FALSE,i-1,&low); + if (low == 0x7F) + { + flag = FALSE; + printram("DQ lower bound error\n"); + break; + } + dram_ioctl(IO_DQO,IOCTL_HIGH,FALSE,i-1,&high); + if((high - low) < DQO_THRESHOLD) + { + flag = FALSE; + printram("DQ Range error\n"); + break; + } + } + + return flag; +} + + +static void rx_input_enable_si2(DRAM_ATTRIBUTE *dram_attr) +{ + //uint32_t count; + //uint8_t d8; + u8 dram_freq; + + dram_freq = DRAMFREQ_1066; //dram_attr->DRAMFreq; + + pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_RDSIT+D0F3IOTIMSEL_DMIOSEL_MANUAL); + //4310-32-LNA-01-start +#if 0 + if(dram_attr->ModuleType == MODULE_TYPE_SODIMM) + { + switch(dram_attr->RawCardType) + { + case RAW_CARD_AO: + case RAW_CARD_AA: + case RAW_CARD_AB: + case RAW_CARD_BO: + case RAW_CARD_BA: + case RAW_CARD_BB: + case RAW_CARD_OA: + case RAW_CARD_OB: + for(count = 0; count < RdsitRefRawCardABTbl_Items; count++) + { + if(dram_freq == DRAMFREQ_1333) + { + d8 = RdsitRefRawCardABTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET]; + }else if(dram_freq == DRAMFREQ_1066) + { + d8 = RdsitRefRawCardABTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET]; + }else + { + d8 = RdsitRefRawCardABTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET]; + } + pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8); + } + break; + case RAW_CARD_AF: + case RAW_CARD_BF: + case RAW_CARD_FA: + case RAW_CARD_FB: + for(count = 0; count < RdsitRefRawCardABFTbl_Items; count++) + { + if(dram_freq == DRAMFREQ_1333) + { + d8 = RdsitRefRawCardABFTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET]; + }else if(dram_freq == DRAMFREQ_1066) + { + d8 = RdsitRefRawCardABFTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET]; + }else + { + d8 = RdsitRefRawCardABFTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET]; + } + pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8); + } + break; + case RAW_CARD_FO: + case RAW_CARD_FF: + case RAW_CARD_OF: + for(count = 0; count < RdsitRefRawCardFTbl_Items; count++) + { + if(dram_freq == DRAMFREQ_1333) + { + d8 = RdsitRefRawCardFTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET]; + }else if(dram_freq == DRAMFREQ_1066) + { + d8 = RdsitRefRawCardFTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET]; + }else + { + d8 = RdsitRefRawCardFTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET]; + } + pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8); + } + + break; + default: + break; + } + + } else +#endif +/* FIXME: + { + for(count = 0; count < RdsitRefUDIMMTbl_Items; count++) + { + if(dram_freq == DRAMFREQ_1333) + { + d8 = RdsitRefUDIMMTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET]; + }else if(dram_freq == DRAMFREQ_1066) + { + d8 = RdsitRefUDIMMTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET]; + }else + { + d8 = RdsitRefUDIMMTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET]; + } + pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8); + } + } + */ + pci_write_config8(MCU, 0x78, 0x28); + pci_write_config8(MCU, 0x79, 0x1c); + pci_write_config8(MCU, 0x7a, 0x28); + pci_write_config8(MCU, 0x7b, 0x28); + pci_write_config8(MCU, 0x7c, 0x2c); + pci_write_config8(MCU, 0x7d, 0x30); + pci_write_config8(MCU, 0x7e, 0x30); + pci_write_config8(MCU, 0x7f, 0x34); +} + +//3410-22-LNA-DRAM-end + +//3410-32-LNA-01-start +static void set_average_setting(DRAM_ATTRIBUTE *dram_attr) +{ + /*FIXME: + uint8_t i,d81, d82,d83, d84; + //set TxDQS + for (i = 0; i < 8; i++) + { + d81 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDqs_Upper_Bound; + d82 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDqs_Upper_Bound; + if( d81 > d82) + { + d81 = d82; + } + d83 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDqs_Lower_Bound; + d84 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDqs_Lower_Bound; + if(d83 < d84) + { + d83 = d84; + } + d81 += d83; + d81 >>= 1; + d82 = d81 & 0x1F; + if(d82 >= 0x1C) + { + d81 -= 0x1C; + }else + { + d81 += 0x04; + } + dram_ioctl(IO_DQSO,IOCTL_MANUAL,TRUE,i,&d81); + } + //set TxDQ + for (i = 0; i < 8; i++) + { + d81 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDq_Upper_Bound; + d82 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDq_Upper_Bound; + if( d81 > d82) + { + d81 = d82; + } + d83 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDq_Lower_Bound; + d84 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDq_Lower_Bound; + if( d83 < d84) + { + d83 = d84; + } + d81 += d83; + d81 >>= 1; + d82 = d81 & 0x1F; + if(d82 >= 0x14) + { + d81 -= 0x14; + }else + { + d81 += 0x0C; + } + dram_ioctl(IO_DQO,IOCTL_MANUAL,TRUE,i,&d81); + } + */ + +} +static void tx_dqs_clock_save_setting(DRAM_ATTRIBUTE *dram_attr, uint8_t dimm_no) +{ + /* FIXME: + uint8_t i; + pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_UPPER); + //save TX DQS Upper Bound + for (i = 0; i < 8; i++) + { + dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDqs_Upper_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i)); + } + //save TX DQS Center Bound + pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_CENTER); + for (i = 0; i < 8; i++) + { + dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDqs_Center = pci_read_config8(MCU, (D0F3DQG0IOTC + i)); + } + //save TX DQS Lower Bound + pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_LOWER); + for (i = 0; i < 8; i++) + { + dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDqs_Lower_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i)); + } + */ +} +static void tx_dq_clock_save_setting(DRAM_ATTRIBUTE *dram_attr, uint8_t dimm_no) +{ + /*FIXME: + uint8_t i; + pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_UPPER); + //save TX DQS Upper Bound + for (i = 0; i < 8; i++) + { + dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDq_Upper_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i)); + } + //save TX DQS Center Bound + pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_CENTER); + for (i = 0; i < 8; i++) + { + dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDq_Center = pci_read_config8(MCU, (D0F3DQG0IOTC + i)); + } + //save TX DQS Lower Bound + pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_LOWER); + for (i = 0; i < 8; i++) + { + dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDq_Lower_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i)); + } + */ +} + +//defined in init_dram_by_rank.c +void reinit_dram(DRAM_ATTRIBUTE *dram_attr); + +#define USE_WLVL 0 + + + +//================================================================== +// DDR3 Code End} +//================================================================== + + +void set_vr_map(uint8_t dimm_i, uint8_t vr_i) +{ + uint8_t vr_reg_off; + uint8_t d8; + + vr_reg_off = D0F3PVRMAP + ((dimm_i & 6) >> 1); + + d8 = pci_read_config8(MCU, vr_reg_off); // 0x54 - 0x55 + if (dimm_i & 8) + { + if (dimm_i & 0x10) + { + d8 = 0; + } else + { + d8 = 0x88; + } + } else { + if (dimm_i & 1) + { + d8 &= 0x0F; + d8 |= (vr_i << 4); + if (!(dimm_i & 0x10)) + { + d8 |= 0x80; + } + } else { + d8 &= 0xF0; + d8 |= vr_i; + if (!(dimm_i & 0x10)) + { + d8 |= 0x08; + } + } + } + + pci_write_config8( MCU, vr_reg_off, d8); // 0x54 - 0x55 +} + +uint32_t get_ddr3_mr3(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type) +{ + + //action_type - action type [MR3_Init/MR3_MPR_On] + + u32 mr3_setting = DDR3_MR3; + + if (action_type == MR3_MPR_ON) + { + mr3_setting |= MR3_DDR3_MPR_EN; + } + return mr3_setting; + +} + +static void dbg_dump_0x78_0x7f(void) +{ + u8 i; + for(i = 0x78; i < 0x80; i ++) + { + printram(" %.2x", pci_read_config8(MCU, i)); + } + printram("\n"); +} + +static void dbg_dump_calib(const u8 what) +{ + u8 reg8; + /* Dump lower bound */ + reg8 = ((what & 0x3) << 2) | 0x1; + pci_write_config8(MCU, 0x70, reg8); + printram("Lower bound : "); + dbg_dump_0x78_0x7f(); + + /* Dump average values */ + reg8 = ((what & 0x3) << 2); + pci_write_config8(MCU, 0x70, reg8); + printram("Average : "); + dbg_dump_0x78_0x7f(); + + /* Dump upper bound */ + reg8 = ((what & 0x3) << 2) | 0x2; + pci_write_config8(MCU, 0x70, reg8); + printram("Upper bound : "); + dbg_dump_0x78_0x7f(); +} + +void io_timing_control(DRAM_ATTRIBUTE *dram_attr) +{ + BOOLEAN result, result1; + uint16_t cal_tx_num = 0; + uint8_t dimm_no; + uint16_t cal_rx_num = 0; + uint8_t i; + uint16_t my_dqsi[8]; + + result = FALSE; + result1 = FALSE; + u32 ncals = 0; + + + + //Cal Rx + preset_si_normal_table(dram_attr); + rx_input_delay_save(dram_attr); + rx_input_enable_si2( dram_attr); + + while(!(result&&result1)) + { + ncals++; + for(i = 0; i < 8; i++) + { + my_dqsi[i] = 0; + } + printram("R"); + for(cal_rx_num = 0; cal_rx_num < 8; cal_rx_num++) + { + rx_input_delay_hw(dram_attr); + rx_input_delay_cal_to_manual( dram_attr, &my_dqsi[0]); + } + + + for(i = 0; i < 8; i++) + { + if(my_dqsi[i] & 0x0007) + { + my_dqsi[i] >>= 3; + my_dqsi[i]++; + }else + { + my_dqsi[i] >>= 3; + } + + dram_ioctl(IO_DQSI,IOCTL_MANUAL, TRUE, i, (uint8_t *)&my_dqsi[i]); + } + + result = check_dqsi_range(dram_attr); + //enable Rx Manual mode + pci_modify_config8(MCU,D0F3DITIMCTL, + D0F3DITIMCTL_RSETDSIDLY | D0F3DITIMCTL_RSETDSIT, + D0F3DITIMCTL_RSETDSIDLY | D0F3DITIMCTL_RSETDSIT); + + //Cal Tx + dimm_no = CAL_FIRST_DIMM; + while(1) + { //3410-32-LNA-01 + result =FALSE; //3410-36-LNA-02 + result1 = FALSE; //3410-36-LNA-02 + printram("T"); + for (cal_tx_num = 0; cal_tx_num < 300; cal_tx_num++) + { + if (USE_WLVL == 1) + { + preset_rdswadvos( dram_attr); + tx_dws_clock_hw( dram_attr); + tx_dqs_clock_cal_to_manual( dram_attr); + tx_dq_clock_hw( dram_attr,dimm_no); + tx_dq_clock_cal_to_mannual( dram_attr); + }else{ + preset_for_lcu_hw( dram_attr); + tx_dqs_clock_lcuhw( dram_attr,dimm_no); + tx_dqs_clock_cal_to_manual( dram_attr); + tx_dqs_clock_save_setting( dram_attr, dimm_no); + + result = check_dqso_range(); + + if(!result) + { + printram("DQS range fail\n"); + dbg_dump_calib(0); + continue; + } + + tx_dq_clock_hw(dram_attr, dimm_no); + tx_dq_clock_cal_to_mannual(dram_attr); + tx_dq_clock_save_setting(dram_attr, dimm_no); + } + + result = check_dqo_range(); + if(!result) + { + printram("DQ range fail\n"); + dbg_dump_calib(1); + continue; + } + //enable Tx Manual mode + pci_modify_config8(MCU,D0F3DOTIMCTL, D0F3DOTIMCTL_RSETTXCLK, D0F3DOTIMCTL_RSETTXCLK); + + result1 = dram_base_test( 0, 0x40,EXTENSIVE, FALSE); + if(result&&result1) + { + break; + } + if((!result) || (!result1)) + { + printram("f"); + } + } + if(result &&result1) + { + if(dimm_no == CAL_SECOND_DIMM) //DIMM1 calibration is done? + { + set_average_setting( dram_attr); + break; + } else { + if(0)//dram_attr->ChADIMMNumber == 2) //3410-35-LNA-02 + { + dimm_no = CAL_SECOND_DIMM; + } else { + break; + } + } + } else { + break; + } + } + //after adjust DQSI + // reinit_dram(dram_attr); //3410-36-LNA-02 + } + + pci_write_config8(MCU, D0F3IOTIMSEL, 0x04); + printram("Calib loop did %u loops\n", ncals); +} \ No newline at end of file diff --git a/src/northbridge/via/vx900/romstrap.inc b/src/northbridge/via/vx900/romstrap.inc new file mode 100644 index 0000000..2eb2de5 --- /dev/null +++ b/src/northbridge/via/vx900/romstrap.inc @@ -0,0 +1,50 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2004 Tyan Computer + * (Written by Yinghai Lu yhlu@tyan.com for Tyan Computer) + * Copyright (C) 2007 Rudolf Marek r.marek@assembler.cz + * Copyright (C) 2009 One Laptop per Child, Association, Inc. + * Copyright (C) 2011-2012 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + */ + +/* This file constructs the ROM strap table for VX900 */ + + .section ".romstrap", "a", @progbits + + .globl __romstrap_start +__romstrap_start: +tblpointer: + .long 0x77886047 + .long 0x00777777 + .long 0x00000000 + .long 0x00000000 + .long 0x00888888 + .long 0x00AA1111 + .long 0x00000000 + .long 0x00000000 + +/* + * The pointer to above table should be at 0xffffffd0, + * the table itself MUST be aligned to 128B it seems! + */ +rspointers: + .long tblpointer // It will be 0xffffffd0 + + .globl __romstrap_end + +__romstrap_end: +.previous diff --git a/src/northbridge/via/vx900/romstrap.lds b/src/northbridge/via/vx900/romstrap.lds new file mode 100644 index 0000000..b51c979 --- /dev/null +++ b/src/northbridge/via/vx900/romstrap.lds @@ -0,0 +1,27 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007 AMD + * (Written by Yinghai Lu yinghai.lu@amd.com for AMD) + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ + */ + +SECTIONS { + . = (0x100000000 - 0x2c) - (__romstrap_end - __romstrap_start); + .romstrap (.): { + *(.romstrap) + } +} diff --git a/src/northbridge/via/vx900/vx900.h b/src/northbridge/via/vx900/vx900.h new file mode 100644 index 0000000..2137daf --- /dev/null +++ b/src/northbridge/via/vx900/vx900.h @@ -0,0 +1,26 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Alexandru Gagniuc mr.nuke.me@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#define VX800_ACPI_IO_BASE 0x0400 + +#define VX900_NB_IOAPIC_ID 0x2 +#define VX900_NB_IOAPIC_BASE 0xfecc000 + +#define VX900_SB_IOAPIC_ID 0x1 +#define VX900_SB_IOAPIC_BASE 0xfec0000 \ No newline at end of file