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 */
36struct 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
51struct 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
59static int
60getmcprops(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
85static int
86getcsprops(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
104static int
105gettbls(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
153static uint64_t
154iaddr_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 */
169static uint32_t
170iaddr_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*/
195static uint64_t
196row_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
217static uint32_t
218iaddr_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*/
248static uint64_t
249col_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 */
279static uint32_t
280iaddr_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*/
326static uint64_t
327bank_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
348static int
349iaddr_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 */
374static int
375iaddr_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
431int
432mc_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 */
478int
479mc_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
649int
650mcamd_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