| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2007 Advanced Micro Devices, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * 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. |
| */ |
| |
| #include <console/console.h> |
| #include <cpu/x86/msr.h> |
| #include <cpu/amd/lxdef.h> |
| #include <arch/io.h> |
| #include <spd.h> |
| #include <stddef.h> |
| #include "southbridge/amd/cs5536/cs5536.h" |
| #include "raminit.h" |
| #include "northbridge.h" |
| |
| static const unsigned char NumColAddr[] = { |
| 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x07, |
| 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F |
| }; |
| |
| static void banner(const char *s) |
| { |
| printk(BIOS_DEBUG, " * %s\n", s); |
| } |
| |
| static void __attribute__((noreturn)) hcf(void) |
| { |
| printk(BIOS_EMERG, "DIE\n"); |
| /* this guarantees we flush the UART fifos (if any) and also |
| * ensures that things, in general, keep going so no debug output |
| * is lost |
| */ |
| while (1) |
| printk(BIOS_EMERG, "%c", 0); |
| } |
| |
| static void auto_size_dimm(unsigned int dimm) |
| { |
| uint32_t dimm_setting; |
| uint16_t dimm_size; |
| uint8_t spd_byte; |
| msr_t msr; |
| |
| dimm_setting = 0; |
| |
| banner("Check present"); |
| /* Check that we have a dimm */ |
| if (spd_read_byte(dimm, SPD_MEMORY_TYPE) == 0xFF) { |
| return; |
| } |
| |
| banner("MODBANKS"); |
| /* Field: Module Banks per DIMM */ |
| /* EEPROM byte usage: (5) Number of DIMM Banks */ |
| spd_byte = spd_read_byte(dimm, SPD_NUM_DIMM_BANKS); |
| if ((MIN_MOD_BANKS > spd_byte) || (spd_byte > MAX_MOD_BANKS)) { |
| printk(BIOS_EMERG, "Number of module banks not compatible\n"); |
| post_code(ERROR_BANK_SET); |
| hcf(); |
| } |
| dimm_setting |= (spd_byte >> 1) << CF07_UPPER_D0_MB_SHIFT; |
| banner("FIELDBANKS"); |
| |
| /* Field: Banks per SDRAM device */ |
| /* EEPROM byte usage: (17) Number of Banks on SDRAM Device */ |
| spd_byte = spd_read_byte(dimm, SPD_NUM_BANKS_PER_SDRAM); |
| if ((MIN_DEV_BANKS > spd_byte) || (spd_byte > MAX_DEV_BANKS)) { |
| printk(BIOS_EMERG, "Number of device banks not compatible\n"); |
| post_code(ERROR_BANK_SET); |
| hcf(); |
| } |
| dimm_setting |= (spd_byte >> 2) << CF07_UPPER_D0_CB_SHIFT; |
| banner("SPDNUMROWS"); |
| |
| /*; Field: DIMM size |
| *; EEPROM byte usage: (3) Number of Row Addresses |
| *; (4) Number of Column Addresses |
| *; (5) Number of DIMM Banks |
| *; (31) Module Bank Density |
| *; Size = Module Density * Module Banks |
| */ |
| if ((spd_read_byte(dimm, SPD_NUM_ROWS) & 0xF0) |
| || (spd_read_byte(dimm, SPD_NUM_COLUMNS) & 0xF0)) { |
| printk(BIOS_EMERG, "Assymetirc DIMM not compatible\n"); |
| post_code(ERROR_UNSUPPORTED_DIMM); |
| hcf(); |
| } |
| banner("SPDBANKDENSITY"); |
| |
| dimm_size = spd_read_byte(dimm, SPD_BANK_DENSITY); |
| banner("DIMMSIZE"); |
| dimm_size |= (dimm_size << 8); /* align so 1GB(bit0) is bit 8, this is a little weird to get gcc to not optimize this out */ |
| dimm_size &= 0x01FC; /* and off 2GB DIMM size : not supported and the 1GB size we just moved up to bit 8 as well as all the extra on top */ |
| |
| /* Module Density * Module Banks */ |
| dimm_size <<= (dimm_setting >> CF07_UPPER_D0_MB_SHIFT) & 1; /* shift to multiply by # DIMM banks */ |
| banner("BEFORT CTZ"); |
| dimm_size = __builtin_ctz(dimm_size); |
| banner("TEST DIMM SIZE > 8"); |
| if (dimm_size > 8) { /* 8 is 1GB only support 1GB per DIMM */ |
| printk(BIOS_EMERG, "Only support up to 1 GB per DIMM\n"); |
| post_code(ERROR_DENSITY_DIMM); |
| hcf(); |
| } |
| dimm_setting |= dimm_size << CF07_UPPER_D0_SZ_SHIFT; |
| banner("PAGESIZE"); |
| |
| /*; Field: PAGE size |
| *; EEPROM byte usage: (4) Number of Column Addresses |
| *; PageSize = 2^# Column Addresses * Data width in bytes (should be 8bytes for a normal DIMM) |
| * |
| *; But this really works by magic. |
| *;If ma[12:0] is the memory address pins, and pa[12:0] is the physical column address |
| *;that MC generates, here is how the MC assigns the pa onto the ma pins: |
| * |
| *;ma 12 11 10 09 08 07 06 05 04 03 02 01 00 |
| *;------------------------------------------- |
| *;pa 09 08 07 06 05 04 03 (7 col addr bits = 1K page size) |
| *;pa 10 09 08 07 06 05 04 03 (8 col addr bits = 2K page size) |
| *;pa 11 10 09 08 07 06 05 04 03 (9 col addr bits = 4K page size) |
| *;pa 12 11 10 09 08 07 06 05 04 03 (10 col addr bits = 8K page size) |
| *;pa 13 AP 12 11 10 09 08 07 06 05 04 03 (11 col addr bits = 16K page size) |
| *;pa 14 13 AP 12 11 10 09 08 07 06 05 04 03 (12 col addr bits = 32K page size) |
| *; *AP = autoprecharge bit |
| * |
| *;Remember that pa[2:0] are zeroed out since it's a 64-bit data bus (8 bytes), |
| *;so lower 3 address bits are dont_cares.So from the table above, |
| *;it's easier to see what the old code is doing: if for example,#col_addr_bits = 7(06h), |
| *;it adds 3 to get 10, then does 2^10 = 1K. Get it?*/ |
| |
| spd_byte = NumColAddr[spd_read_byte(dimm, SPD_NUM_COLUMNS) & 0xF]; |
| banner("MAXCOLADDR"); |
| if (spd_byte > MAX_COL_ADDR) { |
| printk(BIOS_EMERG, "DIMM page size not compatible\n"); |
| post_code(ERROR_SET_PAGE); |
| hcf(); |
| } |
| banner(">12address test"); |
| spd_byte -= 7; |
| if (spd_byte > 5) { /* if the value is above 6 it means > 12 address lines */ |
| spd_byte = 7; /* which means > 32k so set to disabled */ |
| } |
| dimm_setting |= spd_byte << CF07_UPPER_D0_PSZ_SHIFT; /* 0 = 1k, 1 = 2k, 2 = 4k, etc */ |
| |
| banner("RDMSR CF07"); |
| msr = rdmsr(MC_CF07_DATA); |
| banner("WRMSR CF07"); |
| if (dimm == DIMM0) { |
| msr.hi &= 0xFFFF0000; |
| msr.hi |= dimm_setting; |
| } else { |
| msr.hi &= 0x0000FFFF; |
| msr.hi |= dimm_setting << 16; |
| } |
| wrmsr(MC_CF07_DATA, msr); |
| banner("ALL DONE"); |
| } |
| |
| static void checkDDRMax(void) |
| { |
| uint8_t spd_byte0, spd_byte1; |
| uint16_t speed; |
| |
| /* PC133 identifier */ |
| spd_byte0 = spd_read_byte(DIMM0, SPD_MIN_CYCLE_TIME_AT_CAS_MAX); |
| if (spd_byte0 == 0xFF) { |
| spd_byte0 = 0; |
| } |
| spd_byte1 = spd_read_byte(DIMM1, SPD_MIN_CYCLE_TIME_AT_CAS_MAX); |
| if (spd_byte1 == 0xFF) { |
| spd_byte1 = 0; |
| } |
| |
| /* I don't think you need this check. |
| if (spd_byte0 >= 0xA0 || spd_byte1 >= 0xA0) { |
| printk(BIOS_EMERG, "DIMM overclocked. Check GeodeLink Speed\n"); |
| post_code(POST_PLL_MEM_FAIL); |
| hcf(); |
| } */ |
| |
| /* Use the slowest DIMM */ |
| if (spd_byte0 < spd_byte1) { |
| spd_byte0 = spd_byte1; |
| } |
| |
| /* Turn SPD ns time into MHz. Check what the asm does to this math. */ |
| speed = 20000 / (((spd_byte0 >> 4) * 10) + (spd_byte0 & 0x0F)); |
| |
| /* current speed > max speed? */ |
| if (GeodeLinkSpeed() > speed) { |
| printk(BIOS_EMERG, "DIMM overclocked. Check GeodeLink Speed\n"); |
| post_code(POST_PLL_MEM_FAIL); |
| hcf(); |
| } |
| } |
| |
| const uint16_t REF_RATE[] = { 15, 3, 7, 31, 62, 125 }; /* ns */ |
| |
| static void set_refresh_rate(void) |
| { |
| uint8_t spd_byte0, spd_byte1; |
| uint16_t rate0, rate1; |
| msr_t msr; |
| |
| spd_byte0 = spd_read_byte(DIMM0, SPD_REFRESH); |
| spd_byte0 &= 0xF; |
| if (spd_byte0 > 5) { |
| spd_byte0 = 5; |
| } |
| rate0 = REF_RATE[spd_byte0]; |
| |
| spd_byte1 = spd_read_byte(DIMM1, SPD_REFRESH); |
| spd_byte1 &= 0xF; |
| if (spd_byte1 > 5) { |
| spd_byte1 = 5; |
| } |
| rate1 = REF_RATE[spd_byte1]; |
| |
| /* Use the faster rate (lowest number) */ |
| if (rate0 > rate1) { |
| rate0 = rate1; |
| } |
| |
| msr = rdmsr(MC_CF07_DATA); |
| msr.lo |= ((rate0 * (GeodeLinkSpeed() / 2)) / 16) |
| << CF07_LOWER_REF_INT_SHIFT; |
| wrmsr(MC_CF07_DATA, msr); |
| } |
| |
| const uint8_t CASDDR[] = { 5, 5, 2, 6, 3, 7, 4, 0 }; /* 1(1.5), 1.5, 2, 2.5, 3, 3.5, 4, 0 */ |
| |
| static u8 getcasmap(u32 dimm, u16 glspeed) |
| { |
| u16 dimm_speed; |
| u8 spd_byte, casmap, casmap_shift = 0; |
| |
| /************************** DIMM0 **********************************/ |
| casmap = spd_read_byte(dimm, SPD_ACCEPTABLE_CAS_LATENCIES); |
| if (casmap != 0xFF) { |
| /* IF -.5 timing is supported, check -.5 timing > GeodeLink */ |
| spd_byte = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_2ND); |
| if (spd_byte != 0) { |
| /* Turn SPD ns time into MHz. Check what the asm does to this math. */ |
| dimm_speed = 20000 / (((spd_byte >> 4) * 10) + (spd_byte & 0x0F)); |
| if (dimm_speed >= glspeed) { |
| casmap_shift = 1; /* -.5 is a shift of 1 */ |
| /* IF -1 timing is supported, check -1 timing > GeodeLink */ |
| spd_byte = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_3RD); |
| if (spd_byte != 0) { |
| /* Turn SPD ns time into MHz. Check what the asm does to this math. */ |
| dimm_speed = 20000 / (((spd_byte >> 4) * 10) + (spd_byte & 0x0F)); |
| if (dimm_speed >= glspeed) { |
| casmap_shift = 2; /* -1 is a shift of 2 */ |
| } |
| } /* SPD_SDRAM_CYCLE_TIME_3RD (-1) !=0 */ |
| } else { |
| casmap_shift = 0; |
| } |
| } /* SPD_SDRAM_CYCLE_TIME_2ND (-.5) !=0 */ |
| /* set the casmap based on the shift to limit possible CAS settings */ |
| spd_byte = 31 - __builtin_clz((uint32_t) casmap); |
| /* just want bits in the lower byte since we have to cast to a 32 */ |
| casmap &= 0xFF << (spd_byte - casmap_shift); |
| } else { /* No DIMM */ |
| casmap = 0; |
| } |
| return casmap; |
| } |
| |
| static void setCAS(void) |
| { |
| /*;***************************************************************************** |
| ;* |
| ;* setCAS |
| ;* EEPROM byte usage: (18) SDRAM device attributes - CAS latency |
| ;* EEPROM byte usage: (23) SDRAM Minimum Clock Cycle Time @ CLX -.5 |
| ;* EEPROM byte usage: (25) SDRAM Minimum Clock Cycle Time @ CLX -1 |
| ;* |
| ;* The CAS setting is based on the information provided in each DIMMs SPD. |
| ;* The speed at which a DIMM can run is described relative to the slowest |
| ;* CAS the DIMM supports. Each speed for the relative CAS settings is |
| ;* checked that it is within the GeodeLink speed. If it isn't within the GeodeLink |
| ;* speed, the CAS setting is removed from the list of good settings for |
| ;* the DIMM. This is done for both DIMMs and the lists are compared to |
| ;* find the lowest common CAS latency setting. If there are no CAS settings |
| ;* in common we out a ERROR_DIFF_DIMMS (78h) to port 80h and halt. |
| ;* |
| ;* Entry: |
| ;* Exit: Set fastest CAS Latency based on GeodeLink speed and SPD information. |
| ;* Destroys: We really use everything ! |
| ;*****************************************************************************/ |
| uint16_t glspeed; |
| uint8_t spd_byte, casmap0, casmap1; |
| msr_t msr; |
| |
| glspeed = GeodeLinkSpeed(); |
| |
| casmap0 = getcasmap(DIMM0, glspeed); |
| casmap1 = getcasmap(DIMM1, glspeed); |
| |
| /********************* CAS_LAT MAP COMPARE ***************************/ |
| if (casmap0 == 0) { |
| spd_byte = CASDDR[__builtin_ctz(casmap1)]; |
| } else if (casmap1 == 0) { |
| spd_byte = CASDDR[__builtin_ctz(casmap0)]; |
| } else if ((casmap0 &= casmap1)) { |
| spd_byte = CASDDR[__builtin_ctz(casmap0)]; |
| } else { |
| printk(BIOS_EMERG, "DIMM CAS Latencies not compatible\n"); |
| post_code(ERROR_DIFF_DIMMS); |
| hcf(); |
| } |
| |
| msr = rdmsr(MC_CF8F_DATA); |
| msr.lo &= ~(7 << CF8F_LOWER_CAS_LAT_SHIFT); |
| msr.lo |= spd_byte << CF8F_LOWER_CAS_LAT_SHIFT; |
| wrmsr(MC_CF8F_DATA, msr); |
| } |
| |
| static void set_latencies(void) |
| { |
| uint32_t memspeed, dimm_setting; |
| uint8_t spd_byte0, spd_byte1; |
| msr_t msr; |
| |
| memspeed = GeodeLinkSpeed() / 2; |
| dimm_setting = 0; |
| |
| /* MC_CF8F setup */ |
| /* tRAS */ |
| spd_byte0 = spd_read_byte(DIMM0, SPD_tRAS); |
| if (spd_byte0 == 0xFF) { |
| spd_byte0 = 0; |
| } |
| spd_byte1 = spd_read_byte(DIMM1, SPD_tRAS); |
| if (spd_byte1 == 0xFF) { |
| spd_byte1 = 0; |
| } |
| if (spd_byte0 < spd_byte1) { |
| spd_byte0 = spd_byte1; |
| } |
| |
| /* (ns/(1/MHz) = (us*MHz)/1000 = clocks/1000 = clocks) */ |
| spd_byte1 = (spd_byte0 * memspeed) / 1000; |
| if (((spd_byte0 * memspeed) % 1000)) { |
| ++spd_byte1; |
| } |
| dimm_setting |= spd_byte1 << CF8F_LOWER_ACT2PRE_SHIFT; |
| |
| /* tRP */ |
| spd_byte0 = spd_read_byte(DIMM0, SPD_tRP); |
| if (spd_byte0 == 0xFF) { |
| spd_byte0 = 0; |
| } |
| spd_byte1 = spd_read_byte(DIMM1, SPD_tRP); |
| if (spd_byte1 == 0xFF) { |
| spd_byte1 = 0; |
| } |
| if (spd_byte0 < spd_byte1) { |
| spd_byte0 = spd_byte1; |
| } |
| |
| /* (ns/(1/MHz) = (us*MHz)/1000 = clocks/1000 = clocks) */ |
| spd_byte1 = ((spd_byte0 >> 2) * memspeed) / 1000; |
| if ((((spd_byte0 >> 2) * memspeed) % 1000)) { |
| ++spd_byte1; |
| } |
| dimm_setting |= spd_byte1 << CF8F_LOWER_PRE2ACT_SHIFT; |
| |
| /* tRCD */ |
| spd_byte0 = spd_read_byte(DIMM0, SPD_tRCD); |
| if (spd_byte0 == 0xFF) { |
| spd_byte0 = 0; |
| } |
| spd_byte1 = spd_read_byte(DIMM1, SPD_tRCD); |
| if (spd_byte1 == 0xFF) { |
| spd_byte1 = 0; |
| } |
| if (spd_byte0 < spd_byte1) { |
| spd_byte0 = spd_byte1; |
| } |
| |
| /* (ns/(1/MHz) = (us*MHz)/1000 = clocks/1000 = clocks) */ |
| spd_byte1 = ((spd_byte0 >> 2) * memspeed) / 1000; |
| if ((((spd_byte0 >> 2) * memspeed) % 1000)) { |
| ++spd_byte1; |
| } |
| dimm_setting |= spd_byte1 << CF8F_LOWER_ACT2CMD_SHIFT; |
| |
| /* tRRD */ |
| spd_byte0 = spd_read_byte(DIMM0, SPD_tRRD); |
| if (spd_byte0 == 0xFF) { |
| spd_byte0 = 0; |
| } |
| spd_byte1 = spd_read_byte(DIMM1, SPD_tRRD); |
| if (spd_byte1 == 0xFF) { |
| spd_byte1 = 0; |
| } |
| if (spd_byte0 < spd_byte1) { |
| spd_byte0 = spd_byte1; |
| } |
| |
| /* (ns/(1/MHz) = (us*MHz)/1000 = clocks/1000 = clocks) */ |
| spd_byte1 = ((spd_byte0 >> 2) * memspeed) / 1000; |
| if ((((spd_byte0 >> 2) * memspeed) % 1000)) { |
| ++spd_byte1; |
| } |
| dimm_setting |= spd_byte1 << CF8F_LOWER_ACT2ACT_SHIFT; |
| |
| /* tRC = tRP + tRAS */ |
| dimm_setting |= (((dimm_setting >> CF8F_LOWER_ACT2PRE_SHIFT) & 0x0F) + |
| ((dimm_setting >> CF8F_LOWER_PRE2ACT_SHIFT) & 0x07)) |
| << CF8F_LOWER_ACT2ACTREF_SHIFT; |
| |
| msr = rdmsr(MC_CF8F_DATA); |
| msr.lo &= 0xF00000FF; |
| msr.lo |= dimm_setting; |
| msr.hi |= CF8F_UPPER_REORDER_DIS_SET; |
| wrmsr(MC_CF8F_DATA, msr); |
| |
| /* MC_CF1017 setup */ |
| /* tRFC */ |
| spd_byte0 = spd_read_byte(DIMM0, SPD_tRFC); |
| if (spd_byte0 == 0xFF) { |
| spd_byte0 = 0; |
| } |
| spd_byte1 = spd_read_byte(DIMM1, SPD_tRFC); |
| if (spd_byte1 == 0xFF) { |
| spd_byte1 = 0; |
| } |
| if (spd_byte0 < spd_byte1) { |
| spd_byte0 = spd_byte1; |
| } |
| |
| if (spd_byte0) { |
| /* (ns/(1/MHz) = (us*MHz)/1000 = clocks/1000 = clocks) */ |
| spd_byte1 = (spd_byte0 * memspeed) / 1000; |
| if (((spd_byte0 * memspeed) % 1000)) { |
| ++spd_byte1; |
| } |
| } else { /* Not all SPDs have tRFC setting. Use this formula tRFC = tRC + 1 clk */ |
| spd_byte1 = ((dimm_setting >> CF8F_LOWER_ACT2ACTREF_SHIFT) & 0x0F) + 1; |
| } |
| dimm_setting = spd_byte1 << CF1017_LOWER_REF2ACT_SHIFT; /* note this clears the cf8f dimm setting */ |
| msr = rdmsr(MC_CF1017_DATA); |
| msr.lo &= ~(0x1F << CF1017_LOWER_REF2ACT_SHIFT); |
| msr.lo |= dimm_setting; |
| wrmsr(MC_CF1017_DATA, msr); |
| |
| /* tWTR: Set tWTR to 2 for 400MHz and above GLBUS (200MHz mem) other wise it stay default(1) */ |
| if (memspeed > 198) { |
| msr = rdmsr(MC_CF1017_DATA); |
| msr.lo &= ~(0x7 << CF1017_LOWER_WR_TO_RD_SHIFT); |
| msr.lo |= 2 << CF1017_LOWER_WR_TO_RD_SHIFT; |
| wrmsr(MC_CF1017_DATA, msr); |
| } |
| } |
| |
| static void set_extended_mode_registers(void) |
| { |
| uint8_t spd_byte0, spd_byte1; |
| msr_t msr; |
| spd_byte0 = spd_read_byte(DIMM0, SPD_DEVICE_ATTRIBUTES_GENERAL); |
| if (spd_byte0 == 0xFF) { |
| spd_byte0 = 0; |
| } |
| spd_byte1 = spd_read_byte(DIMM1, SPD_DEVICE_ATTRIBUTES_GENERAL); |
| if (spd_byte1 == 0xFF) { |
| spd_byte1 = 0; |
| } |
| spd_byte1 &= spd_byte0; |
| |
| msr = rdmsr(MC_CF07_DATA); |
| if (spd_byte1 & 1) { /* Drive Strength Control */ |
| msr.lo |= CF07_LOWER_EMR_DRV_SET; |
| } |
| if (spd_byte1 & 2) { /* FET Control */ |
| msr.lo |= CF07_LOWER_EMR_QFC_SET; |
| } |
| wrmsr(MC_CF07_DATA, msr); |
| } |
| |
| #undef TLA_MEMORY_DEBUG |
| #ifdef TLA_MEMORY_DEBUG |
| static void EnableMTest(void) |
| { |
| msr_t msr; |
| |
| msr = rdmsr(GLCP_DELAY_CONTROLS); |
| msr.hi &= ~(7 << 20); /* clear bits 54:52 */ |
| if (GeodeLinkSpeed() < 200) { |
| msr.hi |= 2 << 20; |
| } |
| wrmsr(GLCP_DELAY_CONTROLS, msr); |
| |
| msr = rdmsr(MC_CFCLK_DBUG); |
| msr.hi |= |
| CFCLK_UPPER_MTST_B2B_DIS_SET | CFCLK_UPPER_MTEST_EN_SET | |
| CFCLK_UPPER_MTST_RBEX_EN_SET; |
| msr.lo |= CFCLK_LOWER_TRISTATE_DIS_SET; |
| wrmsr(MC_CFCLK_DBUG, msr); |
| |
| printk(BIOS_INFO, "Enabled MTest for TLA debug\n"); |
| } |
| #endif |
| |
| void sdram_set_registers(const struct mem_controller *ctrl) |
| { |
| msr_t msr; |
| uint32_t msrnum; |
| |
| /* Set Timing Control */ |
| msrnum = MC_CF1017_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo &= ~(7 << CF1017_LOWER_RD_TMG_CTL_SHIFT); |
| if (GeodeLinkSpeed() < 334) { |
| msr.lo |= (3 << CF1017_LOWER_RD_TMG_CTL_SHIFT); |
| } else { |
| msr.lo |= (4 << CF1017_LOWER_RD_TMG_CTL_SHIFT); |
| } |
| wrmsr(msrnum, msr); |
| |
| /* Set Refresh Staggering */ |
| msrnum = MC_CF07_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo &= ~0xF0; |
| msr.lo |= 0x40; /* set refresh to 4SDRAM clocks */ |
| wrmsr(msrnum, msr); |
| |
| /* Memory Interleave: Set HOI here otherwise default is LOI */ |
| /* msrnum = MC_CF8F_DATA; |
| msr = rdmsr(msrnum); |
| msr.hi |= CF8F_UPPER_HOI_LOI_SET; |
| wrmsr(msrnum, msr); */ |
| } |
| |
| void sdram_set_spd_registers(const struct mem_controller *ctrl) |
| { |
| uint8_t spd_byte; |
| |
| banner("sdram_set_spd_register"); |
| post_code(POST_MEM_SETUP); // post_70h |
| |
| spd_byte = spd_read_byte(DIMM0, SPD_MODULE_ATTRIBUTES); |
| banner("Check DIMM 0"); |
| /* Check DIMM is not Register and not Buffered DIMMs. */ |
| if ((spd_byte != 0xFF) && (spd_byte & 3)) { |
| printk(BIOS_EMERG, "DIMM0 NOT COMPATIBLE\n"); |
| post_code(ERROR_UNSUPPORTED_DIMM); |
| hcf(); |
| } |
| banner("Check DIMM 1"); |
| spd_byte = spd_read_byte(DIMM1, SPD_MODULE_ATTRIBUTES); |
| if ((spd_byte != 0xFF) && (spd_byte & 3)) { |
| printk(BIOS_EMERG, "DIMM1 NOT COMPATIBLE\n"); |
| post_code(ERROR_UNSUPPORTED_DIMM); |
| hcf(); |
| } |
| |
| post_code(POST_MEM_SETUP2); // post_72h |
| banner("Check DDR MAX"); |
| |
| /* Check that the memory is not overclocked. */ |
| checkDDRMax(); |
| |
| /* Size the DIMMS */ |
| post_code(POST_MEM_SETUP3); // post_73h |
| banner("AUTOSIZE DIMM 0"); |
| auto_size_dimm(DIMM0); |
| post_code(POST_MEM_SETUP4); // post_74h |
| banner("AUTOSIZE DIMM 1"); |
| auto_size_dimm(DIMM1); |
| |
| /* Set CAS latency */ |
| banner("set cas latency"); |
| post_code(POST_MEM_SETUP5); // post_75h |
| setCAS(); |
| |
| /* Set all the other latencies here (tRAS, tRP....) */ |
| banner("set all latency"); |
| set_latencies(); |
| |
| /* Set Extended Mode Registers */ |
| banner("set emrs"); |
| set_extended_mode_registers(); |
| |
| banner("set ref rate"); |
| /* Set Memory Refresh Rate */ |
| set_refresh_rate(); |
| |
| } |
| |
| /* Section 6.1.3, LX processor databooks, BIOS Initialization Sequence |
| * Section 4.1.4, GX/CS5535 GeodeROM Porting guide */ |
| void sdram_enable(int controllers, const struct mem_controller *ctrl) |
| { |
| uint32_t i, msrnum; |
| msr_t msr; |
| |
| /********************************************************************* |
| ;* Turn on MC/DIMM interface per JEDEC |
| ;* 1) Clock stabilizes > 200us |
| ;* 2) Assert CKE |
| ;* 3) Precharge All to put all banks into an idles state |
| ;* 4) EMRS to enable DLL |
| ;* 6) MRS w/ memory config & reset DLL set |
| ;* 7) Wait 200 clocks (2us) |
| ;* 8) Precharge All and 2 Auto refresh |
| ;* 9) MRS w/ memory config & reset DLL clear |
| ;* 8) DDR SDRAM ready for normal operation |
| ;********************************************************************/ |
| post_code(POST_MEM_ENABLE); // post_76h |
| |
| #ifdef TLA_MEMORY_DEBUG |
| /* Only enable MTest for TLA memory debug */ |
| EnableMTest(); |
| #endif |
| |
| /* If both Page Size = "Not Installed" we have a problems and should halt. */ |
| msr = rdmsr(MC_CF07_DATA); |
| if ((msr.hi & ((7 << CF07_UPPER_D1_PSZ_SHIFT) | (7 << CF07_UPPER_D0_PSZ_SHIFT))) == |
| ((7 << CF07_UPPER_D1_PSZ_SHIFT) | (7 << CF07_UPPER_D0_PSZ_SHIFT))) { |
| printk(BIOS_EMERG, "No memory in the system\n"); |
| post_code(ERROR_NO_DIMMS); |
| hcf(); |
| } |
| |
| /* Set CKEs */ |
| msrnum = MC_CFCLK_DBUG; |
| msr = rdmsr(msrnum); |
| msr.lo &= ~(CFCLK_LOWER_MASK_CKE_SET0 | CFCLK_LOWER_MASK_CKE_SET1); |
| wrmsr(msrnum, msr); |
| |
| /* Force Precharge All on next command, EMRS */ |
| msrnum = MC_CFCLK_DBUG; |
| msr = rdmsr(msrnum); |
| msr.lo |= CFCLK_LOWER_FORCE_PRE_SET; |
| wrmsr(msrnum, msr); |
| |
| /* EMRS to enable DLL (pre-setup done in setExtendedModeRegisters) */ |
| msrnum = MC_CF07_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo |= CF07_LOWER_PROG_DRAM_SET | CF07_LOWER_LOAD_MODE_DDR_SET; |
| wrmsr(msrnum, msr); |
| msr.lo &= ~(CF07_LOWER_PROG_DRAM_SET | CF07_LOWER_LOAD_MODE_DDR_SET); |
| wrmsr(msrnum, msr); |
| |
| /* Clear Force Precharge All */ |
| msrnum = MC_CFCLK_DBUG; |
| msr = rdmsr(msrnum); |
| msr.lo &= ~CFCLK_LOWER_FORCE_PRE_SET; |
| wrmsr(msrnum, msr); |
| |
| /* MRS Reset DLL - set */ |
| msrnum = MC_CF07_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo |= CF07_LOWER_PROG_DRAM_SET | CF07_LOWER_LOAD_MODE_DLL_RESET; |
| wrmsr(msrnum, msr); |
| msr.lo &= ~(CF07_LOWER_PROG_DRAM_SET | CF07_LOWER_LOAD_MODE_DLL_RESET); |
| wrmsr(msrnum, msr); |
| |
| /* 2us delay (200 clocks @ 200MHz). We probably really don't need this but.... better safe. */ |
| /* Wait 2 PORT61 ticks. between 15us and 30us */ |
| /* This would be endless if the timer is stuck. */ |
| while ((inb(0x61))); /* find the first edge */ |
| while (!(~inb(0x61))); |
| |
| /* Force Precharge All on the next command, auto-refresh */ |
| msrnum = MC_CFCLK_DBUG; |
| msr = rdmsr(msrnum); |
| msr.lo |= CFCLK_LOWER_FORCE_PRE_SET; |
| wrmsr(msrnum, msr); |
| |
| /* Manually AUTO refresh #1 */ |
| /* If auto refresh was not enabled above we would need to do 8 refreshes to prime the pump before these 2. */ |
| msrnum = MC_CF07_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo |= CF07_LOWER_REF_TEST_SET; |
| wrmsr(msrnum, msr); |
| msr.lo &= ~CF07_LOWER_REF_TEST_SET; |
| wrmsr(msrnum, msr); |
| |
| /* Clear Force Precharge All */ |
| msrnum = MC_CFCLK_DBUG; |
| msr = rdmsr(msrnum); |
| msr.lo &= ~CFCLK_LOWER_FORCE_PRE_SET; |
| wrmsr(msrnum, msr); |
| |
| /* Manually AUTO refresh */ |
| /* The MC should insert the right delay between the refreshes */ |
| msrnum = MC_CF07_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo |= CF07_LOWER_REF_TEST_SET; |
| wrmsr(msrnum, msr); |
| msr.lo &= ~CF07_LOWER_REF_TEST_SET; |
| wrmsr(msrnum, msr); |
| |
| /* MRS Reset DLL - clear */ |
| msrnum = MC_CF07_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo |= CF07_LOWER_PROG_DRAM_SET; |
| wrmsr(msrnum, msr); |
| msr.lo &= ~CF07_LOWER_PROG_DRAM_SET; |
| wrmsr(msrnum, msr); |
| |
| /* Allow MC to tristate during idle cycles with MTEST OFF */ |
| msrnum = MC_CFCLK_DBUG; |
| msr = rdmsr(msrnum); |
| msr.lo &= ~CFCLK_LOWER_TRISTATE_DIS_SET; |
| wrmsr(msrnum, msr); |
| |
| /* Disable SDCLK DIMM1 slot if no DIMM installed to save power. */ |
| msr = rdmsr(MC_CF07_DATA); |
| if ((msr.hi & (7 << CF07_UPPER_D1_PSZ_SHIFT)) == |
| (7 << CF07_UPPER_D1_PSZ_SHIFT)) { |
| msrnum = GLCP_DELAY_CONTROLS; |
| msr = rdmsr(msrnum); |
| msr.hi |= (1 << 23); /* SDCLK bit for 2.0 */ |
| wrmsr(msrnum, msr); |
| } |
| |
| /* Set PMode0 Sensitivity Counter */ |
| msr.lo = 0; /* pmode 0 = 0 most aggressive */ |
| msr.hi = 0x200; /* pmode 1 = 200h */ |
| wrmsr(MC_CF_PMCTR, msr); |
| |
| /* Set PMode1 Up delay enable */ |
| msrnum = MC_CF1017_DATA; |
| msr = rdmsr(msrnum); |
| msr.lo |= (209 << 8); /* bits[15:8] = 209 */ |
| wrmsr(msrnum, msr); |
| |
| banner("DRAM controller init done.\n"); |
| post_code(POST_MEM_SETUP_GOOD); //0x7E |
| |
| /* make sure there is nothing stale in the cache */ |
| /* CAR stack is in the cache __asm__ __volatile__("wbinvd\n"); */ |
| |
| /* The RAM dll needs a write to lock on so generate a few dummy writes */ |
| /* Note: The descriptor needs to be enabled to point at memory */ |
| for (i = 0; i < 5; i++) { |
| write32(zeroptr + i, i); |
| } |
| /* SWAPSiF for PBZ 4112 (Errata 34) */ |
| /* check for failed DLL settings now that we have done a memory write. */ |
| msrnum = GLCP_DELAY_CONTROLS; |
| msr = rdmsr(msrnum); |
| if ((msr.lo & 0x7FF) == 0x104) { |
| |
| /* If you had it you would need to clear out the fail boot count flag */ |
| /* (depending on where it counts from etc). */ |
| |
| /* The reset we are about to perform clears the PM_SSC register in the */ |
| /* 5536 so will need to store the S3 resume flag in NVRAM otherwise */ |
| /* it would do a normal boot */ |
| |
| /* Reset the system */ |
| msrnum = MDD_SOFT_RESET; |
| msr = rdmsr(msrnum); |
| msr.lo |= 1; |
| wrmsr(msrnum, msr); |
| } |
| printk(BIOS_INFO, "RAM DLL lock\n"); |
| |
| } |