hi,
as promised the ram init routine for the intel 430fx chipset. completely untested yet, but seems to compile without errors (at least those i could have typed in ;))
the datasheet for the chipset is here: http://download.intel.com/design/chipsets/datashts/29051801.pdf
the routine does the following: 1. disable all caches 2. set ram size to 0 3. take one row and set its ram size to the maximum value and its type to edo 4. write some data in the row 5. enable edo detection 6. read the data back 7. if the data read is the data written it is edo 8. if the data read is NOT the data written, write some more data 9. read the data back (without edo detection) 10. if the data read is the data written it is fpm 11. if the data read is NOT the data written, the row is empty 12. detect the real size of the row and the dram technology (i'll write some more about that tomorrow, sorry, it's late already :)) 13. clear all settings for the current row 14. restart at 3. as long as not all rows have been processed 15. write the size and type (edo/fpm) of all rows 16. turn the caches back on ;)
the routine isn't doing any tests on dram timings yet. this COULD be implemented or we should ask the user to enter something useful. upto now the slowest dram timings are used which is not that nice but still it should work. anyway, i could implement some more chipset settings regarding pci performance and behaviour but i guess it doesn't belong in the raminit.c, does it? now have a look at the code and try to understand it ;) Holger
/* * This file is part of the LinuxBIOS project. * * Copyright (C) * * 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 <delay.h> #include <device/device.h> #include "raminit.h"
/*----------------------------------------------------------------------------- Macros and definitions. -----------------------------------------------------------------------------*/
/* Uncomment this to enable debugging output. */ #define DEBUG_RAM_SETUP 1
/* Debugging macros. */ #if defined(DEBUG_RAM_SETUP) #define PRINT_DEBUG(x) print_debug(x) #define PRINT_DEBUG_HEX8(x) print_debug_hex8(x) #define PRINT_DEBUG_HEX16(x) print_debug_hex16(x) #define PRINT_DEBUG_HEX32(x) print_debug_hex32(x) #define DUMPNORTH() dump_pci_device(PCI_DEV(0, 0, 0)) #else #define PRINT_DEBUG_(x) #define PRINT_DEBUG_HEX8(x) #define PRINT_DEBUG_HEX16(x) #define PRINT_DEBUG_HEX32(x) #define DUMPNORTH() #endif
/*----------------------------------------------------------------------------- EDO/FP/SDRAM detection routine. ----------------------------------------------------------------------------*/
/** * Initializes a row before it can be checked for DRAM */ static void initialize_row(const struct mem_controller* ctrl, const uint8_t u8_row) {
uint8_t u8_DRAMSize = 0; uint8_t u8_DRAMType = 1;
/* Initialize the current row to EDO and 32MB (maximum size) */ u8_DRAMType <<= u8_row; u8_DRAMSize == SIMM_ROW_SIZE_MAX / SIMM_GRANULARITY;
pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, u8_DRAMSize);
pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_TYPE), u8_DRAMType); }
/** * Clears a row after it has been checked for DRAM */ static void clear_row(const struct mem_controller* ctrl, const uint8_t u8_row) {
uint8_t u8_DRAMSize = 0;
pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, u8_DRAMSize); }
/** * Detects the type of RAM in the current row */ static void detect_type(const struct mem_controller* ctrl, uint8_t* pu8_DRAMSize, uint8_t* pu8_DRAMType, const uint8_t u8_row) {
uint8_t u8_DRAMC = 0; uint32_t u32_AA0 = 0x0; uint32_t u32_AA8 = 0x8; uint32_t u32_DD1 = 0xAAAAAAAAUL; uint32_t u32_DD2 = 0x55555555UL; uint32_t u32_DD3 = 0; uint32_t u32_DD4 = 0;
/* Write some data */ dimm_write32(u32_AA0, u32_DD1); dimm_write32(u32_AA8, u32_DD2);
/* Enable EDO detection mode */ u8_DRAMC = pci_read_config8(ctrl->d0, ((uint8_t)e_DRAM_CONTROL));
u8_DRAMC |= ((uint8_t)e_DRAMC_EDO_DETECT); pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_CONTROL), u8_DRAMC);
/* Read the data back */ dimm_read32(u32_AA0, u32_DD3); dimm_read32(u32_AA8, u32_DD4);
/* Disable EDO detection mode */ u8_DRAMC &= ~((uint8_t)e_DRAMC_EDO_DETECT); pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_CONTROL), u8_DRAMC);
if(u32_DD3 != u32_DD1 || u32_DD4 != u32_DD2) { /* No EDO RAM. Test FPM next */
/* Set the current bank to FPM */ pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_TYPE), 0x00);
/* Write some data */ dimm_write32(u32_AA0, u32_DD1); dimm_write32(u32_AA8, u32_DD2);
/* Read the data back */ dimm_read32(u32_AA0, u32_DD3); dimm_read32(u32_AA8, u32_DD4);
if(u32_DD3 != u32_DD1 || u32_DD4 != u32_DD2) { /* No FPM RAM. No RAM at all! */ *(pu8_DRAMSize) = 0; } else { *(pu8_DRAMType) &= ~(1 << u8_row); } } else { /* EDO found */ *(pu8_DRAMType) |= (1 << u8_row); } }
/** * Detects DRAM row size */ static void detect_size(const struct mem_controller* ctrl, uint8_t* au8_DRAMSize, const uint8_t u8_row) { uint32_t u32_AA0 = 0x0; uint32_t u32_AA8 = 1; uint32_t u32_DD1 = 0x55555555UL; uint32_t u32_DD2 = 0xAAAAAAAAUL; uint32_t u32_DD3 = 0xFEDCBA98UL; uint32_t u32_DD4 = 0; uint8_t u8_size_detected = 0; uint8_t u8_type = 0;
for(;;) { /* Write some data */ dimm_write32(u32_AA0, u32_DD1);
switch (au8_DRAMSize[u8_row]) { case e_4MB : { for(u8_type = 0; u8_type < RCA_8MB_TYPES; u8_type++) { /* Prepare the first address */ u32_AA8 = (1 << s_DRAMT8MB[u8_type].u8_ColumnAddress);
/* Write some more data */ dimm_write32(u32_AA8, u32_DD2);
/* Write even more data to yet another address for asymetric DRAM */ if(s_DRAMT8MB[u8_type].u8_ColumnAddress != s_DRAMT8MB[u8_type].u8_RowAddress) { /* Prepare the second address */ u32_AA8 = (1 << s_DRAMT8MB[u8_type].u8_RowAddress);
/* Write even more data */ dimm_write32(u32_AA8, u32_DD3); }
/* Read data back */ dimm_read32(u32_AA0, u32_DD4);
if(u32_DD1 == u32_DD4) { /* Found DRAM with correct size and technology */ au8_DRAMSize[u8_row] = ((uint8_t)e_4MB); u8_size_detected = 1; break; } }
/* No DRAM was found! */ if(u8_size_detected == 0) { au8_DRAMSize[u8_row] = ((uint8_t)e_EMPTY);
/* FIXME: Not sure if this is really needed */ pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, au8_DRAMSize[u8_row]); }
break; }
case e_8MB : { for(u8_type = 0; u8_type < RCA_8MB_TYPES; u8_type++) { /* Prepare the first address */ u32_AA8 = (1 << s_DRAMT8MB[u8_type].u8_ColumnAddress);
/* Write some more data */ dimm_write32(u32_AA8, u32_DD2);
/* Write even more data to yet another address for asymetric DRAM */ if(s_DRAMT8MB[u8_type].u8_ColumnAddress != s_DRAMT8MB[u8_type].u8_RowAddress) { /* Prepare the second address */ u32_AA8 = (1 << s_DRAMT8MB[u8_type].u8_RowAddress);
/* Write even more data */ dimm_write32(u32_AA8, u32_DD3); }
/* Read data back */ dimm_read32(u32_AA0, u32_DD4);
if(u32_DD1 == u32_DD4) { /* Found DRAM with correct size and technology */ au8_DRAMSize[u8_row] = ((uint8_t)e_8MB); u8_size_detected = 1; break; } }
/* Test for 4MB next if no 8MB DRAM was found */ if(u8_size_detected == 0) { au8_DRAMSize[u8_row] = ((uint8_t)e_4MB);
/* FIXME: Not sure if this is really needed */ pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, au8_DRAMSize[u8_row]); }
break; }
case e_16MB : { for(u8_type = 0; u8_type < RCA_16MB_TYPES; u8_type++) { /* Prepare the first address */ u32_AA8 = (1 << s_DRAMT16MB[u8_type].u8_ColumnAddress);
/* Write some more data */ dimm_write32(u32_AA8, u32_DD2);
/* Write even more data to yet another address for asymetric DRAM */ if(s_DRAMT16MB[u8_type].u8_ColumnAddress != s_DRAMT16MB[u8_type].u8_RowAddress) { /* Prepare the second address */ u32_AA8 = (1 << s_DRAMT16MB[u8_type].u8_RowAddress);
/* Write even more data */ dimm_write32(u32_AA8, u32_DD3); }
/* Read data back */ dimm_read32(u32_AA0, u32_DD4);
if(u32_DD1 == u32_DD4) { /* Found DRAM with correct size and technology */ au8_DRAMSize[u8_row] = ((uint8_t)e_16MB); u8_size_detected = 1; break; } }
/* Test for 8MB next if no 16MB DRAM was found */ if(u8_size_detected == 0) { au8_DRAMSize[u8_row] = ((uint8_t)e_8MB);
/* FIXME: Not sure if this is really needed */ pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, au8_DRAMSize[u8_row]); }
break; }
case e_32MB : { for(u8_type = 0; u8_type < RCA_32MB_TYPES; u8_type++) { /* Prepare the first address */ u32_AA8 = (1 << s_DRAMT32MB[u8_type].u8_ColumnAddress);
/* Write some more data */ dimm_write32(u32_AA8, u32_DD2);
/* Write even more data to yet another address for asymetric DRAM */ if(s_DRAMT32MB[u8_type].u8_ColumnAddress != s_DRAMT32MB[u8_type].u8_RowAddress) { /* Prepare the second address */ u32_AA8 = (1 << s_DRAMT32MB[u8_type].u8_RowAddress);
/* Write even more data */ dimm_write32(u32_AA8, u32_DD3); }
/* Read data back */ dimm_read32(u32_AA0, u32_DD4);
if(u32_DD1 == u32_DD4) { /* Found DRAM with correct size and technology */ au8_DRAMSize[u8_row] = ((uint8_t)e_32MB); u8_size_detected = 1; break; } }
/* Test for 16MB next if no 32MB DRAM was found */ if(u8_size_detected == 0) { au8_DRAMSize[u8_row] = ((uint8_t)e_16MB);
/* FIXME: Not sure if this is really needed */ pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, au8_DRAMSize[u8_row]); }
break; }
default : { /* Unknown DRAM size */ au8_DRAMSize[u8_row] = ((uint8_t)e_EMPTY);
/* FIXME: Not sure if this is really needed */ pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, au8_DRAMSize[u8_row]); break; } }
/* Quit if the size of the current row was detected or an error occured */ if((u8_size_detected != 0) || au8_DRAMSize[u8_row] == ((uint8_t)e_EMPTY)){ break; } } }
static void ram_detection(const struct mem_controller* ctrl) {
uint8_t u8_row = 0; uint8_t au8_DRAMSize[SIMM_SOCKETS]; uint8_t u8_DRAMType = 0; uint8_t u8_DRAMBoundary = 0; uint8_t u8_CacheControl = 0; uint8_t u8_CacheOff = 0;
/* Turn off cache (L1 & L2) */ u8_CacheControl = pci_read_config8(ctrl->d0, ((uint8_t)e_CACHE_CONTROL));
u8_CacheOff = u8_CacheControl & (~(e_CC_FIRST_CACHE_ENABLE) & ~(e_CC_SECONDARY_CACHE_SIZE) & ~(e_CC_SECONDARY_CACHE_MISS));
pci_write_config8(ctrl->d0, ((uint8_t)e_CACHE_CONTROL), u8_CacheOff);
/* Set size of DRAM to 0 for all rows */ for(u8_row = 0; u8_row < SIMM_SOCKETS; u8_row++) { au8_DRAMSize[u8_row] = 0; pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, au8_DRAMSize); }
/* Detect RAM presence and type in all rows */ for(u8_row = 0; u8_row < SIMM_SOCKETS; u8_row++) {
/* Start of DRAM type detection */ /* Initialize the current row */ initialize_row(ctrl, u8_row);
/* Detect type of ram in the current row */ detect_type(ctrl, au8_DRAMSize, &u8_DRAMType, u8_row);
/* Check if any DRAM was found in this row */ if(au8_DRAMSize[u8_row] == 0) { /* No DRAM found. Skip the rest. */ continue; }
/* Detect column address type and the size of the current row */ detect_size(ctrl, au8_DRAMSize, u8_row);
/* Clear all settings made for the current row */ clear_row(ctrl, u8_row); }
/* Turn L1 and L2 caches back on */ pci_write_config8(ctrl->d0, ((uint8_t)e_CACHE_CONTROL), u8_CacheControl);
/* Set the type of all detected DRAM rows */ pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_TYPE), u8_DRAMType);
/* Set the DRAM row boundaries */ for(u8_row = 0; u8_row < SIMM_SOCKETS; u8_row++) { u8_DRAMBoundary += au8_DRAMSize[u8_row];
pci_write_config8(ctrl->d0, ((uint8_t)e_DRAM_ROW_BOUNDARY_BASE) + u8_row, u8_DRAMBoundary); } }
/* * This file is part of the LinuxBIOS project. * * Copyright (C) * * 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 */
#ifndef RAMINIT_H #define RAMINIT_H
#define SIMM_SOCKETS 5 #define SIMM_ROW_SIZE_MAX 32 #define SIMM_GRANULARITY 4
enum E_DRAM_SIZE { e_EMPTY = 0, e_4MB = 1, e_8MB = 2, e_16MB = 4, e_32MB = 8 };
enum E_REGISTERS { e_CACHE_CONTROL = 0x52, e_DRAM_CONTROL = 0x57, e_DRAM_ROW_BOUNDARY_BASE = 0x60, e_DRAM_ROW_TYPE = 0x68 };
enum E_CACHE_CONTROL_MASKS { e_CC_SECONDARY_CACHE_SIZE = 0xC0, e_CC_SRAM_TYPE = 0x30, e_CC_NA_DISABLE = 0x04, e_CC_SECONDARY_CACHE_MISS = 0x02, e_CC_FIRST_CACHE_ENABLE = 0x01 };
enum E_DRAM_CONTROL_MASKS { e_DRAMC_HOLE_ENABLE = 0xC0, e_DRAMC_EDO_DETECT = 0x08, e_DRAMC_HOST_BUS_FREQUENCY = 0x07 };
enum E_DRAM_ROW_BOUNDARY_MASKS { e_DRB_ROW_BOUNDARY = 0x2F };
enum E_DRAM_ROW_TYPE_MASKS { e_DRT_ROW_TYPE = 0x1F };
#define RCA_32MB_TYPES 2 #define RCA_16MB_TYPES 1 #define RCA_8MB_TYPES 1 #define RCA_4MB_TYPES 1
struct S_DRAMTechnology { uint8_t u8_RowAddress; uint8_t u8_ColumnAddress; };
/** * row x col - DRAM size * 12 x 10 - 32MB * 11 x 11 - 32MB */
static struct S_DRAMTechnology s_DRAMT32MB[] = { {24, 22}, {23, 24} };
/** * row x col - DRAM size * 11 x 10 - 16MB */ struct S_DRAMTechnology s_DRAMT16MB[] = { {23, 22} };
/** * row x col - DRAM size * 10 x 10 - 8MB */ struct S_DRAMTechnology s_DRAMT8MB[] = { {21, 22} };
/** * row x col - DRAM size * 10 x 9 - 4MB */ struct S_DRAMTechnology s_DRAMT4MB[] = { {21, 11} };
struct mem_controller { device_t d0; uint16_t channel0[SIMM_SOCKETS]; };
#endif /* RAMINIT_H */