xref: /onnv-gate/usr/src/common/mc/mc-amd/mcamd_rowcol.c (revision 5254:38162db71c7d)
11414Scindi /*
21414Scindi  * CDDL HEADER START
31414Scindi  *
41414Scindi  * The contents of this file are subject to the terms of the
52869Sgavinm  * Common Development and Distribution License (the "License").
62869Sgavinm  * You may not use this file except in compliance with the License.
71414Scindi  *
81414Scindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91414Scindi  * or http://www.opensolaris.org/os/licensing.
101414Scindi  * See the License for the specific language governing permissions
111414Scindi  * and limitations under the License.
121414Scindi  *
131414Scindi  * When distributing Covered Code, include this CDDL HEADER in each
141414Scindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151414Scindi  * If applicable, add the following below this CDDL HEADER, with the
161414Scindi  * fields enclosed by brackets "[]" replaced with your own identifying
171414Scindi  * information: Portions Copyright [yyyy] [name of copyright owner]
181414Scindi  *
191414Scindi  * CDDL HEADER END
201414Scindi  *
21*5254Sgavinm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
221414Scindi  * Use is subject to license terms.
231414Scindi  */
241414Scindi 
251414Scindi #pragma ident	"%Z%%M%	%I%	%E% SMI"
261414Scindi 
271414Scindi #include <mcamd_api.h>
281414Scindi #include <mcamd_err.h>
291414Scindi #include <mcamd_rowcol_impl.h>
301414Scindi 
311414Scindi /*
322869Sgavinm  * Convenience structures to stash MC and CS properties in.
331414Scindi  */
342869Sgavinm struct mcprops {
352869Sgavinm 	mcamd_prop_t num;		/* corresponding chip number */
362869Sgavinm 	mcamd_prop_t rev;		/* revision */
372869Sgavinm 	mcamd_prop_t width;		/* access width */
382869Sgavinm 	mcamd_prop_t base;		/* MC base address */
392869Sgavinm 	mcamd_prop_t lim;		/* MC limit address */
402869Sgavinm 	mcamd_prop_t csbnkmap_reg;	/* chip-select bank map */
412869Sgavinm 	mcamd_prop_t intlven;		/* Node-intlv mask */
422869Sgavinm 	mcamd_prop_t intlvsel;		/* Node-intlv selection for this node */
432869Sgavinm 	mcamd_prop_t csintlvfctr;	/* cs intlv factor on this node */
442869Sgavinm 	mcamd_prop_t bnkswzl;		/* bank-swizzle mode */
452869Sgavinm 	mcamd_prop_t sparecs;		/* spare cs#, if any */
462869Sgavinm 	mcamd_prop_t badcs;		/* substituted cs#, if any */
471414Scindi };
481414Scindi 
492869Sgavinm struct csprops {
502869Sgavinm 	mcamd_prop_t num;		/* chip-select number */
512869Sgavinm 	mcamd_prop_t base;		/* chip-select base address */
522869Sgavinm 	mcamd_prop_t mask;		/* chip-select mask */
532869Sgavinm 	mcamd_prop_t testfail;		/* marked testFail */
542869Sgavinm 	mcamd_prop_t dimmrank;		/* rank number on dimm(s) */
551414Scindi };
561414Scindi 
571414Scindi static int
getmcprops(struct mcamd_hdl * hdl,mcamd_node_t * mc,const char * caller,struct mcprops * pp)581414Scindi getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller,
592869Sgavinm     struct mcprops *pp)
601414Scindi {
612869Sgavinm 	if (!mcamd_get_numprops(hdl,
622869Sgavinm 	    mc, MCAMD_PROP_NUM, &pp->num,
632869Sgavinm 	    mc, MCAMD_PROP_REV, &pp->rev,
642869Sgavinm 	    mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width,
652869Sgavinm 	    mc, MCAMD_PROP_BASE_ADDR, &pp->base,
662869Sgavinm 	    mc, MCAMD_PROP_LIM_ADDR, &pp->lim,
672869Sgavinm 	    mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg,
682869Sgavinm 	    mc, MCAMD_PROP_ILEN, &pp->intlven,
692869Sgavinm 	    mc, MCAMD_PROP_ILSEL, &pp->intlvsel,
702869Sgavinm 	    mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr,
712869Sgavinm 	    mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl,
722869Sgavinm 	    mc, MCAMD_PROP_SPARECS, &pp->sparecs,
732869Sgavinm 	    mc, MCAMD_PROP_BADCS, &pp->badcs,
742869Sgavinm 	    NULL)) {
751414Scindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc "
761414Scindi 		    "props for mc 0x%p\n", caller, mc);
771414Scindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
781414Scindi 	}
791414Scindi 
801414Scindi 	return (0);
811414Scindi }
821414Scindi 
831414Scindi static int
getcsprops(struct mcamd_hdl * hdl,mcamd_node_t * cs,const char * caller,struct csprops * csp)841414Scindi getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
852869Sgavinm     struct csprops *csp)
861414Scindi {
872869Sgavinm 	if (!mcamd_get_numprops(hdl,
882869Sgavinm 	    cs, MCAMD_PROP_NUM, &csp->num,
892869Sgavinm 	    cs, MCAMD_PROP_BASE_ADDR, &csp->base,
902869Sgavinm 	    cs, MCAMD_PROP_MASK, &csp->mask,
912869Sgavinm 	    cs, MCAMD_PROP_TESTFAIL, &csp->testfail,
922869Sgavinm 	    cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank,
932869Sgavinm 	    NULL))  {
941414Scindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs "
951414Scindi 		    "props for cs 0x%p\n", caller, cs);
961414Scindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
97*5254Sgavinm 	}
981414Scindi 
991414Scindi 	return (0);
1001414Scindi }
1011414Scindi 
1021414Scindi static int
gettbls(struct mcamd_hdl * hdl,uint_t csmode,struct mcprops * mcpp,const struct rct_bnkaddrmode ** bamp,const struct rct_rcbmap ** rcbmp,const struct rct_bnkswzlinfo ** swzlp,struct rct_csintlv * csid,const char * caller)1032869Sgavinm gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
1042869Sgavinm     const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp,
1052869Sgavinm     const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid,
1062869Sgavinm     const char *caller)
1071414Scindi {
1082869Sgavinm 	uint_t rev = (uint_t)mcpp->rev;
1092869Sgavinm 	int width = (int)mcpp->width;
1102869Sgavinm 
1112869Sgavinm 	if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) {
1121414Scindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode "
1132869Sgavinm 		    "table for MC rev %d csmode %d\n", caller, rev, csmode);
1141414Scindi 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1151414Scindi 	}
1161414Scindi 
1172869Sgavinm 	if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) {
1181414Scindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map "
1191414Scindi 		    "table for MC rev %d csmode %d\n", caller,
1202869Sgavinm 		    rev, csmode);
1212869Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1222869Sgavinm 	}
1232869Sgavinm 
1242869Sgavinm 	if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) {
1252869Sgavinm 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling "
1262869Sgavinm 		    "table for MC rev %d width %d\n", caller, rev, width);
1271414Scindi 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1281414Scindi 	}
1291414Scindi 
1301414Scindi 	if (csid) {
1312869Sgavinm 		if (mcpp->csintlvfctr > 1) {
1322869Sgavinm 			rct_csintlv_bits(rev, width, csmode,
1331414Scindi 			    mcpp->csintlvfctr, csid);
1341414Scindi 			if (csid->csi_factor == 0) {
1351414Scindi 				mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: "
1361414Scindi 				    "could not work out cs interleave "
1371414Scindi 				    "paramters for MC rev %d, width %d, "
1381414Scindi 				    "csmode %d, factor %d\n", caller,
1392869Sgavinm 				    rev, width, csmode,
1401414Scindi 				    (int)mcpp->csintlvfctr);
1411414Scindi 				return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1421414Scindi 			}
1431414Scindi 		} else {
1441414Scindi 			csid->csi_factor = 0;
1451414Scindi 		}
1461414Scindi 	}
1471414Scindi 
1481414Scindi 	return (0);
1491414Scindi }
1501414Scindi 
1511414Scindi static uint64_t
iaddr_add(struct mcamd_hdl * hdl,uint64_t in,uint64_t add,const char * what)1521414Scindi iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what)
1531414Scindi {
1541414Scindi 	uint64_t new = in | add;
1551414Scindi 
1561414Scindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx",
1571414Scindi 	    what, in, add, new);
1581414Scindi 
1591414Scindi 	return (add);
1601414Scindi }
1611414Scindi 
1621414Scindi /*
1631414Scindi  * Where the number of row/col address bits is ambiguous (affects CG and
1641414Scindi  * earlier only) we will assign the "floating" bit to row address.  If
1651414Scindi  * we adopt the same convention in address reconstruction then all should work.
1661414Scindi  */
1671414Scindi static uint32_t
iaddr_to_row(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,struct rct_csintlv * csid,uint64_t iaddr)1682869Sgavinm iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
1692869Sgavinm     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr)
1701414Scindi {
1711414Scindi 	uint32_t addr = 0;
1721414Scindi 	int abitno, ibitno;
1731414Scindi 	int nbits = bamp->bam_nrows;
1741414Scindi 	int swapped = 0;
1751414Scindi 
1761414Scindi 	for (abitno = 0; abitno < nbits; abitno++) {
1772869Sgavinm 		ibitno = rcbm->rcb_rowbit[abitno];
1781414Scindi 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
1791414Scindi 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
1801414Scindi 			swapped++;
1811414Scindi 		}
1822869Sgavinm 		if (BITVAL(iaddr, ibitno) != 0)
1832869Sgavinm 			SETBIT(addr, abitno);
1841414Scindi 	}
1851414Scindi 
1861414Scindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> "
1871414Scindi 	    "row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped);
1881414Scindi 
1891414Scindi 	return (addr);
1901414Scindi }
1911414Scindi 
1921414Scindi /*ARGSUSED*/
1931414Scindi static uint64_t
row_to_iaddr(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,struct rct_csintlv * csid,uint32_t rowaddr)1942869Sgavinm row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
1952869Sgavinm     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr)
1961414Scindi {
1971414Scindi 	uint64_t iaddr = 0;
1981414Scindi 	int abitno, ibitno;
1991414Scindi 	int nbits = bamp->bam_nrows;
2001414Scindi 
2011414Scindi 	for (abitno = 0; abitno < nbits; abitno++) {
2021414Scindi 		if (BIT(rowaddr, abitno) == 0)
2031414Scindi 			continue;
2042869Sgavinm 		ibitno = rcbm->rcb_rowbit[abitno];
2051414Scindi 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
2061414Scindi 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
2071414Scindi 		}
2081414Scindi 		SETBIT(iaddr, ibitno);
2091414Scindi 	}
2101414Scindi 
2111414Scindi 	return (iaddr);
2121414Scindi }
2131414Scindi 
2141414Scindi 
2151414Scindi static uint32_t
iaddr_to_col(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,uint64_t iaddr)2162869Sgavinm iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
2172869Sgavinm     const struct rct_rcbmap *rcbm, uint64_t iaddr)
2181414Scindi {
2191414Scindi 	uint32_t addr = 0;
2201414Scindi 	int abitno, ibitno, bias = 0;
2211414Scindi 	int nbits = bamp->bam_ncols;
2221414Scindi 
2231414Scindi 	/*
2241414Scindi 	 * Knock off a column bit if the numbers are ambiguous
2251414Scindi 	 */
2261414Scindi 	if (bamp->bam_ambig)
2271414Scindi 		nbits--;
2281414Scindi 
2291414Scindi 	for (abitno = 0; abitno < nbits; abitno++) {
2301414Scindi 		if (abitno == MC_PC_COLADDRBIT)
2311414Scindi 			bias = 1;
2321414Scindi 
2332869Sgavinm 		ibitno = rcbm->rcb_colbit[abitno + bias];
2341414Scindi 
2352869Sgavinm 		if (BITVAL(iaddr, ibitno) != 0)
2361414Scindi 			SETBIT(addr, abitno);
2371414Scindi 	}
2381414Scindi 
2391414Scindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> "
2401414Scindi 	    "col 0x%x\n", iaddr, addr);
2411414Scindi 
2421414Scindi 	return (addr);
2431414Scindi }
2441414Scindi 
2451414Scindi /*ARGSUSED*/
2461414Scindi static uint64_t
col_to_iaddr(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,uint32_t coladdr)2472869Sgavinm col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
2482869Sgavinm     const struct rct_rcbmap *rcbm, uint32_t coladdr)
2491414Scindi {
2501414Scindi 	uint64_t iaddr = 0;
2511414Scindi 	int abitno, ibitno, bias = 0;
2521414Scindi 	int nbits = bamp->bam_ncols;
2531414Scindi 
2541414Scindi 	/*
2551414Scindi 	 * Knock off a column bit if the numbers are ambiguous
2561414Scindi 	 */
2571414Scindi 	if (bamp->bam_ambig)
2581414Scindi 		nbits--;
2591414Scindi 
2601414Scindi 	for (abitno = 0; abitno < nbits; abitno++) {
2611414Scindi 		if (BIT(coladdr, abitno) == 0)
2621414Scindi 			continue;
2631414Scindi 
2641414Scindi 		if (abitno == MC_PC_COLADDRBIT)
2651414Scindi 			bias = 1;
2661414Scindi 
2672869Sgavinm 		ibitno = rcbm->rcb_colbit[abitno + bias];
2681414Scindi 		SETBIT(iaddr, ibitno);
2691414Scindi 	}
2701414Scindi 
2711414Scindi 	return (iaddr);
2721414Scindi }
2731414Scindi 
2741414Scindi /*
2752869Sgavinm  * Extract bank bit arguments and swizzle if requested.
2761414Scindi  */
2771414Scindi static uint32_t
iaddr_to_bank(struct mcamd_hdl * hdl,const struct rct_rcbmap * rcbm,const struct rct_bnkswzlinfo * swzlp,uint64_t iaddr)2782869Sgavinm iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
2792869Sgavinm     const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr)
2801414Scindi {
2811414Scindi 	uint32_t addr = 0;
2821414Scindi 	int abitno, ibitno, i;
2832869Sgavinm 
2842869Sgavinm 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
2852869Sgavinm 		uint32_t val;
2862869Sgavinm 
2872869Sgavinm 		/*
2882869Sgavinm 		 * rcb_bankbit[abitno] tells us which iaddr bit number
2892869Sgavinm 		 * will form bit abitno of the bank address
2902869Sgavinm 		 */
2912869Sgavinm 		ibitno = rcbm->rcb_bankbit[abitno];
2922869Sgavinm 		val = BITVAL(iaddr, ibitno);
2931414Scindi 
2942869Sgavinm 		/*
2952869Sgavinm 		 * If bank swizzling is in operation then xor the bit value
2962869Sgavinm 		 * obtained above with other iaddr bits.
2972869Sgavinm 		 */
2982869Sgavinm 		if (swzlp) {
2992869Sgavinm 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
3002869Sgavinm 				ibitno = swzlp->bswz_rowbits[abitno][i];
3012869Sgavinm 				val ^= BITVAL(iaddr, ibitno);
3022869Sgavinm 			}
3031414Scindi 		}
3042869Sgavinm 
3052869Sgavinm 		if (val)
3062869Sgavinm 			SETBIT(addr, abitno);
3071414Scindi 	}
3081414Scindi 
3091414Scindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> "
3101414Scindi 	    "bank 0x%x\n", iaddr, addr);
3111414Scindi 
3121414Scindi 	return (addr);
3131414Scindi }
3141414Scindi 
3151414Scindi /*
3161414Scindi  * bank_to_iaddr requires the iaddr reconstructed thus far with at least the
3171414Scindi  * row bits repopulated.  That's because in bank swizzle mode
3181414Scindi  * the bank bits are the result of xor'ing three original iaddr bits
3191414Scindi  * together - two of which come from the row address and the third we
3201414Scindi  * can reconstruct here.  Note that a zero bankaddr bit *can* result
3211414Scindi  * in a nonzero iaddr bit (unlike in row and col reconstruction).
3221414Scindi  */
3231414Scindi /*ARGSUSED*/
3241414Scindi static uint64_t
bank_to_iaddr(struct mcamd_hdl * hdl,const struct rct_rcbmap * rcbm,const struct rct_bnkswzlinfo * swzlp,uint64_t partiaddr,uint32_t bankaddr)3252869Sgavinm bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
3262869Sgavinm     const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr)
3271414Scindi {
3281414Scindi 	uint64_t iaddr = 0;
3291414Scindi 	int abitno, pibitno, i;
3301414Scindi 
3312869Sgavinm 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
3321414Scindi 		uint32_t val = BITVAL(bankaddr, abitno);
3332869Sgavinm 		if (swzlp) {
3342869Sgavinm 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
3352869Sgavinm 				pibitno = swzlp->bswz_rowbits[abitno][i];
3361414Scindi 				val ^= BITVAL(partiaddr, pibitno);
3371414Scindi 			}
3381414Scindi 		}
3391414Scindi 		if (val)
3402869Sgavinm 			SETBIT(iaddr, rcbm->rcb_bankbit[abitno]);
3411414Scindi 	}
3421414Scindi 
3431414Scindi 	return (iaddr);
3441414Scindi }
3451414Scindi 
3461414Scindi static int
iaddr_to_rcb(struct mcamd_hdl * hdl,uint_t csmode,struct mcprops * mcpp,uint64_t iaddr,uint32_t * rowp,uint32_t * colp,uint32_t * bankp)3472869Sgavinm iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
3481414Scindi     uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp)
3491414Scindi {
3502869Sgavinm 	const struct rct_bnkaddrmode *bamp;
3512869Sgavinm 	const struct rct_rcbmap *rcbmp;
3522869Sgavinm 	const struct rct_bnkswzlinfo *swzlp = NULL;
3532869Sgavinm 	struct rct_csintlv csi;
3541414Scindi 
3552869Sgavinm 	if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp,
3562869Sgavinm 	    mcpp->bnkswzl ? &swzlp : NULL, &csi,
3572869Sgavinm 	    "iaddr_to_rcb") < 0)
3581414Scindi 		return (-1);	/* errno already set */
3591414Scindi 
3602869Sgavinm 	*rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr);
3612869Sgavinm 	*colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr);
3622869Sgavinm 	*bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr);
3631414Scindi 
3641414Scindi 	return (0);
3651414Scindi }
3661414Scindi 
3671414Scindi /*
3681414Scindi  * Take a reconstructed InputAddr and undo the normalization described in
3691414Scindi  * BKDG 3.29 3.4.4 to include the base address of the MC if no node
3701414Scindi  * interleave or to insert the node interleave selection bits.
3711414Scindi  */
3721414Scindi static int
iaddr_unnormalize(struct mcamd_hdl * hdl,struct mcprops * mcpp,uint64_t iaddr,uint64_t * rsltp)3732869Sgavinm iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr,
3741414Scindi     uint64_t *rsltp)
3751414Scindi {
3761414Scindi 	uint64_t dramaddr;
3771414Scindi 	int intlvbits;
3781414Scindi 
3791414Scindi 	switch (mcpp->intlven) {
3801414Scindi 	case 0x0:
3811414Scindi 		intlvbits = 0;
3821414Scindi 		break;
3831414Scindi 	case 0x1:
3841414Scindi 		intlvbits = 1;
3851414Scindi 		break;
3861414Scindi 	case 0x3:
3871414Scindi 		intlvbits = 2;
3881414Scindi 		break;
3891414Scindi 	case 0x7:
3901414Scindi 		intlvbits = 3;
3911414Scindi 		break;
3921414Scindi 	default:
3931414Scindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: "
3941414Scindi 		    "illegal IntlvEn of %d for MC 0x%p\n",
3951414Scindi 		    (int)mcpp->intlven, (int)mcpp->num);
3961414Scindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
3971414Scindi 	}
3981414Scindi 
3991414Scindi 	if (intlvbits != 0) {
4001414Scindi 		/*
4011414Scindi 		 * For a 2/4/8 way interleave iaddr was formed by excising
4021414Scindi 		 * 1, 2, or 3 bits 12:12, 13:12, or 14:12 from dramaddr,
4031414Scindi 		 * the removed bits having done their job by selecting the
4041414Scindi 		 * responding node.  So we must move bits 35:12 of the
4051414Scindi 		 * reconstructed iaddr up to make a 1, 2 or 3 bit hole and
4061414Scindi 		 * then fill those bits with the current IntlvSel value for
4071414Scindi 		 * this node.  The node base address must be zero if nodes
4081414Scindi 		 * are interleaved.
4092869Sgavinm 		 *
4102869Sgavinm 		 * Note that the DRAM controller InputAddr is still 36 bits
4112869Sgavinm 		 * 35:0 on rev F.
4121414Scindi 		 */
4131414Scindi 		dramaddr = (BITS(iaddr, 35, 12) << intlvbits) |
4141414Scindi 		    (mcpp->intlvsel << 12) | BITS(iaddr, 11, 0);
4151414Scindi 	} else {
4161414Scindi 		dramaddr = iaddr + mcpp->base;
4171414Scindi 	}
4181414Scindi 
4191414Scindi 	*rsltp = dramaddr;
4201414Scindi 
4211414Scindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx "
4221414Scindi 	    "intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n",
4231414Scindi 	    iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base,
4241414Scindi 	    dramaddr);
4251414Scindi 
4261414Scindi 	return (0);
4271414Scindi }
4281414Scindi 
4291414Scindi int
mc_pa_to_offset(struct mcamd_hdl * hdl,mcamd_node_t * mc,mcamd_node_t * cs,uint64_t iaddr,uint64_t * offsetp)4301414Scindi mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
4312869Sgavinm     uint64_t iaddr, uint64_t *offsetp)
4321414Scindi {
4331414Scindi 	mcamd_dimm_offset_un_t offset_un;
4341414Scindi 	uint_t csmode;
4351414Scindi 	uint32_t bankaddr, rowaddr, coladdr;
4362869Sgavinm 	struct mcprops mcp;
4372869Sgavinm 	struct csprops csp;
4381414Scindi 
4391414Scindi 	*offsetp = MCAMD_RC_INVALID_OFFSET;
4401414Scindi 
4411414Scindi 	if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 ||
4421414Scindi 	    getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0)
4431414Scindi 		return (-1);	/* errno already set */
4441414Scindi 
4452869Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
4461414Scindi 
4471414Scindi 	if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr,
4481414Scindi 	    &coladdr, &bankaddr) < 0)
4491414Scindi 		return (-1);	/* errno already set */
4501414Scindi 
4511414Scindi 	offset_un.do_offset = 0;
4521414Scindi 
4531414Scindi 	offset_un.do_valid = 1;
4541414Scindi 	offset_un.do_version = MCAMD_OFFSET_VERSION;
455*5254Sgavinm 	offset_un.do_rank = (uint32_t)csp.dimmrank;
4561414Scindi 	offset_un.do_row = rowaddr;
4571414Scindi 	offset_un.do_bank = bankaddr;
4581414Scindi 	offset_un.do_col = coladdr;
4591414Scindi 
4601414Scindi 	*offsetp = offset_un.do_offset;
4611414Scindi 
4621414Scindi 	return (0);
4631414Scindi }
4641414Scindi 
4651414Scindi /*
4662869Sgavinm  * Given an MC, DIMM and offset (dimm rank, row, col, internal bank) we
4671414Scindi  * find the corresponding chip-select for the rank and then reconstruct
4681414Scindi  * a system address.  In the absence of serial number support it is possible
4691414Scindi  * that we may be asked to perform this operation on a dimm which has been
4701414Scindi  * swapped, perhaps even for a dimm of different size and number of ranks.
4711414Scindi  * This may happen if fmadm repair has not been used.  There are some
4721414Scindi  * unused bits in the offset and we could guard against this a little
4731414Scindi  * by recording in those bit some of the physical characteristic of the
4741414Scindi  * original DIMM such as size, number of ranks etc.
4751414Scindi  */
4761414Scindi int
mc_offset_to_pa(struct mcamd_hdl * hdl,mcamd_node_t * mc,mcamd_node_t * dimm,uint64_t offset,uint64_t * pap)4771414Scindi mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
4781414Scindi     uint64_t offset, uint64_t *pap)
4791414Scindi {
4801414Scindi 	mcamd_node_t *cs;
4811414Scindi 	mcamd_dimm_offset_un_t off_un;
4821414Scindi 	uint32_t rank, rowaddr, bankaddr, coladdr;
4831414Scindi 	uint64_t iaddr = 0;
4842869Sgavinm 	const struct rct_bnkaddrmode *bamp;
4852869Sgavinm 	const struct rct_rcbmap *rcbmp;
4862869Sgavinm 	const struct rct_bnkswzlinfo *swzlp = NULL;
4872869Sgavinm 	struct rct_csintlv csi;
4882869Sgavinm 	struct mcprops mcp;
4892869Sgavinm 	struct csprops csp;
4901414Scindi 	uint64_t csmode;
4912869Sgavinm 	int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo;
4921414Scindi 
4931414Scindi 	off_un.do_offset = offset;
4941414Scindi 	rank = off_un.do_rank;
4951414Scindi 	bankaddr = off_un.do_bank;
4961414Scindi 	rowaddr = off_un.do_row;
4971414Scindi 	coladdr = off_un.do_col;
4981414Scindi 
4992869Sgavinm 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx "
5002869Sgavinm 	    "-> rank %d bank %d row 0x%x col 0x%x\n", offset,
5012869Sgavinm 	    rank, bankaddr, rowaddr, coladdr);
5022869Sgavinm 
5031414Scindi 	if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0)
5041414Scindi 		return (-1);	/* errno already set */
5051414Scindi 
5062869Sgavinm 	maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev);
5072869Sgavinm 	maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev);
5082869Sgavinm 	masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev);
5092869Sgavinm 	masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev);
5102869Sgavinm 
5111414Scindi 	/*
5122869Sgavinm 	 * Find the chip-select on this dimm using the given rank.
5131414Scindi 	 */
5142869Sgavinm 	for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL;
5152869Sgavinm 	    cs = mcamd_cs_next(hdl, dimm, cs)) {
5162869Sgavinm 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
5172869Sgavinm 			return (-1);	/* errno already set */
5182869Sgavinm 
5192869Sgavinm 		if (csp.dimmrank == rank)
5202869Sgavinm 			break;
5211414Scindi 	}
5222869Sgavinm 
5232869Sgavinm 	if (cs == NULL) {
5241414Scindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current "
5252869Sgavinm 		    "dimm in this slot does not have a cs using rank %d\n",
5261414Scindi 		    rank);
5271414Scindi 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5281414Scindi 	}
5291414Scindi 
5302869Sgavinm 	/*
5312869Sgavinm 	 * If the cs# has been substituted by the online spare then the
5322869Sgavinm 	 * given unum is not actually contributing to the system address
5332869Sgavinm 	 * map since all accesses to it are redirected.
5342869Sgavinm 	 *
5352869Sgavinm 	 * If the cs# failed BIOS test it is not in the address map.
5362869Sgavinm 	 *
5372869Sgavinm 	 * If the cs# is the online spare cs# then it is contributing to
5382869Sgavinm 	 * the system address map only if swapped in, and the csbase etc
5392869Sgavinm 	 * parameters to use must be those of the bad cs#.
5402869Sgavinm 	 */
5412869Sgavinm 	if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) {
5422869Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5432869Sgavinm 	} else if (csp.testfail) {
5442869Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5452869Sgavinm 	} else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs &&
5462869Sgavinm 	    mcp.badcs != MC_INVALNUM) {
5472869Sgavinm 		/*
5482869Sgavinm 		 * Iterate over all cs# of this memory controller to find
5492869Sgavinm 		 * the bad one - the bad cs# need not be on the same dimm
5502869Sgavinm 		 * as the spare.
5512869Sgavinm 		 */
5522869Sgavinm 		for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
5532869Sgavinm 		    cs = mcamd_cs_next(hdl, mc, cs)) {
5542869Sgavinm 			mcamd_prop_t csnum;
5551414Scindi 
5562869Sgavinm 			if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM,
5572869Sgavinm 			    &csnum)) {
5582869Sgavinm 				mcamd_dprintf(hdl, MCAMD_DBG_ERR,
5592869Sgavinm 				    "mcamd_offset_to_pa: csnum lookup failed "
5602869Sgavinm 				    "while looking for bad cs#");
5612869Sgavinm 				return (mcamd_set_errno(hdl,
5622869Sgavinm 				    EMCAMD_TREEINVALID));
5632869Sgavinm 			}
5642869Sgavinm 			if (csnum == mcp.badcs)
5652869Sgavinm 				break;
5662869Sgavinm 		}
5671414Scindi 
5682869Sgavinm 		if (cs == NULL) {
5692869Sgavinm 			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: "
5702869Sgavinm 			    "failed to find cs for bad cs#%d\n", mcp.badcs);
5712869Sgavinm 				return (mcamd_set_errno(hdl,
5722869Sgavinm 				    EMCAMD_TREEINVALID));
5732869Sgavinm 		}
5742869Sgavinm 
5752869Sgavinm 		/* found bad cs - reread properties from it instead of spare */
5762869Sgavinm 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
5772869Sgavinm 			return (-1);	/* errno already set */
5782869Sgavinm 	}
5792869Sgavinm 
5802869Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
5812869Sgavinm 
5822869Sgavinm 	if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp,
5832869Sgavinm 	    mcp.bnkswzl ? &swzlp : NULL, &csi,
5841414Scindi 	    "mc_offset_to_pa") < 0)
5851414Scindi 		return (-1);	/* errno already set */
5861414Scindi 
5872869Sgavinm 	/*
5882869Sgavinm 	 * If there are umaskable DRAM InputAddr bits the add those bits
5892869Sgavinm 	 * to iaddr from the cs base address.
5902869Sgavinm 	 */
5912869Sgavinm 	if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) {
5921414Scindi 		iaddr |= iaddr_add(hdl, iaddr,
5932869Sgavinm 		    BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev),
5941414Scindi 		    maskhi_hi + 1), "unmaskable cs basehi bits");
5951414Scindi 	}
5961414Scindi 
5972869Sgavinm 	/*
5982869Sgavinm 	 * basehi bits not meing masked pass straight through to the
5992869Sgavinm 	 * iaddr.
6002869Sgavinm 	 */
6011414Scindi 	iaddr |= iaddr_add(hdl, iaddr,
6021414Scindi 	    BITS(csp.base, maskhi_hi, maskhi_lo) &
6031414Scindi 	    ~BITS(csp.mask, maskhi_hi, maskhi_lo),
6041414Scindi 	    "cs basehi bits not being masked");
6051414Scindi 
6062869Sgavinm 	/*
6072869Sgavinm 	 * if cs interleaving is active then baselo address bit are being
6082869Sgavinm 	 * masked - pass the rest through.
6092869Sgavinm 	 */
6102869Sgavinm 	if (mcp.csintlvfctr > 1) {
6111414Scindi 		iaddr |= iaddr_add(hdl, iaddr,
6121414Scindi 		    BITS(csp.base, masklo_hi, masklo_lo) &
6131414Scindi 		    ~BITS(csp.mask, masklo_hi, masklo_lo),
6141414Scindi 		    "cs baselo bits not being masked");
6151414Scindi 	}
6161414Scindi 
6172869Sgavinm 	/*
6182869Sgavinm 	 * Reconstruct iaddr bits from known row address
6192869Sgavinm 	 */
6201414Scindi 	iaddr |= iaddr_add(hdl, iaddr,
6212869Sgavinm 	    row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr),
6221414Scindi 	    "add iaddr bits from row");
6231414Scindi 
6242869Sgavinm 	/*
6252869Sgavinm 	 * Reconstruct iaddr bits from known column address
6262869Sgavinm 	 */
6271414Scindi 	iaddr |= iaddr_add(hdl, iaddr,
6282869Sgavinm 	    col_to_iaddr(hdl, bamp, rcbmp, coladdr),
6291414Scindi 	    "add iaddr bits from col");
6301414Scindi 
6312869Sgavinm 	/*
6322869Sgavinm 	 * Reconstruct iaddr bits from known internal banksel address
6332869Sgavinm 	 */
6341414Scindi 	iaddr |= iaddr_add(hdl, iaddr,
6352869Sgavinm 	    bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr),
6361414Scindi 	    "add iaddr bits from bank");
6371414Scindi 
6382869Sgavinm 	/*
6392869Sgavinm 	 * Move iaddr up into the range for this MC and insert any
6402869Sgavinm 	 * node interleave selection bits.
6412869Sgavinm 	 */
6421414Scindi 	if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0)
6431414Scindi 		return (-1);	/* errno already set */
6441414Scindi 
6451414Scindi 	return (0);
6461414Scindi }
6471414Scindi 
6481414Scindi int
mcamd_cs_size(struct mcamd_hdl * hdl,mcamd_node_t * mc,int csnum,size_t * szp)6491414Scindi mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp)
6501414Scindi {
6511414Scindi 	uint_t csmode;
6522869Sgavinm 	struct mcprops mcp;
6532869Sgavinm 	const struct rct_bnkaddrmode *bamp;
6541414Scindi 
6551414Scindi 	if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0)
6561414Scindi 		return (-1);	/* errno already set */
6571414Scindi 
6582869Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum);
6591414Scindi 
6602869Sgavinm 	if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL,
6612869Sgavinm 	    "mcamd_cs_size") < 0)
6621414Scindi 		return (-1);	/* errno already set */
6631414Scindi 
6641414Scindi 	*szp = MC_CS_SIZE(bamp, mcp.width);
6651414Scindi 
6661414Scindi 	return (0);
6671414Scindi }
668