2 * This file is part of the coreboot project.
4 * Copyright (C) 2007-2008 Uwe Hermann <uwe@hermann-uwe.de>
5 * Copyright (C) 2009 Maciej Pijanka <maciej.pijanka@gmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include <sdram_mode.h>
28 /*-----------------------------------------------------------------------------
29 Macros and definitions.
30 -----------------------------------------------------------------------------*/
32 /* Uncomment this to enable debugging output. */
34 /* Debugging macros. */
35 #if defined(DEBUG_RAM_SETUP)
36 #define PRINT_DEBUG(x) print_debug(x)
37 #define PRINT_DEBUG_HEX8(x) print_debug_hex8(x)
38 #define PRINT_DEBUG_HEX16(x) print_debug_hex16(x)
39 #define PRINT_DEBUG_HEX32(x) print_debug_hex32(x)
40 #define DUMPNORTH() dump_pci_device(PCI_DEV(0, 0, 0))
42 #define PRINT_DEBUG(x)
43 #define PRINT_DEBUG_HEX8(x)
44 #define PRINT_DEBUG_HEX16(x)
45 #define PRINT_DEBUG_HEX32(x)
49 #define NB PCI_DEV(0, 0, 0)
51 /* DRAMXC[7:5] - DRAM extended control register (SMS). */
52 #define RAM_COMMAND_NORMAL 0x0
53 #define RAM_COMMAND_NOP 0x1 // (NOPCE)
54 #define RAM_COMMAND_PRECHARGE 0x2 // ABPCE
55 #define RAM_COMMAND_MRS 0x3 // MRSCE
56 #define RAM_COMMAND_CBR 0x4 // CBRC
59 /* Table format: register, bitmask, value. */
60 static const long register_values[] = {
63 // BASE is 0x8A but we dont want bit 9 or 10 have ENABLED so 0x8C
64 PACCFG + 1, 0x38, 0x8c,
71 DRAMC, 0x00, 0x00, /* disable refresh for now. */
74 PAM0, 0x00, 0x30, // everything is a mem
82 /* Set the DRBs to zero for now, this will be fixed later. */
92 /* No memory holes. */
96 /*-----------------------------------------------------------------------------
97 SDRAM configuration functions.
98 -----------------------------------------------------------------------------*/
101 * Send the specified RAM command to all DIMMs.
103 * @param command The RAM command to send to the DIMM(s).
105 static void do_ram_command(u32 command)
108 u8 dimm_start, dimm_end;
110 u32 addr, addr_offset;
112 /* Configure the RAM command. */
113 reg16 = pci_read_config16(NB, DRAMXC);
114 reg16 &= 0xff1f; /* Clear bits 7-5. */
115 reg16 |= (u16) (command << 5); /* Write command into bits 7-5. */
116 pci_write_config16(NB, DRAMXC, reg16);
119 * RAM_COMMAND_NORMAL affects only the memory controller and
120 * doesn't need to be "sent" to the DIMMs.
122 if (command == RAM_COMMAND_NORMAL)
125 /* Send the RAM command to each row of memory. */
127 for (i = 0; i < (DIMM_SOCKETS * 2); i++) {
129 caslatency = 3; /* TODO: Dynamically get CAS latency later. */
131 /* before translation it is
133 * M[02:00] Burst Length
134 * M[03:03] Burst Type
135 * M[06:04] Cas Latency
145 * Must Be 00b (Defined mode)
146 * M[09:09] Write Burst Mode
147 * 0 - Programmed burst length
148 * 1 - Single location access
150 * write 0 to ensure compatibility with....
153 /* seems constructed value will be right shifted by 3 bit, thus constructed value
154 * must be left shifted by 3
155 * so possible formula is (caslatency <<4)|(burst_type << 1)|(burst length)
156 * then << 3 shift to compensate shift in Memory Controller
158 if (command == RAM_COMMAND_MRS) {
165 dimm_end = pci_read_config8(NB, DRB + i);
167 addr = (dimm_start * 8 * 1024 * 1024) + addr_offset;
168 if (dimm_end > dimm_start) {
170 PRINT_DEBUG(" Sending RAM command 0x");
171 PRINT_DEBUG_HEX16(reg16);
172 PRINT_DEBUG(" to 0x");
173 PRINT_DEBUG_HEX32(addr);
180 /* Set the start of the next DIMM. */
181 dimm_start = dimm_end;
185 /*-----------------------------------------------------------------------------
186 DIMM-independant configuration functions.
187 -----------------------------------------------------------------------------*/
189 static void spd_enable_refresh(void)
193 reg = pci_read_config8(NB, DRAMC);
195 /* this chipset offer only two choices regarding refresh
196 * refresh disabled, or refresh normal
199 pci_write_config8(NB, DRAMC, reg | 0x01);
200 reg = pci_read_config8(NB, DRAMC);
202 PRINT_DEBUG("spd_enable_refresh: dramc = 0x");
203 PRINT_DEBUG_HEX8(reg);
207 /*-----------------------------------------------------------------------------
209 -----------------------------------------------------------------------------*/
211 static void northbridge_init(void)
215 reg32 = pci_read_config32(NB, APBASE);
216 reg32 &= 0xe8000000U;
217 pci_write_config32(NB, APBASE, reg32);
219 #ifdef DEBUG_RAM_SETUP
221 * apbase dont get set still, no idea what i have doing wrong yet,
222 * i am almost sure that somehow i set it by mistake once, but can't
225 reg32 = pci_read_config32(NB, APBASE);
226 PRINT_DEBUG("APBASE ");
227 PRINT_DEBUG_HEX32(reg32);
234 * This routine sets RAM controller inside northbridge to known state
237 static void sdram_set_registers(void)
241 /* nice banner with FSB shown? do we have
242 * any standart policy about such things?
246 reg16 = pci_read_config16(NB, PACCFG);
247 printk(BIOS_DEBUG, "i82443LX Host Freq: 6%C MHz\r\n", (reg16 & 0x4000) ? '0' : '6');
250 PRINT_DEBUG("Northbridge prior to SDRAM init:\r\n");
255 max = ARRAY_SIZE(register_values);
257 /* Set registers as specified in the register_values[] array. */
258 for (i = 0; i < max; i += 3) {
260 reg = pci_read_config8(NB, register_values[i]);
261 reg &= register_values[i + 1];
262 reg |= register_values[i + 2] & ~(register_values[i + 1]);
263 pci_write_config8(NB, register_values[i], reg);
266 * i am not sure if that is needed, but was usefull
267 * for me to confirm what got written
269 #ifdef DEBUG_RAM_SETUP
270 PRINT_DEBUG(" Set register 0x");
271 PRINT_DEBUG_HEX8(register_values[i]);
272 PRINT_DEBUG(" to 0x");
273 PRINT_DEBUG_HEX8(reg);
274 tmp = pci_read_config8(NB, register_values[i]);
275 PRINT_DEBUG(" readed 0x");
276 PRINT_DEBUG_HEX8(tmp);
280 PRINT_DEBUG(" FAIL ");
286 PRINT_DEBUG("Northbridge atexit sdram set registers\r\n");
291 static void sdram_set_spd_registers(void)
296 for (i = 0; i < DIMM_SOCKETS; i++) {
297 uint16_t ds = 0; // dimm size
299 /* this code skips second bank on each socket (no idea how to fix it now)
304 PRINT_DEBUG(" rows: ");
305 PRINT_DEBUG_HEX8(spd_read_byte(DIMM_SPD_BASE + i, SPD_NUM_DIMM_BANKS) & 0xFF);
306 PRINT_DEBUG(" rowsize: ");
307 PRINT_DEBUG_HEX8(spd_read_byte(DIMM_SPD_BASE + i, SPD_DENSITY_OF_EACH_ROW_ON_MODULE) & 0xFF);
308 PRINT_DEBUG(" modulesize: ");
310 j = spd_read_byte(DIMM_SPD_BASE + i, SPD_NUM_DIMM_BANKS);
316 j = spd_read_byte(DIMM_SPD_BASE + i, SPD_DENSITY_OF_EACH_ROW_ON_MODULE);
321 ds = ds * (j >> 1); // convert from 4MB to 8MB units in place
324 /* This is more or less crude hack
325 * allowing to run this target under qemu (even if that is not really
326 * same hardware emulated),
327 * probably some kconfig expert option should be added to enable/disable
331 if (ds == 0 && memsize == 0)
336 // todo: support for bank with not equal sizes as per jedec standart?
339 * because density is reported in units of 4Mbyte
340 * and rows in device are just value,
341 * and for setting registers we need value in 8Mbyte units
344 PRINT_DEBUG_HEX16(ds);
349 pci_write_config8(NB, DRB + (2*i), memsize);
350 pci_write_config8(NB, DRB + (2*i) + 1, memsize);
352 /* i have no idea why pci_read_config16 not work for
355 ds = pci_read_config8(NB, DRT+1);
357 ds |= pci_read_config8(NB, DRT);
360 PRINT_DEBUG_HEX16(ds);
362 ds &= ~(0x01 << (4 * i));
365 PRINT_DEBUG_HEX16(ds);
369 * modify DRT register if current row isn't empty
370 * code assume its SDRAM plugged (should check if its sdram or EDO,
371 * edo would have 0x00 as constand instead 0x10 for SDRAM
372 * also this code is buggy because ignores second row of each dimm socket
375 /* and as above write_config16 not work here too)
376 * pci_write_config16(NB, DRT, j);
379 pci_write_config8(NB, DRT+1, ds >> 8);
380 pci_write_config8(NB, DRT, ds & 0xFF);
385 PRINT_DEBUG("Mem: 0x");
386 PRINT_DEBUG_HEX16(memsize * 8);
387 PRINT_DEBUG(" MB\r\n");
390 /* maybe we should use some nice die/hlt sequence with printing on console
391 * that no memory found, get lost, i can't run?
392 * maybe such event_handler can be commonly defined routine to decrease
395 PRINT_DEBUG("No memory detected via SPD\r\n");
396 PRINT_DEBUG("Reverting to hardcoded 64M single side dimm in first bank\r\n");
400 /* Set DRAMC. Don't enable refresh for now. */
401 pci_write_config8(NB, DRAMC, 0x00);
403 /* Cas latency 3, and other shouldbe properly from spd too */
404 pci_write_config8(NB, DRAMT, 0xAC);
407 pci_write_config8(NB, PCI_LATENCY_TIMER, 0x40);
409 // set drive strength
410 pci_write_config32(NB, MBSC, 0x00000000);
413 static void sdram_enable(void)
417 /* 0. Wait until power/voltages and clocks are stable (200us). */
420 /* 1. Apply NOP. Wait 200 clock cycles (clock might be 60 or 66 Mhz). */
421 PRINT_DEBUG("RAM Enable 1: Apply NOP\r\n");
422 do_ram_command(RAM_COMMAND_NOP);
425 /* 2. Precharge all. Wait tRP. */
426 PRINT_DEBUG("RAM Enable 2: Precharge all\r\n");
427 do_ram_command(RAM_COMMAND_PRECHARGE);
430 /* 3. Perform 8 refresh cycles. Wait tRC each time. */
431 PRINT_DEBUG("RAM Enable 3: CBR\r\n");
432 for (i = 0; i < 8; i++) {
433 do_ram_command(RAM_COMMAND_CBR);
437 /* 4. Mode register set. Wait two memory cycles. */
438 PRINT_DEBUG("RAM Enable 4: Mode register set\r\n");
439 do_ram_command(RAM_COMMAND_MRS);
442 /* 5. Normal operation. */
443 PRINT_DEBUG("RAM Enable 5: Normal operation\r\n");
444 do_ram_command(RAM_COMMAND_NORMAL);
447 /* 6. Finally enable refresh. */
448 PRINT_DEBUG("RAM Enable 6: Enable refresh\r\n");
449 pci_write_config8(NB, DRAMC, 0x01);
450 spd_enable_refresh();
453 PRINT_DEBUG("Northbridge following SDRAM init:\r\n");