| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2010 Advanced Micro Devices, Inc. |
| * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering |
| * |
| * 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. |
| */ |
| |
| static uint8_t fam15h_rdimm_rc2_ibt_code(struct DCTStatStruc *pDCTstat, uint8_t dct) |
| { |
| uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH); |
| |
| uint8_t package_type; |
| uint8_t control_code = 0; |
| |
| package_type = mctGet_NVbits(NV_PACK_TYPE); |
| uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; |
| |
| /* Obtain number of DIMMs on channel */ |
| uint8_t dimm_count = pDCTstat->MAdimms[dct]; |
| |
| /* FIXME |
| * Assume there is only one register on the RDIMM for now |
| */ |
| uint8_t num_registers = 1; |
| |
| if (package_type == PT_GR) { |
| /* Socket G34 */ |
| /* Fam15h BKDG Rev. 3.14 section 2.10.5.7.1.2.1 Table 85 */ |
| if (MaxDimmsInstallable == 1) { |
| if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { |
| /* DDR3-667 - DDR3-800 */ |
| control_code = 0x1; |
| } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { |
| /* DDR3-1066 - DDR3-1333 */ |
| if (num_registers == 1) { |
| control_code = 0x0; |
| } else { |
| control_code = 0x1; |
| } |
| } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { |
| /* DDR3-1600 - DDR3-1866 */ |
| control_code = 0x0; |
| } |
| } else if (MaxDimmsInstallable == 2) { |
| if (dimm_count == 1) { |
| /* 1 DIMM detected */ |
| if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { |
| /* DDR3-667 - DDR3-800 */ |
| control_code = 0x1; |
| } else if ((MemClkFreq >= 0xa) && (MemClkFreq <= 0x12)) { |
| /* DDR3-1066 - DDR3-1600 */ |
| if (num_registers == 1) { |
| control_code = 0x0; |
| } else { |
| control_code = 0x1; |
| } |
| } |
| } else if (dimm_count == 2) { |
| /* 2 DIMMs detected */ |
| if (num_registers == 1) { |
| control_code = 0x1; |
| } else { |
| control_code = 0x8; |
| } |
| } |
| } else if (MaxDimmsInstallable == 3) { |
| /* TODO |
| * 3 DIMM/channel support unimplemented |
| */ |
| } |
| } else { |
| /* TODO |
| * Other socket support unimplemented |
| */ |
| } |
| |
| return control_code; |
| } |
| |
| static uint16_t memclk_to_freq(uint16_t memclk) { |
| uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800}; |
| uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933}; |
| |
| uint16_t mem_freq = 0; |
| |
| if (is_fam15h()) { |
| if (memclk < 0x17) { |
| mem_freq = fam15h_freq_tab[memclk]; |
| } |
| } else { |
| if ((memclk > 0x0) && (memclk < 0x8)) { |
| mem_freq = fam10h_freq_tab[memclk - 1]; |
| } |
| } |
| |
| return mem_freq; |
| } |
| |
| static uint8_t rc_word_chip_select_lower_bit(void) { |
| if (is_fam15h()) { |
| return 21; |
| } else { |
| return 20; |
| } |
| } |
| |
| static uint32_t rc_word_address_to_ctl_bits(uint32_t address) { |
| if (is_fam15h()) { |
| return (((address >> 3) & 0x1) << 2) << 18 | (address & 0x7); |
| } else { |
| return (((address >> 3) & 0x1) << 2) << 16 | (address & 0x7); |
| } |
| } |
| |
| static uint32_t rc_word_value_to_ctl_bits(uint32_t value) { |
| if (is_fam15h()) { |
| return ((value >> 2) & 0x3) << 18 | ((value & 0x3) << 3); |
| } else { |
| return ((value >> 2) & 0x3) << 16 | ((value & 0x3) << 3); |
| } |
| } |
| |
| static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MrsChipSel, u32 CtrlWordNum) |
| { |
| u8 Dimms, DimmNum; |
| u32 val; |
| uint8_t ddr_voltage_index; |
| uint16_t mem_freq; |
| uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); |
| uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH); |
| |
| DimmNum = (MrsChipSel >> rc_word_chip_select_lower_bit()) & 0xfe; |
| |
| if (dct == 1) |
| DimmNum++; |
| |
| mem_freq = memclk_to_freq(pDCTstat->DIMMAutoSpeed); |
| Dimms = pDCTstat->MAdimms[dct]; |
| |
| ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct); |
| |
| val = 0; |
| if (CtrlWordNum == 0) |
| val = 0x2; |
| 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 (is_fam15h()) { |
| val = (fam15h_rdimm_rc2_ibt_code(pDCTstat, dct) & 0x1) << 2; |
| } else { |
| if (package_type == PT_GR) { |
| /* Socket G34 */ |
| if (MaxDimmsInstallable == 2) { |
| if (Dimms > 1) |
| val = 0x4; |
| } |
| } |
| } |
| } 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 (is_fam15h()) { |
| val = (fam15h_rdimm_rc2_ibt_code(pDCTstat, dct) & 0xe) >> 1; |
| } else { |
| if (package_type == PT_GR) { |
| /* Socket G34 */ |
| if (MaxDimmsInstallable == 2) { |
| val = 0x0; |
| } |
| } |
| } |
| } else if (CtrlWordNum == 9) { |
| val = 0xd; /* DBA1, DBA0, DA3 = 0 */ |
| } else if (CtrlWordNum == 10) { |
| val = 0x0; /* Lowest operating frequency */ |
| } else if (CtrlWordNum == 11) { |
| if (ddr_voltage_index & 0x4) |
| val = 0x2; /* 1.25V */ |
| else if (ddr_voltage_index & 0x2) |
| val = 0x1; /* 1.35V */ |
| else |
| val = 0x0; /* 1.5V */ |
| } else if (CtrlWordNum >= 12) { |
| val = 0x0; /* Unset */ |
| } |
| val &= 0xf; |
| |
| printk(BIOS_SPEW, "Preparing to send DCT %d DIMM RC%d: %02x\n", dct, CtrlWordNum, val); |
| |
| val = MrsChipSel | rc_word_value_to_ctl_bits(val); |
| val |= rc_word_address_to_ctl_bits(CtrlWordNum); |
| |
| return val; |
| } |
| |
| static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t val) |
| { |
| u32 dev = pDCTstat->dev_dct; |
| |
| val |= Get_NB32_DCT(dev, dct, 0x7c) & ~0xffffff; |
| val |= 1 << SendControlWord; |
| Set_NB32_DCT(dev, dct, 0x7c, val); |
| |
| do { |
| val = Get_NB32_DCT(dev, dct, 0x7c); |
| } while (val & (1 << SendControlWord)); |
| } |
| |
| void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, uint8_t dct) |
| { |
| u8 MrsChipSel; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val, cw; |
| |
| mct_Wait(1600); |
| |
| mct_Wait(1200); |
| |
| pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct]; |
| if (pDCTstat->GangedMode & 1) |
| pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0]; |
| |
| for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel += 2) { |
| if (pDCTstat->CSPresent & (1 << MrsChipSel)) { |
| val = Get_NB32_DCT(dev, dct, 0xa8); |
| val &= ~(0xff << 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_DCT(dev, dct, 0xa8, val); |
| |
| for (cw=0; cw <=15; cw ++) { |
| mct_Wait(1600); |
| if (!(cw==6 || cw==7)) { |
| val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), cw); |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val); |
| } |
| } |
| } |
| } |
| |
| mct_Wait(1200); |
| } |
| |
| void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, uint8_t dct) |
| { |
| u32 SaveSpeed = pDCTstat->DIMMAutoSpeed; |
| u32 MrsChipSel; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val; |
| uint16_t mem_freq; |
| |
| pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct]; |
| if (pDCTstat->GangedMode & 1) |
| pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0]; |
| |
| pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq; |
| mem_freq = memclk_to_freq(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_DCT(dev, dct, 0xa8); |
| val &= ~(0xff << 8); |
| val |= (0x3 << (MrsChipSel & ~0x1)) << 8; |
| Set_NB32_DCT(dev, dct, 0xa8, val); |
| |
| /* Resend control word 10 */ |
| uint8_t freq_ctl_val = 0; |
| mct_Wait(1600); |
| switch (mem_freq) { |
| case 333: |
| case 400: |
| freq_ctl_val = 0x0; |
| break; |
| case 533: |
| freq_ctl_val = 0x1; |
| break; |
| case 667: |
| freq_ctl_val = 0x2; |
| break; |
| case 800: |
| freq_ctl_val = 0x3; |
| break; |
| case 933: |
| freq_ctl_val = 0x4; |
| break; |
| } |
| |
| printk(BIOS_SPEW, "Preparing to send DCT %d DIMM RC%d: %02x\n", dct, 10, freq_ctl_val); |
| |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit() | rc_word_address_to_ctl_bits(10) | rc_word_value_to_ctl_bits(freq_ctl_val)); |
| |
| mct_Wait(1600); |
| |
| /* Resend control word 2 */ |
| val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), 2); |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val); |
| |
| mct_Wait(1600); |
| |
| /* Resend control word 8 */ |
| val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), 8); |
| mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val); |
| |
| mct_Wait(1600); |
| } |
| } |
| pDCTstat->DIMMAutoSpeed = SaveSpeed; |
| } |