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 }