aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/amd/amdmct/mct/mctndi_d.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/northbridge/amd/amdmct/mct/mctndi_d.c')
-rw-r--r--src/northbridge/amd/amdmct/mct/mctndi_d.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdmct/mct/mctndi_d.c b/src/northbridge/amd/amdmct/mct/mctndi_d.c
new file mode 100644
index 0000000000..8e96a567e5
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct/mctndi_d.c
@@ -0,0 +1,237 @@
+/*
+ * 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
+ */
+
+
+
+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;
+ u8 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 */
+ print_tx("InterleaveNodes: DRAM Controller Select Low Register = ", 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);
+ }
+ print_tx("InterleaveNodes_D: Status ", pDCTstat->Status);
+ print_tx("InterleaveNodes_D: ErrStatus ", pDCTstat->ErrStatus);
+ print_tx("InterleaveNodes_D: ErrCode ", pDCTstat->ErrCode);
+ print_t("InterleaveNodes_D: Done\n");
+}