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