/* * This file is part of the coreboot project. * * Copyright (C) 2008 VIA Technologies, Inc. * (Written by Aaron Lwe for VIA) * Copyright (C) 2007 Corey Osgood * * 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "cn700.h" #if CONFIG_DEBUG_RAM_SETUP #define PRINT_DEBUG_MEM(x) printk(BIOS_DEBUG, x) #define PRINT_DEBUG_MEM_HEX8(x) printk(BIOS_DEBUG, "%02x", x) #define PRINT_DEBUG_MEM_HEX16(x) printk(BIOS_DEBUG, "%04x", x) #define PRINT_DEBUG_MEM_HEX32(x) printk(BIOS_DEBUG, "%08x", x) #define DUMPNORTH() dump_pci_device(PCI_DEV(0, 0, 0)) #else #define PRINT_DEBUG_MEM(x) #define PRINT_DEBUG_MEM_HEX8(x) #define PRINT_DEBUG_MEM_HEX16(x) #define PRINT_DEBUG_MEM_HEX32(x) #define DUMPNORTH() #endif static void do_ram_command(device_t dev, u8 command) { u8 reg; /* TODO: Support for multiple DIMMs. */ reg = pci_read_config8(dev, DRAM_MISC_CTL); reg &= 0xf8; /* Clear bits 2-0. */ reg |= command; pci_write_config8(dev, DRAM_MISC_CTL, reg); } /** * Configure the bus between the CPU and the northbridge. This might be able to * be moved to post-ram code in the future. For the most part, these registers * should not be messed around with. These are too complex to explain short of * copying the datasheets into the comments, but most of these values are from * the BIOS Porting Guide, so they should work on any board. If they don't, * try the values from your factory BIOS. * * TODO: Changing the DRAM frequency doesn't work (hard lockup). * * @param dev The northbridge's CPU Host Interface (D0F2). */ static void c7_cpu_setup(device_t dev) { /* Host bus interface registers (D0F2 0x50-0x67) */ /* Request phase control */ pci_write_config8(dev, 0x50, 0x88); /* CPU Interface Control */ pci_write_config8(dev, 0x51, 0x7a); pci_write_config8(dev, 0x52, 0x6f); /* Arbitration */ pci_write_config8(dev, 0x53, 0x88); /* Miscellaneous Control */ pci_write_config8(dev, 0x54, 0x1e); pci_write_config8(dev, 0x55, 0x16); /* Write Policy */ pci_write_config8(dev, 0x56, 0x01); /* Miscellaneous Control */ /* * DRAM Operating Frequency (bits 7:5) * 000 : 100MHz 001 : 133MHz * 010 : 166MHz 011 : 200MHz * 100 : 266MHz 101 : 333MHz * 110/111 : Reserved */ /* CPU Miscellaneous Control */ pci_write_config8(dev, 0x59, 0x44); /* Write Policy */ pci_write_config8(dev, 0x5d, 0xb2); /* Bandwidth Timer */ pci_write_config8(dev, 0x5e, 0x88); /* CPU Miscellaneous Control */ pci_write_config8(dev, 0x5f, 0xc7); /* Line DRDY# Timing Control */ pci_write_config8(dev, 0x60, 0xff); pci_write_config8(dev, 0x61, 0xff); pci_write_config8(dev, 0x62, 0x0f); /* QW DRDY# Timing Control */ pci_write_config8(dev, 0x63, 0xff); pci_write_config8(dev, 0x64, 0xff); pci_write_config8(dev, 0x65, 0x0f); /* Read Line Burst DRDY# Timing Control */ pci_write_config8(dev, 0x66, 0xff); pci_write_config8(dev, 0x67, 0x30); /* Host Bus I/O Circuit (see datasheet) */ /* Host Address Pullup/down Driving */ pci_write_config8(dev, 0x70, 0x11); pci_write_config8(dev, 0x71, 0x11); pci_write_config8(dev, 0x72, 0x11); pci_write_config8(dev, 0x73, 0x11); /* Miscellaneous Control */ pci_write_config8(dev, 0x74, 0x35); /* AGTL+ I/O Circuit */ pci_write_config8(dev, 0x75, 0x28); /* AGTL+ Compensation Status */ pci_write_config8(dev, 0x76, 0x74); /* AGTL+ Auto Compensation Offest */ pci_write_config8(dev, 0x77, 0x00); /* Host FSB CKG Control */ pci_write_config8(dev, 0x78, 0x0a); /* Address/Address Clock Output Delay Control */ pci_write_config8(dev, 0x79, 0xaa); /* Address Strobe Input Delay Control */ pci_write_config8(dev, 0x7a, 0x24); /* Address CKG Rising/Falling Time Control */ pci_write_config8(dev, 0x7b, 0xaa); /* Address CKG Clock Rising/Falling Time Control */ pci_write_config8(dev, 0x7c, 0x00); /* Undefined (can't remember why I did this) */ pci_write_config8(dev, 0x7d, 0x6d); pci_write_config8(dev, 0x7e, 0x00); pci_write_config8(dev, 0x7f, 0x00); pci_write_config8(dev, 0x80, 0x1b); pci_write_config8(dev, 0x81, 0x0a); pci_write_config8(dev, 0x82, 0x0a); pci_write_config8(dev, 0x83, 0x0a); } /** * Set up DRAM size according to SPD data. Eventually, DRAM timings should be * done in a similar manner. * * @param ctrl The northbridge devices and SPD addresses. */ static void sdram_set_size(const struct mem_controller *ctrl) { u8 density, ranks, result, col; ranks = spd_read_byte(ctrl->channel0[0], SPD_NUM_DIMM_BANKS); ranks = (ranks & 0x07) + 1; density = spd_read_byte(ctrl->channel0[0], SPD_DENSITY_OF_EACH_ROW_ON_MODULE); switch (density) { case 0x80: result = 0x08; /* 512MB / 64MB = 0x08 */ break; case 0x40: result = 0x04; break; case 0x20: result = 0x02; break; case 0x10: result = 0xff; /* 16GB */ break; case 0x08: result = 0xff; /* 8GB */ break; case 0x04: result = 0xff; /* 4GB */ break; case 0x02: result = 0x20; /* 2GB */ break; case 0x01: result = 0x10; /* 1GB */ break; default: result = 0; } switch (result) { case 0xff: die("DRAM module size too big, not supported by CN700\n"); break; case 0: die("DRAM module has unknown density\n"); break; default: printk(BIOS_DEBUG, "Found %iMB of ram\n", result * ranks * 64); } pci_write_config8(ctrl->d0f3, 0x40, result); pci_write_config8(ctrl->d0f3, 0x48, 0x00); if (ranks == 2) { pci_write_config8(ctrl->d0f3, 0x41, result * ranks); pci_write_config8(ctrl->d0f3, 0x49, result); } /* Size mirror */ pci_write_config8(ctrl->d0f7, 0xe5, (result * ranks) << 2); pci_write_config8(ctrl->d0f7, 0x57, (result * ranks) << 2); /* Low Top Address */ pci_write_config8(ctrl->d0f3, 0x84, 0x00); pci_write_config8(ctrl->d0f3, 0x85, (result * ranks) << 2); pci_write_config8(ctrl->d0f3, 0x88, (result * ranks) << 2); /* Physical-Virtual Mapping */ if (ranks == 2) pci_write_config8(ctrl->d0f3, 0x54, 1 << 7 | 0 << 4 | 1 << 3 | 1 << 0); if (ranks == 1) pci_write_config8(ctrl->d0f3, 0x54, 1 << 7 | 0 << 4); pci_write_config8(ctrl->d0f3, 0x55, 0x00); /* Virtual rank interleave, disable */ pci_write_config32(ctrl->d0f3, 0x58, 0x00); /* MA Map Type */ result = spd_read_byte(ctrl->channel0[0], SPD_NUM_BANKS_PER_SDRAM); if (result == 8) { col = spd_read_byte(ctrl->channel0[0], SPD_NUM_COLUMNS); switch (col) { case 10: pci_write_config8(ctrl->d0f3, 0x50, 0xa0); break; case 11: pci_write_config8(ctrl->d0f3, 0x50, 0xc0); break; case 12: pci_write_config8(ctrl->d0f3, 0x50, 0xe0); break; } } else if (result == 4) { col = spd_read_byte(ctrl->channel0[0], SPD_NUM_COLUMNS); switch (col) { case 9: pci_write_config8(ctrl->d0f3, 0x50, 0x00); break; case 10: pci_write_config8(ctrl->d0f3, 0x50, 0x20); break; case 11: pci_write_config8(ctrl->d0f3, 0x50, 0x40); break; case 12: pci_write_config8(ctrl->d0f3, 0x50, 0x60); break; } } pci_write_config8(ctrl->d0f3, 0x51, 0x00); } /** * Set up various RAM and other control registers statically. Some of these may * not be needed, other should be done with SPD info, but that's a project for * the future. */ static void sdram_set_registers(const struct mem_controller *ctrl) { u8 reg; /* Set WR=5 */ pci_write_config8(ctrl->d0f3, 0x61, 0xe0); /* Set CAS=4 */ pci_write_config8(ctrl->d0f3, 0x62, 0xfa); /* DRAM timing-3 */ pci_write_config8(ctrl->d0f3, 0x63, 0xca); /* DRAM timing-4 */ pci_write_config8(ctrl->d0f3, 0x64, 0xcc); /* DIMM command / Address Selection */ pci_write_config8(ctrl->d0f3, 0x67, 0x00); /* Disable cross bank/multi page mode */ pci_write_config8(ctrl->d0f3, 0x69, 0x00); /* Disable refresh now */ pci_write_config8(ctrl->d0f3, 0x6a, 0x00); /* Frequency 100 MHz */ pci_write_config8(ctrl->d0f3, 0x90, 0x00); pci_write_config8(ctrl->d0f2, 0x57, 0x18); /* Allow manual DLL reset */ pci_write_config8(ctrl->d0f3, 0x6b, 0x10); /* Bank/Rank Interleave Address Select */ pci_write_config8(ctrl->d0f3, 0x52, 0x33); pci_write_config8(ctrl->d0f3, 0x53, 0x3f); /* Set to DDR2 SDRAM, BL=8 (0xc8, 0xc0 for bl=4) */ pci_write_config8(ctrl->d0f3, 0x6c, 0xc8); /* DRAM Bus Turn-Around Setting */ pci_write_config8(ctrl->d0f3, 0x60, 0x03); /* DRAM Arbitration Control */ pci_write_config8(ctrl->d0f3, 0x66, 0x80); /* * DQS Tuning: testing on a couple different boards has shown this is * static, or close enough that it can be. Which is good, because the * tuning function used too many registers. */ /* DQS Output Delay for Channel A */ pci_write_config8(ctrl->d0f3, 0x70, 0x00); /* MD Output Delay for Channel A */ pci_write_config8(ctrl->d0f3, 0x71, 0x01); pci_write_config8(ctrl->d0f3, 0x73, 0x01); /* DRAM arbitration timer */ pci_write_config8(ctrl->d0f3, 0x65, 0xd9); /* DRAM signal timing control */ pci_write_config8(ctrl->d0f3, 0x74, 0x01); pci_write_config8(ctrl->d0f3, 0x75, 0x01); pci_write_config8(ctrl->d0f3, 0x76, 0x06); pci_write_config8(ctrl->d0f3, 0x77, 0x92); pci_write_config8(ctrl->d0f3, 0x78, 0x83); pci_write_config8(ctrl->d0f3, 0x79, 0x83); pci_write_config8(ctrl->d0f3, 0x7a, 0x00); pci_write_config8(ctrl->d0f3, 0x7b, 0x10); /* DRAM clocking control */ pci_write_config8(ctrl->d0f3, 0x91, 0x01); /* CS/CKE Clock Phase Control */ pci_write_config8(ctrl->d0f3, 0x92, 0x02); /* SCMD/MA Clock Phase Control */ pci_write_config8(ctrl->d0f3, 0x93, 0x02); /* DCLKO Feedback Mode Output Control */ pci_write_config8(ctrl->d0f3, 0x94, 0x00); pci_write_config8(ctrl->d0f3, 0x9d, 0x0f); /* SDRAM ODT Control */ pci_write_config8(ctrl->d0f3, 0xda, 0x80); /* Channel A DQ/DQS CKG Output Delay Control */ pci_write_config8(ctrl->d0f3, 0xdc, 0x54); /* Channel A DQ/DQS CKG Output Delay Control */ pci_write_config8(ctrl->d0f3, 0xdd, 0x55); /* ODT lookup table */ pci_write_config8(ctrl->d0f3, 0xd8, 0x01); pci_write_config8(ctrl->d0f3, 0xd9, 0x0a); /* DDR SDRAM control */ pci_write_config8(ctrl->d0f3, 0x6d, 0xc0); pci_write_config8(ctrl->d0f3, 0x6f, 0x41); /* DQ/DQS Strength Control */ pci_write_config8(ctrl->d0f3, 0xd0, 0xaa); /* Compensation Control */ pci_write_config8(ctrl->d0f3, 0xd3, 0x01); /* Enable autocompensation */ /* ODT (some are set with driving select above) */ pci_write_config8(ctrl->d0f3, 0xd4, 0x80); pci_write_config8(ctrl->d0f3, 0xd5, 0x8a); /* Memory Pads Driving and Range Select */ pci_write_config8(ctrl->d0f3, 0xd6, 0xaa); pci_write_config8(ctrl->d0f3, 0xe0, 0xee); pci_write_config8(ctrl->d0f3, 0xe2, 0xac); pci_write_config8(ctrl->d0f3, 0xe4, 0x66); pci_write_config8(ctrl->d0f3, 0xe6, 0x33); pci_write_config8(ctrl->d0f3, 0xe8, 0x86); /* DQS / DQ CKG Duty Cycle Control */ pci_write_config8(ctrl->d0f3, 0xec, 0x00); /* MCLK Output Duty Control */ pci_write_config8(ctrl->d0f3, 0xee, 0x00); /* DQS CKG Input Delay Control */ pci_write_config8(ctrl->d0f3, 0xef, 0x10); /* DRAM duty control */ pci_write_config8(ctrl->d0f3, 0xed, 0x10); /* SMM and APIC decoding, we do not use SMM */ reg = 0x29; pci_write_config8(ctrl->d0f3, 0x86, reg); /* SMM and APIC decoding mirror */ pci_write_config8(ctrl->d0f7, 0xe6, reg); /* DRAM module configuration */ pci_write_config8(ctrl->d0f3, 0x6e, 0x89); } static void sdram_set_post(const struct mem_controller *ctrl) { device_t dev = ctrl->d0f3; /* Enable multipage mode. */ pci_write_config8(dev, 0x69, 0x03); /* Enable refresh. */ pci_write_config8(dev, 0x6a, 0x32); /* VGA device. */ pci_write_config16(dev, 0xa0, (1 << 15)); pci_write_config16(dev, 0xa4, 0x0010); } static void sdram_enable(device_t dev, u8 *rank_address) { u8 i; /* 1. Apply NOP. */ PRINT_DEBUG_MEM("RAM Enable 1: Apply NOP\n"); do_ram_command(dev, RAM_COMMAND_NOP); udelay(100); read32(rank_address + 0x10); /* 2. Precharge all. */ udelay(400); PRINT_DEBUG_MEM("RAM Enable 2: Precharge all\n"); do_ram_command(dev, RAM_COMMAND_PRECHARGE); read32(rank_address + 0x10); /* 3. Mode register set. */ PRINT_DEBUG_MEM("RAM Enable 3: Mode register set\n"); do_ram_command(dev, RAM_COMMAND_MRS); read32(rank_address + 0x120000); /* EMRS DLL Enable */ read32(rank_address + 0x800); /* MRS DLL Reset */ /* 4. Precharge all again. */ PRINT_DEBUG_MEM("RAM Enable 4: Precharge all\n"); do_ram_command(dev, RAM_COMMAND_PRECHARGE); read32(rank_address + 0x0); /* 5. Perform 8 refresh cycles. Wait tRC each time. */ PRINT_DEBUG_MEM("RAM Enable 5: CBR\n"); do_ram_command(dev, RAM_COMMAND_CBR); for (i = 0; i < 8; i++) { read32(rank_address + 0x20); udelay(100); } /* 6. Mode register set. */ PRINT_DEBUG_MEM("RAM Enable 6: Mode register set\n"); /* Safe value for now, BL=8, WR=5, CAS=4 */ /* * (E)MRS values are from the BPG. No direct explanation is given, but * they should somehow conform to the JEDEC DDR2 SDRAM Specification * (JESD79-2C). */ do_ram_command(dev, RAM_COMMAND_MRS); read32(rank_address + 0x002258); /* MRS command */ read32(rank_address + 0x121c20); /* EMRS OCD Default */ read32(rank_address + 0x120020); /* EMRS OCD Calibration Mode Exit */ /* 8. Normal operation */ PRINT_DEBUG_MEM("RAM Enable 7: Normal operation\n"); do_ram_command(dev, RAM_COMMAND_NORMAL); read32(rank_address + 0x30); } /* * Support one DIMM with up to 2 ranks. */ static void ddr_ram_setup(const struct mem_controller *ctrl) { u8 reg; c7_cpu_setup(ctrl->d0f2); sdram_set_registers(ctrl); sdram_set_size(ctrl); sdram_enable(ctrl->d0f3, (u8 *)0); reg = pci_read_config8(ctrl->d0f3, 0x41); if (reg != 0) sdram_enable(ctrl->d0f3, (u8 *)(pci_read_config8(ctrl->d0f3, 0x40) << 26)); sdram_set_post(ctrl); }