Index: src/northbridge/via/vt8601/northbridge.c =================================================================== RCS file: /cvsroot/freebios/freebios/src/northbridge/via/vt8601/northbridge.c,v retrieving revision 1.13 diff -u -r1.13 northbridge.c --- src/northbridge/via/vt8601/northbridge.c 13 May 2003 13:13:51 -0000 1.13 +++ src/northbridge/via/vt8601/northbridge.c 18 May 2003 18:36:09 -0000 @@ -4,22 +4,164 @@ #include #include +/* + * Automatic memory configuration by SONE Takeshi , 05/19/03 + */ + +static unsigned long find_size(unsigned long addr, unsigned long minimum) +{ + unsigned long i; + unsigned long maximum; + volatile long *p; + + /* First, see if there is any RAM. */ + p = (long *) (addr + minimum); + *p = 0x12345678; + p = (long *) (addr + minimum + 8); + *p = 0x87654321; + p = (long *) (addr + minimum); + if (*p != 0x12345678) + return 0; /* No memory */ + + maximum = (0xffUL << 23) - addr; + + /* Write to addresses with only one address bit on, + * in increasing order, from address 8 (assuming 64-bit bus), + * then read address zero to see if it gets wrap-around. + * This way we can detect missing address bit due to incorrect + * MA mapping, or the size of bank when MA mapping is correct. */ + + for (i = 8; i < maximum; i <<= 1) { + if (i < minimum) + continue; + p = (long *) (addr + i); + *p = 0x89abcdef; + p = (long *) addr; + if (*p == 0x89abcdef) + return i; + } + return maximum; +} + +static void set_ma_mapping(struct pci_dev *pcidev, int bank, int type) +{ + unsigned char reg, val; + int shift; + + reg = 0x58 + bank/4; + if (bank%4 >= 2) + shift = 0; + else + shift = 4; + + pci_read_config_byte(pcidev, reg, &val); + val &= ~(0xf << shift); + val |= type << shift; + pci_write_config_byte(pcidev, reg, val); +} + +static int get_ma_mapping(struct pci_dev *pcidev, int bank) +{ + unsigned char reg, val; + int shift; + + reg = 0x58 + bank/4; + if (bank%4 >= 2) + shift = 0; + else + shift = 4; + + pci_read_config_byte(pcidev, reg, &val); + return (val >> shift) & 0xf; +} static unsigned long __sizeram(void) { - unsigned long totalmem; - unsigned char bank, mem, prevmem; - u8 sma_status, sma_size, sma_size_bits; - // fixed so tha banks 56 & 57 are looked at as well. - unsigned long firstbank = 0x5a, lastbank = 0x61; - + u8 sma_status, sma_size_bits; struct pci_dev *pcidev; + unsigned long memtop, highest, size, sma_size; + int bank, i; + static const unsigned char ma_table[] = {0, 8, 0xe}; + unsigned char ma_tmp, val; + extern void cache_enable(void), cache_disable(void); pcidev = pci_find_slot(0, PCI_DEVFN(0,0)); if (! pcidev) return 0; - + + /* In assembly part, we have initialized all RAM chips, + * brought the first equipped RAM bank to address zero, + * and set correct MA mapping type of that bank. + * Now, we have to detect the size of the first bank, + * then configure rest of banks. */ + + /* Cache must be disabled to detect the RAM. */ + cache_disable(); + + /* Find the first bank configured by assembly part. */ + for (bank = 0; bank < 6; bank++) { + pci_read_config_byte(pcidev, 0x5a + bank, &val); + if (val != 0) + break; + } + + memtop = find_size(0, 1024*1024); + memtop &= ~0x7fffff; /* Unit of 8MB */ + + printk_info("Bank%d %dMB (MA type 0x%x)\n", bank, + memtop>>20, get_ma_mapping(pcidev, bank)); + + pci_write_config_byte(pcidev, 0x5a + bank, memtop>>23); + + for (bank++; bank < 6; bank++) { + if (bank & 1) { + /* We don't change MA mapping of this bank + * since it is shared with the previous bank. + * Most possibly this is the other side of a + * double-sided DIMM. */ + size = find_size(memtop, 0); + size &= ~0x7fffff; + if (size) { + printk_info("Bank%d %dMB\n", bank, size>>20); + memtop += size; + } + } else { + /* Try MA mapping types and find the one which gives + * highest address without wrap-around. + * It should be the correct mapping for the DIMM, + * and the returned address is the size of the DIMM. */ + highest = 0; + ma_tmp = 0; + for (i = 0; i < sizeof(ma_table)/sizeof(ma_table[0]); i++) { + set_ma_mapping(pcidev, bank, ma_table[i]); + size = find_size(memtop, 0); + printk_debug("bank %d MA 0x%x: %d bytes\n", + bank, ma_table[i], size); + if (size > highest) { + highest = size; + ma_tmp = ma_table[i]; + } + } + highest &= ~0x7fffff; + if (highest) { + printk_info("Bank%d %dMB (MA type 0x%x)\n", + bank, highest>>20, ma_tmp); + memtop += highest; + } + set_ma_mapping(pcidev, bank, ma_tmp); + } + pci_write_config_byte(pcidev, 0x5a + bank, memtop>>23); + } + /* As well as 6 bank registers above, it seems we have to fill + * these 2 registers. */ + pci_write_config_byte(pcidev, 0x56, memtop>>23); + pci_write_config_byte(pcidev, 0x57, memtop>>23); + + cache_enable(); + + /* Frame buffer size */ + // Documentation on VT8601 - Pg 51 Rev 1.3 Sept 1999 says // Device 0 Offset FB - Frame buffer control // bit @@ -35,41 +177,31 @@ pci_read_config_byte(pcidev, 0xfb, &sma_status); sma_size_bits = (sma_status >> 4) & 0x03; if (sma_size_bits > 0) - sma_size = 0x01 << sma_size_bits; + sma_size = (1024*1024) << sma_size_bits; else sma_size = 0; - for(totalmem = mem = prevmem = 0, bank = firstbank; - bank <= lastbank; bank++) { - // last 2 banks are in regs before first bank so - // wrap round if > 0x5f - unsigned long rbank = (bank > 0x5f) ? bank - 10 : bank; - - pci_read_config_byte(pcidev, rbank, &mem); - - // sanity check. If the mem value is < prevmem, - // that is an error, so skip this step. - if (mem < prevmem) { - printk_err("ERROR: bank 0x%x, mem 0x%x TOO SMALL\n", - rbank, prevmem); - printk_err("Should be >= 0x%x\n", prevmem); - } else - totalmem += (mem - prevmem) * 8; - prevmem = mem; - } - - totalmem -= sma_size; - totalmem *= 1024; + printk_info("Total %dMB + frame buffer %dMB\n", + (memtop - sma_size)>>20, sma_size>>20); + + /* Turn on shadow DRAM at 0xC0000-0xFFFFF so we can write + * PIRQ table, VGA BIOS, Bochs BIOS, etc. */ + printk_debug("Enabling shadow DRAM at 0xC0000-0xFFFFF: "); + pci_write_config_byte(pcidev, 0x61, 0xff); + pci_write_config_byte(pcidev, 0x62, 0xff); + pci_write_config_byte(pcidev, 0x63, 0xf0); + printk_debug("done\n"); - return totalmem; + return (memtop - sma_size) >> 10; // return in kilo bytes } struct mem_range *sizeram(void) { static struct mem_range mem[3]; + mem[0].basek = 0; mem[0].sizek = 640; - mem[1].basek = 1024; + mem[1].basek = 768; mem[1].sizek = __sizeram(); mem[2].basek = 0; mem[2].sizek = 0; @@ -77,19 +209,27 @@ mem[1].sizek = 64*1024; } mem[1].sizek -= mem[1].basek; - return &mem; + return mem; } #ifdef HAVE_FRAMEBUFFER void framebuffer_on() { +#if 0 /* This code has not been working (always reads 0xffff) + * and I still can bring up VGA (with original VGA BIOS under ADLO) + * after disabling this. -- ts1 */ unsigned long devfn; u16 command; devfn = PCI_DEVFN(0, 1); pcibios_read_config_word(0, devfn, 0x3e, &command); - command |= 0x08; + //command |= 0x08; + command |= 0x0c; pcibios_write_config_word(0, devfn, 0x3e, command); + printk_debug("wrote %02x\n", command); + pcibios_read_config_word(0, devfn, 0x3e, &command); + printk_debug("readback %02x\n", command); +#endif } #endif Index: src/northbridge/via/vt8601/raminit.inc =================================================================== RCS file: /cvsroot/freebios/freebios/src/northbridge/via/vt8601/raminit.inc,v retrieving revision 1.2 diff -u -r1.2 raminit.inc --- src/northbridge/via/vt8601/raminit.inc 10 Nov 2002 06:27:47 -0000 1.2 +++ src/northbridge/via/vt8601/raminit.inc 18 May 2003 18:36:09 -0000 @@ -25,52 +25,92 @@ * correctly - at least on my VIA epia motherboard. 64MB DIMM in slot 0. */ -#define loop200 $0x5000 -#define loop100 $0x2500 +/* Added automatic detection of first equipped bank and its MA mapping type. + * (Rest of configuration is done in C) + * 5/19/03 by SONE Takeshi + */ + +// Set to 1 if your DIMMs are PC133 +// Note that I'm assuming CPU's FSB frequency is 133MHz. If your CPU runs +// at another bus speed, you might need to change some of register values. +#ifndef DIMM_PC133 +#define DIMM_PC133 0 +#endif + +// Set to 1 if your DIMMs are CL=2 +#ifndef DIMM_CL2 +#define DIMM_CL2 0 +#endif + + +/* Stable ~1 usec delay by hitting unused ISA port. */ +#define UDELAY(x) movl $x,%ecx; 9: outb %al,$0x81; loop 9b + +#define DIMMS_READ(x) \ + movl 0x00000000+x, %eax; \ + movl 0x10000000+x, %eax; \ + movl 0x20000000+x, %eax; \ + movl 0x30000000+x, %eax; \ + movl 0x40000000+x, %eax; \ + movl 0x50000000+x, %eax + +#define DIMMS_WRITE(x) \ + movl %eax, 0x00000000+x; \ + movl %eax, 0x10000000+x; \ + movl %eax, 0x20000000+x; \ + movl %eax, 0x30000000+x; \ + movl %eax, 0x40000000+x; \ + movl %eax, 0x50000000+x raminit: intel_chip_post_macro(0x35) -/*; new code... pulled from via stuff.*/ -/* initialize registers */ // memory clk enable. We are not using ECC CS_WRITE($0x78, $0x01) // dram control, see the book. - CS_WRITE($0x68, $0x42) +#if DIMM_PC133 + CS_WRITE($0x68, $0x52) +#else + CS_WRITE($0x68, $0x42) +#endif // dram control, see the book. - CS_WRITE($0x6B, $0x0d) - // 64/128 MB dram - CS_WRITE($0x58, $0x80) - // 64/128 MB dram - CS_WRITE($0x59, $0x00) - // bank 0 ends at 64 MB - CS_WRITE($0x5A, $0x08) - // bank 1 ends at 64 MB - CS_WRITE($0x5B, $0x08) - // bank 2 ends at 64 MB - CS_WRITE($0x5C, $0x08) - // bank 2 ends at 64 MB - CS_WRITE($0x5D, $0x08) - // bank 2 ends at 64 MB - CS_WRITE($0x5E, $0x08) - // bank 2 ends at 64 MB - CS_WRITE($0x5F, $0x08) + CS_WRITE($0x6B, $0x0c) + // Initial setting, 256MB in each bank, will be rewritten later. + CS_WRITE($0x5A, $0x20) + CS_WRITE($0x5B, $0x40) + CS_WRITE($0x5C, $0x60) + CS_WRITE($0x5D, $0x80) + CS_WRITE($0x5E, $0xA0) + CS_WRITE($0x5F, $0xC0) + // It seems we have to take care of these 2 registers as if + // they are bank 6 and 7. + CS_WRITE($0x56, $0xC0) + CS_WRITE($0x57, $0xC0) // SDRAM in all banks CS_WRITE($0x60, $0x3F) // DRAM timing. I'm suspicious of this // This is for all banks, 64 is 0,1. 65 is 2,3. 66 is 4,5. - // ras precharge 4T, RAS pulse 5T, CAS 2T - // as per the note below, we change to cas 3 2000/8/31 + // ras precharge 4T, RAS pulse 5T // cas2 is 0xd6, cas3 is 0xe6 // we're also backing off write pulse width to 2T, so result is 0xee - CS_WRITE($0x64, $0xe6) - CS_WRITE($0x65, $0x95) - CS_WRITE($0x66, $0x95) - - // dram frequency select. We set 66/66. - // no 256 m, enable 4K pages for 64M dram. - CS_WRITE($0x69, $0xac) +#if DIMM_CL2 + CS_WRITE($0x64, $0xd4) + CS_WRITE($0x65, $0xd4) + CS_WRITE($0x66, $0xd4) +#else // CL=3 + CS_WRITE($0x64, $0xe4) + CS_WRITE($0x65, $0xe4) + CS_WRITE($0x66, $0xe4) +#endif + + // dram frequency select. + // enable 4K pages for 64M dram. +#if DIMM_PC133 + CS_WRITE($0x69, $0x3c) +#else + CS_WRITE($0x69, $0xac) +#endif // refresh counter, disabled. CS_WRITE($0x6A, $0x00) // clkenable configuration. kevinh FIXME - add precharge @@ -80,102 +120,188 @@ // As per Cindy Lee, set to 0x37, not 0x57 CS_WRITE($0x6D, $0x7f) + /* Initialize all banks at once */ + /* begin to initialize*/ // I forget why we need this, but we do mov $0xa55a5aa5, %eax - mov %eax, 0 - mov %eax, 0x4000000 + DIMMS_WRITE(0) /* set NOP*/ CS_WRITE($0x6C, $0x01) - /* wait 200us*/ // You need to do the memory reference. That causes the nop cycle. - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop200) - + DIMMS_READ(0) + UDELAY(400) /* set precharge */ CS_WRITE($0x6C, $0x02) /* dummy reads*/ - mov 0x0, %eax - mov 0x4000000, %eax + DIMMS_READ(0) + UDELAY(200) /* set CBR*/ CS_WRITE($0x6C, $0x04) -/* do 8 reads and wait 100us between each - from via*/ - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) - mov 0x0, %eax - mov 0x4000000, %eax - DELAY(loop100) +/* do 8 reads and wait >100us between each - from via*/ + DIMMS_READ(0) + UDELAY(200) + DIMMS_READ(0) + UDELAY(200) + DIMMS_READ(0) + UDELAY(200) + DIMMS_READ(0) + UDELAY(200) + DIMMS_READ(0) + UDELAY(200) + DIMMS_READ(0) + UDELAY(200) + DIMMS_READ(0) + UDELAY(200) + DIMMS_READ(0) + UDELAY(200) /* set MRS*/ - // 0x150 is cas2. We are now using 0x1d0, which is cas3 CS_WRITE($0x6c, $0x03) - movl $0x1d0, %ecx - movl (%ecx), %eax - movl $0x40001d0, %ecx - movl (%ecx), %eax +#if DIMM_CL2 + DIMMS_READ(0x150) +#else // CL=3 + DIMMS_READ(0x1d0) +#endif + UDELAY(200) /* set to normal mode */ CS_WRITE($0x6C, $0x08) movl $0x55aa55aa, %eax - mov %eax, 0x0 - mov 0x0, %eax + DIMMS_WRITE(0) + DIMMS_READ(0) + UDELAY(200) // Set the refresh rate. +#if DIMM_PC133 + CS_WRITE($0x6A, $0x86) +#else CS_WRITE($0x6A, $0x65) +#endif // enable multi-page open - CS_WRITE($0x6B, $0x01) + CS_WRITE($0x6B, $0x0d) - -/* From Mike Fan: - Hi all: - If you are porting PM133, then you have to set DRAM Row Ending Address. - You did not set Rx56 and Rx57 in intel_pm133ram.S. - That register setting is like Rx5A~Rx5F. - Maybe could fix the mem wrapping issue. - (from Ron Minnich) - My manual says these are non-cacheable region registers. - (Turns out the manual is wrong. However, this did not help. - 2000/8/31 8:49 am I am setting all dram to cas3, and if that fails, - I'll be trying some of Cindy's other recommendations. - DRAM is currently CAS2. Symptom is an explosion in free_all_bootmem_core, - In the loop where it is freeing bootmem alloc pages from low mem. - 2000/8/31: 10:57 No change, Linux still crashes. We'll try Cindy Lee's recommendation - RE Register 0x6d, set it to 0x37. All our other settings conform to her - other recommendations. We also need to see whether we should be setting - Fixed MTRRs, but that seems unlikely. - 2000/8/31: 5:56 PM. No significant change. We're going to try to use 1 cycle writes - instead of 2. None of this feels like it is the real problem. Fixed MTRRs - helped a tiny bit. We can get to schedule() before we crash, but only - if we set a breakpoint after the first loop in free_all_bootmem_core - */ - CS_WRITE($0x56, $0x10) - CS_WRITE($0x57, $0x10) + /* Begin auto-detection + * Find the first bank with DIMM equipped. */ + + /* Maximum possible memory in bank 0, none in other banks. + * Starting from bank 0, we's fill 0 in these registers + * until memory is found. */ + CS_WRITE($0x5A, $0xff) + CS_WRITE($0x5B, $0xff) + CS_WRITE($0x5C, $0xff) + CS_WRITE($0x5D, $0xff) + CS_WRITE($0x5E, $0xff) + CS_WRITE($0x5F, $0xff) + CS_WRITE($0x56, $0xff) + CS_WRITE($0x57, $0xff) + + movl $0x5A, %ebx // first bank +1: + /* Write different values to 0 and 8, then read from 0. + * If values of address 0 match, we have something there. */ + movl $0x12345678, %eax + movl %eax, 0 + movl $0x87654321, %edx + movl %edx, 8 + movl 0, %edx + cmpl %eax, %edx + je 2f + /* No memory in this bank. Tell it to the bridge. */ + movl %ebx, %eax + xorl %edx, %edx + PCI_WRITE_CONFIG_BYTE + incl %ebx + cmpl $0x60, %ebx + jne 1b + /* No memory at all! */ + CONSOLE_EMERG_TX_STRING($msg_nomem) +1: + hlt + jmp 1b +2: + + /* Detect MA mapping type of the first bank. */ + + jmp raminit_ma +raminit_ma_reg_table: + /* Values for MA type register to try */ + .word 0x0000, 0x8088, 0xe0ee + .word 0xffff // end mark + +raminit_ma: + xorl %esi, %esi // highest address + movl $raminit_ma_reg_table, %ebx +1: + movw (%ebx), %cx + cmpw $0xffff, %cx + je raminit_ma_done + movl $0x58, %eax + PCI_WRITE_CONFIG_WORD + + xorl %eax, %eax + movl %eax, (%eax) + + // Write to addresses with only one address bit on, + // from 0x80000000 to 0x00000008 (lower 3 bits are ignored, assuming + // 64-bit bus). + // Then what is read at address 0 is the value written to the lowest + // address where it gets wrap-around. That address is either the size + // of the bank, or a missing bit due to incorrect MA mapping. + movl $0x80000000, %eax +2: + movl %eax, (%eax) + shrl $1, %eax + cmpl $4, %eax + jne 2b + + movl 0, %eax + cmpl %eax, %esi + jnc 3f + + // This is the current best MA mapping. + // Save the address and its MA mapping value. + movl %eax, %esi + movl %ecx, %edi +3: + incl %ebx + incl %ebx + jmp 1b + + +raminit_ma_done: + // Set the best (hopefully correct) MA mapping type. + movl $0x58, %eax + movl %edi, %ecx + PCI_WRITE_CONFIG_WORD + + CONSOLE_DEBUG_TX_STRING($msg_enabled) + CONSOLE_DEBUG_TX_HEX32(%esi) + CONSOLE_DEBUG_TX_STRING($msg_bytes) + + /* + * We have the size of first bank in %esi, but throwing it away. + * Sizing will again be done in C, because we'll configure the rest + * of banks in there anyway. + */ + + //CALLSP(dumpnorth) intel_chip_post_macro(0x36) + + + .section ".rom.data" +msg_nomem: + .asciz "No memory\r\n" +msg_enabled: + .asciz "Enabled first bank of RAM: 0x" +msg_bytes: + .asciz " bytes\r\n" + .previous