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