diff options
author | Marc Jones <marc.jones@amd.com> | 2007-12-19 01:32:08 +0000 |
---|---|---|
committer | Marc Jones <marc.jones@amd.com> | 2007-12-19 01:32:08 +0000 |
commit | 8ae8c8822068ef1722c08073ffa4ecc25633cbee (patch) | |
tree | 8c7bbf2f7b791081e486439a9b7ffb2fd6e649ac /src/northbridge/amd/amdmct/mct/mctpro_d.c | |
parent | 2006b38fed2f5f3680de1736f7fc878823f2f93b (diff) | |
download | coreboot-8ae8c8822068ef1722c08073ffa4ecc25633cbee.tar.xz |
Initial AMD Barcelona support for rev Bx.
These are the core files for HyperTransport, DDR2 Memory, and multi-core initialization.
Signed-off-by: Marc Jones <marc.jones@amd.com>
Reviewed-by: Jordan Crouse <jordan.crouse@amd.com>
Acked-by: Myles Watson <myles@pel.cs.byu.edu>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3014 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/amd/amdmct/mct/mctpro_d.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct/mctpro_d.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdmct/mct/mctpro_d.c b/src/northbridge/amd/amdmct/mct/mctpro_d.c new file mode 100644 index 0000000000..70d92c6b72 --- /dev/null +++ b/src/northbridge/amd/amdmct/mct/mctpro_d.c @@ -0,0 +1,406 @@ +/* + * This file is part of the LinuxBIOS 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 as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 + */ + + +static u32 CheckNBCOFAutoPrechg(struct DCTStatStruc *pDCTstat, u32 dct); +static u8 mct_AdjustDQSPosDelay_D(struct DCTStatStruc *pDCTstat, u8 dly); + +void EarlySampleSupport_D(void) +{ +} + + +u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val) +{ + u32 tmp; + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + val &= 0x0FFFFFFF; + if(pDCTstat->MAdimms[dct] > 1) + val |= 0x10000000; + } + + return val; +} + + +u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val) +{ + /* Bug#10695:One MEMCLK Bubble Writes Don't Do X4 X8 Switching Correctly + * Solution: BIOS should set DRAM Timing High[Twrwr] > 00b + * ( F2x[1, 0]8C[1:0] > 00b). Silicon Status: Fixed in Rev B + * FIXME: check if this is still required. + */ + u32 tmp; + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + if(!(val & (3<<12) )) + val |= 1<<12; + } + return val; +} + + +void mct_ForceAutoPrecharge_D(struct DCTStatStruc *pDCTstat, u32 dct) +{ + u32 tmp; + u32 reg; + u32 reg_off; + u32 dev; + u32 val; + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + if(CheckNBCOFAutoPrechg(pDCTstat, dct)) { + dev = pDCTstat->dev_dct; + reg_off = 0x100 * dct; + reg = 0x90 + reg_off; /* Dram Configuration Lo */ + val = Get_NB32(dev, reg); + val |= 1<<ForceAutoPchg; + if(!pDCTstat->GangedMode) + val |= 1<<BurstLength32; + Set_NB32(dev, reg, val); + + reg = 0x88 + reg_off; /* cx=Dram Timing Lo */ + val = Get_NB32(dev, reg); + val |= 0x000F0000; /* Trc = 0Fh */ + Set_NB32(dev, reg, val); + } + } +} + + +void mct_EndDQSTraining_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA) +{ + /* Bug#13341: Prefetch is getting killed when the limit is reached in + * PrefDramTrainMode + * Solution: Explicitly clear the PrefDramTrainMode bit after training + * sequence in order to ensure resumption of normal HW prefetch + * behavior. + * NOTE -- this has been documented with a note at the end of this + * section in the BKDG (although, admittedly, the note does not really + * stand out). + * Silicon Status: Fixed in Rev B ( confirm) + * FIXME: check this. + */ + + u32 tmp; + u32 dev; + u32 reg; + u32 val; + u32 Node; + + for(Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + struct DCTStatStruc *pDCTstat; + pDCTstat = pDCTstatA + Node; + + if(!pDCTstat->NodePresent) break; + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + dev = pDCTstat->dev_dct; + reg = 0x11c; + val = Get_NB32(dev, reg); + val &= ~(1<<PrefDramTrainMode); + Set_NB32(dev, reg, val); + } + } +} + + + + +void mct_BeforeDQSTrain_Samp_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat) +{ + /* Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations + * In TX FIFO + * Solution: BIOS should program DRAM Control Register[RdPtrInit] = 5h, + * (F2x[1, 0]78[3:0] = 5h). + * Silicon Status: Fixed In Rev B0 + */ + + /* Bug#15880: Determine validity of reset settings for DDR PHY timing + * regi.. + * Solutiuon: At least, set WrDqs fine delay to be 0 for DDR2 training. + */ + + u32 dev; + u32 reg_off; + u32 index_reg; + u32 index; + u32 reg; + u32 val; + u32 tmp; + u32 Channel; + + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + + dev = pDCTstat->dev_dct; + index = 0; + + for(Channel = 0; Channel<2; Channel++) { + index_reg = 0x98 + 0x100 * Channel; + val = Get_NB32_index_wait(dev, index_reg, 0x0d004007); + val |= 0x3ff; + Set_NB32_index_wait(dev, index_reg, 0x0d0f4f07, val); + } + + for(Channel = 0; Channel<2; Channel++) { + if(pDCTstat->GangedMode && Channel) + break; + reg_off = 0x100 * Channel; + reg = 0x78 + reg_off; + val = Get_NB32(dev, reg); + val &= ~(0x07); + val |= 5; + Set_NB32(dev, reg, val); + } + + for(Channel = 0; Channel<2; Channel++) { + reg_off = 0x100 * Channel; + val = 0; + index_reg = 0x98 + reg_off; + for( index = 0x30; index < (0x45 + 1); index++) { + Set_NB32_index_wait(dev, index_reg, index, val); + } + } + + } +} + + +u32 Modify_D3CMP(struct DCTStatStruc *pDCTstat, u32 dct, u32 value) +{ + /* Errata#189: Reads To Phy Driver Calibration Register and Phy + * Predriver Calibration Register Do Not Return Bit 27. + * Solution: See #41322 for details. + * BIOS can modify bit 27 of the Phy Driver Calibration register + * as follows: + * 1. Read F2x[1, 0]9C_x09 + * 2. Read F2x[1, 0]9C_x0D004201 + * 3. Set F2x[1, 0]9C_x09[27] = F2x[1, 0]9C_x0D004201[10] + * BIOS can modify bit 27 of the Phy Predriver Calibration register + * as follows: + * 1. Read F2x[1, 0]9C_x0A + * 2. Read F2x[1, 0]9C_x0D004209 + * 3. Set F2x[1, 0]9C_x0A[27] = F2x[1, 0]9C_x0D004209[10] + * Silicon Status: Fixed planned for DR-B0 + */ + + u32 dev; + u32 index_reg; + u32 index; + u32 val; + u32 tmp; + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + dev = pDCTstat->dev_dct; + index_reg = 0x98 + 0x100 * dct; + index = 0x0D004201; + val = Get_NB32_index_wait(dev, index_reg, index); + value &= ~(1<<27); + value |= ((val>>10) & 1) << 27; + } + return value; +} + + +void SyncSetting(struct DCTStatStruc *pDCTstat) +{ + /* Errata#198: AddrCmdSetup, CsOdtSetup, and CkeSetup Require Identical + * Programming For Both Channels in Ganged Mode + * Solution: The BIOS must program the following DRAM timing parameters + * the same for both channels: + * 1. F2x[1, 0]9C_x04[21] (AddrCmdSetup) + * 2. F2x[1, 0]9C_x04[15] (CsOdtSetup) + * 3. F2x[1, 0]9C_x04[5]) (CkeSetup) + * That is, if the AddrCmdSetup, CsOdtSetup, or CkeSetup is + * set to 1'b1 for one of the controllers, then the corresponding + * AddrCmdSetup, CsOdtSetup, or CkeSetup must be set to 1'b1 for the + * other controller. + * Silicon Status: Fix TBD + */ + + u32 tmp; + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + pDCTstat->CH_ODC_CTL[1] = pDCTstat->CH_ODC_CTL[0]; + pDCTstat->CH_ADDR_TMG[1] = pDCTstat->CH_ADDR_TMG[0]; + } +} + + +static u32 CheckNBCOFAutoPrechg(struct DCTStatStruc *pDCTstat, u32 dct) +{ + u32 ret = 0; + u32 lo, hi; + u32 msr; + u32 val; + u32 valx, valy; + u32 NbDid; + + /* 3 * (Fn2xD4[NBFid]+4)/(2^NbDid)/(3+Fn2x94[MemClkFreq]) */ + msr = 0xC0010071; + _RDMSR(msr, &lo, &hi); + NbDid = (lo>>22) & 1; + + val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100 * dct); + valx = ((val & 0x07) + 3)<<NbDid; + print_tx("MemClk:", valx >> NbDid); + + val = Get_NB32(pDCTstat->dev_nbmisc, 0xd4); + valy = ((val & 0x1f) + 4) * 3; + print_tx("NB COF:", valy >> NbDid); + + val = valy/valx; + if((val==3) && (valy%valx)) /* 3 < NClk/MemClk < 4 */ + ret = 1; + + return ret; +} + + +void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct) +{ + u32 tmp; + u32 Speed; + u32 ch, ch_start, ch_end; + u32 index_reg; + u32 index; + u32 dev; + u32 val; + + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + Speed = pDCTstat->Speed; + /* MemClkFreq = 333MHz or 533Mhz */ + if((Speed == 3) || (Speed == 2)) { + if(pDCTstat->GangedMode) { + ch_start = 0; + ch_end = 2; + } else { + ch_start = dct; + ch_end = dct+1; + } + dev = pDCTstat->dev_dct; + index = 0x0D00E001; + for(ch=ch_start; ch<ch_end; ch++) { + index_reg = 0x98 + 0x100 * ch; + val = Get_NB32_index(dev, index_reg, 0x0D00E001); + val &= ~(0xf0); + val |= 0x80; + Set_NB32_index(dev, index_reg, 0x0D01E001, val); + } + } + + } +} + + +static u8 mct_AdjustDelay_D(struct DCTStatStruc *pDCTstat, u8 dly) +{ + u8 skip = 0; + dly &= 0x1f; + if ((dly >= MIN_FENCE) && (dly <= MAX_FENCE)) + skip = 1; + + return skip; +} + + +static u8 mct_checkFenceHoleAdjust_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 DQSDelay, + u8 ChipSel, u8 *result) +{ + u8 ByteLane; + u32 tmp; + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + if (pDCTstat->Direction == DQS_WRITEDIR) { + if ((pDCTstat->Speed == 2) || (pDCTstat->Speed == 3)) { + if(DQSDelay == 13) { + if (*result == 0xFF) { + for (ByteLane = 0; ByteLane < 8; ByteLane++) { + pDCTstat->DQSDelay = 13; + pDCTstat->ByteLane = ByteLane; + /* store the value into the data structure */ + StoreDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel); + } + return 1; + } + } + } + if (mct_AdjustDQSPosDelay_D(pDCTstat, DQSDelay)) { + *result = 0; + } + } + } + return 0; +} + + +static u8 mct_AdjustDQSPosDelay_D(struct DCTStatStruc *pDCTstat, u8 dly) +{ + u8 skip = 0; + + dly &= 0x1f; + if ((dly >= MIN_DQS_WR_FENCE) && (dly <= MAX_DQS_WR_FENCE)) + skip = 1; + + return skip; + +} + +static void beforeInterleaveChannels_D(struct DCTStatStruc *pDCTstatA, u8 *enabled) { + + if (pDCTstatA->LogicalCPUID & (AMD_DR_Ax)) + *enabled = 0; +} + + +static u8 mctDoAxRdPtrInit_D(struct DCTStatStruc *pDCTstat, u8 *Rdtr) +{ + u32 tmp; + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + *Rdtr = 5; + return 1; + } + return 0; +} + + +static void mct_AdjustScrub_D(struct DCTStatStruc *pDCTstat, u16 *scrub_request) { + + /* Erratum #202: disable DCache scrubber for Ax parts */ + + if (pDCTstat->LogicalCPUID & (AMD_DR_Ax)) { + *scrub_request = 0; + pDCTstat->ErrStatus |= 1 << SB_DCBKScrubDis; + } +} + |