| /* |
| * 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. |
| */ |
| |
| void InterleaveNodes_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| /* Applies Node memory interleaving if enabled and if all criteria are met. */ |
| u8 Node; |
| u32 Base; |
| u32 MemSize, MemSize0 = 0; |
| u32 Dct0MemSize = 0, DctSelBase, DctSelBaseOffset = 0; |
| u8 Nodes; |
| u8 NodesWmem; |
| u8 DoIntlv; |
| u8 _NdIntCap; |
| u8 _SWHole; |
| u32 HWHoleSz; |
| u32 DramHoleAddrReg; |
| u32 HoleBase; |
| u32 dev0; |
| u32 reg0; |
| u32 val; |
| u8 i; |
| struct DCTStatStruc *pDCTstat; |
| |
| DoIntlv = mctGet_NVbits(NV_NodeIntlv); |
| |
| _NdIntCap = 0; |
| HWHoleSz = 0; /*For HW remapping, NOT Node hoisting. */ |
| |
| pDCTstat = pDCTstatA + 0; |
| dev0 = pDCTstat->dev_host; |
| Nodes = ((Get_NB32(dev0, 0x60) >> 4) & 0x7) + 1; |
| |
| dev0 = pDCTstat->dev_map; |
| reg0 = 0x40; |
| |
| NodesWmem = 0; |
| Node = 0; |
| |
| while (DoIntlv && (Node < Nodes)) { |
| pDCTstat = pDCTstatA + Node; |
| if (pMCTstat->GStatus & (1 << GSB_SpIntRemapHole)) { |
| pMCTstat->GStatus |= 1 << GSB_HWHole; |
| _SWHole = 0; |
| } else if (pDCTstat->Status & (1 << SB_SWNodeHole)) { |
| _SWHole = 1; |
| } else { |
| _SWHole = 0; |
| } |
| |
| if (!_SWHole) { |
| Base = Get_NB32(dev0, reg0); |
| if (Base & 1) { |
| NodesWmem++; |
| Base &= 0xFFFF0000; /* Base[39:8] */ |
| |
| if (pDCTstat->Status & (1 << SB_HWHole)) { |
| |
| /* to get true amount of dram, |
| * subtract out memory hole if HW dram remapping */ |
| DramHoleAddrReg = Get_NB32(pDCTstat->dev_map, 0xF0); |
| HWHoleSz = DramHoleAddrReg >> 16; |
| HWHoleSz = (((~HWHoleSz) + 1) & 0xFF); |
| HWHoleSz <<= 24-8; |
| } |
| /* check to see if the amount of memory on each channel |
| * are the same on all nodes */ |
| |
| DctSelBase = Get_NB32(pDCTstat->dev_dct, 0x114); |
| if (DctSelBase) { |
| DctSelBase <<= 8; |
| if (pDCTstat->Status & (1 << SB_HWHole)) { |
| if (DctSelBase >= 0x1000000) { |
| DctSelBase -= HWHoleSz; |
| } |
| } |
| DctSelBaseOffset -= Base; |
| if (Node == 0) { |
| Dct0MemSize = DctSelBase; |
| } else if (DctSelBase != Dct0MemSize) { |
| break; |
| } |
| } |
| |
| MemSize = Get_NB32(dev0, reg0 + 4); |
| MemSize &= 0xFFFF0000; |
| MemSize += 0x00010000; |
| MemSize -= Base; |
| if (pDCTstat->Status & (1 << SB_HWHole)) { |
| MemSize -= HWHoleSz; |
| } |
| if (Node == 0) { |
| MemSize0 = MemSize; |
| } else if (MemSize0 != MemSize) { |
| break; |
| } |
| } else { |
| break; |
| } |
| } else { |
| break; |
| } |
| Node++; |
| reg0 += 8; |
| } |
| |
| if (Node == Nodes) { |
| /* if all nodes have memory and no Node had SW memhole */ |
| if (Nodes == 2 || Nodes == 4 || Nodes == 8) |
| _NdIntCap = 1; |
| } |
| |
| if (!_NdIntCap) |
| DoIntlv = 0; |
| |
| if (pMCTstat->GStatus & 1 << (GSB_SpIntRemapHole)) { |
| HWHoleSz = pMCTstat->HoleBase; |
| if (HWHoleSz == 0) { |
| HWHoleSz = mctGet_NVbits(NV_BottomIO) & 0xFF; |
| HWHoleSz <<= 24-8; |
| } |
| HWHoleSz = ((~HWHoleSz) + 1) & 0x00FF0000; |
| } |
| |
| if (DoIntlv) { |
| MCTMemClr_D(pMCTstat, pDCTstatA); |
| /* Program Interleaving enabled on Node 0 map only.*/ |
| MemSize0 <<= bsf(Nodes); /* MemSize = MemSize*2 (or 4, or 8) */ |
| Dct0MemSize <<= bsf(Nodes); |
| MemSize0 += HWHoleSz; |
| Base = ((Nodes - 1) << 8) | 3; |
| reg0 = 0x40; |
| Node = 0; |
| while (Node < Nodes) { |
| Set_NB32(dev0, reg0, Base); |
| MemSize = MemSize0; |
| MemSize--; |
| MemSize &= 0xFFFF0000; |
| MemSize |= Node << 8; /* set IntlvSel[2:0] field */ |
| MemSize |= Node; /* set DstNode[2:0] field */ |
| Set_NB32(dev0, reg0 + 4, MemSize0); |
| reg0 += 8; |
| Node++; |
| } |
| |
| /* set base/limit to F1x120/124 per Node */ |
| Node = 0; |
| while (Node < Nodes) { |
| pDCTstat = pDCTstatA + Node; |
| pDCTstat->NodeSysBase = 0; |
| MemSize = MemSize0; |
| MemSize -= HWHoleSz; |
| MemSize--; |
| pDCTstat->NodeSysLimit = MemSize; |
| Set_NB32(pDCTstat->dev_map, 0x120, Node << 21); |
| MemSize = MemSize0; |
| MemSize--; |
| MemSize >>= 19; |
| val = Base; |
| val &= 0x700; |
| val <<= 13; |
| val |= MemSize; |
| Set_NB32(pDCTstat->dev_map, 0x124, val); |
| |
| if (pMCTstat->GStatus & (1 << GSB_HWHole)) { |
| HoleBase = pMCTstat->HoleBase; |
| if (Dct0MemSize >= HoleBase) { |
| val = HWHoleSz; |
| if (Node == 0) { |
| val += Dct0MemSize; |
| } |
| } else { |
| val = HWHoleSz + Dct0MemSize; |
| } |
| |
| val >>= 8; /* DramHoleOffset */ |
| HoleBase <<= 8; /* DramHoleBase */ |
| val |= HoleBase; |
| val |= 1 << DramMemHoistValid; |
| val |= 1 << DramHoleValid; |
| Set_NB32(pDCTstat->dev_map, 0xF0, val); |
| } |
| |
| Set_NB32(pDCTstat->dev_dct, 0x114, Dct0MemSize >> 8); /* DctSelBaseOffset */ |
| val = Get_NB32(pDCTstat->dev_dct, 0x110); |
| val &= 0x7FF; |
| val |= Dct0MemSize >> 8; |
| Set_NB32(pDCTstat->dev_dct, 0x110, val); /* DctSelBaseAddr */ |
| printk(BIOS_DEBUG, "InterleaveNodes: DRAM Controller Select Low Register = %x\n", val); |
| Node++; |
| } |
| |
| /* Copy Node 0 into other Nodes' CSRs */ |
| Node = 1; |
| while (Node < Nodes) { |
| pDCTstat = pDCTstatA + Node; |
| |
| for (i = 0x40; i <= 0x80; i++) { |
| val = Get_NB32(dev0, i); |
| Set_NB32(pDCTstat->dev_map, i, val); |
| } |
| |
| val = Get_NB32(dev0, 0xF0); |
| Set_NB32(pDCTstat->dev_map, 0xF0, val); |
| Node++; |
| } |
| pMCTstat->GStatus = (1 << GSB_NodeIntlv); |
| } |
| printk(BIOS_DEBUG, "InterleaveNodes_D: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "InterleaveNodes_D: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "InterleaveNodes_D: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "InterleaveNodes_D: Done\n\n"); |
| } |