1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  *
  21  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  22  * Use is subject to license terms.
  23  */
  24 
  25 /*
  26  * Copyright (c) 2018, Joyent, Inc.
  27  */
  28 
  29 #include <mcamd_api.h>
  30 #include <mcamd_err.h>
  31 #include <mcamd_rowcol_impl.h>
  32 
  33 /*
  34  * Convenience structures to stash MC and CS properties in.
  35  */
  36 struct mcprops {
  37         mcamd_prop_t num;               /* corresponding chip number */
  38         mcamd_prop_t rev;               /* revision */
  39         mcamd_prop_t width;             /* access width */
  40         mcamd_prop_t base;              /* MC base address */
  41         mcamd_prop_t lim;               /* MC limit address */
  42         mcamd_prop_t csbnkmap_reg;      /* chip-select bank map */
  43         mcamd_prop_t intlven;           /* Node-intlv mask */
  44         mcamd_prop_t intlvsel;          /* Node-intlv selection for this node */
  45         mcamd_prop_t csintlvfctr;       /* cs intlv factor on this node */
  46         mcamd_prop_t bnkswzl;           /* bank-swizzle mode */
  47         mcamd_prop_t sparecs;           /* spare cs#, if any */
  48         mcamd_prop_t badcs;             /* substituted cs#, if any */
  49 };
  50 
  51 struct csprops {
  52         mcamd_prop_t num;               /* chip-select number */
  53         mcamd_prop_t base;              /* chip-select base address */
  54         mcamd_prop_t mask;              /* chip-select mask */
  55         mcamd_prop_t testfail;          /* marked testFail */
  56         mcamd_prop_t dimmrank;          /* rank number on dimm(s) */
  57 };
  58 
  59 static int
  60 getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller,
  61     struct mcprops *pp)
  62 {
  63         if (!mcamd_get_numprops(hdl,
  64             mc, MCAMD_PROP_NUM, &pp->num,
  65             mc, MCAMD_PROP_REV, &pp->rev,
  66             mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width,
  67             mc, MCAMD_PROP_BASE_ADDR, &pp->base,
  68             mc, MCAMD_PROP_LIM_ADDR, &pp->lim,
  69             mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg,
  70             mc, MCAMD_PROP_ILEN, &pp->intlven,
  71             mc, MCAMD_PROP_ILSEL, &pp->intlvsel,
  72             mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr,
  73             mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl,
  74             mc, MCAMD_PROP_SPARECS, &pp->sparecs,
  75             mc, MCAMD_PROP_BADCS, &pp->badcs,
  76             NULL)) {
  77                 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc "
  78                     "props for mc 0x%p\n", caller, mc);
  79                 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
  80         }
  81 
  82         return (0);
  83 }
  84 
  85 static int
  86 getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
  87     struct csprops *csp)
  88 {
  89         if (!mcamd_get_numprops(hdl,
  90             cs, MCAMD_PROP_NUM, &csp->num,
  91             cs, MCAMD_PROP_BASE_ADDR, &csp->base,
  92             cs, MCAMD_PROP_MASK, &csp->mask,
  93             cs, MCAMD_PROP_TESTFAIL, &csp->testfail,
  94             cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank,
  95             NULL))  {
  96                 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs "
  97                     "props for cs 0x%p\n", caller, cs);
  98                 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
  99         }
 100 
 101         return (0);
 102 }
 103 
 104 static int
 105 gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
 106     const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp,
 107     const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid,
 108     const char *caller)
 109 {
 110         uint_t rev = (uint_t)mcpp->rev;
 111         int width = (int)mcpp->width;
 112 
 113         if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) {
 114                 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode "
 115                     "table for MC rev %d csmode %d\n", caller, rev, csmode);
 116                 return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
 117         }
 118 
 119         if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) {
 120                 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map "
 121                     "table for MC rev %d csmode %d\n", caller,
 122                     rev, csmode);
 123                 return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
 124         }
 125 
 126         if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) {
 127                 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling "
 128                     "table for MC rev %d width %d\n", caller, rev, width);
 129                 return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
 130         }
 131 
 132         if (csid) {
 133                 if (mcpp->csintlvfctr > 1) {
 134                         rct_csintlv_bits(rev, width, csmode,
 135                             mcpp->csintlvfctr, csid);
 136                         if (csid->csi_factor == 0) {
 137                                 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: "
 138                                     "could not work out cs interleave "
 139                                     "paramters for MC rev %d, width %d, "
 140                                     "csmode %d, factor %d\n", caller,
 141                                     rev, width, csmode,
 142                                     (int)mcpp->csintlvfctr);
 143                                 return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
 144                         }
 145                 } else {
 146                         csid->csi_factor = 0;
 147                 }
 148         }
 149 
 150         return (0);
 151 }
 152 
 153 static uint64_t
 154 iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what)
 155 {
 156         uint64_t new = in | add;
 157 
 158         mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx",
 159             what, in, add, new);
 160 
 161         return (add);
 162 }
 163 
 164 /*
 165  * Where the number of row/col address bits is ambiguous (affects CG and
 166  * earlier only) we will assign the "floating" bit to row address.  If
 167  * we adopt the same convention in address reconstruction then all should work.
 168  */
 169 static uint32_t
 170 iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
 171     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr)
 172 {
 173         uint32_t addr = 0;
 174         int abitno, ibitno;
 175         int nbits = bamp->bam_nrows;
 176         int swapped = 0;
 177 
 178         for (abitno = 0; abitno < nbits; abitno++) {
 179                 ibitno = rcbm->rcb_rowbit[abitno];
 180                 if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
 181                         ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
 182                         swapped++;
 183                 }
 184                 if (BITVAL(iaddr, ibitno) != 0)
 185                         SETBIT(addr, abitno);
 186         }
 187 
 188         mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> "
 189             "row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped);
 190 
 191         return (addr);
 192 }
 193 
 194 /*ARGSUSED*/
 195 static uint64_t
 196 row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
 197     const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr)
 198 {
 199         uint64_t iaddr = 0;
 200         int abitno, ibitno;
 201         int nbits = bamp->bam_nrows;
 202 
 203         for (abitno = 0; abitno < nbits; abitno++) {
 204                 if (BIT(rowaddr, abitno) == 0)
 205                         continue;
 206                 ibitno = rcbm->rcb_rowbit[abitno];
 207                 if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
 208                         ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
 209                 }
 210                 SETBIT(iaddr, ibitno);
 211         }
 212 
 213         return (iaddr);
 214 }
 215 
 216 
 217 static uint32_t
 218 iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
 219     const struct rct_rcbmap *rcbm, uint64_t iaddr)
 220 {
 221         uint32_t addr = 0;
 222         int abitno, ibitno, bias = 0;
 223         int nbits = bamp->bam_ncols;
 224 
 225         /*
 226          * Knock off a column bit if the numbers are ambiguous
 227          */
 228         if (bamp->bam_ambig)
 229                 nbits--;
 230 
 231         for (abitno = 0; abitno < nbits; abitno++) {
 232                 if (abitno == MC_PC_COLADDRBIT)
 233                         bias = 1;
 234 
 235                 ibitno = rcbm->rcb_colbit[abitno + bias];
 236 
 237                 if (BITVAL(iaddr, ibitno) != 0)
 238                         SETBIT(addr, abitno);
 239         }
 240 
 241         mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> "
 242             "col 0x%x\n", iaddr, addr);
 243 
 244         return (addr);
 245 }
 246 
 247 /*ARGSUSED*/
 248 static uint64_t
 249 col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
 250     const struct rct_rcbmap *rcbm, uint32_t coladdr)
 251 {
 252         uint64_t iaddr = 0;
 253         int abitno, ibitno, bias = 0;
 254         int nbits = bamp->bam_ncols;
 255 
 256         /*
 257          * Knock off a column bit if the numbers are ambiguous
 258          */
 259         if (bamp->bam_ambig)
 260                 nbits--;
 261 
 262         for (abitno = 0; abitno < nbits; abitno++) {
 263                 if (BIT(coladdr, abitno) == 0)
 264                         continue;
 265 
 266                 if (abitno == MC_PC_COLADDRBIT)
 267                         bias = 1;
 268 
 269                 ibitno = rcbm->rcb_colbit[abitno + bias];
 270                 SETBIT(iaddr, ibitno);
 271         }
 272 
 273         return (iaddr);
 274 }
 275 
 276 /*
 277  * Extract bank bit arguments and swizzle if requested.
 278  */
 279 static uint32_t
 280 iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
 281     const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr)
 282 {
 283         uint32_t addr = 0;
 284         int abitno, ibitno, i;
 285 
 286         for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
 287                 uint32_t val;
 288 
 289                 /*
 290                  * rcb_bankbit[abitno] tells us which iaddr bit number
 291                  * will form bit abitno of the bank address
 292                  */
 293                 ibitno = rcbm->rcb_bankbit[abitno];
 294                 val = BITVAL(iaddr, ibitno);
 295 
 296                 /*
 297                  * If bank swizzling is in operation then xor the bit value
 298                  * obtained above with other iaddr bits.
 299                  */
 300                 if (swzlp) {
 301                         for (i = 0; i < MC_RC_SWZLBITS; i++) {
 302                                 ibitno = swzlp->bswz_rowbits[abitno][i];
 303                                 val ^= BITVAL(iaddr, ibitno);
 304                         }
 305                 }
 306 
 307                 if (val)
 308                         SETBIT(addr, abitno);
 309         }
 310 
 311         mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> "
 312             "bank 0x%x\n", iaddr, addr);
 313 
 314         return (addr);
 315 }
 316 
 317 /*
 318  * bank_to_iaddr requires the iaddr reconstructed thus far with at least the
 319  * row bits repopulated.  That's because in bank swizzle mode
 320  * the bank bits are the result of xor'ing three original iaddr bits
 321  * together - two of which come from the row address and the third we
 322  * can reconstruct here.  Note that a zero bankaddr bit *can* result
 323  * in a nonzero iaddr bit (unlike in row and col reconstruction).
 324  */
 325 /*ARGSUSED*/
 326 static uint64_t
 327 bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
 328     const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr)
 329 {
 330         uint64_t iaddr = 0;
 331         int abitno, pibitno, i;
 332 
 333         for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
 334                 uint32_t val = BITVAL(bankaddr, abitno);
 335                 if (swzlp) {
 336                         for (i = 0; i < MC_RC_SWZLBITS; i++) {
 337                                 pibitno = swzlp->bswz_rowbits[abitno][i];
 338                                 val ^= BITVAL(partiaddr, pibitno);
 339                         }
 340                 }
 341                 if (val)
 342                         SETBIT(iaddr, rcbm->rcb_bankbit[abitno]);
 343         }
 344 
 345         return (iaddr);
 346 }
 347 
 348 static int
 349 iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
 350     uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp)
 351 {
 352         const struct rct_bnkaddrmode *bamp;
 353         const struct rct_rcbmap *rcbmp;
 354         const struct rct_bnkswzlinfo *swzlp = NULL;
 355         struct rct_csintlv csi;
 356 
 357         if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp,
 358             mcpp->bnkswzl ? &swzlp : NULL, &csi,
 359             "iaddr_to_rcb") < 0)
 360                 return (-1);    /* errno already set */
 361 
 362         *rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr);
 363         *colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr);
 364         *bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr);
 365 
 366         return (0);
 367 }
 368 
 369 /*
 370  * Take a reconstructed InputAddr and undo the normalization described in
 371  * BKDG 3.29 3.4.4 to include the base address of the MC if no node
 372  * interleave or to insert the node interleave selection bits.
 373  */
 374 static int
 375 iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr,
 376     uint64_t *rsltp)
 377 {
 378         uint64_t dramaddr;
 379         int intlvbits;
 380 
 381         switch (mcpp->intlven) {
 382         case 0x0:
 383                 intlvbits = 0;
 384                 break;
 385         case 0x1:
 386                 intlvbits = 1;
 387                 break;
 388         case 0x3:
 389                 intlvbits = 2;
 390                 break;
 391         case 0x7:
 392                 intlvbits = 3;
 393                 break;
 394         default:
 395                 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: "
 396                     "illegal IntlvEn of %d for MC 0x%p\n",
 397                     (int)mcpp->intlven, (int)mcpp->num);
 398                 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
 399         }
 400 
 401         if (intlvbits != 0) {
 402                 /*
 403                  * For a 2/4/8 way interleave iaddr was formed by excising
 404                  * 1, 2, or 3 bits 12:12, 13:12, or 14:12 from dramaddr,
 405                  * the removed bits having done their job by selecting the
 406                  * responding node.  So we must move bits 35:12 of the
 407                  * reconstructed iaddr up to make a 1, 2 or 3 bit hole and
 408                  * then fill those bits with the current IntlvSel value for
 409                  * this node.  The node base address must be zero if nodes
 410                  * are interleaved.
 411                  *
 412                  * Note that the DRAM controller InputAddr is still 36 bits
 413                  * 35:0 on rev F.
 414                  */
 415                 dramaddr = (BITS(iaddr, 35, 12) << intlvbits) |
 416                     (mcpp->intlvsel << 12) | BITS(iaddr, 11, 0);
 417         } else {
 418                 dramaddr = iaddr + mcpp->base;
 419         }
 420 
 421         *rsltp = dramaddr;
 422 
 423         mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx "
 424             "intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n",
 425             iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base,
 426             dramaddr);
 427 
 428         return (0);
 429 }
 430 
 431 int
 432 mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
 433     uint64_t iaddr, uint64_t *offsetp)
 434 {
 435         mcamd_dimm_offset_un_t offset_un;
 436         uint_t csmode;
 437         uint32_t bankaddr, rowaddr, coladdr;
 438         struct mcprops mcp;
 439         struct csprops csp;
 440 
 441         *offsetp = MCAMD_RC_INVALID_OFFSET;
 442 
 443         if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 ||
 444             getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0)
 445                 return (-1);    /* errno already set */
 446 
 447         csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
 448 
 449         if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr,
 450             &coladdr, &bankaddr) < 0)
 451                 return (-1);    /* errno already set */
 452 
 453         offset_un.do_offset = 0;
 454 
 455         offset_un.do_valid = 1;
 456         offset_un.do_version = MCAMD_OFFSET_VERSION;
 457         offset_un.do_rank = (uint32_t)csp.dimmrank;
 458         offset_un.do_row = rowaddr;
 459         offset_un.do_bank = bankaddr;
 460         offset_un.do_col = coladdr;
 461 
 462         *offsetp = offset_un.do_offset;
 463 
 464         return (0);
 465 }
 466 
 467 /*
 468  * Given an MC, DIMM and offset (dimm rank, row, col, internal bank) we
 469  * find the corresponding chip-select for the rank and then reconstruct
 470  * a system address.  In the absence of serial number support it is possible
 471  * that we may be asked to perform this operation on a dimm which has been
 472  * swapped, perhaps even for a dimm of different size and number of ranks.
 473  * This may happen if fmadm repair has not been used.  There are some
 474  * unused bits in the offset and we could guard against this a little
 475  * by recording in those bit some of the physical characteristic of the
 476  * original DIMM such as size, number of ranks etc.
 477  */
 478 int
 479 mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
 480     uint64_t offset, uint64_t *pap)
 481 {
 482         mcamd_node_t *cs;
 483         mcamd_dimm_offset_un_t off_un;
 484         uint32_t rank, rowaddr, bankaddr, coladdr;
 485         uint64_t iaddr = 0;
 486         const struct rct_bnkaddrmode *bamp;
 487         const struct rct_rcbmap *rcbmp;
 488         const struct rct_bnkswzlinfo *swzlp = NULL;
 489         struct rct_csintlv csi;
 490         struct mcprops mcp;
 491         struct csprops csp;
 492         uint64_t csmode;
 493         int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo;
 494 
 495         off_un.do_offset = offset;
 496         rank = off_un.do_rank;
 497         bankaddr = off_un.do_bank;
 498         rowaddr = off_un.do_row;
 499         coladdr = off_un.do_col;
 500 
 501         mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx "
 502             "-> rank %d bank %d row 0x%x col 0x%x\n", offset,
 503             rank, bankaddr, rowaddr, coladdr);
 504 
 505         if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0)
 506                 return (-1);    /* errno already set */
 507 
 508         maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev);
 509         maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev);
 510         masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev);
 511         masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev);
 512 
 513         /*
 514          * Find the chip-select on this dimm using the given rank.
 515          */
 516         for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL;
 517             cs = mcamd_cs_next(hdl, dimm, cs)) {
 518                 if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
 519                         return (-1);    /* errno already set */
 520 
 521                 if (csp.dimmrank == rank)
 522                         break;
 523         }
 524 
 525         if (cs == NULL) {
 526                 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current "
 527                     "dimm in this slot does not have a cs using rank %d\n",
 528                     rank);
 529                 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
 530         }
 531 
 532         /*
 533          * If the cs# has been substituted by the online spare then the
 534          * given unum is not actually contributing to the system address
 535          * map since all accesses to it are redirected.
 536          *
 537          * If the cs# failed BIOS test it is not in the address map.
 538          *
 539          * If the cs# is the online spare cs# then it is contributing to
 540          * the system address map only if swapped in, and the csbase etc
 541          * parameters to use must be those of the bad cs#.
 542          */
 543         if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) {
 544                 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
 545         } else if (csp.testfail) {
 546                 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
 547         } else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs &&
 548             mcp.badcs != MC_INVALNUM) {
 549                 /*
 550                  * Iterate over all cs# of this memory controller to find
 551                  * the bad one - the bad cs# need not be on the same dimm
 552                  * as the spare.
 553                  */
 554                 for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
 555                     cs = mcamd_cs_next(hdl, mc, cs)) {
 556                         mcamd_prop_t csnum;
 557 
 558                         if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM,
 559                             &csnum)) {
 560                                 mcamd_dprintf(hdl, MCAMD_DBG_ERR,
 561                                     "mcamd_offset_to_pa: csnum lookup failed "
 562                                     "while looking for bad cs#");
 563                                 return (mcamd_set_errno(hdl,
 564                                     EMCAMD_TREEINVALID));
 565                         }
 566                         if (csnum == mcp.badcs)
 567                                 break;
 568                 }
 569 
 570                 if (cs == NULL) {
 571                         mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: "
 572                             "failed to find cs for bad cs#%d\n", mcp.badcs);
 573                         return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
 574                 }
 575 
 576                 /* found bad cs - reread properties from it instead of spare */
 577                 if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
 578                         return (-1);    /* errno already set */
 579         }
 580 
 581         csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
 582 
 583         if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp,
 584             mcp.bnkswzl ? &swzlp : NULL, &csi,
 585             "mc_offset_to_pa") < 0)
 586                 return (-1);    /* errno already set */
 587 
 588         /*
 589          * If there are umaskable DRAM InputAddr bits the add those bits
 590          * to iaddr from the cs base address.
 591          */
 592         if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) {
 593                 iaddr |= iaddr_add(hdl, iaddr,
 594                     BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev),
 595                     maskhi_hi + 1), "unmaskable cs basehi bits");
 596         }
 597 
 598         /*
 599          * basehi bits not meing masked pass straight through to the
 600          * iaddr.
 601          */
 602         iaddr |= iaddr_add(hdl, iaddr,
 603             BITS(csp.base, maskhi_hi, maskhi_lo) &
 604             ~BITS(csp.mask, maskhi_hi, maskhi_lo),
 605             "cs basehi bits not being masked");
 606 
 607         /*
 608          * if cs interleaving is active then baselo address bit are being
 609          * masked - pass the rest through.
 610          */
 611         if (mcp.csintlvfctr > 1) {
 612                 iaddr |= iaddr_add(hdl, iaddr,
 613                     BITS(csp.base, masklo_hi, masklo_lo) &
 614                     ~BITS(csp.mask, masklo_hi, masklo_lo),
 615                     "cs baselo bits not being masked");
 616         }
 617 
 618         /*
 619          * Reconstruct iaddr bits from known row address
 620          */
 621         iaddr |= iaddr_add(hdl, iaddr,
 622             row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr),
 623             "add iaddr bits from row");
 624 
 625         /*
 626          * Reconstruct iaddr bits from known column address
 627          */
 628         iaddr |= iaddr_add(hdl, iaddr,
 629             col_to_iaddr(hdl, bamp, rcbmp, coladdr),
 630             "add iaddr bits from col");
 631 
 632         /*
 633          * Reconstruct iaddr bits from known internal banksel address
 634          */
 635         iaddr |= iaddr_add(hdl, iaddr,
 636             bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr),
 637             "add iaddr bits from bank");
 638 
 639         /*
 640          * Move iaddr up into the range for this MC and insert any
 641          * node interleave selection bits.
 642          */
 643         if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0)
 644                 return (-1);    /* errno already set */
 645 
 646         return (0);
 647 }
 648 
 649 int
 650 mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp)
 651 {
 652         uint_t csmode;
 653         struct mcprops mcp;
 654         const struct rct_bnkaddrmode *bamp;
 655 
 656         if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0)
 657                 return (-1);    /* errno already set */
 658 
 659         csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum);
 660 
 661         if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL,
 662             "mcamd_cs_size") < 0)
 663                 return (-1);    /* errno already set */
 664 
 665         *szp = MC_CS_SIZE(bamp, mcp.width);
 666 
 667         return (0);
 668 }