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 a18b5e15e9d2068ea64b5853701380ea16f25d59 Author: Alexandru Gagniuc mr.nuke.me@gmail.com Date: Mon Aug 1 05:16:20 2011 -0500
NOTFORMERGE: VX900 early init
Early initialization for VIA VX900.
SMBUS is functional. Raminit works with 1 rank, but untested with several ranks.
Change-Id: I7624944dbc05fbf3019897a116954d71dfda0031 Signed-off-by: Alexandru Gagniuc mr.nuke.me@gmail.com --- src/arch/x86/include/arch/romcc_io.h | 28 + 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/include/device/pci_ids.h | 22 + src/mainboard/via/Kconfig | 3 + 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 | 89 +++ src/northbridge/via/Kconfig | 1 + src/northbridge/via/Makefile.inc | 1 + src/northbridge/via/vx900/Kconfig | 23 + src/northbridge/via/vx900/Makefile.inc | 34 + 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 | 1063 +++++++++++++++++++++++++++++ src/northbridge/via/vx900/romstrap.inc | 50 ++ src/northbridge/via/vx900/romstrap.lds | 27 + src/northbridge/via/vx900/vx900.h | 26 + 26 files changed, 2807 insertions(+), 0 deletions(-)
diff --git a/src/arch/x86/include/arch/romcc_io.h b/src/arch/x86/include/arch/romcc_io.h index 37fb7ab..8876b28 100644 --- a/src/arch/x86/include/arch/romcc_io.h +++ b/src/arch/x86/include/arch/romcc_io.h @@ -244,6 +244,34 @@ static inline __attribute__((always_inline)) void pci_write_config32(device_t de #endif }
+static inline __attribute__((always_inline)) +void pci_mod_config8(device_t dev, unsigned int where, + uint8_t clr_mask, uint8_t set_mask) +{ + uint8_t reg8 = pci_read_config8(dev, where); + reg8 &= ~clr_mask; + reg8 |= set_mask; + pci_write_config8(dev, where, reg8); +} +static inline __attribute__((always_inline)) +void pci_mod_config16(device_t dev, unsigned int where, + uint16_t clr_mask, uint16_t set_mask) +{ + uint16_t reg16 = pci_read_config16(dev, where); + reg16 &= ~clr_mask; + reg16 |= set_mask; + pci_write_config16(dev, where, reg16); +} +static inline __attribute__((always_inline)) +void pci_mod_config32(device_t dev, unsigned int where, + uint32_t clr_mask, uint32_t set_mask) +{ + uint32_t reg32 = pci_read_config32(dev, where); + reg32 &= ~clr_mask; + reg32 |= set_mask; + pci_write_config32(dev, where, reg32); +} + #define PCI_DEV_INVALID (0xffffffffU) static inline device_t pci_io_locate_device(unsigned pci_id, device_t dev) { 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/include/device/pci_ids.h b/src/include/device/pci_ids.h index fd886da..7a8600e 100644 --- a/src/include/device/pci_ids.h +++ b/src/include/device/pci_ids.h @@ -1337,6 +1337,28 @@ #define PCI_DEVICE_ID_VIA_VX855_VGA 0x5122 #define PCI_DEVICE_ID_VIA_VX855_VLINK 0x7409 #define PCI_DEVICE_ID_VIA_VX855_MEMCTRL 0x3409 +/* VIA VX900 PCI IDs */ +#define PCI_DEVICE_ID_VIA_VX900_HOST_BR 0x0410 +#define PCI_DEVICE_ID_VIA_VX900_ERROR_REPORT 0x1410 +#define PCI_DEVICE_ID_VIA_VX900_CPU_CTR 0x2410 +#define PCI_DEVICE_ID_VIA_VX900_DRAM_CTR 0x3410 +#define PCI_DEVICE_ID_VIA_VX900_PWR_MGMT 0x4410 +#define PCI_DEVICE_ID_VIA_VX900_TRAF_CTR 0x5410 +#define PCI_DEVICE_ID_VIA_VX900_SCRATCH_REG 0x6410 +#define PCI_DEVICE_ID_VIA_VX900_NORTH_NB_SB_CTR 0x7410 +#define PCI_DEVICE_ID_VIA_VX900_LPC 0x8410 +#define PCI_DEVICE_ID_VIA_VX900_PEX1 0xa410 +#define PCI_DEVICE_ID_VIA_VX900_PEX2 0xb410 +#define PCI_DEVICE_ID_VIA_VX900_PEX3 0xc410 +#define PCI_DEVICE_ID_VIA_VX900_PEX4 0xd410 +#define PCI_DEVICE_ID_VIA_VX900_PEX_CTR 0xe410 +#define PCI_DEVICE_ID_VIA_VX900_SOUTH_NB_SB_CTR 0xa535 +#define PCI_DEVICE_ID_VIA_VX900_PCI_BR 0xb535 +#define PCI_DEVICE_ID_VIA_VX900_VGA 0x7122 +#define PCI_DEVICE_ID_VIA_VX900_VGA_UNKNOWN 0x9170 +#define PCI_DEVICE_ID_VIA_VX900_HDAC 0x3288 +#define PCI_DEVICE_ID_VIA_VX900_ETHERNER 0x3119 +/* VIA CN700 */ #define PCI_DEVICE_ID_VIA_CN700_AGP 0x0314 #define PCI_DEVICE_ID_VIA_CN700_ERR 0x1314 #define PCI_DEVICE_ID_VIA_CN700_HOST 0x2314 diff --git a/src/mainboard/via/Kconfig b/src/mainboard/via/Kconfig index 6980548..48f4055 100644 --- a/src/mainboard/via/Kconfig +++ b/src/mainboard/via/Kconfig @@ -9,6 +9,8 @@ config BOARD_VIA_EPIA_CN bool "EPIA-CN" config BOARD_VIA_EPIA_M700 bool "EPIA-M700" +config BOARD_VIA_EPIA_M850 + bool "EPIA-M850" config BOARD_VIA_EPIA_M bool "EPIA-M" config BOARD_VIA_EPIA_N @@ -23,6 +25,7 @@ endchoice source "src/mainboard/via/epia/Kconfig" source "src/mainboard/via/epia-cn/Kconfig" source "src/mainboard/via/epia-m700/Kconfig" +source "src/mainboard/via/epia-m850/Kconfig" source "src/mainboard/via/epia-m/Kconfig" source "src/mainboard/via/epia-n/Kconfig" source "src/mainboard/via/pc2500e/Kconfig" diff --git a/src/mainboard/via/epia-m850/Kconfig b/src/mainboard/via/epia-m850/Kconfig new file mode 100644 index 0000000..9c07f47 --- /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..8f68bf7 --- /dev/null +++ b/src/mainboard/via/epia-m850/romstage.c @@ -0,0 +1,89 @@ +/* + * 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(0x4e, 0x10) + +/* 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); + + + device_t hbc = PCI_DEV(0,0,2); + pci_write_config8(hbc, 0x50, 0x08); + pci_write_config8(hbc, 0x51, 0x3c); + pci_write_config8(hbc, 0x52, 0xcf); + pci_write_config8(hbc, 0x53, 0x44); + pci_write_config8(hbc, 0x54, 0x70); + pci_write_config8(hbc, 0x55, 0x20); + pci_write_config8(hbc, 0x56, 0x63); + pci_write_config8(hbc, 0x5d, 0xa2); + pci_write_config8(hbc, 0x5e, 0x44); + pci_write_config8(hbc, 0x5f, 0x46); + + if(0)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/Kconfig b/src/northbridge/via/Kconfig index 2c38acf..8a747b9 100644 --- a/src/northbridge/via/Kconfig +++ b/src/northbridge/via/Kconfig @@ -4,3 +4,4 @@ source src/northbridge/via/cn400/Kconfig source src/northbridge/via/vt8601/Kconfig source src/northbridge/via/vt8623/Kconfig source src/northbridge/via/vx800/Kconfig +source src/northbridge/via/vx900/Kconfig diff --git a/src/northbridge/via/Makefile.inc b/src/northbridge/via/Makefile.inc index 75cb15b..2e74b61 100644 --- a/src/northbridge/via/Makefile.inc +++ b/src/northbridge/via/Makefile.inc @@ -4,4 +4,5 @@ subdirs-$(CONFIG_NORTHBRIDGE_VIA_CN700) += cn700 subdirs-$(CONFIG_NORTHBRIDGE_VIA_CX700) += cx700 subdirs-$(CONFIG_NORTHBRIDGE_VIA_CN400) += cn400 subdirs-$(CONFIG_NORTHBRIDGE_VIA_VX800) += vx800 +subdirs-$(CONFIG_NORTHBRIDGE_VIA_VX900) += vx900
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..89c8763 --- /dev/null +++ b/src/northbridge/via/vx900/Makefile.inc @@ -0,0 +1,34 @@ +## +## 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 += ./../../../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..37aae81 --- /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 &= ~(0x07); + 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 &= ~(0x07); + 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); + } + 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, + DDR3_MR0_BURST_LENGTH_CHOP); + 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 &= ~(0x07); + 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..6268070 --- /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_dq_cr; + delay_range rx_dqs; + delay_range tx_dq; + delay_range tx_dqs; +} 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..f1ca45a --- /dev/null +++ b/src/northbridge/via/vx900/raminit_ddr3.c @@ -0,0 +1,1063 @@ +/* + * 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_TxDQS 0 +#define VX900_CALIB_TxDQ 1 +#define VX900_CALIB_RxDQS 2 +#define VX900_CALIB_RxDQ_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, 0x03}, /* 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 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; + 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 align 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); + + /* 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; + + /* 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 1 +#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); + + /* 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; + + /* We need to disable refresh commands so that they don't interfere */ + const u8 ref_cnt = pci_read_config8(MCU, 0xc7); + pci_write_config8(MCU, 0xc7, 0); + /* 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); + + /* Restore the refresh counter*/ + //pci_write_config8(MCU, 0xc7, ref_cnt); + if(ref_cnt); + + /* FIXME: should we save it before, or should we just set it as is */ + pci_write_config16(MCU, 0x52, 0x5911); +} + +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_dram_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"); + + /**** Read delay control ****/ + /* MD Input Data Push Timing Control; + * use values recommended in datasheet + * Setting this too low causes the Rx window to move below the range we + * need it so we can capture it with Rx_78_7f + * This causes Rx calibrations to be too close to 0, and Tx + * calibrations will fail. + * Setting this too high causes the window to move above the range. + */ + 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 increases the MD input delay - WHY ? */ + pci_mod_config8(MCU, 0xef, (3<<4), 3<<4); + + + /**** Write delay control ****/ + /* 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 does this - Output preamble ?*/ + 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); + + /* It's very important that we keep all ranks which are not calibrated + * mapped to VR3. Even if we disable them, if they are mapped to VR0 + * (the rank we use for calibrations), the calibrations may fail in + * unexpected ways. */ + vx900_pr_map_all_vr3(); + + for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++) + { + /* Do we have a valid DIMM? */ + if(ranks->phys_rank_size[i] + ranks->phys_rank_size[i+1] == 0 ) + continue; + + /* Map the first rank of the DIMM to VR0 */ + vx900_map_pr_vr(2*i, 0); + + /* Run calibrations */if(1){ + vx900_rx_capture_range_calib(); + vx900_read_delay_range(&(delay_cal.rx_dq_cr), + VX900_CALIB_RxDQ_CR); + dump_delay_range(delay_cal.rx_dq_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("Bypassing RxCR 78-7f calibration with:\n"); + dump_delay(dly); + /* We need to put the setting on manual mode */ + pci_mod_config8(MCU, 0x71, 0, 0x10); + vx900_delay_calib_mode_select(VX900_CALIB_RxDQ_CR, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(dly); + + /************* RxDQS *************/ + vx900_rx_dqs_delay_calib(); + vx900_read_delay_range(&(delay_cal.rx_dqs), VX900_CALIB_RxDQS); + printram("RX DQS calibration results\n"); + dump_delay_range(delay_cal.rx_dqs); + + vx900_rxdqs_adjust(&(delay_cal.rx_dqs)); + + vx900_read_delay_range(&(delay_cal.rx_dqs), VX900_CALIB_RxDQS); + printram("RX DQS calibration results after adjustment\n"); + dump_delay_range(delay_cal.rx_dqs); + + /* FIXME: Vendor BIOS does it again (enable auto-precharge) - WHY? */ + pci_write_config8(MCU, 0x69, 0xce); + + /* FIXME: this is done by vendor BIOS, and recommended by VIA + * However, datasheet says that bit[7] is reserved, and + * calibration works just as well if we don't set this to 1b . + * Should we really do this, or can we drop it ? */ + 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_TxDQ, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(dly); + vx900_delay_calib_mode_select(VX900_CALIB_TxDQS, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(dly); + } + + /************* TxDQS *************/ + vx900_tx_dqs_delay_calib(); + + vx900_read_delay_range(&(delay_cal.tx_dqs), VX900_CALIB_TxDQS); + printram("Tx DQS calibration results\n"); + dump_delay_range(delay_cal.tx_dqs); + /************* 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_dq), VX900_CALIB_TxDQ); + printram("TX DQ delay calibration results:\n"); + dump_delay_range(delay_cal.tx_dq); + + /* write manual settings */ + pci_mod_config8(MCU, 0x75, 0, 0x01); + vx900_delay_calib_mode_select(VX900_CALIB_TxDQS, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(delay_cal.tx_dqs.avg); + vx900_delay_calib_mode_select(VX900_CALIB_TxDQ, VX900_CALIB_MANUAL); + vx900_write_0x78_0x7f(delay_cal.tx_dq.avg); + } +} + +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_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; + + /* 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 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); /* self-refresh */ + + //pci_write_config8(MCU, 0xd3, 0x00); /* ODT auto-compensation */ + + /* Enable automatic triggering of short ZQ calibration */ + pci_write_config8(MCU, 0xc8, 0x80); + +} + +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"); + } + + //device_t HBC = PCI_DEV(0,0,2); + //pci_mod_config8(HBC, 0x51, 0x80, 0); + //pci_mod_config8(HBC, 0x54, 0, 0x20); + + //pci_mod_config8(MCU, 0x97, 0, 0x80); + + 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) Set refresh counter based on DRAM frequency */ + vx900_dram_set_refresh_counter(&ctrl_prop); + /* 9) Calibrate receive and transmit delays */ + vx900_dram_calibrate_delays(&ctrl_prop, &ranks); + /* 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/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