xref: /illumos-gate/usr/src/common/mc/mc-amd/mcamd_rowcol.c (revision 503609a9497e27f206d815a06ce90a747d2ce573)
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