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  *
2120c794b3Sgavinm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
227aec1d6eScindi  * Use is subject to license terms.
237aec1d6eScindi  */
247aec1d6eScindi 
25*981fe1b1SJohn Levon /*
26*981fe1b1SJohn Levon  * Copyright (c) 2018, Joyent, Inc.
27*981fe1b1SJohn Levon  */
287aec1d6eScindi 
297aec1d6eScindi #include <mcamd_api.h>
307aec1d6eScindi #include <mcamd_err.h>
317aec1d6eScindi #include <mcamd_rowcol_impl.h>
327aec1d6eScindi 
337aec1d6eScindi /*
348a40a695Sgavinm  * Convenience structures to stash MC and CS properties in.
357aec1d6eScindi  */
368a40a695Sgavinm struct mcprops {
378a40a695Sgavinm 	mcamd_prop_t num;		/* corresponding chip number */
388a40a695Sgavinm 	mcamd_prop_t rev;		/* revision */
398a40a695Sgavinm 	mcamd_prop_t width;		/* access width */
408a40a695Sgavinm 	mcamd_prop_t base;		/* MC base address */
418a40a695Sgavinm 	mcamd_prop_t lim;		/* MC limit address */
428a40a695Sgavinm 	mcamd_prop_t csbnkmap_reg;	/* chip-select bank map */
438a40a695Sgavinm 	mcamd_prop_t intlven;		/* Node-intlv mask */
448a40a695Sgavinm 	mcamd_prop_t intlvsel;		/* Node-intlv selection for this node */
458a40a695Sgavinm 	mcamd_prop_t csintlvfctr;	/* cs intlv factor on this node */
468a40a695Sgavinm 	mcamd_prop_t bnkswzl;		/* bank-swizzle mode */
478a40a695Sgavinm 	mcamd_prop_t sparecs;		/* spare cs#, if any */
488a40a695Sgavinm 	mcamd_prop_t badcs;		/* substituted cs#, if any */
497aec1d6eScindi };
507aec1d6eScindi 
518a40a695Sgavinm struct csprops {
528a40a695Sgavinm 	mcamd_prop_t num;		/* chip-select number */
538a40a695Sgavinm 	mcamd_prop_t base;		/* chip-select base address */
548a40a695Sgavinm 	mcamd_prop_t mask;		/* chip-select mask */
558a40a695Sgavinm 	mcamd_prop_t testfail;		/* marked testFail */
568a40a695Sgavinm 	mcamd_prop_t dimmrank;		/* rank number on dimm(s) */
577aec1d6eScindi };
587aec1d6eScindi 
597aec1d6eScindi static int
getmcprops(struct mcamd_hdl * hdl,mcamd_node_t * mc,const char * caller,struct mcprops * pp)607aec1d6eScindi getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller,
618a40a695Sgavinm     struct mcprops *pp)
627aec1d6eScindi {
638a40a695Sgavinm 	if (!mcamd_get_numprops(hdl,
648a40a695Sgavinm 	    mc, MCAMD_PROP_NUM, &pp->num,
658a40a695Sgavinm 	    mc, MCAMD_PROP_REV, &pp->rev,
668a40a695Sgavinm 	    mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width,
678a40a695Sgavinm 	    mc, MCAMD_PROP_BASE_ADDR, &pp->base,
688a40a695Sgavinm 	    mc, MCAMD_PROP_LIM_ADDR, &pp->lim,
698a40a695Sgavinm 	    mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg,
708a40a695Sgavinm 	    mc, MCAMD_PROP_ILEN, &pp->intlven,
718a40a695Sgavinm 	    mc, MCAMD_PROP_ILSEL, &pp->intlvsel,
728a40a695Sgavinm 	    mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr,
738a40a695Sgavinm 	    mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl,
748a40a695Sgavinm 	    mc, MCAMD_PROP_SPARECS, &pp->sparecs,
758a40a695Sgavinm 	    mc, MCAMD_PROP_BADCS, &pp->badcs,
768a40a695Sgavinm 	    NULL)) {
777aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc "
787aec1d6eScindi 		    "props for mc 0x%p\n", caller, mc);
797aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
807aec1d6eScindi 	}
817aec1d6eScindi 
827aec1d6eScindi 	return (0);
837aec1d6eScindi }
847aec1d6eScindi 
857aec1d6eScindi static int
getcsprops(struct mcamd_hdl * hdl,mcamd_node_t * cs,const char * caller,struct csprops * csp)867aec1d6eScindi getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
878a40a695Sgavinm     struct csprops *csp)
887aec1d6eScindi {
898a40a695Sgavinm 	if (!mcamd_get_numprops(hdl,
908a40a695Sgavinm 	    cs, MCAMD_PROP_NUM, &csp->num,
918a40a695Sgavinm 	    cs, MCAMD_PROP_BASE_ADDR, &csp->base,
928a40a695Sgavinm 	    cs, MCAMD_PROP_MASK, &csp->mask,
938a40a695Sgavinm 	    cs, MCAMD_PROP_TESTFAIL, &csp->testfail,
948a40a695Sgavinm 	    cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank,
958a40a695Sgavinm 	    NULL))  {
967aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs "
977aec1d6eScindi 		    "props for cs 0x%p\n", caller, cs);
987aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
9920c794b3Sgavinm 	}
1007aec1d6eScindi 
1017aec1d6eScindi 	return (0);
1027aec1d6eScindi }
1037aec1d6eScindi 
1047aec1d6eScindi 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)1058a40a695Sgavinm gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
1068a40a695Sgavinm     const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp,
1078a40a695Sgavinm     const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid,
1088a40a695Sgavinm     const char *caller)
1097aec1d6eScindi {
1108a40a695Sgavinm 	uint_t rev = (uint_t)mcpp->rev;
1118a40a695Sgavinm 	int width = (int)mcpp->width;
1128a40a695Sgavinm 
1138a40a695Sgavinm 	if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) {
1147aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode "
1158a40a695Sgavinm 		    "table for MC rev %d csmode %d\n", caller, rev, csmode);
1167aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1177aec1d6eScindi 	}
1187aec1d6eScindi 
1198a40a695Sgavinm 	if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) {
1207aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map "
1217aec1d6eScindi 		    "table for MC rev %d csmode %d\n", caller,
1228a40a695Sgavinm 		    rev, csmode);
1238a40a695Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1248a40a695Sgavinm 	}
1258a40a695Sgavinm 
1268a40a695Sgavinm 	if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) {
1278a40a695Sgavinm 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling "
1288a40a695Sgavinm 		    "table for MC rev %d width %d\n", caller, rev, width);
1297aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1307aec1d6eScindi 	}
1317aec1d6eScindi 
1327aec1d6eScindi 	if (csid) {
1338a40a695Sgavinm 		if (mcpp->csintlvfctr > 1) {
1348a40a695Sgavinm 			rct_csintlv_bits(rev, width, csmode,
1357aec1d6eScindi 			    mcpp->csintlvfctr, csid);
1367aec1d6eScindi 			if (csid->csi_factor == 0) {
1377aec1d6eScindi 				mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: "
1387aec1d6eScindi 				    "could not work out cs interleave "
1397aec1d6eScindi 				    "paramters for MC rev %d, width %d, "
1407aec1d6eScindi 				    "csmode %d, factor %d\n", caller,
1418a40a695Sgavinm 				    rev, width, csmode,
1427aec1d6eScindi 				    (int)mcpp->csintlvfctr);
1437aec1d6eScindi 				return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
1447aec1d6eScindi 			}
1457aec1d6eScindi 		} else {
1467aec1d6eScindi 			csid->csi_factor = 0;
1477aec1d6eScindi 		}
1487aec1d6eScindi 	}
1497aec1d6eScindi 
1507aec1d6eScindi 	return (0);
1517aec1d6eScindi }
1527aec1d6eScindi 
1537aec1d6eScindi static uint64_t
iaddr_add(struct mcamd_hdl * hdl,uint64_t in,uint64_t add,const char * what)1547aec1d6eScindi iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what)
1557aec1d6eScindi {
1567aec1d6eScindi 	uint64_t new = in | add;
1577aec1d6eScindi 
1587aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx",
1597aec1d6eScindi 	    what, in, add, new);
1607aec1d6eScindi 
1617aec1d6eScindi 	return (add);
1627aec1d6eScindi }
1637aec1d6eScindi 
1647aec1d6eScindi /*
1657aec1d6eScindi  * Where the number of row/col address bits is ambiguous (affects CG and
1667aec1d6eScindi  * earlier only) we will assign the "floating" bit to row address.  If
1677aec1d6eScindi  * we adopt the same convention in address reconstruction then all should work.
1687aec1d6eScindi  */
1697aec1d6eScindi 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)1708a40a695Sgavinm iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
1718a40a695Sgavinm     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr)
1727aec1d6eScindi {
1737aec1d6eScindi 	uint32_t addr = 0;
1747aec1d6eScindi 	int abitno, ibitno;
1757aec1d6eScindi 	int nbits = bamp->bam_nrows;
1767aec1d6eScindi 	int swapped = 0;
1777aec1d6eScindi 
1787aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
1798a40a695Sgavinm 		ibitno = rcbm->rcb_rowbit[abitno];
1807aec1d6eScindi 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
1817aec1d6eScindi 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
1827aec1d6eScindi 			swapped++;
1837aec1d6eScindi 		}
1848a40a695Sgavinm 		if (BITVAL(iaddr, ibitno) != 0)
1858a40a695Sgavinm 			SETBIT(addr, abitno);
1867aec1d6eScindi 	}
1877aec1d6eScindi 
1887aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> "
1897aec1d6eScindi 	    "row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped);
1907aec1d6eScindi 
1917aec1d6eScindi 	return (addr);
1927aec1d6eScindi }
1937aec1d6eScindi 
1947aec1d6eScindi /*ARGSUSED*/
1957aec1d6eScindi 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)1968a40a695Sgavinm row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
1978a40a695Sgavinm     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr)
1987aec1d6eScindi {
1997aec1d6eScindi 	uint64_t iaddr = 0;
2007aec1d6eScindi 	int abitno, ibitno;
2017aec1d6eScindi 	int nbits = bamp->bam_nrows;
2027aec1d6eScindi 
2037aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
2047aec1d6eScindi 		if (BIT(rowaddr, abitno) == 0)
2057aec1d6eScindi 			continue;
2068a40a695Sgavinm 		ibitno = rcbm->rcb_rowbit[abitno];
2077aec1d6eScindi 		if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
2087aec1d6eScindi 			ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
2097aec1d6eScindi 		}
2107aec1d6eScindi 		SETBIT(iaddr, ibitno);
2117aec1d6eScindi 	}
2127aec1d6eScindi 
2137aec1d6eScindi 	return (iaddr);
2147aec1d6eScindi }
2157aec1d6eScindi 
2167aec1d6eScindi 
2177aec1d6eScindi static uint32_t
iaddr_to_col(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,uint64_t iaddr)2188a40a695Sgavinm iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
2198a40a695Sgavinm     const struct rct_rcbmap *rcbm, uint64_t iaddr)
2207aec1d6eScindi {
2217aec1d6eScindi 	uint32_t addr = 0;
2227aec1d6eScindi 	int abitno, ibitno, bias = 0;
2237aec1d6eScindi 	int nbits = bamp->bam_ncols;
2247aec1d6eScindi 
2257aec1d6eScindi 	/*
2267aec1d6eScindi 	 * Knock off a column bit if the numbers are ambiguous
2277aec1d6eScindi 	 */
2287aec1d6eScindi 	if (bamp->bam_ambig)
2297aec1d6eScindi 		nbits--;
2307aec1d6eScindi 
2317aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
2327aec1d6eScindi 		if (abitno == MC_PC_COLADDRBIT)
2337aec1d6eScindi 			bias = 1;
2347aec1d6eScindi 
2358a40a695Sgavinm 		ibitno = rcbm->rcb_colbit[abitno + bias];
2367aec1d6eScindi 
2378a40a695Sgavinm 		if (BITVAL(iaddr, ibitno) != 0)
2387aec1d6eScindi 			SETBIT(addr, abitno);
2397aec1d6eScindi 	}
2407aec1d6eScindi 
2417aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> "
2427aec1d6eScindi 	    "col 0x%x\n", iaddr, addr);
2437aec1d6eScindi 
2447aec1d6eScindi 	return (addr);
2457aec1d6eScindi }
2467aec1d6eScindi 
2477aec1d6eScindi /*ARGSUSED*/
2487aec1d6eScindi static uint64_t
col_to_iaddr(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,uint32_t coladdr)2498a40a695Sgavinm col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
2508a40a695Sgavinm     const struct rct_rcbmap *rcbm, uint32_t coladdr)
2517aec1d6eScindi {
2527aec1d6eScindi 	uint64_t iaddr = 0;
2537aec1d6eScindi 	int abitno, ibitno, bias = 0;
2547aec1d6eScindi 	int nbits = bamp->bam_ncols;
2557aec1d6eScindi 
2567aec1d6eScindi 	/*
2577aec1d6eScindi 	 * Knock off a column bit if the numbers are ambiguous
2587aec1d6eScindi 	 */
2597aec1d6eScindi 	if (bamp->bam_ambig)
2607aec1d6eScindi 		nbits--;
2617aec1d6eScindi 
2627aec1d6eScindi 	for (abitno = 0; abitno < nbits; abitno++) {
2637aec1d6eScindi 		if (BIT(coladdr, abitno) == 0)
2647aec1d6eScindi 			continue;
2657aec1d6eScindi 
2667aec1d6eScindi 		if (abitno == MC_PC_COLADDRBIT)
2677aec1d6eScindi 			bias = 1;
2687aec1d6eScindi 
2698a40a695Sgavinm 		ibitno = rcbm->rcb_colbit[abitno + bias];
2707aec1d6eScindi 		SETBIT(iaddr, ibitno);
2717aec1d6eScindi 	}
2727aec1d6eScindi 
2737aec1d6eScindi 	return (iaddr);
2747aec1d6eScindi }
2757aec1d6eScindi 
2767aec1d6eScindi /*
2778a40a695Sgavinm  * Extract bank bit arguments and swizzle if requested.
2787aec1d6eScindi  */
2797aec1d6eScindi static uint32_t
iaddr_to_bank(struct mcamd_hdl * hdl,const struct rct_rcbmap * rcbm,const struct rct_bnkswzlinfo * swzlp,uint64_t iaddr)2808a40a695Sgavinm iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
2818a40a695Sgavinm     const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr)
2827aec1d6eScindi {
2837aec1d6eScindi 	uint32_t addr = 0;
2847aec1d6eScindi 	int abitno, ibitno, i;
2857aec1d6eScindi 
2868a40a695Sgavinm 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
2878a40a695Sgavinm 		uint32_t val;
2888a40a695Sgavinm 
2898a40a695Sgavinm 		/*
2908a40a695Sgavinm 		 * rcb_bankbit[abitno] tells us which iaddr bit number
2918a40a695Sgavinm 		 * will form bit abitno of the bank address
2928a40a695Sgavinm 		 */
2938a40a695Sgavinm 		ibitno = rcbm->rcb_bankbit[abitno];
2948a40a695Sgavinm 		val = BITVAL(iaddr, ibitno);
2958a40a695Sgavinm 
2968a40a695Sgavinm 		/*
2978a40a695Sgavinm 		 * If bank swizzling is in operation then xor the bit value
2988a40a695Sgavinm 		 * obtained above with other iaddr bits.
2998a40a695Sgavinm 		 */
3008a40a695Sgavinm 		if (swzlp) {
3018a40a695Sgavinm 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
3028a40a695Sgavinm 				ibitno = swzlp->bswz_rowbits[abitno][i];
3038a40a695Sgavinm 				val ^= BITVAL(iaddr, ibitno);
3048a40a695Sgavinm 			}
3057aec1d6eScindi 		}
3068a40a695Sgavinm 
3078a40a695Sgavinm 		if (val)
3088a40a695Sgavinm 			SETBIT(addr, abitno);
3097aec1d6eScindi 	}
3107aec1d6eScindi 
3117aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> "
3127aec1d6eScindi 	    "bank 0x%x\n", iaddr, addr);
3137aec1d6eScindi 
3147aec1d6eScindi 	return (addr);
3157aec1d6eScindi }
3167aec1d6eScindi 
3177aec1d6eScindi /*
3187aec1d6eScindi  * bank_to_iaddr requires the iaddr reconstructed thus far with at least the
3197aec1d6eScindi  * row bits repopulated.  That's because in bank swizzle mode
3207aec1d6eScindi  * the bank bits are the result of xor'ing three original iaddr bits
3217aec1d6eScindi  * together - two of which come from the row address and the third we
3227aec1d6eScindi  * can reconstruct here.  Note that a zero bankaddr bit *can* result
3237aec1d6eScindi  * in a nonzero iaddr bit (unlike in row and col reconstruction).
3247aec1d6eScindi  */
3257aec1d6eScindi /*ARGSUSED*/
3267aec1d6eScindi 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)3278a40a695Sgavinm bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
3288a40a695Sgavinm     const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr)
3297aec1d6eScindi {
3307aec1d6eScindi 	uint64_t iaddr = 0;
3317aec1d6eScindi 	int abitno, pibitno, i;
3327aec1d6eScindi 
3338a40a695Sgavinm 	for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
3347aec1d6eScindi 		uint32_t val = BITVAL(bankaddr, abitno);
3358a40a695Sgavinm 		if (swzlp) {
3368a40a695Sgavinm 			for (i = 0; i < MC_RC_SWZLBITS; i++) {
3378a40a695Sgavinm 				pibitno = swzlp->bswz_rowbits[abitno][i];
3387aec1d6eScindi 				val ^= BITVAL(partiaddr, pibitno);
3397aec1d6eScindi 			}
3407aec1d6eScindi 		}
3417aec1d6eScindi 		if (val)
3428a40a695Sgavinm 			SETBIT(iaddr, rcbm->rcb_bankbit[abitno]);
3437aec1d6eScindi 	}
3447aec1d6eScindi 
3457aec1d6eScindi 	return (iaddr);
3467aec1d6eScindi }
3477aec1d6eScindi 
3487aec1d6eScindi 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)3498a40a695Sgavinm iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
3507aec1d6eScindi     uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp)
3517aec1d6eScindi {
3528a40a695Sgavinm 	const struct rct_bnkaddrmode *bamp;
3538a40a695Sgavinm 	const struct rct_rcbmap *rcbmp;
3548a40a695Sgavinm 	const struct rct_bnkswzlinfo *swzlp = NULL;
3558a40a695Sgavinm 	struct rct_csintlv csi;
3568a40a695Sgavinm 
3578a40a695Sgavinm 	if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp,
3588a40a695Sgavinm 	    mcpp->bnkswzl ? &swzlp : NULL, &csi,
3598a40a695Sgavinm 	    "iaddr_to_rcb") < 0)
3607aec1d6eScindi 		return (-1);	/* errno already set */
3617aec1d6eScindi 
3628a40a695Sgavinm 	*rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr);
3638a40a695Sgavinm 	*colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr);
3648a40a695Sgavinm 	*bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr);
3657aec1d6eScindi 
3667aec1d6eScindi 	return (0);
3677aec1d6eScindi }
3687aec1d6eScindi 
3697aec1d6eScindi /*
3707aec1d6eScindi  * Take a reconstructed InputAddr and undo the normalization described in
3717aec1d6eScindi  * BKDG 3.29 3.4.4 to include the base address of the MC if no node
3727aec1d6eScindi  * interleave or to insert the node interleave selection bits.
3737aec1d6eScindi  */
3747aec1d6eScindi static int
iaddr_unnormalize(struct mcamd_hdl * hdl,struct mcprops * mcpp,uint64_t iaddr,uint64_t * rsltp)3758a40a695Sgavinm iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr,
3767aec1d6eScindi     uint64_t *rsltp)
3777aec1d6eScindi {
3787aec1d6eScindi 	uint64_t dramaddr;
3797aec1d6eScindi 	int intlvbits;
3807aec1d6eScindi 
3817aec1d6eScindi 	switch (mcpp->intlven) {
3827aec1d6eScindi 	case 0x0:
3837aec1d6eScindi 		intlvbits = 0;
3847aec1d6eScindi 		break;
3857aec1d6eScindi 	case 0x1:
3867aec1d6eScindi 		intlvbits = 1;
3877aec1d6eScindi 		break;
3887aec1d6eScindi 	case 0x3:
3897aec1d6eScindi 		intlvbits = 2;
3907aec1d6eScindi 		break;
3917aec1d6eScindi 	case 0x7:
3927aec1d6eScindi 		intlvbits = 3;
3937aec1d6eScindi 		break;
3947aec1d6eScindi 	default:
3957aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: "
3967aec1d6eScindi 		    "illegal IntlvEn of %d for MC 0x%p\n",
3977aec1d6eScindi 		    (int)mcpp->intlven, (int)mcpp->num);
3987aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
3997aec1d6eScindi 	}
4007aec1d6eScindi 
4017aec1d6eScindi 	if (intlvbits != 0) {
4027aec1d6eScindi 		/*
4037aec1d6eScindi 		 * For a 2/4/8 way interleave iaddr was formed by excising
4047aec1d6eScindi 		 * 1, 2, or 3 bits 12:12, 13:12, or 14:12 from dramaddr,
4057aec1d6eScindi 		 * the removed bits having done their job by selecting the
4067aec1d6eScindi 		 * responding node.  So we must move bits 35:12 of the
4077aec1d6eScindi 		 * reconstructed iaddr up to make a 1, 2 or 3 bit hole and
4087aec1d6eScindi 		 * then fill those bits with the current IntlvSel value for
4097aec1d6eScindi 		 * this node.  The node base address must be zero if nodes
4107aec1d6eScindi 		 * are interleaved.
4118a40a695Sgavinm 		 *
4128a40a695Sgavinm 		 * Note that the DRAM controller InputAddr is still 36 bits
4138a40a695Sgavinm 		 * 35:0 on rev F.
4147aec1d6eScindi 		 */
4157aec1d6eScindi 		dramaddr = (BITS(iaddr, 35, 12) << intlvbits) |
4167aec1d6eScindi 		    (mcpp->intlvsel << 12) | BITS(iaddr, 11, 0);
4177aec1d6eScindi 	} else {
4187aec1d6eScindi 		dramaddr = iaddr + mcpp->base;
4197aec1d6eScindi 	}
4207aec1d6eScindi 
4217aec1d6eScindi 	*rsltp = dramaddr;
4227aec1d6eScindi 
4237aec1d6eScindi 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx "
4247aec1d6eScindi 	    "intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n",
4257aec1d6eScindi 	    iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base,
4267aec1d6eScindi 	    dramaddr);
4277aec1d6eScindi 
4287aec1d6eScindi 	return (0);
4297aec1d6eScindi }
4307aec1d6eScindi 
4317aec1d6eScindi int
mc_pa_to_offset(struct mcamd_hdl * hdl,mcamd_node_t * mc,mcamd_node_t * cs,uint64_t iaddr,uint64_t * offsetp)4327aec1d6eScindi mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
4338a40a695Sgavinm     uint64_t iaddr, uint64_t *offsetp)
4347aec1d6eScindi {
4357aec1d6eScindi 	mcamd_dimm_offset_un_t offset_un;
4367aec1d6eScindi 	uint_t csmode;
4377aec1d6eScindi 	uint32_t bankaddr, rowaddr, coladdr;
4388a40a695Sgavinm 	struct mcprops mcp;
4398a40a695Sgavinm 	struct csprops csp;
4407aec1d6eScindi 
4417aec1d6eScindi 	*offsetp = MCAMD_RC_INVALID_OFFSET;
4427aec1d6eScindi 
4437aec1d6eScindi 	if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 ||
4447aec1d6eScindi 	    getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0)
4457aec1d6eScindi 		return (-1);	/* errno already set */
4467aec1d6eScindi 
4478a40a695Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
4487aec1d6eScindi 
4497aec1d6eScindi 	if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr,
4507aec1d6eScindi 	    &coladdr, &bankaddr) < 0)
4517aec1d6eScindi 		return (-1);	/* errno already set */
4527aec1d6eScindi 
4537aec1d6eScindi 	offset_un.do_offset = 0;
4547aec1d6eScindi 
4557aec1d6eScindi 	offset_un.do_valid = 1;
4567aec1d6eScindi 	offset_un.do_version = MCAMD_OFFSET_VERSION;
45720c794b3Sgavinm 	offset_un.do_rank = (uint32_t)csp.dimmrank;
4587aec1d6eScindi 	offset_un.do_row = rowaddr;
4597aec1d6eScindi 	offset_un.do_bank = bankaddr;
4607aec1d6eScindi 	offset_un.do_col = coladdr;
4617aec1d6eScindi 
4627aec1d6eScindi 	*offsetp = offset_un.do_offset;
4637aec1d6eScindi 
4647aec1d6eScindi 	return (0);
4657aec1d6eScindi }
4667aec1d6eScindi 
4677aec1d6eScindi /*
4688a40a695Sgavinm  * Given an MC, DIMM and offset (dimm rank, row, col, internal bank) we
4697aec1d6eScindi  * find the corresponding chip-select for the rank and then reconstruct
4707aec1d6eScindi  * a system address.  In the absence of serial number support it is possible
4717aec1d6eScindi  * that we may be asked to perform this operation on a dimm which has been
4727aec1d6eScindi  * swapped, perhaps even for a dimm of different size and number of ranks.
4737aec1d6eScindi  * This may happen if fmadm repair has not been used.  There are some
4747aec1d6eScindi  * unused bits in the offset and we could guard against this a little
4757aec1d6eScindi  * by recording in those bit some of the physical characteristic of the
4767aec1d6eScindi  * original DIMM such as size, number of ranks etc.
4777aec1d6eScindi  */
4787aec1d6eScindi int
mc_offset_to_pa(struct mcamd_hdl * hdl,mcamd_node_t * mc,mcamd_node_t * dimm,uint64_t offset,uint64_t * pap)4797aec1d6eScindi mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
4807aec1d6eScindi     uint64_t offset, uint64_t *pap)
4817aec1d6eScindi {
4827aec1d6eScindi 	mcamd_node_t *cs;
4837aec1d6eScindi 	mcamd_dimm_offset_un_t off_un;
4847aec1d6eScindi 	uint32_t rank, rowaddr, bankaddr, coladdr;
4857aec1d6eScindi 	uint64_t iaddr = 0;
4868a40a695Sgavinm 	const struct rct_bnkaddrmode *bamp;
4878a40a695Sgavinm 	const struct rct_rcbmap *rcbmp;
4888a40a695Sgavinm 	const struct rct_bnkswzlinfo *swzlp = NULL;
4898a40a695Sgavinm 	struct rct_csintlv csi;
4908a40a695Sgavinm 	struct mcprops mcp;
4918a40a695Sgavinm 	struct csprops csp;
4927aec1d6eScindi 	uint64_t csmode;
4938a40a695Sgavinm 	int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo;
4947aec1d6eScindi 
4957aec1d6eScindi 	off_un.do_offset = offset;
4967aec1d6eScindi 	rank = off_un.do_rank;
4977aec1d6eScindi 	bankaddr = off_un.do_bank;
4987aec1d6eScindi 	rowaddr = off_un.do_row;
4997aec1d6eScindi 	coladdr = off_un.do_col;
5007aec1d6eScindi 
5018a40a695Sgavinm 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx "
5028a40a695Sgavinm 	    "-> rank %d bank %d row 0x%x col 0x%x\n", offset,
5038a40a695Sgavinm 	    rank, bankaddr, rowaddr, coladdr);
5048a40a695Sgavinm 
5057aec1d6eScindi 	if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0)
5067aec1d6eScindi 		return (-1);	/* errno already set */
5077aec1d6eScindi 
5088a40a695Sgavinm 	maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev);
5098a40a695Sgavinm 	maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev);
5108a40a695Sgavinm 	masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev);
5118a40a695Sgavinm 	masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev);
5128a40a695Sgavinm 
5137aec1d6eScindi 	/*
5148a40a695Sgavinm 	 * Find the chip-select on this dimm using the given rank.
5157aec1d6eScindi 	 */
5168a40a695Sgavinm 	for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL;
5178a40a695Sgavinm 	    cs = mcamd_cs_next(hdl, dimm, cs)) {
5188a40a695Sgavinm 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
5198a40a695Sgavinm 			return (-1);	/* errno already set */
5208a40a695Sgavinm 
5218a40a695Sgavinm 		if (csp.dimmrank == rank)
5228a40a695Sgavinm 			break;
5237aec1d6eScindi 	}
5248a40a695Sgavinm 
5258a40a695Sgavinm 	if (cs == NULL) {
5267aec1d6eScindi 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current "
5278a40a695Sgavinm 		    "dimm in this slot does not have a cs using rank %d\n",
5287aec1d6eScindi 		    rank);
5297aec1d6eScindi 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5307aec1d6eScindi 	}
5317aec1d6eScindi 
5328a40a695Sgavinm 	/*
5338a40a695Sgavinm 	 * If the cs# has been substituted by the online spare then the
5348a40a695Sgavinm 	 * given unum is not actually contributing to the system address
5358a40a695Sgavinm 	 * map since all accesses to it are redirected.
5368a40a695Sgavinm 	 *
5378a40a695Sgavinm 	 * If the cs# failed BIOS test it is not in the address map.
5388a40a695Sgavinm 	 *
5398a40a695Sgavinm 	 * If the cs# is the online spare cs# then it is contributing to
5408a40a695Sgavinm 	 * the system address map only if swapped in, and the csbase etc
5418a40a695Sgavinm 	 * parameters to use must be those of the bad cs#.
5428a40a695Sgavinm 	 */
5438a40a695Sgavinm 	if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) {
5448a40a695Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5458a40a695Sgavinm 	} else if (csp.testfail) {
5468a40a695Sgavinm 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
5478a40a695Sgavinm 	} else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs &&
5488a40a695Sgavinm 	    mcp.badcs != MC_INVALNUM) {
5498a40a695Sgavinm 		/*
5508a40a695Sgavinm 		 * Iterate over all cs# of this memory controller to find
5518a40a695Sgavinm 		 * the bad one - the bad cs# need not be on the same dimm
5528a40a695Sgavinm 		 * as the spare.
5538a40a695Sgavinm 		 */
5548a40a695Sgavinm 		for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
5558a40a695Sgavinm 		    cs = mcamd_cs_next(hdl, mc, cs)) {
5568a40a695Sgavinm 			mcamd_prop_t csnum;
5578a40a695Sgavinm 
5588a40a695Sgavinm 			if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM,
5598a40a695Sgavinm 			    &csnum)) {
5608a40a695Sgavinm 				mcamd_dprintf(hdl, MCAMD_DBG_ERR,
5618a40a695Sgavinm 				    "mcamd_offset_to_pa: csnum lookup failed "
5628a40a695Sgavinm 				    "while looking for bad cs#");
5638a40a695Sgavinm 				return (mcamd_set_errno(hdl,
5648a40a695Sgavinm 				    EMCAMD_TREEINVALID));
5658a40a695Sgavinm 			}
5668a40a695Sgavinm 			if (csnum == mcp.badcs)
5678a40a695Sgavinm 				break;
5688a40a695Sgavinm 		}
5698a40a695Sgavinm 
5708a40a695Sgavinm 		if (cs == NULL) {
5718a40a695Sgavinm 			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: "
5728a40a695Sgavinm 			    "failed to find cs for bad cs#%d\n", mcp.badcs);
573*981fe1b1SJohn Levon 			return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
5748a40a695Sgavinm 		}
5758a40a695Sgavinm 
5768a40a695Sgavinm 		/* found bad cs - reread properties from it instead of spare */
5778a40a695Sgavinm 		if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
5788a40a695Sgavinm 			return (-1);	/* errno already set */
5798a40a695Sgavinm 	}
5807aec1d6eScindi 
5818a40a695Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
5827aec1d6eScindi 
5838a40a695Sgavinm 	if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp,
5848a40a695Sgavinm 	    mcp.bnkswzl ? &swzlp : NULL, &csi,
5857aec1d6eScindi 	    "mc_offset_to_pa") < 0)
5867aec1d6eScindi 		return (-1);	/* errno already set */
5877aec1d6eScindi 
5888a40a695Sgavinm 	/*
5898a40a695Sgavinm 	 * If there are umaskable DRAM InputAddr bits the add those bits
5908a40a695Sgavinm 	 * to iaddr from the cs base address.
5918a40a695Sgavinm 	 */
5928a40a695Sgavinm 	if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) {
5937aec1d6eScindi 		iaddr |= iaddr_add(hdl, iaddr,
5948a40a695Sgavinm 		    BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev),
5957aec1d6eScindi 		    maskhi_hi + 1), "unmaskable cs basehi bits");
5967aec1d6eScindi 	}
5977aec1d6eScindi 
5988a40a695Sgavinm 	/*
5998a40a695Sgavinm 	 * basehi bits not meing masked pass straight through to the
6008a40a695Sgavinm 	 * iaddr.
6018a40a695Sgavinm 	 */
6027aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6037aec1d6eScindi 	    BITS(csp.base, maskhi_hi, maskhi_lo) &
6047aec1d6eScindi 	    ~BITS(csp.mask, maskhi_hi, maskhi_lo),
6057aec1d6eScindi 	    "cs basehi bits not being masked");
6067aec1d6eScindi 
6078a40a695Sgavinm 	/*
6088a40a695Sgavinm 	 * if cs interleaving is active then baselo address bit are being
6098a40a695Sgavinm 	 * masked - pass the rest through.
6108a40a695Sgavinm 	 */
6118a40a695Sgavinm 	if (mcp.csintlvfctr > 1) {
6127aec1d6eScindi 		iaddr |= iaddr_add(hdl, iaddr,
6137aec1d6eScindi 		    BITS(csp.base, masklo_hi, masklo_lo) &
6147aec1d6eScindi 		    ~BITS(csp.mask, masklo_hi, masklo_lo),
6157aec1d6eScindi 		    "cs baselo bits not being masked");
6167aec1d6eScindi 	}
6177aec1d6eScindi 
6188a40a695Sgavinm 	/*
6198a40a695Sgavinm 	 * Reconstruct iaddr bits from known row address
6208a40a695Sgavinm 	 */
6217aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6228a40a695Sgavinm 	    row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr),
6237aec1d6eScindi 	    "add iaddr bits from row");
6247aec1d6eScindi 
6258a40a695Sgavinm 	/*
6268a40a695Sgavinm 	 * Reconstruct iaddr bits from known column address
6278a40a695Sgavinm 	 */
6287aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6298a40a695Sgavinm 	    col_to_iaddr(hdl, bamp, rcbmp, coladdr),
6307aec1d6eScindi 	    "add iaddr bits from col");
6317aec1d6eScindi 
6328a40a695Sgavinm 	/*
6338a40a695Sgavinm 	 * Reconstruct iaddr bits from known internal banksel address
6348a40a695Sgavinm 	 */
6357aec1d6eScindi 	iaddr |= iaddr_add(hdl, iaddr,
6368a40a695Sgavinm 	    bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr),
6377aec1d6eScindi 	    "add iaddr bits from bank");
6387aec1d6eScindi 
6398a40a695Sgavinm 	/*
6408a40a695Sgavinm 	 * Move iaddr up into the range for this MC and insert any
6418a40a695Sgavinm 	 * node interleave selection bits.
6428a40a695Sgavinm 	 */
6437aec1d6eScindi 	if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0)
6447aec1d6eScindi 		return (-1);	/* errno already set */
6457aec1d6eScindi 
6467aec1d6eScindi 	return (0);
6477aec1d6eScindi }
6487aec1d6eScindi 
6497aec1d6eScindi int
mcamd_cs_size(struct mcamd_hdl * hdl,mcamd_node_t * mc,int csnum,size_t * szp)6507aec1d6eScindi mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp)
6517aec1d6eScindi {
6527aec1d6eScindi 	uint_t csmode;
6538a40a695Sgavinm 	struct mcprops mcp;
6548a40a695Sgavinm 	const struct rct_bnkaddrmode *bamp;
6557aec1d6eScindi 
6567aec1d6eScindi 	if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0)
6577aec1d6eScindi 		return (-1);	/* errno already set */
6587aec1d6eScindi 
6598a40a695Sgavinm 	csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum);
6607aec1d6eScindi 
6618a40a695Sgavinm 	if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL,
6628a40a695Sgavinm 	    "mcamd_cs_size") < 0)
6637aec1d6eScindi 		return (-1);	/* errno already set */
6647aec1d6eScindi 
6657aec1d6eScindi 	*szp = MC_CS_SIZE(bamp, mcp.width);
6667aec1d6eScindi 
6677aec1d6eScindi 	return (0);
6687aec1d6eScindi }
669