Home | History | Annotate | Download | only in mc-amd
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  *
     21  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     22  * Use is subject to license terms.
     23  */
     24 
     25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     26 
     27 #include <mcamd_api.h>
     28 #include <mcamd_err.h>
     29 #include <mcamd_rowcol_impl.h>
     30 
     31 /*
     32  * Convenience structures to stash MC and CS properties in.
     33  */
     34 struct mcprops {
     35 	mcamd_prop_t num;		/* corresponding chip number */
     36 	mcamd_prop_t rev;		/* revision */
     37 	mcamd_prop_t width;		/* access width */
     38 	mcamd_prop_t base;		/* MC base address */
     39 	mcamd_prop_t lim;		/* MC limit address */
     40 	mcamd_prop_t csbnkmap_reg;	/* chip-select bank map */
     41 	mcamd_prop_t intlven;		/* Node-intlv mask */
     42 	mcamd_prop_t intlvsel;		/* Node-intlv selection for this node */
     43 	mcamd_prop_t csintlvfctr;	/* cs intlv factor on this node */
     44 	mcamd_prop_t bnkswzl;		/* bank-swizzle mode */
     45 	mcamd_prop_t sparecs;		/* spare cs#, if any */
     46 	mcamd_prop_t badcs;		/* substituted cs#, if any */
     47 };
     48 
     49 struct csprops {
     50 	mcamd_prop_t num;		/* chip-select number */
     51 	mcamd_prop_t base;		/* chip-select base address */
     52 	mcamd_prop_t mask;		/* chip-select mask */
     53 	mcamd_prop_t testfail;		/* marked testFail */
     54 	mcamd_prop_t dimmrank;		/* rank number on dimm(s) */
     55 };
     56 
     57 static int
     58 getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller,
     59     struct mcprops *pp)
     60 {
     61 	if (!mcamd_get_numprops(hdl,
     62 	    mc, MCAMD_PROP_NUM, &pp->num,
     63 	    mc, MCAMD_PROP_REV, &pp->rev,
     64 	    mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width,
     65 	    mc, MCAMD_PROP_BASE_ADDR, &pp->base,
     66 	    mc, MCAMD_PROP_LIM_ADDR, &pp->lim,
     67 	    mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg,
     68 	    mc, MCAMD_PROP_ILEN, &pp->intlven,
     69 	    mc, MCAMD_PROP_ILSEL, &pp->intlvsel,
     70 	    mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr,
     71 	    mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl,
     72 	    mc, MCAMD_PROP_SPARECS, &pp->sparecs,
     73 	    mc, MCAMD_PROP_BADCS, &pp->badcs,
     74 	    NULL)) {
     75 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc "
     76 		    "props for mc 0x%p\n", caller, mc);
     77 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
     78 	}
     79 
     80 	return (0);
     81 }
     82 
     83 static int
     84 getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
     85     struct csprops *csp)
     86 {
     87 	if (!mcamd_get_numprops(hdl,
     88 	    cs, MCAMD_PROP_NUM, &csp->num,
     89 	    cs, MCAMD_PROP_BASE_ADDR, &csp->base,
     90 	    cs, MCAMD_PROP_MASK, &csp->mask,
     91 	    cs, MCAMD_PROP_TESTFAIL, &csp->testfail,
     92 	    cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank,
     93 	    NULL))  {
     94 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs "
     95 		    "props for cs 0x%p\n", caller, cs);
     96 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
     97 	}
     98 
     99 	return (0);
    100 }
    101 
    102 static int
    103 gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
    104     const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp,
    105     const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid,
    106     const char *caller)
    107 {
    108 	uint_t rev = (uint_t)mcpp->rev;
    109 	int width = (int)mcpp->width;
    110 
    111 	if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) {
    112 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode "
    113 		    "table for MC rev %d csmode %d\n", caller, rev, csmode);
    114 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
    115 	}
    116 
    117 	if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) {
    118 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map "
    119 		    "table for MC rev %d csmode %d\n", caller,
    120 		    rev, csmode);
    121 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
    122 	}
    123 
    124 	if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) {
    125 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling "
    126 		    "table for MC rev %d width %d\n", caller, rev, width);
    127 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
    128 	}
    129 
    130 	if (csid) {
    131 		if (mcpp->csintlvfctr > 1) {
    132 			rct_csintlv_bits(rev, width, csmode,
    133 			    mcpp->csintlvfctr, csid);
    134 			if (csid->csi_factor == 0) {
    135 				mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: "
    136 				    "could not work out cs interleave "
    137 				    "paramters for MC rev %d, width %d, "
    138 				    "csmode %d, factor %d\n", caller,
    139 				    rev, width, csmode,
    140 				    (int)mcpp->csintlvfctr);
    141 				return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
    142 			}
    143 		} else {
    144 			csid->csi_factor = 0;
    145 		}
    146 	}
    147 
    148 	return (0);
    149 }
    150 
    151 static uint64_t
    152 iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what)
    153 {
    154 	uint64_t new = in | add;
    155 
    156 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx",
    157 	    what, in, add, new);
    158 
    159 	return (add);
    160 }
    161 
    162 /*
    163  * Where the number of row/col address bits is ambiguous (affects CG and
    164  * earlier only) we will assign the "floating" bit to row address.  If
    165  * we adopt the same convention in address reconstruction then all should work.
    166  */
    167 static uint32_t
    168 iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
    169     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr)
    170 {
    171 	uint32_t addr = 0;
    172 	int abitno, ibitno;
    173 	int nbits = bamp->bam_nrows;
    174 	int swapped = 0;
    175 
    176 	for (abitno = 0; abitno < nbits; abitno++) {
    177 		ibitno = rcbm->rcb_rowbit[abitno];
    178 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
    179 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
    180 			swapped++;
    181 		}
    182 		if (BITVAL(iaddr, ibitno) != 0)
    183 			SETBIT(addr, abitno);
    184 	}
    185 
    186 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> "
    187 	    "row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped);
    188 
    189 	return (addr);
    190 }
    191 
    192 /*ARGSUSED*/
    193 static uint64_t
    194 row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
    195     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr)
    196 {
    197 	uint64_t iaddr = 0;
    198 	int abitno, ibitno;
    199 	int nbits = bamp->bam_nrows;
    200 
    201 	for (abitno = 0; abitno < nbits; abitno++) {
    202 		if (BIT(rowaddr, abitno) == 0)
    203 			continue;
    204 		ibitno = rcbm->rcb_rowbit[abitno];
    205 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
    206 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
    207 		}
    208 		SETBIT(iaddr, ibitno);
    209 	}
    210 
    211 	return (iaddr);
    212 }
    213 
    214 
    215 static uint32_t
    216 iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
    217     const struct rct_rcbmap *rcbm, uint64_t iaddr)
    218 {
    219 	uint32_t addr = 0;
    220 	int abitno, ibitno, bias = 0;
    221 	int nbits = bamp->bam_ncols;
    222 
    223 	/*
    224 	 * Knock off a column bit if the numbers are ambiguous
    225 	 */
    226 	if (bamp->bam_ambig)
    227 		nbits--;
    228 
    229 	for (abitno = 0; abitno < nbits; abitno++) {
    230 		if (abitno == MC_PC_COLADDRBIT)
    231 			bias = 1;
    232 
    233 		ibitno = rcbm->rcb_colbit[abitno + bias];
    234 
    235 		if (BITVAL(iaddr, ibitno) != 0)
    236 			SETBIT(addr, abitno);
    237 	}
    238 
    239 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> "
    240 	    "col 0x%x\n", iaddr, addr);
    241 
    242 	return (addr);
    243 }
    244 
    245 /*ARGSUSED*/
    246 static uint64_t
    247 col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
    248     const struct rct_rcbmap *rcbm, uint32_t coladdr)
    249 {
    250 	uint64_t iaddr = 0;
    251 	int abitno, ibitno, bias = 0;
    252 	int nbits = bamp->bam_ncols;
    253 
    254 	/*
    255 	 * Knock off a column bit if the numbers are ambiguous
    256 	 */
    257 	if (bamp->bam_ambig)
    258 		nbits--;
    259 
    260 	for (abitno = 0; abitno < nbits; abitno++) {
    261 		if (BIT(coladdr, abitno) == 0)
    262 			continue;
    263 
    264 		if (abitno == MC_PC_COLADDRBIT)
    265 			bias = 1;
    266 
    267 		ibitno = rcbm->rcb_colbit[abitno + bias];
    268 		SETBIT(iaddr, ibitno);
    269 	}
    270 
    271 	return (iaddr);
    272 }
    273 
    274 /*
    275  * Extract bank bit arguments and swizzle if requested.
    276  */
    277 static uint32_t
    278 iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
    279     const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr)
    280 {
    281 	uint32_t addr = 0;
    282 	int abitno, ibitno, i;
    283 
    284 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
    285 		uint32_t val;
    286 
    287 		/*
    288 		 * rcb_bankbit[abitno] tells us which iaddr bit number
    289 		 * will form bit abitno of the bank address
    290 		 */
    291 		ibitno = rcbm->rcb_bankbit[abitno];
    292 		val = BITVAL(iaddr, ibitno);
    293 
    294 		/*
    295 		 * If bank swizzling is in operation then xor the bit value
    296 		 * obtained above with other iaddr bits.
    297 		 */
    298 		if (swzlp) {
    299 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
    300 				ibitno = swzlp->bswz_rowbits[abitno][i];
    301 				val ^= BITVAL(iaddr, ibitno);
    302 			}
    303 		}
    304 
    305 		if (val)
    306 			SETBIT(addr, abitno);
    307 	}
    308 
    309 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> "
    310 	    "bank 0x%x\n", iaddr, addr);
    311 
    312 	return (addr);
    313 }
    314 
    315 /*
    316  * bank_to_iaddr requires the iaddr reconstructed thus far with at least the
    317  * row bits repopulated.  That's because in bank swizzle mode
    318  * the bank bits are the result of xor'ing three original iaddr bits
    319  * together - two of which come from the row address and the third we
    320  * can reconstruct here.  Note that a zero bankaddr bit *can* result
    321  * in a nonzero iaddr bit (unlike in row and col reconstruction).
    322  */
    323 /*ARGSUSED*/
    324 static uint64_t
    325 bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
    326     const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr)
    327 {
    328 	uint64_t iaddr = 0;
    329 	int abitno, pibitno, i;
    330 
    331 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
    332 		uint32_t val = BITVAL(bankaddr, abitno);
    333 		if (swzlp) {
    334 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
    335 				pibitno = swzlp->bswz_rowbits[abitno][i];
    336 				val ^= BITVAL(partiaddr, pibitno);
    337 			}
    338 		}
    339 		if (val)
    340 			SETBIT(iaddr, rcbm->rcb_bankbit[abitno]);
    341 	}
    342 
    343 	return (iaddr);
    344 }
    345 
    346 static int
    347 iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
    348     uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp)
    349 {
    350 	const struct rct_bnkaddrmode *bamp;
    351 	const struct rct_rcbmap *rcbmp;
    352 	const struct rct_bnkswzlinfo *swzlp = NULL;
    353 	struct rct_csintlv csi;
    354 
    355 	if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp,
    356 	    mcpp->bnkswzl ? &swzlp : NULL, &csi,
    357 	    "iaddr_to_rcb") < 0)
    358 		return (-1);	/* errno already set */
    359 
    360 	*rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr);
    361 	*colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr);
    362 	*bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr);
    363 
    364 	return (0);
    365 }
    366 
    367 /*
    368  * Take a reconstructed InputAddr and undo the normalization described in
    369  * BKDG 3.29 3.4.4 to include the base address of the MC if no node
    370  * interleave or to insert the node interleave selection bits.
    371  */
    372 static int
    373 iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr,
    374     uint64_t *rsltp)
    375 {
    376 	uint64_t dramaddr;
    377 	int intlvbits;
    378 
    379 	switch (mcpp->intlven) {
    380 	case 0x0:
    381 		intlvbits = 0;
    382 		break;
    383 	case 0x1:
    384 		intlvbits = 1;
    385 		break;
    386 	case 0x3:
    387 		intlvbits = 2;
    388 		break;
    389 	case 0x7:
    390 		intlvbits = 3;
    391 		break;
    392 	default:
    393 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: "
    394 		    "illegal IntlvEn of %d for MC 0x%p\n",
    395 		    (int)mcpp->intlven, (int)mcpp->num);
    396 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
    397 	}
    398 
    399 	if (intlvbits != 0) {
    400 		/*
    401 		 * For a 2/4/8 way interleave iaddr was formed by excising
    402 		 * 1, 2, or 3 bits 12:12, 13:12, or 14:12 from dramaddr,
    403 		 * the removed bits having done their job by selecting the
    404 		 * responding node.  So we must move bits 35:12 of the
    405 		 * reconstructed iaddr up to make a 1, 2 or 3 bit hole and
    406 		 * then fill those bits with the current IntlvSel value for
    407 		 * this node.  The node base address must be zero if nodes
    408 		 * are interleaved.
    409 		 *
    410 		 * Note that the DRAM controller InputAddr is still 36 bits
    411 		 * 35:0 on rev F.
    412 		 */
    413 		dramaddr = (BITS(iaddr, 35, 12) << intlvbits) |
    414 		    (mcpp->intlvsel << 12) | BITS(iaddr, 11, 0);
    415 	} else {
    416 		dramaddr = iaddr + mcpp->base;
    417 	}
    418 
    419 	*rsltp = dramaddr;
    420 
    421 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx "
    422 	    "intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n",
    423 	    iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base,
    424 	    dramaddr);
    425 
    426 	return (0);
    427 }
    428 
    429 int
    430 mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
    431     uint64_t iaddr, uint64_t *offsetp)
    432 {
    433 	mcamd_dimm_offset_un_t offset_un;
    434 	uint_t csmode;
    435 	uint32_t bankaddr, rowaddr, coladdr;
    436 	struct mcprops mcp;
    437 	struct csprops csp;
    438 
    439 	*offsetp = MCAMD_RC_INVALID_OFFSET;
    440 
    441 	if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 ||
    442 	    getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0)
    443 		return (-1);	/* errno already set */
    444 
    445 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
    446 
    447 	if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr,
    448 	    &coladdr, &bankaddr) < 0)
    449 		return (-1);	/* errno already set */
    450 
    451 	offset_un.do_offset = 0;
    452 
    453 	offset_un.do_valid = 1;
    454 	offset_un.do_version = MCAMD_OFFSET_VERSION;
    455 	offset_un.do_rank = (uint32_t)csp.dimmrank;
    456 	offset_un.do_row = rowaddr;
    457 	offset_un.do_bank = bankaddr;
    458 	offset_un.do_col = coladdr;
    459 
    460 	*offsetp = offset_un.do_offset;
    461 
    462 	return (0);
    463 }
    464 
    465 /*
    466  * Given an MC, DIMM and offset (dimm rank, row, col, internal bank) we
    467  * find the corresponding chip-select for the rank and then reconstruct
    468  * a system address.  In the absence of serial number support it is possible
    469  * that we may be asked to perform this operation on a dimm which has been
    470  * swapped, perhaps even for a dimm of different size and number of ranks.
    471  * This may happen if fmadm repair has not been used.  There are some
    472  * unused bits in the offset and we could guard against this a little
    473  * by recording in those bit some of the physical characteristic of the
    474  * original DIMM such as size, number of ranks etc.
    475  */
    476 int
    477 mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
    478     uint64_t offset, uint64_t *pap)
    479 {
    480 	mcamd_node_t *cs;
    481 	mcamd_dimm_offset_un_t off_un;
    482 	uint32_t rank, rowaddr, bankaddr, coladdr;
    483 	uint64_t iaddr = 0;
    484 	const struct rct_bnkaddrmode *bamp;
    485 	const struct rct_rcbmap *rcbmp;
    486 	const struct rct_bnkswzlinfo *swzlp = NULL;
    487 	struct rct_csintlv csi;
    488 	struct mcprops mcp;
    489 	struct csprops csp;
    490 	uint64_t csmode;
    491 	int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo;
    492 
    493 	off_un.do_offset = offset;
    494 	rank = off_un.do_rank;
    495 	bankaddr = off_un.do_bank;
    496 	rowaddr = off_un.do_row;
    497 	coladdr = off_un.do_col;
    498 
    499 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx "
    500 	    "-> rank %d bank %d row 0x%x col 0x%x\n", offset,
    501 	    rank, bankaddr, rowaddr, coladdr);
    502 
    503 	if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0)
    504 		return (-1);	/* errno already set */
    505 
    506 	maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev);
    507 	maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev);
    508 	masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev);
    509 	masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev);
    510 
    511 	/*
    512 	 * Find the chip-select on this dimm using the given rank.
    513 	 */
    514 	for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL;
    515 	    cs = mcamd_cs_next(hdl, dimm, cs)) {
    516 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
    517 			return (-1);	/* errno already set */
    518 
    519 		if (csp.dimmrank == rank)
    520 			break;
    521 	}
    522 
    523 	if (cs == NULL) {
    524 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current "
    525 		    "dimm in this slot does not have a cs using rank %d\n",
    526 		    rank);
    527 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
    528 	}
    529 
    530 	/*
    531 	 * If the cs# has been substituted by the online spare then the
    532 	 * given unum is not actually contributing to the system address
    533 	 * map since all accesses to it are redirected.
    534 	 *
    535 	 * If the cs# failed BIOS test it is not in the address map.
    536 	 *
    537 	 * If the cs# is the online spare cs# then it is contributing to
    538 	 * the system address map only if swapped in, and the csbase etc
    539 	 * parameters to use must be those of the bad cs#.
    540 	 */
    541 	if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) {
    542 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
    543 	} else if (csp.testfail) {
    544 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
    545 	} else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs &&
    546 	    mcp.badcs != MC_INVALNUM) {
    547 		/*
    548 		 * Iterate over all cs# of this memory controller to find
    549 		 * the bad one - the bad cs# need not be on the same dimm
    550 		 * as the spare.
    551 		 */
    552 		for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
    553 		    cs = mcamd_cs_next(hdl, mc, cs)) {
    554 			mcamd_prop_t csnum;
    555 
    556 			if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM,
    557 			    &csnum)) {
    558 				mcamd_dprintf(hdl, MCAMD_DBG_ERR,
    559 				    "mcamd_offset_to_pa: csnum lookup failed "
    560 				    "while looking for bad cs#");
    561 				return (mcamd_set_errno(hdl,
    562 				    EMCAMD_TREEINVALID));
    563 			}
    564 			if (csnum == mcp.badcs)
    565 				break;
    566 		}
    567 
    568 		if (cs == NULL) {
    569 			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: "
    570 			    "failed to find cs for bad cs#%d\n", mcp.badcs);
    571 				return (mcamd_set_errno(hdl,
    572 				    EMCAMD_TREEINVALID));
    573 		}
    574 
    575 		/* found bad cs - reread properties from it instead of spare */
    576 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
    577 			return (-1);	/* errno already set */
    578 	}
    579 
    580 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
    581 
    582 	if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp,
    583 	    mcp.bnkswzl ? &swzlp : NULL, &csi,
    584 	    "mc_offset_to_pa") < 0)
    585 		return (-1);	/* errno already set */
    586 
    587 	/*
    588 	 * If there are umaskable DRAM InputAddr bits the add those bits
    589 	 * to iaddr from the cs base address.
    590 	 */
    591 	if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) {
    592 		iaddr |= iaddr_add(hdl, iaddr,
    593 		    BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev),
    594 		    maskhi_hi + 1), "unmaskable cs basehi bits");
    595 	}
    596 
    597 	/*
    598 	 * basehi bits not meing masked pass straight through to the
    599 	 * iaddr.
    600 	 */
    601 	iaddr |= iaddr_add(hdl, iaddr,
    602 	    BITS(csp.base, maskhi_hi, maskhi_lo) &
    603 	    ~BITS(csp.mask, maskhi_hi, maskhi_lo),
    604 	    "cs basehi bits not being masked");
    605 
    606 	/*
    607 	 * if cs interleaving is active then baselo address bit are being
    608 	 * masked - pass the rest through.
    609 	 */
    610 	if (mcp.csintlvfctr > 1) {
    611 		iaddr |= iaddr_add(hdl, iaddr,
    612 		    BITS(csp.base, masklo_hi, masklo_lo) &
    613 		    ~BITS(csp.mask, masklo_hi, masklo_lo),
    614 		    "cs baselo bits not being masked");
    615 	}
    616 
    617 	/*
    618 	 * Reconstruct iaddr bits from known row address
    619 	 */
    620 	iaddr |= iaddr_add(hdl, iaddr,
    621 	    row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr),
    622 	    "add iaddr bits from row");
    623 
    624 	/*
    625 	 * Reconstruct iaddr bits from known column address
    626 	 */
    627 	iaddr |= iaddr_add(hdl, iaddr,
    628 	    col_to_iaddr(hdl, bamp, rcbmp, coladdr),
    629 	    "add iaddr bits from col");
    630 
    631 	/*
    632 	 * Reconstruct iaddr bits from known internal banksel address
    633 	 */
    634 	iaddr |= iaddr_add(hdl, iaddr,
    635 	    bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr),
    636 	    "add iaddr bits from bank");
    637 
    638 	/*
    639 	 * Move iaddr up into the range for this MC and insert any
    640 	 * node interleave selection bits.
    641 	 */
    642 	if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0)
    643 		return (-1);	/* errno already set */
    644 
    645 	return (0);
    646 }
    647 
    648 int
    649 mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp)
    650 {
    651 	uint_t csmode;
    652 	struct mcprops mcp;
    653 	const struct rct_bnkaddrmode *bamp;
    654 
    655 	if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0)
    656 		return (-1);	/* errno already set */
    657 
    658 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum);
    659 
    660 	if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL,
    661 	    "mcamd_cs_size") < 0)
    662 		return (-1);	/* errno already set */
    663 
    664 	*szp = MC_CS_SIZE(bamp, mcp.width);
    665 
    666 	return (0);
    667 }
    668