blob: d3b2aad2de6c3f43452a1a0a9215ef12c91d8ed6 [file] [log] [blame]
/*
* 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
*/
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;
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");
}