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