| /* |
| * This file is part of the coreboot 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. |
| */ |
| |
| |
| /*---------------------------------------------------------------------------- |
| * MODULES USED |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| #undef FILECODE |
| #define FILECODE 0xF002 |
| #include "h3finit.h" |
| #include "h3ffeat.h" |
| #include "h3ncmn.h" |
| #include "AsPsNb.h" |
| |
| |
| /*---------------------------------------------------------------------------- |
| * DEFINITIONS AND MACROS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /* CPU Northbridge Functions */ |
| #define CPU_HTNB_FUNC_00 0 |
| #define CPU_HTNB_FUNC_04 4 |
| #define CPU_ADDR_FUNC_01 1 |
| #define CPU_NB_FUNC_03 3 |
| #define CPU_NB_FUNC_05 5 |
| |
| /* Function 0 registers */ |
| #define REG_ROUTE0_0X40 0x40 |
| #define REG_ROUTE1_0X44 0x44 |
| #define REG_NODE_ID_0X60 0x60 |
| #define REG_UNIT_ID_0X64 0x64 |
| #define REG_LINK_TRANS_CONTROL_0X68 0x68 |
| #define REG_LINK_INIT_CONTROL_0X6C 0x6c |
| #define REG_HT_CAP_BASE_0X80 0x80 |
| #define REG_NORTHBRIDGE_CFG_3X8C 0x8c |
| #define REG_HT_LINK_RETRY0_0X130 0x130 |
| #define REG_HT_TRAFFIC_DIST_0X164 0x164 |
| #define REG_HT_LINK_EXT_CONTROL0_0X170 0x170 |
| |
| #define HT_CONTROL_CLEAR_CRC (~(3 << 8)) |
| |
| /* Function 1 registers */ |
| #define REG_ADDR_CONFIG_MAP0_1XE0 0xE0 |
| #define CPU_ADDR_NUM_CONFIG_MAPS 4 |
| |
| /* Function 3 registers */ |
| #define REG_NB_SRI_XBAR_BUF_3X70 0x70 |
| #define REG_NB_MCT_XBAR_BUF_3X78 0x78 |
| #define REG_NB_FIFOPTR_3XDC 0xDC |
| #define REG_NB_CAPABILITY_3XE8 0xE8 |
| #define REG_NB_CPUID_3XFC 0xFC |
| #define REG_NB_LINK_XCS_TOKEN0_3X148 0x148 |
| #define REG_NB_DOWNCORE_3X190 0x190 |
| #define REG_NB_CAPABILITY_5X84 0x84 |
| |
| /* Function 4 registers */ |
| |
| |
| /*---------------------------------------------------------------------------- |
| * TYPEDEFS AND STRUCTURES |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| /*---------------------------------------------------------------------------- |
| * PROTOTYPES OF LOCAL FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /*************************************************************************** |
| *** FAMILY/NORTHBRIDGE SPECIFIC FUNCTIONS *** |
| ***************************************************************************/ |
| |
| static inline uint8_t is_fam15h(void) |
| { |
| uint8_t fam15h = 0; |
| uint32_t family; |
| |
| family = cpuid_eax(0x80000001); |
| family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); |
| |
| if (family >= 0x6f) |
| /* Family 15h or later */ |
| fam15h = 1; |
| |
| return fam15h; |
| } |
| |
| static inline uint8_t is_gt_rev_d(void) |
| { |
| uint8_t fam15h = 0; |
| uint8_t rev_gte_d = 0; |
| uint32_t family; |
| uint32_t model; |
| |
| family = model = cpuid_eax(0x80000001); |
| model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4); |
| family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); |
| |
| if (family >= 0x6f) |
| /* Family 15h or later */ |
| fam15h = 1; |
| |
| if ((model >= 0x8) || fam15h) |
| /* Revision D or later */ |
| rev_gte_d = 1; |
| |
| return rev_gte_d; |
| } |
| |
| /***************************************************************************//** |
| * |
| * SBDFO |
| * makeLinkBase(u8 currentNode, u8 currentLink) |
| * |
| * Description: |
| * Private to northbridge implementation. Return the HT Host capability base |
| * PCI config address for a link. |
| * |
| * Parameters: |
| * @param[in] node = the node this link is on |
| * @param[in] link = the link |
| * |
| *****************************************************************************/ |
| static SBDFO makeLinkBase(u8 node, u8 link) |
| { |
| SBDFO linkBase; |
| |
| /* With rev F can not be called with a 4th link or with the sublinks */ |
| if (link < 4) |
| linkBase = MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_HT_CAP_BASE_0X80 + link*HT_HOST_CAP_SIZE); |
| else |
| linkBase = MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_04, |
| REG_HT_CAP_BASE_0X80 + (link-4)*HT_HOST_CAP_SIZE); |
| return linkBase; |
| } |
| |
| /***************************************************************************//** |
| * |
| * void |
| * setHtControlRegisterBits(SBDFO reg, u8 hiBit, u8 loBit, u32 *pValue) |
| * |
| * Description: |
| * Private to northbridge implementation. Provide a common routine for accessing the |
| * HT Link Control registers (84, a4, c4, e4), to enforce not clearing the |
| * HT CRC error bits. Replaces direct use of AmdPCIWriteBits(). |
| * NOTE: This routine is called for IO Devices as well as CPUs! |
| * |
| * Parameters: |
| * @param[in] reg = the PCI config address the control register |
| * @param[in] hiBit = the high bit number |
| * @param[in] loBit = the low bit number |
| * @param[in] pValue = the value to write to that bit range. Bit 0 => loBit. |
| * |
| *****************************************************************************/ |
| static void setHtControlRegisterBits(SBDFO reg, u8 hiBit, u8 loBit, u32 *pValue) |
| { |
| u32 temp, mask; |
| |
| ASSERT((hiBit < 32) && (loBit < 32) && (hiBit >= loBit) && ((reg & 0x3) == 0)); |
| ASSERT((hiBit < 8) || (loBit > 9)); |
| |
| /* A 1<<32 == 1<<0 due to x86 SHL instruction, so skip if that is the case */ |
| if ((hiBit-loBit) != 31) |
| mask = (((u32)1 << (hiBit-loBit+1))-1); |
| else |
| mask = (u32)0xFFFFFFFF; |
| |
| AmdPCIRead(reg, &temp); |
| temp &= ~(mask << loBit); |
| temp |= (*pValue & mask) << loBit; |
| temp &= (u32)HT_CONTROL_CLEAR_CRC; |
| AmdPCIWrite(reg, &temp); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * writeRoutingTable(u8 node, u8 target, u8 Link, cNorthBridge *nb) |
| * |
| * Description: |
| * This routine will modify the routing tables on the |
| * SourceNode to cause it to route both request and response traffic to the |
| * targetNode through the specified Link. |
| * |
| * NOTE: This routine is to be used for early discovery and initialization. The |
| * final routing tables must be loaded some other way because this |
| * routine does not address the issue of probes, or independent request |
| * response paths. |
| * |
| * Parameters: |
| * @param[in] node = the node that will have it's routing tables modified. |
| * @param[in] target = For routing to node target |
| * @param[in] link = Link from node to target |
| * @param[in] *nb = this northbridge |
| * |
| *****************************************************************************/ |
| |
| static void writeRoutingTable(u8 node, u8 target, u8 link, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 temp = (nb->selfRouteResponseMask | nb->selfRouteRequestMask) << (link + 1); |
| ASSERT((node < nb->maxNodes) && (target < nb->maxNodes) && (link < nb->maxLinks)); |
| AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_ROUTE0_0X40 + target*4), |
| &temp); |
| #else |
| STOP_HERE; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * writeNodeID(u8 node, u8 nodeID, cNorthBridge *nb) |
| * |
| * Description: |
| * Modifies the NodeID register on the target node |
| * |
| * Parameters: |
| * @param[in] node = the node that will have its NodeID altered. |
| * @param[in] nodeID = the new value for NodeID |
| * @param[in] *nb = this northbridge |
| * |
| *****************************************************************************/ |
| |
| static void writeNodeID(u8 node, u8 nodeID, cNorthBridge *nb) |
| { |
| u32 temp; |
| ASSERT((node < nb->maxNodes) && (nodeID < nb->maxNodes)); |
| if (is_fam15h()) { |
| temp = 1; |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NORTHBRIDGE_CFG_3X8C), |
| 22, 22, &temp); |
| } |
| temp = nodeID; |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_NODE_ID_0X60), |
| 2, 0, &temp); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * readDefLnk(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Read the DefLnk (the source link of the current packet) |
| * from node |
| * |
| * Parameters: |
| * @param[in] node = the node that will have its NodeID altered. |
| * @param[in] *nb = this northbridge |
| * @return The HyperTransport link where the request to |
| * read the default link came from. Since this |
| * code is running on the BSP, this should be the link |
| * pointing back towards the BSP. |
| * |
| *****************************************************************************/ |
| |
| static u8 readDefLnk(u8 node, cNorthBridge *nb) |
| { |
| u32 deflink = 0; |
| SBDFO licr; |
| u32 temp; |
| |
| licr = MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_LINK_INIT_CONTROL_0X6C); |
| |
| ASSERT((node < nb->maxNodes)); |
| AmdPCIReadBits(licr, 3, 2, &deflink); |
| AmdPCIReadBits(licr, 8, 8, &temp); /* on rev F, this bit is reserved == 0 */ |
| deflink |= temp << 2; |
| return (u8)deflink; |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * enableRoutingTables(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Turns routing tables on for a given node |
| * |
| * Parameters: |
| * @param[in] node = the node that will have it's routing tables enabled |
| * @param[in] *nb = this northbridge |
| * |
| *****************************************************************************/ |
| |
| static void enableRoutingTables(u8 node, cNorthBridge *nb) |
| { |
| u32 temp = 0; |
| ASSERT((node < nb->maxNodes)); |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_LINK_INIT_CONTROL_0X6C), |
| 0, 0, &temp); |
| } |
| |
| |
| /***************************************************************************//** |
| * |
| * static BOOL |
| * verifyLinkIsCoherent(u8 node, u8 Link, cNorthBridge *nbk) |
| * |
| * Description: |
| * Verify that the link is coherent, connected, and ready |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] link = the link on that Node to examine |
| * @param[in] *nb = this northbridge |
| * @return true - The link has the following status |
| * linkCon=1, Link is connected |
| * InitComplete=1, Link initialization is complete |
| * NC=0, Link is coherent |
| * UniP-cLDT=0, Link is not Uniprocessor cLDT |
| * LinkConPend=0 Link connection is not pending |
| * false- The link has some other status |
| * |
| *****************************************************************************/ |
| |
| static BOOL verifyLinkIsCoherent(u8 node, u8 link, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| |
| u32 linkType; |
| SBDFO linkBase; |
| |
| ASSERT((node < nb->maxNodes) && (link < nb->maxLinks)); |
| |
| linkBase = makeLinkBase(node, link); |
| |
| /* FN0_98/A4/C4 = LDT Type Register */ |
| AmdPCIRead(linkBase + HTHOST_LINK_TYPE_REG, &linkType); |
| |
| /* Verify LinkCon=1, InitComplete=1, NC=0, UniP-cLDT=0, LinkConPend=0 */ |
| return (linkType & HTHOST_TYPE_MASK) == HTHOST_TYPE_COHERENT; |
| #else |
| return 0; |
| #endif /* HT_BUILD_NC_ONLY */ |
| } |
| |
| /***************************************************************************//** |
| * |
| * static bool |
| * readTrueLinkFailStatus(u8 node, u8 link, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Return the LinkFailed status AFTER an attempt is made to clear the bit. |
| * Also, call event notify if a Hardware Fault caused a synch flood on a previous boot. |
| * |
| * The table below summarizes correct responses of this routine. |
| * Family before after unconnected Notify? return |
| * 0F 0 0 0 No 0 |
| * 0F 1 0 0 Yes 0 |
| * 0F 1 1 X No 1 |
| * 10 0 0 0 No 0 |
| * 10 1 0 0 Yes 0 |
| * 10 1 0 3 No 1 |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] link = the link on that node to examine |
| * @param[in] *pDat = access to call back routine |
| * @param[in] *nb = this northbridge |
| * @return true - the link is not connected or has hard error |
| * false- if the link is connected |
| * |
| *****************************************************************************/ |
| |
| static BOOL readTrueLinkFailStatus(u8 node, u8 link, sMainData *pDat, cNorthBridge *nb) |
| { |
| u32 before, after, unconnected, crc; |
| SBDFO linkBase; |
| |
| ASSERT((node < nb->maxNodes) && (link < nb->maxLinks)); |
| |
| linkBase = makeLinkBase(node, link); |
| |
| /* Save the CRC status before doing anything else. |
| * Read, Clear, the Re-read the error bits in the Link Control Register |
| * FN0_84/A4/C4[4] = LinkFail bit |
| * and the connection status, TransOff and EndOfChain |
| */ |
| AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 9, 8, &crc); |
| AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 4, 4, &before); |
| setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 4, 4, &before); |
| AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 4, 4, &after); |
| AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 7, 6, &unconnected); |
| |
| if (before != after) |
| { |
| if (!unconnected) |
| { |
| if (crc != 0) |
| { |
| /* A synch flood occurred due to HT CRC */ |
| if (pDat->HtBlock->AMD_CB_EventNotify) |
| { |
| /* Pass the node and link on which the generic synch flood event occurred. */ |
| sHtEventHWHtCrc evt; |
| evt.eSize = sizeof(sHtEventHWHtCrc); |
| evt.node = node; |
| evt.link = link; |
| evt.laneMask = (uint8)crc; |
| |
| pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_HW_FAULT, |
| HT_EVENT_HW_HTCRC, |
| (u8 *)&evt); |
| } |
| } |
| else |
| { |
| /* Some synch flood occurred */ |
| if (pDat->HtBlock->AMD_CB_EventNotify) |
| { |
| /* Pass the node and link on which the generic synch flood event occurred. */ |
| sHtEventHWSynchFlood evt; |
| evt.eSize = sizeof(sHtEventHWSynchFlood); |
| evt.node = node; |
| evt.link = link; |
| |
| pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_HW_FAULT, |
| HT_EVENT_HW_SYNCHFLOOD, |
| (u8 *)&evt); |
| } |
| } |
| } |
| } |
| return ((after != 0) || unconnected); |
| } |
| |
| |
| /***************************************************************************//** |
| * |
| * static u8 |
| * readToken(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Read the token stored in the scratchpad register |
| * NOTE: The location used to store the token is arbitrary. The only |
| * requirement is that the location warm resets to zero, and that |
| * using it will have no ill-effects during HyperTransport initialization. |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] *nb = this northbridge |
| * @return the Token read from the node |
| * |
| *****************************************************************************/ |
| static u8 readToken(u8 node, cNorthBridge *nb) |
| { |
| u32 temp; |
| |
| ASSERT((node < nb->maxNodes)); |
| /* Use CpuCnt as a scratch register */ |
| /* Limiting use to 4 bits makes code GH to rev F compatible. */ |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_NODE_ID_0X60), |
| 19, 16, &temp); |
| |
| return (u8)temp; |
| } |
| |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * writeToken(u8 node, u8 Value, cNorthBridge *nb) |
| * |
| * Description: |
| * Write the token stored in the scratchpad register |
| * NOTE: The location used to store the token is arbitrary. The only |
| * requirement is that the location warm resets to zero, and that |
| * using it will have no ill-effects during HyperTransport initialization. |
| * Limiting use to 4 bits makes code GH to rev F compatible. |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param value |
| * @param[in] *nb = this northbridge |
| * |
| *****************************************************************************/ |
| static void writeToken(u8 node, u8 value, cNorthBridge *nb) |
| { |
| u32 temp = value; |
| ASSERT((node < nb->maxNodes)); |
| /* Use CpuCnt as a scratch register */ |
| /* Limiting use to 4 bits makes code GH to rev F compatible. */ |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_NODE_ID_0X60), |
| 19, 16, &temp); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static u8 |
| * fam0FGetNumCoresOnNode(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Return the number of cores (1 based count) on node. |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] *nb = this northbridge |
| * @return = the number of cores |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static u8 fam0FGetNumCoresOnNode(u8 node, cNorthBridge *nb) |
| { |
| u32 temp; |
| |
| ASSERT((node < nb->maxNodes)); |
| /* Read CmpCap */ |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CAPABILITY_3XE8), |
| 13, 12, &temp); |
| |
| /* and add one */ |
| return (u8)(temp+1); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static u8 |
| * fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Return the number of cores (1 based count) on node. |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] *nb = this northbridge |
| * @return = the number of cores |
| * |
| * |
| */ |
| static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb) |
| { |
| u32 temp, leveling, cores; |
| u8 i; |
| |
| ASSERT((node < nb->maxNodes)); |
| /* Read CmpCap [2][1:0] */ |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CAPABILITY_3XE8), |
| 15, 12, &temp); |
| |
| /* bits[15,13,12] specify the cores */ |
| temp = ((temp & 8) >> 1) + (temp & 3); |
| cores = temp + 1; |
| |
| /* Support Downcoring */ |
| AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_DOWNCORE_3X190), |
| 3, 0, &leveling); |
| for (i=0; i<cores; i++) |
| { |
| if (leveling & ((u32) 1 << i)) |
| { |
| temp--; |
| } |
| } |
| return (u8)(temp+1); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static u8 |
| * fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Return the number of cores (1 based count) on node. |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] *nb = this northbridge |
| * @return = the number of cores |
| * |
| * |
| */ |
| static u8 fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb) |
| { |
| u32 temp, leveling, cores; |
| u8 i; |
| |
| ASSERT((node < nb->maxNodes)); |
| /* Read CmpCap [7:0] */ |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_05, |
| REG_NB_CAPABILITY_5X84), |
| 7, 0, &temp); |
| |
| /* bits[7:0] specify the cores */ |
| temp = temp & 0xff; |
| cores = temp + 1; |
| |
| /* Support Downcoring */ |
| AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_DOWNCORE_3X190), |
| 31, 0, &leveling); |
| for (i=0; i<cores; i++) |
| { |
| if (leveling & ((u32) 1 << i)) |
| { |
| temp--; |
| } |
| } |
| return (u8)(temp+1); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * setTotalNodesAndCores(u8 node, u8 totalNodes, u8 totalCores, cNorthBridge *nb) |
| * |
| * Description: |
| * Write the total number of cores and nodes to the node |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] totalNodes = the total number of nodes |
| * @param[in] totalCores = the total number of cores |
| * @param[in] *nb = this northbridge |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static void setTotalNodesAndCores(u8 node, u8 totalNodes, u8 totalCores, cNorthBridge *nb) |
| { |
| SBDFO nodeIDReg; |
| u32 temp; |
| |
| ASSERT((node < nb->maxNodes) && (totalNodes <= nb->maxNodes)); |
| nodeIDReg = MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_NODE_ID_0X60); |
| |
| temp = totalCores-1; |
| /* Rely on max number of nodes:cores for rev F and GH to make |
| * this code work, even though we write reserved bit 20 on rev F it will be |
| * zero in that case. |
| */ |
| AmdPCIWriteBits(nodeIDReg, 20, 16, &temp); |
| temp = totalNodes-1; |
| AmdPCIWriteBits(nodeIDReg, 6, 4, &temp); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * limitNodes(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Limit coherent config accesses to cpus as indicated by nodecnt. |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] *nb = this northbridge |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static void limitNodes(u8 node, cNorthBridge *nb) |
| { |
| u32 temp = 1; |
| ASSERT((node < nb->maxNodes)); |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_LINK_TRANS_CONTROL_0X68), |
| 15, 15, &temp); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * writeFullRoutingTable(u8 node, u8 target, u8 reqLink, u8 rspLink, u32 BClinks, cNorthBridge *nb) |
| * |
| * Description: |
| * Write the routing table entry for node to target, using the request link, response |
| * link, and broadcast links provided. |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] target = the target node for these routes |
| * @param[in] reqLink = the link for requests to target |
| * @param[in] rspLink = the link for responses to target |
| * @param[in] bClinks = the broadcast links |
| * @param[in] *nb = this northbridge |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static void writeFullRoutingTable(u8 node, u8 target, u8 reqLink, u8 rspLink, u32 bClinks, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 value = 0; |
| |
| ASSERT((node < nb->maxNodes) && (target < nb->maxNodes)); |
| if (reqLink == ROUTETOSELF) |
| value |= nb->selfRouteRequestMask; |
| else |
| value |= nb->selfRouteRequestMask << (reqLink+1); |
| |
| if (rspLink == ROUTETOSELF) |
| value |= nb->selfRouteResponseMask; |
| else |
| value |= nb->selfRouteResponseMask << (rspLink+1); |
| |
| /* Allow us to accept a Broadcast ourselves, then set broadcasts for routes */ |
| value |= (u32)1 << nb->broadcastSelfBit; |
| value |= (u32)bClinks << (nb->broadcastSelfBit + 1); |
| |
| AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_HTNB_FUNC_00, |
| REG_ROUTE0_0X40 + target*4), &value); |
| #else |
| STOP_HERE; |
| #endif /* HT_BUILD_NC_ONLY */ |
| } |
| |
| /***************************************************************************//** |
| * |
| * static u32 |
| * makeKey(u8 currentNode) |
| * |
| * Description: |
| * Private routine to northbridge code. |
| * Determine whether a node is compatible with the discovered configuration so |
| * far. Currently, that means the family, extended family of the new node are the |
| * same as the BSP's. |
| * |
| * Parameters: |
| * @param[in] node = the node |
| * @return = the key value |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static u32 makeKey(u8 node) |
| { |
| u32 extFam, baseFam; |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CPUID_3XFC), |
| 27, 20, &extFam); |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CPUID_3XFC), |
| 11, 8, &baseFam); |
| return ((u32)(baseFam << 8) | extFam); |
| } |
| |
| |
| /***************************************************************************//** |
| * |
| * static BOOL |
| * isCompatible(u8 currentNode, cNorthBridge *nb) |
| * |
| * Description: |
| * Determine whether a node is compatible with the discovered configuration so |
| * far. Currently, that means the family, extended family of the new node are the |
| * same as the BSP's. |
| * |
| * Parameters: |
| * @param[in] node = the node |
| * @param[in] *nb = this northbridge |
| * @return = true: the new is compatible, false: it is not |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static BOOL isCompatible(u8 node, cNorthBridge *nb) |
| { |
| return (makeKey(node) == nb->compatibleKey); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static BOOL |
| * fam0fIsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Get node capability and update the minimum supported system capability. |
| * Return whether the current configuration exceeds the capability. |
| * |
| * Parameters: |
| * @param[in] node = the node |
| * @param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered |
| * @param[in] *nb = this northbridge |
| * @return true: system is capable of current config. |
| * false: system is not capable of current config. |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static BOOL fam0fIsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 temp; |
| u8 maxNodes; |
| |
| ASSERT(node < nb->maxNodes); |
| |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CAPABILITY_3XE8), |
| 2, 1, &temp); |
| if (temp > 1) |
| { |
| maxNodes = 8; |
| } else { |
| if (temp == 1) |
| { |
| maxNodes = 2; |
| } else { |
| maxNodes = 1; |
| } |
| } |
| if (pDat->sysMpCap > maxNodes) |
| { |
| pDat->sysMpCap = maxNodes; |
| } |
| /* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */ |
| return (pDat->sysMpCap > pDat->NodesDiscovered); |
| #else |
| return 1; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * |
| * static BOOL |
| * fam10IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Get node capability and update the minimum supported system capability. |
| * Return whether the current configuration exceeds the capability. |
| * |
| * Parameters: |
| * @param[in] node = the node |
| * @param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered |
| * @param[in] *nb = this northbridge |
| * @return true: system is capable of current config. |
| * false: system is not capable of current config. |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static BOOL fam10IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 temp; |
| u8 maxNodes; |
| |
| ASSERT(node < nb->maxNodes); |
| |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CAPABILITY_3XE8), |
| 18, 16, &temp); |
| |
| if (temp != 0) |
| { |
| maxNodes = (1 << (~temp & 0x3)); /* That is, 1, 2, 4, or 8 */ |
| } |
| else |
| { |
| maxNodes = 8; |
| } |
| |
| if (pDat->sysMpCap > maxNodes) |
| { |
| pDat->sysMpCap = maxNodes; |
| } |
| /* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */ |
| return (pDat->sysMpCap > pDat->NodesDiscovered); |
| #else |
| return 1; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * |
| * static BOOL |
| * fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Get node capability and update the minimum supported system capability. |
| * Return whether the current configuration exceeds the capability. |
| * |
| * Parameters: |
| * @param[in] node = the node |
| * @param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered |
| * @param[in] *nb = this northbridge |
| * @return true: system is capable of current config. |
| * false: system is not capable of current config. |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static BOOL fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 temp; |
| u8 maxNodes; |
| |
| ASSERT(node < nb->maxNodes); |
| |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CAPABILITY_3XE8), |
| 18, 16, &temp); |
| |
| if (temp != 0) |
| { |
| maxNodes = (1 << (~temp & 0x3)); /* That is, 1, 2, 4, or 8 */ |
| } |
| else |
| { |
| /* Check if CPU package is dual node */ |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CAPABILITY_3XE8), |
| 29, 29, &temp); |
| if (temp) |
| maxNodes = 4; |
| else |
| maxNodes = 8; |
| } |
| |
| if (pDat->sysMpCap > maxNodes) |
| { |
| pDat->sysMpCap = maxNodes; |
| } |
| /* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */ |
| return (pDat->sysMpCap > pDat->NodesDiscovered); |
| #else |
| return 1; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * fam0fStopLink(u8 currentNode, u8 currentLink, cNorthBridge *nb) |
| * |
| * Description: |
| * Disable a cHT link on node by setting F0x[E4, C4, A4, 84][TransOff, EndOfChain]=1 |
| * |
| * Parameters: |
| * @param[in] node = the node this link is on |
| * @param[in] link = the link to stop |
| * @param[in] *nb = this northbridge |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static void fam0fStopLink(u8 node, u8 link, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 temp; |
| SBDFO linkBase; |
| |
| ASSERT((node < nb->maxNodes) && (link < nb->maxLinks)); |
| |
| linkBase = makeLinkBase(node, link); |
| |
| /* Set TransOff, EndOfChain */ |
| temp = 3; |
| setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 7, 6, &temp); |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * commonVoid() |
| * |
| * Description: |
| * Nothing. |
| * |
| * Parameters: |
| * None. |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static void commonVoid(void) |
| { |
| } |
| |
| /***************************************************************************//** |
| * |
| * static BOOL |
| * commonReturnFalse() |
| * |
| * Description: |
| * Return False. |
| * |
| * Parameters: |
| * @return = false |
| * |
| */ |
| static BOOL commonReturnFalse(void) |
| { |
| return 0; |
| } |
| |
| /*************************************************************************** |
| *** Non-coherent init code *** |
| *** Northbridge access routines *** |
| ***************************************************************************/ |
| |
| /***************************************************************************//** |
| * |
| * static u8 |
| * readSbLink(cNorthBridge *nb) |
| * |
| * Description: |
| * Return the link to the Southbridge |
| * |
| * Parameters: |
| * @param[in] *nb = this northbridge |
| * @return the link to the southbridge |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static u8 readSbLink(cNorthBridge *nb) |
| { |
| u32 temp; |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(0), |
| makePCIBusFromNode(0), |
| makePCIDeviceFromNode(0), |
| CPU_HTNB_FUNC_00, |
| REG_UNIT_ID_0X64), |
| 10, 8, &temp); |
| return (u8)temp; |
| } |
| |
| /***************************************************************************//** |
| * |
| * static BOOL |
| * verifyLinkIsNonCoherent(u8 node, u8 link, cNorthBridge *nb) |
| * |
| * Description: |
| * Verify that the link is non-coherent, connected, and ready |
| * |
| * Parameters: |
| * @param[in] node = the node that will be examined |
| * @param[in] link = the Link on that node to examine |
| * @param[in] *nb = this northbridge |
| * @return = true - The link has the following status |
| * LinkCon=1, Link is connected |
| * InitComplete=1,Link initilization is complete |
| * NC=1, Link is coherent |
| * UniP-cLDT=0, Link is not Uniprocessor cLDT |
| * LinkConPend=0 Link connection is not pending |
| * false- The link has some other status |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static BOOL verifyLinkIsNonCoherent(u8 node, u8 link, cNorthBridge *nb) |
| { |
| u32 linkType; |
| SBDFO linkBase; |
| |
| ASSERT((node < nb->maxNodes) && (link < nb->maxLinks)); |
| |
| linkBase = makeLinkBase(node, link); |
| |
| /* FN0_98/A4/C4 = LDT Type Register */ |
| AmdPCIRead(linkBase + HTHOST_LINK_TYPE_REG, &linkType); |
| |
| /* Verify linkCon=1, InitComplete=1, NC=0, UniP-cLDT=0, LinkConPend=0 */ |
| return (linkType & HTHOST_TYPE_MASK) == HTHOST_TYPE_NONCOHERENT; |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * ht3SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Configure and enable config access to a non-coherent chain for the given bus range. |
| * |
| * Parameters: |
| * @param[in] cfgMapIndex = the map entry to set |
| * @param[in] secBus = The secondary bus number to use |
| * @param[in] subBus = The subordinate bus number to use |
| * @param[in] targetNode = The node that shall be the recipient of the traffic |
| * @param[in] targetLink = The link that shall be the recipient of the traffic |
| * @param[in] pDat = our global state |
| * @param[in] *nb = this northbridge |
| * |
| * --------------------------------------------------------------------------------------- |
| */ |
| static void ht3SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb) |
| { |
| u8 curNode; |
| SBDFO linkBase; |
| u32 temp; |
| |
| linkBase = makeLinkBase(targetNode, targetLink); |
| |
| ASSERT(secBus <= subBus); |
| temp = secBus; |
| AmdPCIWriteBits(linkBase + HTHOST_ISOC_REG, 15, 8, &temp); |
| |
| /* For target link, note that rev F uses bits 9:8 and only with GH is bit 10 |
| * set to indicate a sublink. For node, we are currently not supporting Extended |
| * routing tables. |
| */ |
| temp = ((u32)subBus << 24) + ((u32)secBus << 16) + ((u32)targetLink << 8) |
| + ((u32)targetNode << 4) + (u32)3; |
| for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++) |
| AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(curNode), |
| makePCIBusFromNode(curNode), |
| makePCIDeviceFromNode(curNode), |
| CPU_ADDR_FUNC_01, |
| REG_ADDR_CONFIG_MAP0_1XE0 + 4*cfgMapIndex), |
| &temp); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * ht1SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Configure and enable config access to a non-coherent chain for the given bus range. |
| * |
| * Parameters: |
| * @param[in] cfgMapIndex = the map entry to set |
| * @param[in] secBus = The secondary bus number to use |
| * @param[in] subBus = The subordinate bus number to use |
| * @param[in] targetNode = The node that shall be the recipient of the traffic |
| * @param[in] targetLink = The link that shall be the recipient of the traffic |
| * @param[in] pDat = our global state |
| * @param[in] *nb = this northbridge |
| * |
| ******************************************************************************/ |
| static void ht1SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb) |
| { |
| u8 curNode; |
| SBDFO linkBase; |
| u32 temp; |
| |
| linkBase = makeLinkBase(targetNode, targetLink); |
| |
| ASSERT(secBus <= subBus); |
| temp = secBus; |
| AmdPCIWriteBits(linkBase + HTHOST_ISOC_REG, 15, 8, &temp); |
| |
| temp = subBus; |
| AmdPCIWriteBits(linkBase + HTHOST_ISOC_REG, 23, 16, &temp); |
| |
| /* For target link, note that rev F uses bits 9:8 and only with GH is bit 10 |
| * set to indicate a sublink. For node, we are currently not supporting Extended |
| * routing tables. |
| */ |
| temp = ((u32)subBus << 24) + ((u32)secBus << 16) + ((u32)targetLink << 8) |
| + ((u32)targetNode << 4) + (u32)3; |
| for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++) |
| AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(curNode), |
| makePCIBusFromNode(curNode), |
| makePCIDeviceFromNode(curNode), |
| CPU_ADDR_FUNC_01, |
| REG_ADDR_CONFIG_MAP0_1XE0 + 4*cfgMapIndex), |
| &temp); |
| } |
| |
| /*************************************************************************** |
| *** Link Optimization *** |
| ***************************************************************************/ |
| |
| /** |
| * static u8 |
| * convertBitsToWidth(u8 value, cNorthBridge *nb) |
| * |
| * Description: |
| * Given the bits set in the register field, return the width it represents |
| * |
| * Parameters: |
| * @param[in] value = The bits for the register |
| * @param[in] *nb = this northbridge |
| * @return The width |
| * |
| ******************************************************************************/ |
| static u8 convertBitsToWidth(u8 value, cNorthBridge *nb) |
| { |
| switch(value) { |
| case 1: return 16; |
| case 0: return 8; |
| case 5: return 4; |
| case 4: return 2; |
| default: STOP_HERE; /* This is an error internal condition */ |
| } |
| return 0; // shut up GCC. |
| } |
| |
| /***************************************************************************//** |
| * |
| * static u8 |
| * convertWidthToBits(u8 value, cNorthBridge *nb) |
| * |
| * Description: |
| * Translate a desired width setting to the bits to set in the register field |
| * |
| * Parameters: |
| * @param[in] value = The width |
| * @param[in] *nb = this northbridge |
| * @return The bits for the register |
| * |
| ******************************************************************************/ |
| static u8 convertWidthToBits(u8 value, cNorthBridge *nb) |
| { |
| switch (value) { |
| case 16: return 1; |
| case 8: return 0; |
| case 4: return 5; |
| case 2: return 4; |
| default: STOP_HERE; /* This is an internal error condition */ |
| } |
| return 0; // shut up GCC |
| } |
| |
| /***************************************************************************//** |
| * |
| * static u16 |
| * ht1NorthBridgeFreqMask(u8 NodeID, cNorthBridge *nb) |
| * |
| * Description: |
| * Return a mask that eliminates HT frequencies that cannot be used due to a slow |
| * northbridge frequency. |
| * |
| * Parameters: |
| * @param[in] node = Result could (later) be for a specific node |
| * @param[in] *nb = this northbridge |
| * @return Frequency mask |
| * |
| ******************************************************************************/ |
| static uint32_t ht1NorthBridgeFreqMask(u8 node, cNorthBridge *nb) |
| { |
| /* only up to HT1 speeds */ |
| return (HT_FREQUENCY_LIMIT_HT1_ONLY); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static u16 |
| * fam10NorthBridgeFreqMask(u8 NodeID, cNorthBridge *nb) |
| * |
| * Description: |
| * Return a mask that eliminates HT frequencies that cannot be used due to a slow |
| * northbridge frequency. |
| * |
| * Parameters: |
| * @param[in] node = Result could (later) be for a specific node |
| * @param[in] *nb = this northbridge |
| * @return = Frequency mask |
| * |
| ******************************************************************************/ |
| static uint32_t fam10NorthBridgeFreqMask(u8 node, cNorthBridge *nb) |
| { |
| u8 nbCOF; |
| uint32_t supported; |
| |
| nbCOF = getMinNbCOF(); |
| /* |
| * nbCOF is minimum northbridge speed in hundreds of MHz. |
| * HT can not go faster than the minimum speed of the northbridge. |
| */ |
| if ((nbCOF >= 6) && (nbCOF < 10)) |
| { |
| /* Generation 1 HT link frequency */ |
| /* Convert frequency to bit and all less significant bits, |
| * by setting next power of 2 and subtracting 1. |
| */ |
| supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1; |
| } |
| else if ((nbCOF >= 10) && (nbCOF <= 32)) |
| { |
| /* Generation 3 HT link frequency |
| * Assume error retry is enabled on all Gen 3 links |
| */ |
| if (is_gt_rev_d()) { |
| nbCOF *= 2; |
| if (nbCOF > 32) |
| nbCOF = 32; |
| } |
| |
| /* Convert frequency to bit and all less significant bits, |
| * by setting next power of 2 and subtracting 1. |
| */ |
| supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1; |
| } |
| else if (nbCOF > 32) |
| { |
| supported = HT_FREQUENCY_LIMIT_3200M; |
| } |
| /* unlikely cases, but include as a defensive measure, also avoid trick above */ |
| else if (nbCOF == 4) |
| { |
| supported = HT_FREQUENCY_LIMIT_400M; |
| } |
| else if (nbCOF == 2) |
| { |
| supported = HT_FREQUENCY_LIMIT_200M; |
| } |
| else |
| { |
| STOP_HERE; |
| supported = HT_FREQUENCY_LIMIT_200M; |
| } |
| |
| return (fixEarlySampleFreqCapability(supported)); |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * gatherLinkData(sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * For all discovered links, populate the port list with the frequency and width |
| * capabilities. |
| * |
| * Parameters: |
| * @param[in,out] pDat = our global state, port list |
| * @param[in] *nb = this northbridge |
| * |
| ******************************************************************************/ |
| static void gatherLinkData(sMainData *pDat, cNorthBridge *nb) |
| { |
| u8 i; |
| SBDFO linkBase; |
| u32 temp; |
| |
| for (i = 0; i < pDat->TotalLinks*2; i++) |
| { |
| if (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) |
| { |
| linkBase = makeLinkBase(pDat->PortList[i].NodeID, pDat->PortList[i].Link); |
| |
| pDat->PortList[i].Pointer = linkBase; |
| |
| AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 22, 20, &temp); |
| pDat->PortList[i].PrvWidthOutCap = convertBitsToWidth((u8)temp, pDat->nb); |
| |
| AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 18, 16, &temp); |
| pDat->PortList[i].PrvWidthInCap = convertBitsToWidth((u8)temp, pDat->nb); |
| |
| AmdPCIReadBits(linkBase + HTHOST_FREQ_REV_REG, 31, 16, &temp); |
| pDat->PortList[i].PrvFrequencyCap = temp & 0x7FFF /* Mask off bit 15, reserved value */ |
| & nb->northBridgeFreqMask(pDat->PortList[i].NodeID, pDat->nb); |
| if (is_gt_rev_d()) { |
| AmdPCIReadBits(linkBase + HTHOST_FREQ_REV_REG_2, 15, 1, &temp); |
| temp &= 0x7; /* Mask off reserved values */ |
| pDat->PortList[i].PrvFrequencyCap |= (temp << 17); |
| } |
| } |
| else |
| { |
| linkBase = pDat->PortList[i].Pointer; |
| if (pDat->PortList[i].Link == 1) |
| linkBase += HTSLAVE_LINK01_OFFSET; |
| |
| AmdPCIReadBits(linkBase + HTSLAVE_LINK_CONTROL_0_REG, 22, 20, &temp); |
| pDat->PortList[i].PrvWidthOutCap = convertBitsToWidth((u8)temp, pDat->nb); |
| |
| AmdPCIReadBits(linkBase + HTSLAVE_LINK_CONTROL_0_REG, 18, 16, &temp); |
| pDat->PortList[i].PrvWidthInCap = convertBitsToWidth((u8)temp, pDat->nb); |
| |
| AmdPCIReadBits(linkBase + HTSLAVE_FREQ_REV_0_REG, 31, 16, &temp); |
| pDat->PortList[i].PrvFrequencyCap = (u16)temp; |
| |
| if (pDat->HtBlock->AMD_CB_DeviceCapOverride) |
| { |
| linkBase &= 0xFFFFF000; |
| AmdPCIRead(linkBase, &temp); |
| |
| pDat->HtBlock->AMD_CB_DeviceCapOverride( |
| pDat->PortList[i].NodeID, |
| pDat->PortList[i].HostLink, |
| pDat->PortList[i].HostDepth, |
| (u8)SBDFO_SEG(pDat->PortList[i].Pointer), |
| (u8)SBDFO_BUS(pDat->PortList[i].Pointer), |
| (u8)SBDFO_DEV(pDat->PortList[i].Pointer), |
| temp, |
| pDat->PortList[i].Link, |
| &(pDat->PortList[i].PrvWidthInCap), |
| &(pDat->PortList[i].PrvWidthOutCap), |
| &(pDat->PortList[i].PrvFrequencyCap)); |
| } |
| } |
| } |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * setLinkData(sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Change the hardware state for all links according to the now optimized data in the |
| * port list data structure. |
| * |
| * Parameters: |
| * @param[in] pDat = our global state, port list |
| * @param[in] *nb = this northbridge |
| * |
| ******************************************************************************/ |
| static void setLinkData(sMainData *pDat, cNorthBridge *nb) |
| { |
| u8 i; |
| SBDFO linkBase; |
| u32 temp, temp2, frequency_index, widthin, widthout, bits; |
| |
| for (i = 0; i < pDat->TotalLinks*2; i++) |
| { |
| |
| ASSERT(pDat->PortList[i&0xFE].SelWidthOut == pDat->PortList[(i&0xFE)+1].SelWidthIn); |
| ASSERT(pDat->PortList[i&0xFE].SelWidthIn == pDat->PortList[(i&0xFE)+1].SelWidthOut); |
| ASSERT(pDat->PortList[i&0xFE].SelFrequency == pDat->PortList[(i&0xFE)+1].SelFrequency); |
| |
| if (pDat->PortList[i].SelRegang) |
| { |
| ASSERT(pDat->PortList[i].Type == PORTLIST_TYPE_CPU); |
| ASSERT(pDat->PortList[i].Link < 4); |
| temp = 1; |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID), |
| makePCIBusFromNode(pDat->PortList[i].NodeID), |
| makePCIDeviceFromNode(pDat->PortList[i].NodeID), |
| CPU_HTNB_FUNC_00, |
| REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link), |
| 0, 0, &temp); |
| } |
| |
| if (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) |
| { |
| if (pDat->HtBlock->AMD_CB_OverrideCpuPort) |
| pDat->HtBlock->AMD_CB_OverrideCpuPort(pDat->PortList[i].NodeID, |
| pDat->PortList[i].Link, |
| &(pDat->PortList[i].SelWidthIn), |
| &(pDat->PortList[i].SelWidthOut), |
| &(pDat->PortList[i].SelFrequency)); |
| } |
| else |
| { |
| if (pDat->HtBlock->AMD_CB_OverrideDevicePort) |
| pDat->HtBlock->AMD_CB_OverrideDevicePort(pDat->PortList[i].NodeID, |
| pDat->PortList[i].HostLink, |
| pDat->PortList[i].HostDepth, |
| pDat->PortList[i].Link, |
| &(pDat->PortList[i].SelWidthIn), |
| &(pDat->PortList[i].SelWidthOut), |
| &(pDat->PortList[i].SelFrequency)); |
| } |
| |
| linkBase = pDat->PortList[i].Pointer; |
| if ((pDat->PortList[i].Type == PORTLIST_TYPE_IO) && (pDat->PortList[i].Link == 1)) |
| linkBase += HTSLAVE_LINK01_OFFSET; |
| |
| /* Some IO devices don't work properly when setting widths, so write them in a single operation, |
| * rather than individually. |
| */ |
| widthout = convertWidthToBits(pDat->PortList[i].SelWidthOut, pDat->nb); |
| ASSERT(widthout == 1 || widthout == 0 || widthout == 5 || widthout == 4); |
| widthin = convertWidthToBits(pDat->PortList[i].SelWidthIn, pDat->nb); |
| ASSERT(widthin == 1 || widthin == 0 || widthin == 5 || widthin == 4); |
| |
| temp = (widthin & 7) | ((widthout & 7) << 4); |
| setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 31, 24, &temp); |
| |
| temp = pDat->PortList[i].SelFrequency; |
| if (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) |
| { |
| ASSERT((temp >= HT_FREQUENCY_600M && temp <= HT_FREQUENCY_3200M) |
| || (temp == HT_FREQUENCY_200M) || (temp == HT_FREQUENCY_400M)); |
| frequency_index = temp; |
| if (temp > 0xf) { |
| temp2 = (temp >> 4) & 0x1; |
| temp &= 0xf; |
| } else { |
| temp2 = 0x0; |
| } |
| /* NOTE |
| * The Family 15h BKDG Rev. 3.14 is wrong |
| * Freq[4] must be set before Freq[3:0], otherwise the register writes will be ignored! |
| */ |
| if (is_gt_rev_d()) |
| AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG_2, 0, 0, &temp2); |
| AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG, 11, 8, &temp); |
| if (frequency_index > HT_FREQUENCY_1000M) /* Gen1 = 200MHz -> 1000MHz, Gen3 = 1200MHz -> 3200MHz */ |
| { |
| /* Enable for Gen3 frequencies */ |
| temp = 1; |
| } |
| else |
| { |
| /* Disable for Gen1 frequencies */ |
| temp = 0; |
| } |
| /* HT3 retry mode enable / disable */ |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID), |
| makePCIBusFromNode(pDat->PortList[i].NodeID), |
| makePCIDeviceFromNode(pDat->PortList[i].NodeID), |
| CPU_HTNB_FUNC_00, |
| REG_HT_LINK_RETRY0_0X130 + 4*pDat->PortList[i].Link), |
| 0, 0, &temp); |
| /* and Scrambling enable / disable */ |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID), |
| makePCIBusFromNode(pDat->PortList[i].NodeID), |
| makePCIDeviceFromNode(pDat->PortList[i].NodeID), |
| CPU_HTNB_FUNC_00, |
| REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link), |
| 3, 3, &temp); |
| } |
| else |
| { |
| SBDFO currentPtr; |
| BOOL isFound; |
| |
| ASSERT(temp <= HT_FREQUENCY_3200M); |
| /* Write the frequency setting */ |
| AmdPCIWriteBits(linkBase + HTSLAVE_FREQ_REV_0_REG, 11, 8, &temp); |
| |
| /* Handle additional HT3 frequency requirements, if needed, |
| * or clear them if switching down to ht1 on a warm reset. |
| * Gen1 = 200MHz -> 1000MHz, Gen3 = 1200MHz -> 2600MHz |
| * |
| * Even though we assert if debugging, we need to check that the capability was found |
| * always, since this is an unknown hardware device, also we are taking |
| * unqualified frequency from the call backs |
| * (could be trying to do ht3 on an ht1 IO device). |
| */ |
| |
| if (temp > HT_FREQUENCY_1000M) |
| { |
| /* Enabling features if gen 3 */ |
| bits = 1; |
| } |
| else |
| { |
| /* Disabling features if gen 1 */ |
| bits = 0; |
| } |
| |
| /* Retry Enable */ |
| isFound = FALSE; |
| currentPtr = linkBase & (u32)0xFFFFF000; /* Set PCI Offset to 0 */ |
| do |
| { |
| AmdPCIFindNextCap(¤tPtr); |
| if (currentPtr != ILLEGAL_SBDFO) |
| { |
| AmdPCIRead(currentPtr, &temp); |
| /* HyperTransport Retry Capability? */ |
| if (IS_HT_RETRY_CAPABILITY(temp)) |
| { |
| ASSERT(pDat->PortList[i].Link < 2); |
| AmdPCIWriteBits(currentPtr + HTRETRY_CONTROL_REG, |
| pDat->PortList[i].Link*16, |
| pDat->PortList[i].Link*16, |
| &bits); |
| isFound = TRUE; |
| } |
| /* Some other capability, keep looking */ |
| } |
| else |
| { |
| /* If we are turning it off, that may mean the device was only ht1 capable, |
| * so don't complain that we can't do it. |
| */ |
| if (bits != 0) |
| { |
| if (pDat->HtBlock->AMD_CB_EventNotify) |
| { |
| sHtEventOptRequiredCap evt; |
| evt.eSize = sizeof(sHtEventOptRequiredCap); |
| evt.node = pDat->PortList[i].NodeID; |
| evt.link = pDat->PortList[i].HostLink; |
| evt.depth = pDat->PortList[i].HostDepth; |
| |
| pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_WARNING, |
| HT_EVENT_OPT_REQUIRED_CAP_RETRY, |
| (u8 *)&evt); |
| } |
| STOP_HERE; |
| } |
| isFound = TRUE; |
| } |
| } while (!isFound); |
| |
| /* Scrambling enable */ |
| isFound = FALSE; |
| currentPtr = linkBase & (u32)0xFFFFF000; /* Set PCI Offset to 0 */ |
| do |
| { |
| AmdPCIFindNextCap(¤tPtr); |
| if (currentPtr != ILLEGAL_SBDFO) |
| { |
| AmdPCIRead(currentPtr, &temp); |
| /* HyperTransport Gen3 Capability? */ |
| if (IS_HT_GEN3_CAPABILITY(temp)) |
| { |
| ASSERT(pDat->PortList[i].Link < 2); |
| AmdPCIWriteBits((currentPtr + |
| HTGEN3_LINK_TRAINING_0_REG + |
| pDat->PortList[i].Link*HTGEN3_LINK01_OFFSET), |
| 3, 3, &bits); |
| isFound = TRUE; |
| } |
| /* Some other capability, keep looking */ |
| } |
| else |
| { |
| /* If we are turning it off, that may mean the device was only ht1 capable, |
| * so don't complain that we can't do it. |
| */ |
| if (bits != 0) |
| { |
| if (pDat->HtBlock->AMD_CB_EventNotify) |
| { |
| sHtEventOptRequiredCap evt; |
| evt.eSize = sizeof(sHtEventOptRequiredCap); |
| evt.node = pDat->PortList[i].NodeID; |
| evt.link = pDat->PortList[i].HostLink; |
| evt.depth = pDat->PortList[i].HostDepth; |
| |
| pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_WARNING, |
| HT_EVENT_OPT_REQUIRED_CAP_GEN3, |
| (u8 *)&evt); |
| } |
| STOP_HERE; |
| } |
| isFound = TRUE; |
| } |
| } while (!isFound); |
| } |
| } |
| } |
| |
| /***************************************************************************//** |
| * |
| * void |
| * fam0fWriteHTLinkCmdBufferAlloc(u8 node, u8 link, u8 req, u8 preq, u8 rsp, u8 prb) |
| * |
| * Description: |
| * Set the command buffer allocations in the buffer count register for the node and link. |
| * The command buffer settings in the low 16 bits are the same on both |
| * family 10h and family 0fh northbridges. |
| * |
| * Parameters: |
| * @param[in] node = The node to set allocations on |
| * @param[in] link = the link to set allocations on |
| * @param[in] req = non-posted Request Command Buffers |
| * @param[in] preq = Posted Request Command Buffers |
| * @param[in] rsp = Response Command Buffers |
| * @param[in] prb = Probe Command Buffers |
| * |
| ******************************************************************************/ |
| #ifndef HT_BUILD_NC_ONLY |
| |
| static void fam0fWriteHTLinkCmdBufferAlloc(u8 node, u8 link, u8 req, u8 preq, u8 rsp, u8 prb) |
| { |
| u32 temp; |
| SBDFO currentPtr; |
| |
| currentPtr = makeLinkBase(node, link); |
| currentPtr += HTHOST_BUFFER_COUNT_REG; |
| |
| /* non-posted Request Command Buffers */ |
| temp = req; |
| AmdPCIWriteBits(currentPtr, 3, 0, &temp); |
| /* Posted Request Command Buffers */ |
| temp = preq; |
| AmdPCIWriteBits(currentPtr, 7, 4, &temp); |
| /* Response Command Buffers */ |
| temp = rsp; |
| AmdPCIWriteBits(currentPtr, 11, 8, &temp); |
| /* Probe Command Buffers */ |
| temp = prb; |
| AmdPCIWriteBits(currentPtr, 15, 12, &temp); |
| /* LockBc */ |
| temp = 1; |
| AmdPCIWriteBits(currentPtr, 31, 31, &temp); |
| } |
| #endif /* HT_BUILD_NC_ONLY */ |
| |
| /***************************************************************************//** |
| * |
| * void |
| * fam0fWriteHTLinkDatBufferAlloc(u8 node, u8 link, u8 reqD, u8 preqD, u8 rspD) |
| * |
| * Description: |
| * Set the data buffer allocations in the buffer count register for the node and link. |
| * The command buffer settings in the high 16 bits are not the same on both |
| * family 10h and family 0fh northbridges. |
| * |
| * Parameters: |
| * @param[in] node = The node to set allocations on |
| * @param[in] link = the link to set allocations on |
| * @param[in] reqD = non-posted Request Data Buffers |
| * @param[in] preqD = Posted Request Data Buffers |
| * @param[in] rspD = Response Data Buffers |
| * |
| ******************************************************************************/ |
| #ifndef HT_BUILD_NC_ONLY |
| |
| static void fam0fWriteHTLinkDatBufferAlloc(u8 node, u8 link, u8 reqD, u8 preqD, u8 rspD) |
| { |
| u32 temp; |
| SBDFO currentPtr; |
| |
| currentPtr = makeLinkBase(node, link); |
| currentPtr += HTHOST_BUFFER_COUNT_REG; |
| |
| /* Request Data Buffers */ |
| temp = reqD; |
| AmdPCIWriteBits(currentPtr, 18, 16, &temp); |
| /* Posted Request Data Buffers */ |
| temp = preqD; |
| AmdPCIWriteBits(currentPtr, 22, 20, &temp); |
| /* Response Data Buffers */ |
| temp = rspD; |
| AmdPCIWriteBits(currentPtr, 26, 24, &temp); |
| } |
| #endif /* HT_BUILD_NC_ONLY */ |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * ht3WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb) |
| * |
| * Description: |
| * Set the traffic distribution register for the links provided. |
| * |
| * Parameters: |
| * @param[in] links01 = coherent links from node 0 to 1 |
| * @param[in] links10 = coherent links from node 1 to 0 |
| * @param[in] nb = this northbridge |
| * |
| ******************************************************************************/ |
| static void ht3WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 temp; |
| |
| /* Node 0 */ |
| /* DstLnk */ |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(0), |
| makePCIBusFromNode(0), |
| makePCIDeviceFromNode(0), |
| CPU_HTNB_FUNC_00, |
| REG_HT_TRAFFIC_DIST_0X164), |
| 23, 16, &links01); |
| /* DstNode = 1, cHTPrbDistEn=1, cHTRspDistEn=1, cHTReqDistEn=1 */ |
| temp = 0x0107; |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(0), |
| makePCIBusFromNode(0), |
| makePCIDeviceFromNode(0), |
| CPU_HTNB_FUNC_00, |
| REG_HT_TRAFFIC_DIST_0X164), |
| 15, 0, &temp); |
| |
| /* Node 1 */ |
| /* DstLnk */ |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(1), |
| makePCIBusFromNode(1), |
| makePCIDeviceFromNode(1), |
| CPU_HTNB_FUNC_00, |
| REG_HT_TRAFFIC_DIST_0X164), |
| 23, 16, &links10); |
| /* DstNode = 0, cHTPrbDistEn=1, cHTRspDistEn=1, cHTReqDistEn=1 */ |
| temp = 0x0007; |
| AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(1), |
| makePCIBusFromNode(1), |
| makePCIDeviceFromNode(1), |
| CPU_HTNB_FUNC_00, |
| REG_HT_TRAFFIC_DIST_0X164), |
| 15, 0, &temp); |
| #endif /* HT_BUILD_NC_ONLY */ |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * ht1WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb) |
| * |
| * Description: |
| * Traffic distribution is more complex in this case as the routing table must be |
| * adjusted to use one link for requests and the other for responses. Also, |
| * perform the buffer tunings on the links required for this config. |
| * |
| * Parameters: |
| * @param[in] links01 = coherent links from node 0 to 1 |
| * @param[in] links10 = coherent links from node 1 to 0 |
| * @param[in] nb = this northbridge |
| * |
| ******************************************************************************/ |
| static void ht1WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u32 route01, route10; |
| u8 req0, req1, rsp0, rsp1, nclink; |
| |
| /* |
| * Get the current request route for 0->1 and 1->0. This will indicate which of the links |
| * in links01 are connected to which links in links10. Since we have to route to distribute |
| * traffic, we need to know that. The link used by htinit will become the request, probe link. |
| * the other link will be used for responses. |
| */ |
| |
| /* Get the routes, and hang on to them, we will write them back updated. */ |
| AmdPCIRead(MAKE_SBDFO(makePCISegmentFromNode(0), |
| makePCIBusFromNode(0), |
| makePCIDeviceFromNode(0), |
| CPU_HTNB_FUNC_00, |
| REG_ROUTE1_0X44), |
| &route01); |
| AmdPCIRead(MAKE_SBDFO(makePCISegmentFromNode(1), |
| makePCIBusFromNode(1), |
| makePCIDeviceFromNode(1), |
| CPU_HTNB_FUNC_00, |
| REG_ROUTE0_0X40), |
| &route10); |
| |
| /* Convert the request routes to a link number. Note "0xE" is ht1 nb specific. |
| * Find the response link numbers. |
| */ |
| ASSERT((route01 & 0xE) && (route10 & 0xE)); /* no route! error! */ |
| req0 = (u8)AmdBitScanReverse((route01 & 0xE)) - 1; |
| req1 = (u8)AmdBitScanReverse((route10 & 0xE)) - 1; |
| /* Now, find the other link for the responses */ |
| rsp0 = (u8)AmdBitScanReverse((links01 & ~((u32)1 << req0))); |
| rsp1 = (u8)AmdBitScanReverse((links10 & ~((u32)1 << req1))); |
| |
| /* ht1 nb restriction, must have exactly two links */ |
| ASSERT(((((links01 & ~((u32)1 << req0)) & ~((u32)1 << rsp0))) == 0) |
| && ((((links10 & ~((u32)1 << req1)) & ~((u32)1 << rsp1))) == 0)); |
| |
| route01 = (route01 & ~0x0E00) | ((u32)0x0100<<(rsp0 + 1)); |
| route10 = (route10 & ~0x0E00) | ((u32)0x0100<<(rsp1 + 1)); |
| |
| AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(0), |
| makePCIBusFromNode(0), |
| makePCIDeviceFromNode(0), |
| CPU_HTNB_FUNC_00, |
| REG_ROUTE1_0X44), |
| &route01); |
| |
| AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(1), |
| makePCIBusFromNode(1), |
| makePCIDeviceFromNode(1), |
| CPU_HTNB_FUNC_00, |
| REG_ROUTE0_0X40), |
| &route10); |
| |
| /* While we otherwise do buffer tunings elsewhere, for the dual cHT DP case with |
| * ht1 northbridges like family 0Fh, do the tunings here where we have all the |
| * link and route info at hand and don't need to recalculate it. |
| */ |
| |
| /* Node 0, Request / Probe Link (note family F only has links < 4) */ |
| fam0fWriteHTLinkCmdBufferAlloc(0, req0, 6, 3, 1, 6); |
| fam0fWriteHTLinkDatBufferAlloc(0, req0, 4, 3, 1); |
| /* Node 0, Response Link (note family F only has links < 4) */ |
| fam0fWriteHTLinkCmdBufferAlloc(0, rsp0, 1, 0, 15, 0); |
| fam0fWriteHTLinkDatBufferAlloc(0, rsp0, 1, 1, 6); |
| /* Node 1, Request / Probe Link (note family F only has links < 4) */ |
| fam0fWriteHTLinkCmdBufferAlloc(1, req1, 6, 3, 1, 6); |
| fam0fWriteHTLinkDatBufferAlloc(1, req1, 4, 3, 1); |
| /* Node 1, Response Link (note family F only has links < 4) */ |
| fam0fWriteHTLinkCmdBufferAlloc(1, rsp1, 1, 0, 15, 0); |
| fam0fWriteHTLinkDatBufferAlloc(1, rsp1, 1, 1, 6); |
| |
| /* Node 0, is the third link non-coherent? */ |
| nclink = (u8)AmdBitScanReverse(((u8)0x07 & ~((u32)1 << req0) & ~((u32)1 << rsp0))); |
| if (nb->verifyLinkIsNonCoherent(0, nclink, nb)) |
| { |
| fam0fWriteHTLinkCmdBufferAlloc(0, nclink, 6, 5, 2, 0); |
| } |
| |
| /* Node 1, is the third link non-coherent? */ |
| nclink = (u8)AmdBitScanReverse(((u8)0x07 & ~((u32)1 << req1) & ~((u32)1 << rsp1))); |
| if (nb->verifyLinkIsNonCoherent(1, nclink, nb)) |
| { |
| fam0fWriteHTLinkCmdBufferAlloc(1, nclink, 6, 5, 2, 0); |
| } |
| #endif /* HT_BUILD_NC_ONLY */ |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * fam0fBufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Buffer tunings are inherently northbridge specific. Check for specific configs |
| * which require adjustments and apply any standard workarounds to this node. |
| * |
| * Parameters: |
| * @param[in] node = the node to |
| * @param[in] *pDat = coherent links from node 0 to 1 |
| * @param[in] nb = this northbridge |
| * |
| ******************************************************************************/ |
| static void fam0fBufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb) |
| { |
| #ifndef HT_BUILD_NC_ONLY |
| u8 i; |
| u32 temp; |
| SBDFO currentPtr; |
| |
| ASSERT(node < nb->maxNodes); |
| |
| /* Fix the FIFO pointer register before changing speeds */ |
| currentPtr = MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_FIFOPTR_3XDC); |
| for (i=0; i < nb->maxLinks; i++) |
| { |
| temp = 0; |
| if (nb->verifyLinkIsCoherent(node, i, nb)) |
| { |
| temp = 0x26; |
| ASSERT(i<3); |
| AmdPCIWriteBits(currentPtr, 8*i + 5, 8*i, &temp); |
| } |
| else |
| { |
| if (nb->verifyLinkIsNonCoherent(node, i, nb)) |
| { |
| temp = 0x25; |
| ASSERT(i<3); |
| AmdPCIWriteBits(currentPtr, 8*i + 5, 8*i, &temp); |
| } |
| } |
| } |
| /* |
| * 8P Buffer tuning. |
| * Either apply the BKDG tunings or, if applicable, apply the more restrictive errata 153 |
| * workaround. |
| * If 8 nodes, Check this node for 'inner' or 'outer'. |
| * Tune each link based on coherent or non-coherent |
| */ |
| if (pDat->NodesDiscovered >= 6) |
| { |
| u8 j; |
| BOOL isOuter; |
| BOOL isErrata153; |
| |
| /* This is for family 0Fh, so assuming dual core max then 7 or 8 nodes are required |
| * to be in the situation of 14 or more cores. We checked nodes above, cross check |
| * that the number of cores is 14 or more. We want both 14 cores with at least 7 or 8 nodes |
| * not one condition alone, to apply the errata 153 workaround. Otherwise, 7 or 8 rev F |
| * nodes use the BKDG tuning. |
| */ |
| |
| isErrata153 = 0; |
| |
| AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(0), |
| makePCIBusFromNode(0), |
| makePCIDeviceFromNode(0), |
| CPU_HTNB_FUNC_00, |
| REG_NODE_ID_0X60), |
| 19, 16, &temp); |
| |
| if (temp >= 14) |
| { |
| /* Check whether we need to do errata 153 tuning or BKDG tuning. |
| * Errata 153 applies to JH-1, JH-2 and older. It is fixed in JH-3 |
| * (and, one assumes, from there on). |
| */ |
| for (i=0; i < (pDat->NodesDiscovered +1); i++) |
| { |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(i), |
| makePCIBusFromNode(i), |
| makePCIDeviceFromNode(i), |
| CPU_NB_FUNC_03, |
| REG_NB_CPUID_3XFC), |
| 7, 0, &temp); |
| if (((u8)temp & ~0x40) < 0x13) |
| { |
| isErrata153 = 1; |
| break; |
| } |
| } |
| } |
| |
| for (i=0; i < CPU_ADDR_NUM_CONFIG_MAPS; i++) |
| { |
| isOuter = FALSE; |
| /* Check for outer node by scanning the config maps on node 0 for one |
| * which is assigned to this node. |
| */ |
| currentPtr = MAKE_SBDFO(makePCISegmentFromNode(0), |
| makePCIBusFromNode(0), |
| makePCIDeviceFromNode(0), |
| CPU_ADDR_FUNC_01, |
| REG_ADDR_CONFIG_MAP0_1XE0 + (4 * i)); |
| AmdPCIReadBits (currentPtr, 1, 0, &temp); |
| /* Make sure this config map is valid, if it is it will be enabled for read/write */ |
| if (temp == 3) |
| { |
| /* It's valid, get the node (that node is an outer node) */ |
| AmdPCIReadBits (currentPtr, 6, 4, &temp); |
| /* Is the node we're working on now? */ |
| if (node == (u8)temp) |
| { |
| /* This is an outer node. Tune it appropriately. */ |
| for (j=0; j < nb->maxLinks; j++) |
| { |
| if (isErrata153) |
| { |
| if (nb->verifyLinkIsCoherent(node, j, nb)) |
| { |
| fam0fWriteHTLinkCmdBufferAlloc(node, j, 1, 1, 6, 4); |
| } |
| else |
| { |
| if (nb->verifyLinkIsNonCoherent(node, j, nb)) |
| { |
| fam0fWriteHTLinkCmdBufferAlloc(node, j, 5, 4, 1, 0); |
| } |
| } |
| } |
| else |
| { |
| if (nb->verifyLinkIsCoherent(node, j, nb)) |
| { |
| fam0fWriteHTLinkCmdBufferAlloc(node, j, 1, 1, 8, 5); |
| } |
| } |
| } |
| /* |
| * SRI to XBAR Buffer Counts are correct for outer node at power on defaults. |
| */ |
| isOuter = TRUE; |
| break; |
| } |
| } |
| /* We fill config maps in ascending order, so if we didn't use this one, we're done. */ |
| else break; |
| } |
| if (!isOuter) |
| { |
| if (isErrata153) |
| { |
| /* Tuning for inner node coherent links */ |
| for (j=0; j < nb->maxLinks; j++) |
| { |
| if (nb->verifyLinkIsCoherent(node, j, nb)) |
| { |
| fam0fWriteHTLinkCmdBufferAlloc(node, j, 2, 1, 5, 4); |
| } |
| |
| } |
| /* SRI to XBAR Buffer Count for inner nodes, zero DReq and DPReq */ |
| temp = 0; |
| AmdPCIWriteBits (MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_SRI_XBAR_BUF_3X70), |
| 31, 28, &temp); |
| } |
| } |
| |
| /* |
| * Tune MCT to XBAR Buffer Count the same an all nodes, 2 Probes, 5 Response |
| */ |
| if (isErrata153) |
| { |
| temp = 0x25; |
| AmdPCIWriteBits (MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_MCT_XBAR_BUF_3X78), |
| 14, 8, &temp); |
| } |
| } |
| #endif /* HT_BUILD_NC_ONLY */ |
| } |
| |
| /***************************************************************************//** |
| * |
| * static void |
| * fam10BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb) |
| * |
| * Description: |
| * Buffer tunings are inherently northbridge specific. Check for specific configs |
| * which require adjustments and apply any standard workarounds to this node. |
| * |
| * Parameters: |
| * @param[in] node = the node to tune |
| * @param[in] *pDat = global state |
| * @param[in] nb = this northbridge |
| * |
| ******************************************************************************/ |
| static void fam10BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb) |
| { |
| u32 temp; |
| SBDFO currentPtr; |
| u8 i; |
| |
| ASSERT(node < nb->maxNodes); |
| |
| /* |
| * Link to XCS Token Count Tuning |
| * |
| * For each active link that we reganged (so this unfortunately can't go into the PCI reg |
| * table), we have to switch the Link to XCS Token Counts to the ganged state. |
| * We do this here for the non-uma case, which is to write the values that would have |
| * been power on defaults if the link was ganged at cold reset. |
| */ |
| for (i = 0; i < pDat->TotalLinks*2; i++) |
| { |
| if ((pDat->PortList[i].NodeID == node) && (pDat->PortList[i].Type == PORTLIST_TYPE_CPU)) |
| { |
| /* If the link is greater than 4, this is a sublink 1, so it is not reganged. */ |
| if (pDat->PortList[i].Link < 4) |
| { |
| currentPtr = MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_LINK_XCS_TOKEN0_3X148 + 4*pDat->PortList[i].Link); |
| if (pDat->PortList[i].SelRegang) |
| { |
| /* Handle all the regang Token count adjustments */ |
| |
| /* Sublink 0: [Probe0tok] = 2 [Rsp0tok] = 2 [PReq0tok] = 2 [Req0tok] = 2 */ |
| temp = 0xAA; |
| AmdPCIWriteBits(currentPtr, 7, 0, &temp); |
| /* Sublink 1: [Probe1tok] = 0 [Rsp1tok] = 0 [PReq1tok] = 0 [Req1tok] = 0 */ |
| temp = 0; |
| AmdPCIWriteBits(currentPtr, 23, 16, &temp); |
| /* [FreeTok] = 3 */ |
| temp = 3; |
| AmdPCIWriteBits(currentPtr, 15, 14, &temp); |
| } |
| else |
| { |
| /* Read the regang bit in hardware */ |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID), |
| makePCIBusFromNode(pDat->PortList[i].NodeID), |
| makePCIDeviceFromNode(pDat->PortList[i].NodeID), |
| CPU_HTNB_FUNC_00, |
| REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link), |
| 0, 0, &temp); |
| if (temp == 1) |
| { |
| /* handle a minor adjustment for stapped ganged links. If SelRegang is false we |
| * didn't do the regang, so if the bit is on then it's hardware strapped. |
| */ |
| |
| /* [FreeTok] = 3 */ |
| temp = 3; |
| AmdPCIWriteBits(currentPtr, 15, 14, &temp); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * North Bridge 'constructor'. |
| * |
| */ |
| |
| /***************************************************************************//** |
| * |
| * void |
| * newNorthBridge(u8 node, cNorthBridge *nb) |
| * |
| * Description: |
| * Construct a new northbridge. This routine encapsulates knowledge of how to tell |
| * significant differences between families of supported northbridges and what routines |
| * can be used in common and which are unique. A fully populated northbridge interface |
| * is provided by nb. |
| * |
| * Parameters: |
| * @param node |
| * @param[out] nb = the caller's northbridge structure to initialize. |
| * |
| ******************************************************************************/ |
| void newNorthBridge(u8 node, cNorthBridge *nb) |
| { |
| u32 match; |
| u32 extFam, baseFam, model; |
| |
| cNorthBridge fam15 = |
| { |
| #ifdef HT_BUILD_NC_ONLY |
| 8, |
| 1, |
| 12, |
| #else |
| 8, |
| 8, |
| 64, |
| #endif /* HT_BUILD_NC_ONLY*/ |
| writeRoutingTable, |
| writeNodeID, |
| readDefLnk, |
| enableRoutingTables, |
| verifyLinkIsCoherent, |
| readTrueLinkFailStatus, |
| readToken, |
| writeToken, |
| fam15GetNumCoresOnNode, |
| setTotalNodesAndCores, |
| limitNodes, |
| writeFullRoutingTable, |
| isCompatible, |
| fam15IsCapable, |
| (void (*)(u8, u8, cNorthBridge*))commonVoid, |
| (BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse, |
| readSbLink, |
| verifyLinkIsNonCoherent, |
| ht3SetCFGAddrMap, |
| convertBitsToWidth, |
| convertWidthToBits, |
| fam10NorthBridgeFreqMask, |
| gatherLinkData, |
| setLinkData, |
| ht3WriteTrafficDistribution, |
| fam10BufferOptimizations, |
| 0x00000001, |
| 0x00000200, |
| 18, |
| 0x00000f06 |
| }; |
| |
| cNorthBridge fam10 = |
| { |
| #ifdef HT_BUILD_NC_ONLY |
| 8, |
| 1, |
| 12, |
| #else |
| 8, |
| 8, |
| 64, |
| #endif /* HT_BUILD_NC_ONLY*/ |
| writeRoutingTable, |
| writeNodeID, |
| readDefLnk, |
| enableRoutingTables, |
| verifyLinkIsCoherent, |
| readTrueLinkFailStatus, |
| readToken, |
| writeToken, |
| fam10GetNumCoresOnNode, |
| setTotalNodesAndCores, |
| limitNodes, |
| writeFullRoutingTable, |
| isCompatible, |
| fam10IsCapable, |
| (void (*)(u8, u8, cNorthBridge*))commonVoid, |
| (BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse, |
| readSbLink, |
| verifyLinkIsNonCoherent, |
| ht3SetCFGAddrMap, |
| convertBitsToWidth, |
| convertWidthToBits, |
| fam10NorthBridgeFreqMask, |
| gatherLinkData, |
| setLinkData, |
| ht3WriteTrafficDistribution, |
| fam10BufferOptimizations, |
| 0x00000001, |
| 0x00000200, |
| 18, |
| 0x00000f01 |
| }; |
| |
| cNorthBridge fam0f = |
| { |
| #ifdef HT_BUILD_NC_ONLY |
| 3, |
| 1, |
| 12, |
| #else |
| 3, |
| 8, |
| 32, |
| #endif /* HT_BUILD_NC_ONLY*/ |
| writeRoutingTable, |
| writeNodeID, |
| readDefLnk, |
| enableRoutingTables, |
| verifyLinkIsCoherent, |
| readTrueLinkFailStatus, |
| readToken, |
| writeToken, |
| fam0FGetNumCoresOnNode, |
| setTotalNodesAndCores, |
| limitNodes, |
| writeFullRoutingTable, |
| isCompatible, |
| fam0fIsCapable, |
| fam0fStopLink, |
| (BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse, |
| readSbLink, |
| verifyLinkIsNonCoherent, |
| ht1SetCFGAddrMap, |
| convertBitsToWidth, |
| convertWidthToBits, |
| ht1NorthBridgeFreqMask, |
| gatherLinkData, |
| setLinkData, |
| ht1WriteTrafficDistribution, |
| fam0fBufferOptimizations, |
| 0x00000001, |
| 0x00000100, |
| 16, |
| 0x00000f00 |
| }; |
| |
| /* Start with enough of the key to identify the northbridge interface */ |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CPUID_3XFC), |
| 27, 20, &extFam); |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CPUID_3XFC), |
| 11, 8, &baseFam); |
| AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), |
| makePCIBusFromNode(node), |
| makePCIDeviceFromNode(node), |
| CPU_NB_FUNC_03, |
| REG_NB_CPUID_3XFC), |
| 7, 4, &model); |
| match = (u32)((baseFam << 8) | extFam); |
| |
| /* Test each in turn looking for a match. |
| * Initialize the struct if found. |
| */ |
| if (match == fam15.compatibleKey) |
| { |
| Amdmemcpy((void *)nb, (const void *)&fam15, (u32) sizeof(cNorthBridge)); |
| } |
| else if (match == fam10.compatibleKey) |
| { |
| Amdmemcpy((void *)nb, (const void *)&fam10, (u32) sizeof(cNorthBridge)); |
| } |
| else |
| { |
| if (match == fam0f.compatibleKey) |
| { |
| Amdmemcpy((void *)nb, (const void *)&fam0f, (u32) sizeof(cNorthBridge)); |
| } |
| else |
| { |
| STOP_HERE; |
| } |
| } |
| |
| /* Update the initial limited key to the real one, which may include other matching info */ |
| nb->compatibleKey = makeKey(node); |
| } |