| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2010 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 mct_ControlRC(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u32 MrsChipSel, u32 CtrlWordNum) |
| { |
| u8 Dimms, DimmNum, MaxDimm, Speed; |
| u32 val; |
| u32 dct = 0; |
| u32 reg_off = 0; |
| |
| DimmNum = (MrsChipSel >> 20) & 0xFE; |
| |
| /* assume dct=0; */ |
| /* if (dct == 1) */ |
| /* DimmNum ++; */ |
| /* cl +=8; */ |
| |
| MaxDimm = mctGet_NVbits(NV_MAX_DIMMS); |
| Speed = pDCTstat->DIMMAutoSpeed; |
| |
| if (pDCTstat->CSPresent_DCT[0] > 0) { |
| dct = 0; |
| } else if (pDCTstat->CSPresent_DCT[1] > 0 ){ |
| dct = 1; |
| DimmNum ++; |
| } |
| reg_off = 0x100 * dct; |
| Dimms = pDCTstat->MAdimms[dct]; |
| |
| val = 0; |
| if (CtrlWordNum == 0) |
| val |= 1 << 1; |
| else if (CtrlWordNum == 1) { |
| if (!((pDCTstat->DimmDRPresent | pDCTstat->DimmQRPresent) & (1 << DimmNum))) |
| val |= 0xC; /* if single rank, set DBA1 and DBA0 */ |
| } else if (CtrlWordNum == 2) { |
| if (MaxDimm == 4) { |
| if (Speed == 4) { |
| if (((pDCTstat->DimmQRPresent & (1 << DimmNum)) && (Dimms == 1)) || (Dimms == 2)) |
| if (!(pDCTstat->MirrPresU_NumRegR & (1 << DimmNum))) |
| val |= 1 << 2; |
| } else { |
| if (pDCTstat->MirrPresU_NumRegR & (1 << DimmNum)) |
| val |= 1 << 2; |
| } |
| } else { |
| if (Dimms > 1) |
| val |= 1 << 2; |
| } |
| } else if (CtrlWordNum == 3) { |
| val |= (pDCTstat->CtrlWrd3 >> (DimmNum << 2)) & 0xFF; |
| } else if (CtrlWordNum == 4) { |
| val |= (pDCTstat->CtrlWrd4 >> (DimmNum << 2)) & 0xFF; |
| } else if (CtrlWordNum == 5) { |
| val |= (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xFF; |
| } else if (CtrlWordNum == 8) { |
| if (MaxDimm == 4) |
| if (Speed == 4) |
| if (pDCTstat->MirrPresU_NumRegR & (1 << DimmNum)) |
| val |= 1 << 2; |
| } else if (CtrlWordNum == 9) { |
| val |= 0xD; /* DBA1, DBA0, DA3 = 0 */ |
| } |
| val &= 0xffffff0f; |
| |
| val = MrsChipSel | ((val >> 2) & 3) << 16 | ((val & 3) << 3); |
| |
| /* transfer Control word number to address [BA2,A2,A1,A0] */ |
| if (CtrlWordNum > 7) { |
| val |= 1 << 18; |
| CtrlWordNum &= 7; |
| } |
| val |= CtrlWordNum; |
| |
| return val; |
| } |
| |
| static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u32 val) |
| { |
| u32 reg_off = 0; |
| u32 dev = pDCTstat->dev_dct; |
| |
| if (pDCTstat->CSPresent_DCT[0] > 0) { |
| reg_off = 0; |
| } else if (pDCTstat->CSPresent_DCT[1] > 0 ){ |
| reg_off = 0x100; |
| } |
| |
| val |= Get_NB32(dev, reg_off + 0x7C) & ~0xFFFFFF; |
| val |= 1 << SendControlWord; |
| Set_NB32(dev, reg_off + 0x7C, val); |
| |
| do { |
| val = Get_NB32(dev, reg_off + 0x7C); |
| } while (val & (1 << SendControlWord)); |
| } |
| |
| void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 MrsChipSel; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val, cw; |
| u32 reg_off = 0x100 * dct; |
| |
| mct_Wait(1600); |
| |
| mct_Wait(1200); |
| |
| for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) { |
| if (pDCTstat->CSPresent & (1 << MrsChipSel)) { |
| val = Get_NB32(dev, reg_off + 0xA8); |
| val &= ~(0xF << 8); |
| |
| switch (MrsChipSel) { |
| case 0: |
| case 1: |
| val |= 3 << 8; |
| case 2: |
| case 3: |
| val |= (3 << 2) << 8; |
| case 4: |
| case 5: |
| val |= (3 << 4) << 8; |
| case 6: |
| case 7: |
| val |= (3 << 6) << 8; |
| } |
| Set_NB32(dev, reg_off + 0xA8 , val); |
| |
| for (cw=0; cw <=15; cw ++) { |
| mct_Wait(1600); |
| if (!(cw==6 || cw==7)) { |
| val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, cw); |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, val); |
| } |
| } |
| } |
| } |
| |
| mct_Wait(1200); |
| } |
| |
| void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 SaveSpeed = pDCTstat->DIMMAutoSpeed; |
| u32 MrsChipSel; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val; |
| |
| pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq; |
| for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) { |
| if (pDCTstat->CSPresent & (1 << MrsChipSel)) { |
| /* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */ |
| val = Get_NB32(dev, 0xA8); /* TODO: dct * 0x100 + 0xA8 */ |
| val &= ~(0xFF << 8); |
| val |= (0x3 << (MrsChipSel & 0xFE)) << 8; |
| Set_NB32(dev, 0xA8, val); /* TODO: dct * 0x100 + 0xA8 */ |
| |
| /* Resend control word 10 */ |
| mct_Wait(1600); |
| switch (pDCTstat->TargetFreq) { |
| case 5: |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x4000A); |
| break; |
| case 6: |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x40012); |
| break; |
| case 7: |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x4001A); |
| break; |
| } |
| |
| mct_Wait(1600); |
| |
| /* Resend control word 2 */ |
| val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 2); |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, val); |
| |
| mct_Wait(1600); |
| |
| /* Resend control word 8 */ |
| val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 8); |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, val); |
| |
| mct_Wait(1600); |
| } |
| } |
| pDCTstat->DIMMAutoSpeed = SaveSpeed; |
| } |