mcamd_patounum.c revision 7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fe
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Given a physical address and an optional syndrome, determine the
* name of the memory module that contains it.
*/
#include <mcamd_api.h>
#include <mcamd_err.h>
#define LO_DIMM 0x1
#define UP_DIMM 0x2
static int
{
"lookup required properties");
}
(int)mcnum);
}
/*
* Rev E and later added the DRAM Hole Address Register for
* memory hoisting. In earlier revisions memory hoisting is
* achieved by following some algorithm to modify the CS bases etc,
* and this pa to unum algorithm will simply see those modified
* values. But if the Hole Address Register is being used then
* we need to reduce any address at or above 4GB by the size of
* the hole.
*/
"valid; pa decremented from 0x%llx to 0x%llx for "
}
if (ilen != 0) {
int pailsel;
}
"PA 0x%llx in a %d-way node interleave indicates "
"selection %d, MC %d has ilsel of %d\n",
}
if (ilen == 1)
else if (ilen == 3)
else if (ilen == 7)
} else {
}
"[0x%llx, 0x%llx] of MC %d; normalized address for cs compare "
return (0);
}
static int
{
int match;
"required properties\n");
return (0);
}
"%smatch CS %d (base 0x%llx, mask 0x%llx)\n", iaddr,
return (match);
}
static int
{
int i;
int offsetdimm;
"lookup required properties\n");
}
}
unump->unum_board = 0;
for (i = 0; i < MC_UNUM_NDIMM; i++) {
}
switch (which) {
case LO_DIMM:
offsetdimm = lonum;
break;
case UP_DIMM:
offsetdimm = upnum;
break;
offsetdimm = lonum;
break;
}
if (!incloff) {
return (0);
}
/*
* We wish to calculate a dimm offset. In the paired case we will
* lookup the lodimm (see offsetdimm above).
*/
"to lookup dimm number property\n");
continue;
}
if (dnum == offsetdimm)
break;
}
"find dimm with number %d for offset calculation\n",
}
/*
* mc_pa_to_offset sets the offset to an invalid value if
* it hits an error.
*/
return (0);
}
/*
* We have translated a system address to a (node, chip-select). That
* identifies one (in 64-bit MC mode) or two (in 128-bit MC mode DIMMs,
* uncorrectable ChipKill error we can interpret the address alignment and
* syndrome to deduce whether we are on the lodimm or updimm.
*/
static int
{
"to lookup required properties\n");
}
/*
* In 64 bit mode only LO dimms are occupied.
*/
if (accwidth == 64) {
"therefore LO_DIMM\n");
return (LO_DIMM);
}
if (syndtype == AMD_SYNDTYPE_ECC) {
/*
* 64/8 ECC is checked separately for the upper and lower
* halves, so even an uncorrectable error is contained within
* one of the two halves. The error address is accurate to
* 8 bytes, so bit 4 distinguises upper from lower.
*/
"and PA 0x%llx is in %s half\n", pa,
}
/*
* ChipKill ECC (necessarily in 128-bit mode.
*/
/*
* A correctable ChipKill syndrome and we can tell
* which half the error was in from the symbol number.
*/
&check) == 0)
"ChipKill symbol %d (%s %d..%d), so LODIMM\n", sym,
return (LO_DIMM);
} else {
"ChipKill symbol %d (%s %d..%d), so UPDIMM\n", sym,
return (UP_DIMM);
}
} else {
/*
* An uncorrectable error while in ChipKill ECC mode - can't
* tell which dimm or dimms the errors lie within.
*/
"uncorrectable ChipKill, could be either LODIMM "
"or UPDIMM\n");
}
}
/*
* Brute-force BKDG pa to cs translation. The following is from BKDG 3.29
* so is for revisions prior to F. It is coded to look as much like the
* BKDG code as possible.
*/
static int
{
int which;
/*
* Variables as per BKDG
*/
int Ilog;
/*
* Additional variables which we need since we will reading
* MC properties instead of PCI config space, and the MC properties
* are stored in a cooked state.
*/
&prop_intlvsel)) {
"to lookup required properties\n");
}
/*
* Brute force deconstruction of the MC properties. If we decide to
* keep this then we need some of the mcamd.g defines available to us.
*/
DramBase &= 0xffff0000;
DramLimit |= 0x0000ffff;
HoleEn &= 0x00000001;
"SystemAddr 0x%x derived from PA 0x%llx is not in the "
"address range [0x%x, 0x%x] of MC %d\n",
}
if (IntlvEn) {
switch (IntlvEn) {
case 1:
Ilog = 1;
break;
case 3:
Ilog = 2;
break;
case 7:
Ilog = 3;
break;
default:
return (mcamd_set_errno(hdl,
}
} else {
/* not this node */
"Node interleaving, MC node %d not selected\n",
(int)mcnum);
}
} else {
/* No interleave */
}
InputAddr -= HoleOffset;
&prop_csbase) ||
&prop_csmask) ||
"failed to read cs properties\n");
}
CSBase &= 0xffe0fe00;
"match for chip select %d of MC %d\n", (int)csnum,
(int)mcnum);
syndtype)) < 0)
return (-1); /* errno is set for us */
/*
* The BKDG algorithm drops low-order bits that
* are unimportant in deriving chip-select but are
* perform offset calculation in this case.
*/
return (-1); /* errno is set for us */
return (0);
}
}
"for MC %d but no cs responds\n", (int)mcnum);
}
/*ARGSUSED*/
static int
{
int which;
#ifdef DEBUG
int bkdgres;
/*
* We perform the translation twice, once using the brute-force
* approach of the BKDG and again using a more elegant but more
* difficult to review against the BKDG approach. Note that both
* approaches need to change for rev F since it increases max CS
* size and so iaddr calculation etc changes.
*/
#endif
return (-1); /* errno is set for us */
break;
}
return (-1); /* errno is set for us */
return (-1); /* errno is set for us */
#ifdef DEBUG
#ifndef _KERNEL
/* offset is not checked - see note in BKDG algorithm */
#endif /* !_KERNEL */
#endif /* DEBUG */
return (0);
}
int
{
return (0);
break;
}
return (-1); /* errno is set for us */
}