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 }