xref: /titanic_51/usr/src/common/mc/mc-amd/mcamd_rowcol.c (revision 20c794b39650d115e17a15983b6b82e46238cf45)
17aec1d6eScindi /*
27aec1d6eScindi  * CDDL HEADER START
37aec1d6eScindi  *
47aec1d6eScindi  * The contents of this file are subject to the terms of the
58a40a695Sgavinm  * Common Development and Distribution License (the "License").
68a40a695Sgavinm  * You may not use this file except in compliance with the License.
77aec1d6eScindi  *
87aec1d6eScindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97aec1d6eScindi  * or http://www.opensolaris.org/os/licensing.
107aec1d6eScindi  * See the License for the specific language governing permissions
117aec1d6eScindi  * and limitations under the License.
127aec1d6eScindi  *
137aec1d6eScindi  * When distributing Covered Code, include this CDDL HEADER in each
147aec1d6eScindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157aec1d6eScindi  * If applicable, add the following below this CDDL HEADER, with the
167aec1d6eScindi  * fields enclosed by brackets "[]" replaced with your own identifying
177aec1d6eScindi  * information: Portions Copyright [yyyy] [name of copyright owner]
187aec1d6eScindi  *
197aec1d6eScindi  * CDDL HEADER END
207aec1d6eScindi  *
21*20c794b3Sgavinm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
227aec1d6eScindi  * Use is subject to license terms.
237aec1d6eScindi  */
247aec1d6eScindi 
257aec1d6eScindi #pragma ident	"%Z%%M%	%I%	%E% SMI"
267aec1d6eScindi 
277aec1d6eScindi #include <mcamd_api.h>
287aec1d6eScindi #include <mcamd_err.h>
297aec1d6eScindi #include <mcamd_rowcol_impl.h>
307aec1d6eScindi 
317aec1d6eScindi /*
328a40a695Sgavinm  * Convenience structures to stash MC and CS properties in.
337aec1d6eScindi  */
348a40a695Sgavinm struct mcprops {
358a40a695Sgavinm 	mcamd_prop_t num;		/* corresponding chip number */
368a40a695Sgavinm 	mcamd_prop_t rev;		/* revision */
378a40a695Sgavinm 	mcamd_prop_t width;		/* access width */
388a40a695Sgavinm 	mcamd_prop_t base;		/* MC base address */
398a40a695Sgavinm 	mcamd_prop_t lim;		/* MC limit address */
408a40a695Sgavinm 	mcamd_prop_t csbnkmap_reg;	/* chip-select bank map */
418a40a695Sgavinm 	mcamd_prop_t intlven;		/* Node-intlv mask */
428a40a695Sgavinm 	mcamd_prop_t intlvsel;		/* Node-intlv selection for this node */
438a40a695Sgavinm 	mcamd_prop_t csintlvfctr;	/* cs intlv factor on this node */
448a40a695Sgavinm 	mcamd_prop_t bnkswzl;		/* bank-swizzle mode */
458a40a695Sgavinm 	mcamd_prop_t sparecs;		/* spare cs#, if any */
468a40a695Sgavinm 	mcamd_prop_t badcs;		/* substituted cs#, if any */
477aec1d6eScindi };
487aec1d6eScindi 
498a40a695Sgavinm struct csprops {
508a40a695Sgavinm 	mcamd_prop_t num;		/* chip-select number */
518a40a695Sgavinm 	mcamd_prop_t base;		/* chip-select base address */
528a40a695Sgavinm 	mcamd_prop_t mask;		/* chip-select mask */
538a40a695Sgavinm 	mcamd_prop_t testfail;		/* marked testFail */
548a40a695Sgavinm 	mcamd_prop_t dimmrank;		/* rank number on dimm(s) */
557aec1d6eScindi };
567aec1d6eScindi 
577aec1d6eScindi static int
587aec1d6eScindi getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller,
598a40a695Sgavinm     struct mcprops *pp)
607aec1d6eScindi {
618a40a695Sgavinm 	if (!mcamd_get_numprops(hdl,
628a40a695Sgavinm 	    mc, MCAMD_PROP_NUM, &pp->num,
638a40a695Sgavinm 	    mc, MCAMD_PROP_REV, &pp->rev,
648a40a695Sgavinm 	    mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width,
658a40a695Sgavinm 	    mc, MCAMD_PROP_BASE_ADDR, &pp->base,
668a40a695Sgavinm 	    mc, MCAMD_PROP_LIM_ADDR, &pp->lim,
678a40a695Sgavinm 	    mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg,
688a40a695Sgavinm 	    mc, MCAMD_PROP_ILEN, &pp->intlven,
698a40a695Sgavinm 	    mc, MCAMD_PROP_ILSEL, &pp->intlvsel,
708a40a695Sgavinm 	    mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr,
718a40a695Sgavinm 	    mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl,
728a40a695Sgavinm 	    mc, MCAMD_PROP_SPARECS, &pp->sparecs,
738a40a695Sgavinm 	    mc, MCAMD_PROP_BADCS, &pp->badcs,
748a40a695Sgavinm 	    NULL)) {
757aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc "
767aec1d6eScindi 		    "props for mc 0x%p\n", caller, mc);
777aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
787aec1d6eScindi 	}
797aec1d6eScindi 
807aec1d6eScindi 	return (0);
817aec1d6eScindi }
827aec1d6eScindi 
837aec1d6eScindi static int
847aec1d6eScindi getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
858a40a695Sgavinm     struct csprops *csp)
867aec1d6eScindi {
878a40a695Sgavinm 	if (!mcamd_get_numprops(hdl,
888a40a695Sgavinm 	    cs, MCAMD_PROP_NUM, &csp->num,
898a40a695Sgavinm 	    cs, MCAMD_PROP_BASE_ADDR, &csp->base,
908a40a695Sgavinm 	    cs, MCAMD_PROP_MASK, &csp->mask,
918a40a695Sgavinm 	    cs, MCAMD_PROP_TESTFAIL, &csp->testfail,
928a40a695Sgavinm 	    cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank,
938a40a695Sgavinm 	    NULL))  {
947aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs "
957aec1d6eScindi 		    "props for cs 0x%p\n", caller, cs);
967aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
977aec1d6eScindi 	}
987aec1d6eScindi 
997aec1d6eScindi 	return (0);
1007aec1d6eScindi }
1017aec1d6eScindi 
1027aec1d6eScindi static int
1038a40a695Sgavinm gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
1048a40a695Sgavinm     const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp,
1058a40a695Sgavinm     const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid,
1068a40a695Sgavinm     const char *caller)
1077aec1d6eScindi {
1088a40a695Sgavinm 	uint_t rev = (uint_t)mcpp->rev;
1098a40a695Sgavinm 	int width = (int)mcpp->width;
1108a40a695Sgavinm 
1118a40a695Sgavinm 	if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) {
1127aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode "
1138a40a695Sgavinm 		    "table for MC rev %d csmode %d\n", caller, rev, csmode);
1147aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1157aec1d6eScindi 	}
1167aec1d6eScindi 
1178a40a695Sgavinm 	if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) {
1187aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map "
1197aec1d6eScindi 		    "table for MC rev %d csmode %d\n", caller,
1208a40a695Sgavinm 		    rev, csmode);
1218a40a695Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1228a40a695Sgavinm 	}
1238a40a695Sgavinm 
1248a40a695Sgavinm 	if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) {
1258a40a695Sgavinm 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling "
1268a40a695Sgavinm 		    "table for MC rev %d width %d\n", caller, rev, width);
1277aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1287aec1d6eScindi 	}
1297aec1d6eScindi 
1307aec1d6eScindi 	if (csid) {
1318a40a695Sgavinm 		if (mcpp->csintlvfctr > 1) {
1328a40a695Sgavinm 			rct_csintlv_bits(rev, width, csmode,
1337aec1d6eScindi 			    mcpp->csintlvfctr, csid);
1347aec1d6eScindi 			if (csid->csi_factor == 0) {
1357aec1d6eScindi 				mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: "
1367aec1d6eScindi 				    "could not work out cs interleave "
1377aec1d6eScindi 				    "paramters for MC rev %d, width %d, "
1387aec1d6eScindi 				    "csmode %d, factor %d\n", caller,
1398a40a695Sgavinm 				    rev, width, csmode,
1407aec1d6eScindi 				    (int)mcpp->csintlvfctr);
1417aec1d6eScindi 				return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1427aec1d6eScindi 			}
1437aec1d6eScindi 		} else {
1447aec1d6eScindi 			csid->csi_factor = 0;
1457aec1d6eScindi 		}
1467aec1d6eScindi 	}
1477aec1d6eScindi 
1487aec1d6eScindi 	return (0);
1497aec1d6eScindi }
1507aec1d6eScindi 
1517aec1d6eScindi static uint64_t
1527aec1d6eScindi iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what)
1537aec1d6eScindi {
1547aec1d6eScindi 	uint64_t new = in | add;
1557aec1d6eScindi 
1567aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx",
1577aec1d6eScindi 	    what, in, add, new);
1587aec1d6eScindi 
1597aec1d6eScindi 	return (add);
1607aec1d6eScindi }
1617aec1d6eScindi 
1627aec1d6eScindi /*
1637aec1d6eScindi  * Where the number of row/col address bits is ambiguous (affects CG and
1647aec1d6eScindi  * earlier only) we will assign the "floating" bit to row address.  If
1657aec1d6eScindi  * we adopt the same convention in address reconstruction then all should work.
1667aec1d6eScindi  */
1677aec1d6eScindi static uint32_t
1688a40a695Sgavinm iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
1698a40a695Sgavinm     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr)
1707aec1d6eScindi {
1717aec1d6eScindi 	uint32_t addr = 0;
1727aec1d6eScindi 	int abitno, ibitno;
1737aec1d6eScindi 	int nbits = bamp->bam_nrows;
1747aec1d6eScindi 	int swapped = 0;
1757aec1d6eScindi 
1767aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
1778a40a695Sgavinm 		ibitno = rcbm->rcb_rowbit[abitno];
1787aec1d6eScindi 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
1797aec1d6eScindi 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
1807aec1d6eScindi 			swapped++;
1817aec1d6eScindi 		}
1828a40a695Sgavinm 		if (BITVAL(iaddr, ibitno) != 0)
1838a40a695Sgavinm 			SETBIT(addr, abitno);
1847aec1d6eScindi 	}
1857aec1d6eScindi 
1867aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> "
1877aec1d6eScindi 	    "row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped);
1887aec1d6eScindi 
1897aec1d6eScindi 	return (addr);
1907aec1d6eScindi }
1917aec1d6eScindi 
1927aec1d6eScindi /*ARGSUSED*/
1937aec1d6eScindi static uint64_t
1948a40a695Sgavinm row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
1958a40a695Sgavinm     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr)
1967aec1d6eScindi {
1977aec1d6eScindi 	uint64_t iaddr = 0;
1987aec1d6eScindi 	int abitno, ibitno;
1997aec1d6eScindi 	int nbits = bamp->bam_nrows;
2007aec1d6eScindi 
2017aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
2027aec1d6eScindi 		if (BIT(rowaddr, abitno) == 0)
2037aec1d6eScindi 			continue;
2048a40a695Sgavinm 		ibitno = rcbm->rcb_rowbit[abitno];
2057aec1d6eScindi 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
2067aec1d6eScindi 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
2077aec1d6eScindi 		}
2087aec1d6eScindi 		SETBIT(iaddr, ibitno);
2097aec1d6eScindi 	}
2107aec1d6eScindi 
2117aec1d6eScindi 	return (iaddr);
2127aec1d6eScindi }
2137aec1d6eScindi 
2147aec1d6eScindi 
2157aec1d6eScindi static uint32_t
2168a40a695Sgavinm iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
2178a40a695Sgavinm     const struct rct_rcbmap *rcbm, uint64_t iaddr)
2187aec1d6eScindi {
2197aec1d6eScindi 	uint32_t addr = 0;
2207aec1d6eScindi 	int abitno, ibitno, bias = 0;
2217aec1d6eScindi 	int nbits = bamp->bam_ncols;
2227aec1d6eScindi 
2237aec1d6eScindi 	/*
2247aec1d6eScindi 	 * Knock off a column bit if the numbers are ambiguous
2257aec1d6eScindi 	 */
2267aec1d6eScindi 	if (bamp->bam_ambig)
2277aec1d6eScindi 		nbits--;
2287aec1d6eScindi 
2297aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
2307aec1d6eScindi 		if (abitno == MC_PC_COLADDRBIT)
2317aec1d6eScindi 			bias = 1;
2327aec1d6eScindi 
2338a40a695Sgavinm 		ibitno = rcbm->rcb_colbit[abitno + bias];
2347aec1d6eScindi 
2358a40a695Sgavinm 		if (BITVAL(iaddr, ibitno) != 0)
2367aec1d6eScindi 			SETBIT(addr, abitno);
2377aec1d6eScindi 	}
2387aec1d6eScindi 
2397aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> "
2407aec1d6eScindi 	    "col 0x%x\n", iaddr, addr);
2417aec1d6eScindi 
2427aec1d6eScindi 	return (addr);
2437aec1d6eScindi }
2447aec1d6eScindi 
2457aec1d6eScindi /*ARGSUSED*/
2467aec1d6eScindi static uint64_t
2478a40a695Sgavinm col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
2488a40a695Sgavinm     const struct rct_rcbmap *rcbm, uint32_t coladdr)
2497aec1d6eScindi {
2507aec1d6eScindi 	uint64_t iaddr = 0;
2517aec1d6eScindi 	int abitno, ibitno, bias = 0;
2527aec1d6eScindi 	int nbits = bamp->bam_ncols;
2537aec1d6eScindi 
2547aec1d6eScindi 	/*
2557aec1d6eScindi 	 * Knock off a column bit if the numbers are ambiguous
2567aec1d6eScindi 	 */
2577aec1d6eScindi 	if (bamp->bam_ambig)
2587aec1d6eScindi 		nbits--;
2597aec1d6eScindi 
2607aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
2617aec1d6eScindi 		if (BIT(coladdr, abitno) == 0)
2627aec1d6eScindi 			continue;
2637aec1d6eScindi 
2647aec1d6eScindi 		if (abitno == MC_PC_COLADDRBIT)
2657aec1d6eScindi 			bias = 1;
2667aec1d6eScindi 
2678a40a695Sgavinm 		ibitno = rcbm->rcb_colbit[abitno + bias];
2687aec1d6eScindi 		SETBIT(iaddr, ibitno);
2697aec1d6eScindi 	}
2707aec1d6eScindi 
2717aec1d6eScindi 	return (iaddr);
2727aec1d6eScindi }
2737aec1d6eScindi 
2747aec1d6eScindi /*
2758a40a695Sgavinm  * Extract bank bit arguments and swizzle if requested.
2767aec1d6eScindi  */
2777aec1d6eScindi static uint32_t
2788a40a695Sgavinm iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
2798a40a695Sgavinm     const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr)
2807aec1d6eScindi {
2817aec1d6eScindi 	uint32_t addr = 0;
2827aec1d6eScindi 	int abitno, ibitno, i;
2837aec1d6eScindi 
2848a40a695Sgavinm 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
2858a40a695Sgavinm 		uint32_t val;
2868a40a695Sgavinm 
2878a40a695Sgavinm 		/*
2888a40a695Sgavinm 		 * rcb_bankbit[abitno] tells us which iaddr bit number
2898a40a695Sgavinm 		 * will form bit abitno of the bank address
2908a40a695Sgavinm 		 */
2918a40a695Sgavinm 		ibitno = rcbm->rcb_bankbit[abitno];
2928a40a695Sgavinm 		val = BITVAL(iaddr, ibitno);
2938a40a695Sgavinm 
2948a40a695Sgavinm 		/*
2958a40a695Sgavinm 		 * If bank swizzling is in operation then xor the bit value
2968a40a695Sgavinm 		 * obtained above with other iaddr bits.
2978a40a695Sgavinm 		 */
2988a40a695Sgavinm 		if (swzlp) {
2998a40a695Sgavinm 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
3008a40a695Sgavinm 				ibitno = swzlp->bswz_rowbits[abitno][i];
3018a40a695Sgavinm 				val ^= BITVAL(iaddr, ibitno);
3027aec1d6eScindi 			}
3038a40a695Sgavinm 		}
3048a40a695Sgavinm 
3058a40a695Sgavinm 		if (val)
3068a40a695Sgavinm 			SETBIT(addr, abitno);
3077aec1d6eScindi 	}
3087aec1d6eScindi 
3097aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> "
3107aec1d6eScindi 	    "bank 0x%x\n", iaddr, addr);
3117aec1d6eScindi 
3127aec1d6eScindi 	return (addr);
3137aec1d6eScindi }
3147aec1d6eScindi 
3157aec1d6eScindi /*
3167aec1d6eScindi  * bank_to_iaddr requires the iaddr reconstructed thus far with at least the
3177aec1d6eScindi  * row bits repopulated.  That's because in bank swizzle mode
3187aec1d6eScindi  * the bank bits are the result of xor'ing three original iaddr bits
3197aec1d6eScindi  * together - two of which come from the row address and the third we
3207aec1d6eScindi  * can reconstruct here.  Note that a zero bankaddr bit *can* result
3217aec1d6eScindi  * in a nonzero iaddr bit (unlike in row and col reconstruction).
3227aec1d6eScindi  */
3237aec1d6eScindi /*ARGSUSED*/
3247aec1d6eScindi static uint64_t
3258a40a695Sgavinm bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
3268a40a695Sgavinm     const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr)
3277aec1d6eScindi {
3287aec1d6eScindi 	uint64_t iaddr = 0;
3297aec1d6eScindi 	int abitno, pibitno, i;
3307aec1d6eScindi 
3318a40a695Sgavinm 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
3327aec1d6eScindi 		uint32_t val = BITVAL(bankaddr, abitno);
3338a40a695Sgavinm 		if (swzlp) {
3348a40a695Sgavinm 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
3358a40a695Sgavinm 				pibitno = swzlp->bswz_rowbits[abitno][i];
3367aec1d6eScindi 				val ^= BITVAL(partiaddr, pibitno);
3377aec1d6eScindi 			}
3387aec1d6eScindi 		}
3397aec1d6eScindi 		if (val)
3408a40a695Sgavinm 			SETBIT(iaddr, rcbm->rcb_bankbit[abitno]);
3417aec1d6eScindi 	}
3427aec1d6eScindi 
3437aec1d6eScindi 	return (iaddr);
3447aec1d6eScindi }
3457aec1d6eScindi 
3467aec1d6eScindi static int
3478a40a695Sgavinm iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
3487aec1d6eScindi     uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp)
3497aec1d6eScindi {
3508a40a695Sgavinm 	const struct rct_bnkaddrmode *bamp;
3518a40a695Sgavinm 	const struct rct_rcbmap *rcbmp;
3528a40a695Sgavinm 	const struct rct_bnkswzlinfo *swzlp = NULL;
3538a40a695Sgavinm 	struct rct_csintlv csi;
3547aec1d6eScindi 
3558a40a695Sgavinm 	if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp,
3568a40a695Sgavinm 	    mcpp->bnkswzl ? &swzlp : NULL, &csi,
3578a40a695Sgavinm 	    "iaddr_to_rcb") < 0)
3587aec1d6eScindi 		return (-1);	/* errno already set */
3597aec1d6eScindi 
3608a40a695Sgavinm 	*rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr);
3618a40a695Sgavinm 	*colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr);
3628a40a695Sgavinm 	*bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr);
3637aec1d6eScindi 
3647aec1d6eScindi 	return (0);
3657aec1d6eScindi }
3667aec1d6eScindi 
3677aec1d6eScindi /*
3687aec1d6eScindi  * Take a reconstructed InputAddr and undo the normalization described in
3697aec1d6eScindi  * BKDG 3.29 3.4.4 to include the base address of the MC if no node
3707aec1d6eScindi  * interleave or to insert the node interleave selection bits.
3717aec1d6eScindi  */
3727aec1d6eScindi static int
3738a40a695Sgavinm iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr,
3747aec1d6eScindi     uint64_t *rsltp)
3757aec1d6eScindi {
3767aec1d6eScindi 	uint64_t dramaddr;
3777aec1d6eScindi 	int intlvbits;
3787aec1d6eScindi 
3797aec1d6eScindi 	switch (mcpp->intlven) {
3807aec1d6eScindi 	case 0x0:
3817aec1d6eScindi 		intlvbits = 0;
3827aec1d6eScindi 		break;
3837aec1d6eScindi 	case 0x1:
3847aec1d6eScindi 		intlvbits = 1;
3857aec1d6eScindi 		break;
3867aec1d6eScindi 	case 0x3:
3877aec1d6eScindi 		intlvbits = 2;
3887aec1d6eScindi 		break;
3897aec1d6eScindi 	case 0x7:
3907aec1d6eScindi 		intlvbits = 3;
3917aec1d6eScindi 		break;
3927aec1d6eScindi 	default:
3937aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: "
3947aec1d6eScindi 		    "illegal IntlvEn of %d for MC 0x%p\n",
3957aec1d6eScindi 		    (int)mcpp->intlven, (int)mcpp->num);
3967aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
3977aec1d6eScindi 	}
3987aec1d6eScindi 
3997aec1d6eScindi 	if (intlvbits != 0) {
4007aec1d6eScindi 		/*
4017aec1d6eScindi 		 * For a 2/4/8 way interleave iaddr was formed by excising
4027aec1d6eScindi 		 * 1, 2, or 3 bits 12:12, 13:12, or 14:12 from dramaddr,
4037aec1d6eScindi 		 * the removed bits having done their job by selecting the
4047aec1d6eScindi 		 * responding node.  So we must move bits 35:12 of the
4057aec1d6eScindi 		 * reconstructed iaddr up to make a 1, 2 or 3 bit hole and
4067aec1d6eScindi 		 * then fill those bits with the current IntlvSel value for
4077aec1d6eScindi 		 * this node.  The node base address must be zero if nodes
4087aec1d6eScindi 		 * are interleaved.
4098a40a695Sgavinm 		 *
4108a40a695Sgavinm 		 * Note that the DRAM controller InputAddr is still 36 bits
4118a40a695Sgavinm 		 * 35:0 on rev F.
4127aec1d6eScindi 		 */
4137aec1d6eScindi 		dramaddr = (BITS(iaddr, 35, 12) << intlvbits) |
4147aec1d6eScindi 		    (mcpp->intlvsel << 12) | BITS(iaddr, 11, 0);
4157aec1d6eScindi 	} else {
4167aec1d6eScindi 		dramaddr = iaddr + mcpp->base;
4177aec1d6eScindi 	}
4187aec1d6eScindi 
4197aec1d6eScindi 	*rsltp = dramaddr;
4207aec1d6eScindi 
4217aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx "
4227aec1d6eScindi 	    "intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n",
4237aec1d6eScindi 	    iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base,
4247aec1d6eScindi 	    dramaddr);
4257aec1d6eScindi 
4267aec1d6eScindi 	return (0);
4277aec1d6eScindi }
4287aec1d6eScindi 
4297aec1d6eScindi int
4307aec1d6eScindi mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
4318a40a695Sgavinm     uint64_t iaddr, uint64_t *offsetp)
4327aec1d6eScindi {
4337aec1d6eScindi 	mcamd_dimm_offset_un_t offset_un;
4347aec1d6eScindi 	uint_t csmode;
4357aec1d6eScindi 	uint32_t bankaddr, rowaddr, coladdr;
4368a40a695Sgavinm 	struct mcprops mcp;
4378a40a695Sgavinm 	struct csprops csp;
4387aec1d6eScindi 
4397aec1d6eScindi 	*offsetp = MCAMD_RC_INVALID_OFFSET;
4407aec1d6eScindi 
4417aec1d6eScindi 	if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 ||
4427aec1d6eScindi 	    getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0)
4437aec1d6eScindi 		return (-1);	/* errno already set */
4447aec1d6eScindi 
4458a40a695Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
4467aec1d6eScindi 
4477aec1d6eScindi 	if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr,
4487aec1d6eScindi 	    &coladdr, &bankaddr) < 0)
4497aec1d6eScindi 		return (-1);	/* errno already set */
4507aec1d6eScindi 
4517aec1d6eScindi 	offset_un.do_offset = 0;
4527aec1d6eScindi 
4537aec1d6eScindi 	offset_un.do_valid = 1;
4547aec1d6eScindi 	offset_un.do_version = MCAMD_OFFSET_VERSION;
455*20c794b3Sgavinm 	offset_un.do_rank = (uint32_t)csp.dimmrank;
4567aec1d6eScindi 	offset_un.do_row = rowaddr;
4577aec1d6eScindi 	offset_un.do_bank = bankaddr;
4587aec1d6eScindi 	offset_un.do_col = coladdr;
4597aec1d6eScindi 
4607aec1d6eScindi 	*offsetp = offset_un.do_offset;
4617aec1d6eScindi 
4627aec1d6eScindi 	return (0);
4637aec1d6eScindi }
4647aec1d6eScindi 
4657aec1d6eScindi /*
4668a40a695Sgavinm  * Given an MC, DIMM and offset (dimm rank, row, col, internal bank) we
4677aec1d6eScindi  * find the corresponding chip-select for the rank and then reconstruct
4687aec1d6eScindi  * a system address.  In the absence of serial number support it is possible
4697aec1d6eScindi  * that we may be asked to perform this operation on a dimm which has been
4707aec1d6eScindi  * swapped, perhaps even for a dimm of different size and number of ranks.
4717aec1d6eScindi  * This may happen if fmadm repair has not been used.  There are some
4727aec1d6eScindi  * unused bits in the offset and we could guard against this a little
4737aec1d6eScindi  * by recording in those bit some of the physical characteristic of the
4747aec1d6eScindi  * original DIMM such as size, number of ranks etc.
4757aec1d6eScindi  */
4767aec1d6eScindi int
4777aec1d6eScindi mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
4787aec1d6eScindi     uint64_t offset, uint64_t *pap)
4797aec1d6eScindi {
4807aec1d6eScindi 	mcamd_node_t *cs;
4817aec1d6eScindi 	mcamd_dimm_offset_un_t off_un;
4827aec1d6eScindi 	uint32_t rank, rowaddr, bankaddr, coladdr;
4837aec1d6eScindi 	uint64_t iaddr = 0;
4848a40a695Sgavinm 	const struct rct_bnkaddrmode *bamp;
4858a40a695Sgavinm 	const struct rct_rcbmap *rcbmp;
4868a40a695Sgavinm 	const struct rct_bnkswzlinfo *swzlp = NULL;
4878a40a695Sgavinm 	struct rct_csintlv csi;
4888a40a695Sgavinm 	struct mcprops mcp;
4898a40a695Sgavinm 	struct csprops csp;
4907aec1d6eScindi 	uint64_t csmode;
4918a40a695Sgavinm 	int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo;
4927aec1d6eScindi 
4937aec1d6eScindi 	off_un.do_offset = offset;
4947aec1d6eScindi 	rank = off_un.do_rank;
4957aec1d6eScindi 	bankaddr = off_un.do_bank;
4967aec1d6eScindi 	rowaddr = off_un.do_row;
4977aec1d6eScindi 	coladdr = off_un.do_col;
4987aec1d6eScindi 
4998a40a695Sgavinm 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx "
5008a40a695Sgavinm 	    "-> rank %d bank %d row 0x%x col 0x%x\n", offset,
5018a40a695Sgavinm 	    rank, bankaddr, rowaddr, coladdr);
5028a40a695Sgavinm 
5037aec1d6eScindi 	if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0)
5047aec1d6eScindi 		return (-1);	/* errno already set */
5057aec1d6eScindi 
5068a40a695Sgavinm 	maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev);
5078a40a695Sgavinm 	maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev);
5088a40a695Sgavinm 	masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev);
5098a40a695Sgavinm 	masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev);
5108a40a695Sgavinm 
5117aec1d6eScindi 	/*
5128a40a695Sgavinm 	 * Find the chip-select on this dimm using the given rank.
5137aec1d6eScindi 	 */
5148a40a695Sgavinm 	for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL;
5158a40a695Sgavinm 	    cs = mcamd_cs_next(hdl, dimm, cs)) {
5168a40a695Sgavinm 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
5178a40a695Sgavinm 			return (-1);	/* errno already set */
5188a40a695Sgavinm 
5198a40a695Sgavinm 		if (csp.dimmrank == rank)
5208a40a695Sgavinm 			break;
5217aec1d6eScindi 	}
5228a40a695Sgavinm 
5238a40a695Sgavinm 	if (cs == NULL) {
5247aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current "
5258a40a695Sgavinm 		    "dimm in this slot does not have a cs using rank %d\n",
5267aec1d6eScindi 		    rank);
5277aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5287aec1d6eScindi 	}
5297aec1d6eScindi 
5308a40a695Sgavinm 	/*
5318a40a695Sgavinm 	 * If the cs# has been substituted by the online spare then the
5328a40a695Sgavinm 	 * given unum is not actually contributing to the system address
5338a40a695Sgavinm 	 * map since all accesses to it are redirected.
5348a40a695Sgavinm 	 *
5358a40a695Sgavinm 	 * If the cs# failed BIOS test it is not in the address map.
5368a40a695Sgavinm 	 *
5378a40a695Sgavinm 	 * If the cs# is the online spare cs# then it is contributing to
5388a40a695Sgavinm 	 * the system address map only if swapped in, and the csbase etc
5398a40a695Sgavinm 	 * parameters to use must be those of the bad cs#.
5408a40a695Sgavinm 	 */
5418a40a695Sgavinm 	if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) {
5428a40a695Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5438a40a695Sgavinm 	} else if (csp.testfail) {
5448a40a695Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5458a40a695Sgavinm 	} else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs &&
5468a40a695Sgavinm 	    mcp.badcs != MC_INVALNUM) {
5478a40a695Sgavinm 		/*
5488a40a695Sgavinm 		 * Iterate over all cs# of this memory controller to find
5498a40a695Sgavinm 		 * the bad one - the bad cs# need not be on the same dimm
5508a40a695Sgavinm 		 * as the spare.
5518a40a695Sgavinm 		 */
5528a40a695Sgavinm 		for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
5538a40a695Sgavinm 		    cs = mcamd_cs_next(hdl, mc, cs)) {
5548a40a695Sgavinm 			mcamd_prop_t csnum;
5558a40a695Sgavinm 
5568a40a695Sgavinm 			if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM,
5578a40a695Sgavinm 			    &csnum)) {
5588a40a695Sgavinm 				mcamd_dprintf(hdl, MCAMD_DBG_ERR,
5598a40a695Sgavinm 				    "mcamd_offset_to_pa: csnum lookup failed "
5608a40a695Sgavinm 				    "while looking for bad cs#");
5618a40a695Sgavinm 				return (mcamd_set_errno(hdl,
5628a40a695Sgavinm 				    EMCAMD_TREEINVALID));
5638a40a695Sgavinm 			}
5648a40a695Sgavinm 			if (csnum == mcp.badcs)
5658a40a695Sgavinm 				break;
5668a40a695Sgavinm 		}
5678a40a695Sgavinm 
5688a40a695Sgavinm 		if (cs == NULL) {
5698a40a695Sgavinm 			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: "
5708a40a695Sgavinm 			    "failed to find cs for bad cs#%d\n", mcp.badcs);
5718a40a695Sgavinm 				return (mcamd_set_errno(hdl,
5728a40a695Sgavinm 				    EMCAMD_TREEINVALID));
5738a40a695Sgavinm 		}
5748a40a695Sgavinm 
5758a40a695Sgavinm 		/* found bad cs - reread properties from it instead of spare */
5767aec1d6eScindi 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
5777aec1d6eScindi 			return (-1);	/* errno already set */
5788a40a695Sgavinm 	}
5797aec1d6eScindi 
5808a40a695Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
5817aec1d6eScindi 
5828a40a695Sgavinm 	if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp,
5838a40a695Sgavinm 	    mcp.bnkswzl ? &swzlp : NULL, &csi,
5847aec1d6eScindi 	    "mc_offset_to_pa") < 0)
5857aec1d6eScindi 		return (-1);	/* errno already set */
5867aec1d6eScindi 
5878a40a695Sgavinm 	/*
5888a40a695Sgavinm 	 * If there are umaskable DRAM InputAddr bits the add those bits
5898a40a695Sgavinm 	 * to iaddr from the cs base address.
5908a40a695Sgavinm 	 */
5918a40a695Sgavinm 	if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) {
5927aec1d6eScindi 		iaddr |= iaddr_add(hdl, iaddr,
5938a40a695Sgavinm 		    BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev),
5947aec1d6eScindi 		    maskhi_hi + 1), "unmaskable cs basehi bits");
5957aec1d6eScindi 	}
5967aec1d6eScindi 
5978a40a695Sgavinm 	/*
5988a40a695Sgavinm 	 * basehi bits not meing masked pass straight through to the
5998a40a695Sgavinm 	 * iaddr.
6008a40a695Sgavinm 	 */
6017aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6027aec1d6eScindi 	    BITS(csp.base, maskhi_hi, maskhi_lo) &
6037aec1d6eScindi 	    ~BITS(csp.mask, maskhi_hi, maskhi_lo),
6047aec1d6eScindi 	    "cs basehi bits not being masked");
6057aec1d6eScindi 
6068a40a695Sgavinm 	/*
6078a40a695Sgavinm 	 * if cs interleaving is active then baselo address bit are being
6088a40a695Sgavinm 	 * masked - pass the rest through.
6098a40a695Sgavinm 	 */
6108a40a695Sgavinm 	if (mcp.csintlvfctr > 1) {
6117aec1d6eScindi 		iaddr |= iaddr_add(hdl, iaddr,
6127aec1d6eScindi 		    BITS(csp.base, masklo_hi, masklo_lo) &
6137aec1d6eScindi 		    ~BITS(csp.mask, masklo_hi, masklo_lo),
6147aec1d6eScindi 		    "cs baselo bits not being masked");
6157aec1d6eScindi 	}
6167aec1d6eScindi 
6178a40a695Sgavinm 	/*
6188a40a695Sgavinm 	 * Reconstruct iaddr bits from known row address
6198a40a695Sgavinm 	 */
6207aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6218a40a695Sgavinm 	    row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr),
6227aec1d6eScindi 	    "add iaddr bits from row");
6237aec1d6eScindi 
6248a40a695Sgavinm 	/*
6258a40a695Sgavinm 	 * Reconstruct iaddr bits from known column address
6268a40a695Sgavinm 	 */
6277aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6288a40a695Sgavinm 	    col_to_iaddr(hdl, bamp, rcbmp, coladdr),
6297aec1d6eScindi 	    "add iaddr bits from col");
6307aec1d6eScindi 
6318a40a695Sgavinm 	/*
6328a40a695Sgavinm 	 * Reconstruct iaddr bits from known internal banksel address
6338a40a695Sgavinm 	 */
6347aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6358a40a695Sgavinm 	    bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr),
6367aec1d6eScindi 	    "add iaddr bits from bank");
6377aec1d6eScindi 
6388a40a695Sgavinm 	/*
6398a40a695Sgavinm 	 * Move iaddr up into the range for this MC and insert any
6408a40a695Sgavinm 	 * node interleave selection bits.
6418a40a695Sgavinm 	 */
6427aec1d6eScindi 	if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0)
6437aec1d6eScindi 		return (-1);	/* errno already set */
6447aec1d6eScindi 
6457aec1d6eScindi 	return (0);
6467aec1d6eScindi }
6477aec1d6eScindi 
6487aec1d6eScindi int
6497aec1d6eScindi mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp)
6507aec1d6eScindi {
6517aec1d6eScindi 	uint_t csmode;
6528a40a695Sgavinm 	struct mcprops mcp;
6538a40a695Sgavinm 	const struct rct_bnkaddrmode *bamp;
6547aec1d6eScindi 
6557aec1d6eScindi 	if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0)
6567aec1d6eScindi 		return (-1);	/* errno already set */
6577aec1d6eScindi 
6588a40a695Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum);
6597aec1d6eScindi 
6608a40a695Sgavinm 	if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL,
6618a40a695Sgavinm 	    "mcamd_cs_size") < 0)
6627aec1d6eScindi 		return (-1);	/* errno already set */
6637aec1d6eScindi 
6647aec1d6eScindi 	*szp = MC_CS_SIZE(bamp, mcp.width);
6657aec1d6eScindi 
6667aec1d6eScindi 	return (0);
6677aec1d6eScindi }
668