| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| * |
| * Copyright 2007 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <mcamd_api.h> |
| #include <mcamd_err.h> |
| #include <mcamd_rowcol_impl.h> |
| |
| /* |
| * Convenience structures to stash MC and CS properties in. |
| */ |
| struct mcprops { |
| mcamd_prop_t num; /* corresponding chip number */ |
| mcamd_prop_t rev; /* revision */ |
| mcamd_prop_t width; /* access width */ |
| mcamd_prop_t base; /* MC base address */ |
| mcamd_prop_t lim; /* MC limit address */ |
| mcamd_prop_t csbnkmap_reg; /* chip-select bank map */ |
| mcamd_prop_t intlven; /* Node-intlv mask */ |
| mcamd_prop_t intlvsel; /* Node-intlv selection for this node */ |
| mcamd_prop_t csintlvfctr; /* cs intlv factor on this node */ |
| mcamd_prop_t bnkswzl; /* bank-swizzle mode */ |
| mcamd_prop_t sparecs; /* spare cs#, if any */ |
| mcamd_prop_t badcs; /* substituted cs#, if any */ |
| }; |
| |
| struct csprops { |
| mcamd_prop_t num; /* chip-select number */ |
| mcamd_prop_t base; /* chip-select base address */ |
| mcamd_prop_t mask; /* chip-select mask */ |
| mcamd_prop_t testfail; /* marked testFail */ |
| mcamd_prop_t dimmrank; /* rank number on dimm(s) */ |
| }; |
| |
| static int |
| getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller, |
| struct mcprops *pp) |
| { |
| if (!mcamd_get_numprops(hdl, |
| mc, MCAMD_PROP_NUM, &pp->num, |
| mc, MCAMD_PROP_REV, &pp->rev, |
| mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width, |
| mc, MCAMD_PROP_BASE_ADDR, &pp->base, |
| mc, MCAMD_PROP_LIM_ADDR, &pp->lim, |
| mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg, |
| mc, MCAMD_PROP_ILEN, &pp->intlven, |
| mc, MCAMD_PROP_ILSEL, &pp->intlvsel, |
| mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr, |
| mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl, |
| mc, MCAMD_PROP_SPARECS, &pp->sparecs, |
| mc, MCAMD_PROP_BADCS, &pp->badcs, |
| NULL)) { |
| mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc " |
| "props for mc 0x%p\n", caller, mc); |
| return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); |
| } |
| |
| return (0); |
| } |
| |
| static int |
| getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller, |
| struct csprops *csp) |
| { |
| if (!mcamd_get_numprops(hdl, |
| cs, MCAMD_PROP_NUM, &csp->num, |
| cs, MCAMD_PROP_BASE_ADDR, &csp->base, |
| cs, MCAMD_PROP_MASK, &csp->mask, |
| cs, MCAMD_PROP_TESTFAIL, &csp->testfail, |
| cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank, |
| NULL)) { |
| mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs " |
| "props for cs 0x%p\n", caller, cs); |
| return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); |
| } |
| |
| return (0); |
| } |
| |
| static int |
| gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp, |
| const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp, |
| const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid, |
| const char *caller) |
| { |
| uint_t rev = (uint_t)mcpp->rev; |
| int width = (int)mcpp->width; |
| |
| if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) { |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode " |
| "table for MC rev %d csmode %d\n", caller, rev, csmode); |
| return (mcamd_set_errno(hdl, EMCAMD_NOTSUP)); |
| } |
| |
| if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) { |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map " |
| "table for MC rev %d csmode %d\n", caller, |
| rev, csmode); |
| return (mcamd_set_errno(hdl, EMCAMD_NOTSUP)); |
| } |
| |
| if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) { |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling " |
| "table for MC rev %d width %d\n", caller, rev, width); |
| return (mcamd_set_errno(hdl, EMCAMD_NOTSUP)); |
| } |
| |
| if (csid) { |
| if (mcpp->csintlvfctr > 1) { |
| rct_csintlv_bits(rev, width, csmode, |
| mcpp->csintlvfctr, csid); |
| if (csid->csi_factor == 0) { |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: " |
| "could not work out cs interleave " |
| "paramters for MC rev %d, width %d, " |
| "csmode %d, factor %d\n", caller, |
| rev, width, csmode, |
| (int)mcpp->csintlvfctr); |
| return (mcamd_set_errno(hdl, EMCAMD_NOTSUP)); |
| } |
| } else { |
| csid->csi_factor = 0; |
| } |
| } |
| |
| return (0); |
| } |
| |
| static uint64_t |
| iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what) |
| { |
| uint64_t new = in | add; |
| |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx", |
| what, in, add, new); |
| |
| return (add); |
| } |
| |
| /* |
| * Where the number of row/col address bits is ambiguous (affects CG and |
| * earlier only) we will assign the "floating" bit to row address. If |
| * we adopt the same convention in address reconstruction then all should work. |
| */ |
| static uint32_t |
| iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp, |
| const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr) |
| { |
| uint32_t addr = 0; |
| int abitno, ibitno; |
| int nbits = bamp->bam_nrows; |
| int swapped = 0; |
| |
| for (abitno = 0; abitno < nbits; abitno++) { |
| ibitno = rcbm->rcb_rowbit[abitno]; |
| if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) { |
| ibitno = MC_RC_CSI_BITSWAP(csid, ibitno); |
| swapped++; |
| } |
| if (BITVAL(iaddr, ibitno) != 0) |
| SETBIT(addr, abitno); |
| } |
| |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> " |
| "row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped); |
| |
| return (addr); |
| } |
| |
| /*ARGSUSED*/ |
| static uint64_t |
| row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp, |
| const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr) |
| { |
| uint64_t iaddr = 0; |
| int abitno, ibitno; |
| int nbits = bamp->bam_nrows; |
| |
| for (abitno = 0; abitno < nbits; abitno++) { |
| if (BIT(rowaddr, abitno) == 0) |
| continue; |
| ibitno = rcbm->rcb_rowbit[abitno]; |
| if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) { |
| ibitno = MC_RC_CSI_BITSWAP(csid, ibitno); |
| } |
| SETBIT(iaddr, ibitno); |
| } |
| |
| return (iaddr); |
| } |
| |
| |
| static uint32_t |
| iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp, |
| const struct rct_rcbmap *rcbm, uint64_t iaddr) |
| { |
| uint32_t addr = 0; |
| int abitno, ibitno, bias = 0; |
| int nbits = bamp->bam_ncols; |
| |
| /* |
| * Knock off a column bit if the numbers are ambiguous |
| */ |
| if (bamp->bam_ambig) |
| nbits--; |
| |
| for (abitno = 0; abitno < nbits; abitno++) { |
| if (abitno == MC_PC_COLADDRBIT) |
| bias = 1; |
| |
| ibitno = rcbm->rcb_colbit[abitno + bias]; |
| |
| if (BITVAL(iaddr, ibitno) != 0) |
| SETBIT(addr, abitno); |
| } |
| |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> " |
| "col 0x%x\n", iaddr, addr); |
| |
| return (addr); |
| } |
| |
| /*ARGSUSED*/ |
| static uint64_t |
| col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp, |
| const struct rct_rcbmap *rcbm, uint32_t coladdr) |
| { |
| uint64_t iaddr = 0; |
| int abitno, ibitno, bias = 0; |
| int nbits = bamp->bam_ncols; |
| |
| /* |
| * Knock off a column bit if the numbers are ambiguous |
| */ |
| if (bamp->bam_ambig) |
| nbits--; |
| |
| for (abitno = 0; abitno < nbits; abitno++) { |
| if (BIT(coladdr, abitno) == 0) |
| continue; |
| |
| if (abitno == MC_PC_COLADDRBIT) |
| bias = 1; |
| |
| ibitno = rcbm->rcb_colbit[abitno + bias]; |
| SETBIT(iaddr, ibitno); |
| } |
| |
| return (iaddr); |
| } |
| |
| /* |
| * Extract bank bit arguments and swizzle if requested. |
| */ |
| static uint32_t |
| iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm, |
| const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr) |
| { |
| uint32_t addr = 0; |
| int abitno, ibitno, i; |
| |
| for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) { |
| uint32_t val; |
| |
| /* |
| * rcb_bankbit[abitno] tells us which iaddr bit number |
| * will form bit abitno of the bank address |
| */ |
| ibitno = rcbm->rcb_bankbit[abitno]; |
| val = BITVAL(iaddr, ibitno); |
| |
| /* |
| * If bank swizzling is in operation then xor the bit value |
| * obtained above with other iaddr bits. |
| */ |
| if (swzlp) { |
| for (i = 0; i < MC_RC_SWZLBITS; i++) { |
| ibitno = swzlp->bswz_rowbits[abitno][i]; |
| val ^= BITVAL(iaddr, ibitno); |
| } |
| } |
| |
| if (val) |
| SETBIT(addr, abitno); |
| } |
| |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> " |
| "bank 0x%x\n", iaddr, addr); |
| |
| return (addr); |
| } |
| |
| /* |
| * bank_to_iaddr requires the iaddr reconstructed thus far with at least the |
| * row bits repopulated. That's because in bank swizzle mode |
| * the bank bits are the result of xor'ing three original iaddr bits |
| * together - two of which come from the row address and the third we |
| * can reconstruct here. Note that a zero bankaddr bit *can* result |
| * in a nonzero iaddr bit (unlike in row and col reconstruction). |
| */ |
| /*ARGSUSED*/ |
| static uint64_t |
| bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm, |
| const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr) |
| { |
| uint64_t iaddr = 0; |
| int abitno, pibitno, i; |
| |
| for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) { |
| uint32_t val = BITVAL(bankaddr, abitno); |
| if (swzlp) { |
| for (i = 0; i < MC_RC_SWZLBITS; i++) { |
| pibitno = swzlp->bswz_rowbits[abitno][i]; |
| val ^= BITVAL(partiaddr, pibitno); |
| } |
| } |
| if (val) |
| SETBIT(iaddr, rcbm->rcb_bankbit[abitno]); |
| } |
| |
| return (iaddr); |
| } |
| |
| static int |
| iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp, |
| uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp) |
| { |
| const struct rct_bnkaddrmode *bamp; |
| const struct rct_rcbmap *rcbmp; |
| const struct rct_bnkswzlinfo *swzlp = NULL; |
| struct rct_csintlv csi; |
| |
| if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp, |
| mcpp->bnkswzl ? &swzlp : NULL, &csi, |
| "iaddr_to_rcb") < 0) |
| return (-1); /* errno already set */ |
| |
| *rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr); |
| *colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr); |
| *bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr); |
| |
| return (0); |
| } |
| |
| /* |
| * Take a reconstructed InputAddr and undo the normalization described in |
| * BKDG 3.29 3.4.4 to include the base address of the MC if no node |
| * interleave or to insert the node interleave selection bits. |
| */ |
| static int |
| iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr, |
| uint64_t *rsltp) |
| { |
| uint64_t dramaddr; |
| int intlvbits; |
| |
| switch (mcpp->intlven) { |
| case 0x0: |
| intlvbits = 0; |
| break; |
| case 0x1: |
| intlvbits = 1; |
| break; |
| case 0x3: |
| intlvbits = 2; |
| break; |
| case 0x7: |
| intlvbits = 3; |
| break; |
| default: |
| mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: " |
| "illegal IntlvEn of %d for MC 0x%p\n", |
| (int)mcpp->intlven, (int)mcpp->num); |
| return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); |
| } |
| |
| if (intlvbits != 0) { |
| /* |
| * For a 2/4/8 way interleave iaddr was formed by excising |
| * 1, 2, or 3 bits 12:12, 13:12, or 14:12 from dramaddr, |
| * the removed bits having done their job by selecting the |
| * responding node. So we must move bits 35:12 of the |
| * reconstructed iaddr up to make a 1, 2 or 3 bit hole and |
| * then fill those bits with the current IntlvSel value for |
| * this node. The node base address must be zero if nodes |
| * are interleaved. |
| * |
| * Note that the DRAM controller InputAddr is still 36 bits |
| * 35:0 on rev F. |
| */ |
| dramaddr = (BITS(iaddr, 35, 12) << intlvbits) | |
| (mcpp->intlvsel << 12) | BITS(iaddr, 11, 0); |
| } else { |
| dramaddr = iaddr + mcpp->base; |
| } |
| |
| *rsltp = dramaddr; |
| |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx " |
| "intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n", |
| iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base, |
| dramaddr); |
| |
| return (0); |
| } |
| |
| int |
| mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs, |
| uint64_t iaddr, uint64_t *offsetp) |
| { |
| mcamd_dimm_offset_un_t offset_un; |
| uint_t csmode; |
| uint32_t bankaddr, rowaddr, coladdr; |
| struct mcprops mcp; |
| struct csprops csp; |
| |
| *offsetp = MCAMD_RC_INVALID_OFFSET; |
| |
| if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 || |
| getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0) |
| return (-1); /* errno already set */ |
| |
| csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num); |
| |
| if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr, |
| &coladdr, &bankaddr) < 0) |
| return (-1); /* errno already set */ |
| |
| offset_un.do_offset = 0; |
| |
| offset_un.do_valid = 1; |
| offset_un.do_version = MCAMD_OFFSET_VERSION; |
| offset_un.do_rank = (uint32_t)csp.dimmrank; |
| offset_un.do_row = rowaddr; |
| offset_un.do_bank = bankaddr; |
| offset_un.do_col = coladdr; |
| |
| *offsetp = offset_un.do_offset; |
| |
| return (0); |
| } |
| |
| /* |
| * Given an MC, DIMM and offset (dimm rank, row, col, internal bank) we |
| * find the corresponding chip-select for the rank and then reconstruct |
| * a system address. In the absence of serial number support it is possible |
| * that we may be asked to perform this operation on a dimm which has been |
| * swapped, perhaps even for a dimm of different size and number of ranks. |
| * This may happen if fmadm repair has not been used. There are some |
| * unused bits in the offset and we could guard against this a little |
| * by recording in those bit some of the physical characteristic of the |
| * original DIMM such as size, number of ranks etc. |
| */ |
| int |
| mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm, |
| uint64_t offset, uint64_t *pap) |
| { |
| mcamd_node_t *cs; |
| mcamd_dimm_offset_un_t off_un; |
| uint32_t rank, rowaddr, bankaddr, coladdr; |
| uint64_t iaddr = 0; |
| const struct rct_bnkaddrmode *bamp; |
| const struct rct_rcbmap *rcbmp; |
| const struct rct_bnkswzlinfo *swzlp = NULL; |
| struct rct_csintlv csi; |
| struct mcprops mcp; |
| struct csprops csp; |
| uint64_t csmode; |
| int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo; |
| |
| off_un.do_offset = offset; |
| rank = off_un.do_rank; |
| bankaddr = off_un.do_bank; |
| rowaddr = off_un.do_row; |
| coladdr = off_un.do_col; |
| |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx " |
| "-> rank %d bank %d row 0x%x col 0x%x\n", offset, |
| rank, bankaddr, rowaddr, coladdr); |
| |
| if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0) |
| return (-1); /* errno already set */ |
| |
| maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev); |
| maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev); |
| masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev); |
| masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev); |
| |
| /* |
| * Find the chip-select on this dimm using the given rank. |
| */ |
| for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL; |
| cs = mcamd_cs_next(hdl, dimm, cs)) { |
| if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0) |
| return (-1); /* errno already set */ |
| |
| if (csp.dimmrank == rank) |
| break; |
| } |
| |
| if (cs == NULL) { |
| mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current " |
| "dimm in this slot does not have a cs using rank %d\n", |
| rank); |
| return (mcamd_set_errno(hdl, EMCAMD_NOADDR)); |
| } |
| |
| /* |
| * If the cs# has been substituted by the online spare then the |
| * given unum is not actually contributing to the system address |
| * map since all accesses to it are redirected. |
| * |
| * If the cs# failed BIOS test it is not in the address map. |
| * |
| * If the cs# is the online spare cs# then it is contributing to |
| * the system address map only if swapped in, and the csbase etc |
| * parameters to use must be those of the bad cs#. |
| */ |
| if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) { |
| return (mcamd_set_errno(hdl, EMCAMD_NOADDR)); |
| } else if (csp.testfail) { |
| return (mcamd_set_errno(hdl, EMCAMD_NOADDR)); |
| } else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs && |
| mcp.badcs != MC_INVALNUM) { |
| /* |
| * Iterate over all cs# of this memory controller to find |
| * the bad one - the bad cs# need not be on the same dimm |
| * as the spare. |
| */ |
| for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL; |
| cs = mcamd_cs_next(hdl, mc, cs)) { |
| mcamd_prop_t csnum; |
| |
| if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, |
| &csnum)) { |
| mcamd_dprintf(hdl, MCAMD_DBG_ERR, |
| "mcamd_offset_to_pa: csnum lookup failed " |
| "while looking for bad cs#"); |
| return (mcamd_set_errno(hdl, |
| EMCAMD_TREEINVALID)); |
| } |
| if (csnum == mcp.badcs) |
| break; |
| } |
| |
| if (cs == NULL) { |
| mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: " |
| "failed to find cs for bad cs#%d\n", mcp.badcs); |
| return (mcamd_set_errno(hdl, |
| EMCAMD_TREEINVALID)); |
| } |
| |
| /* found bad cs - reread properties from it instead of spare */ |
| if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0) |
| return (-1); /* errno already set */ |
| } |
| |
| csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num); |
| |
| if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp, |
| mcp.bnkswzl ? &swzlp : NULL, &csi, |
| "mc_offset_to_pa") < 0) |
| return (-1); /* errno already set */ |
| |
| /* |
| * If there are umaskable DRAM InputAddr bits the add those bits |
| * to iaddr from the cs base address. |
| */ |
| if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) { |
| iaddr |= iaddr_add(hdl, iaddr, |
| BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev), |
| maskhi_hi + 1), "unmaskable cs basehi bits"); |
| } |
| |
| /* |
| * basehi bits not meing masked pass straight through to the |
| * iaddr. |
| */ |
| iaddr |= iaddr_add(hdl, iaddr, |
| BITS(csp.base, maskhi_hi, maskhi_lo) & |
| ~BITS(csp.mask, maskhi_hi, maskhi_lo), |
| "cs basehi bits not being masked"); |
| |
| /* |
| * if cs interleaving is active then baselo address bit are being |
| * masked - pass the rest through. |
| */ |
| if (mcp.csintlvfctr > 1) { |
| iaddr |= iaddr_add(hdl, iaddr, |
| BITS(csp.base, masklo_hi, masklo_lo) & |
| ~BITS(csp.mask, masklo_hi, masklo_lo), |
| "cs baselo bits not being masked"); |
| } |
| |
| /* |
| * Reconstruct iaddr bits from known row address |
| */ |
| iaddr |= iaddr_add(hdl, iaddr, |
| row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr), |
| "add iaddr bits from row"); |
| |
| /* |
| * Reconstruct iaddr bits from known column address |
| */ |
| iaddr |= iaddr_add(hdl, iaddr, |
| col_to_iaddr(hdl, bamp, rcbmp, coladdr), |
| "add iaddr bits from col"); |
| |
| /* |
| * Reconstruct iaddr bits from known internal banksel address |
| */ |
| iaddr |= iaddr_add(hdl, iaddr, |
| bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr), |
| "add iaddr bits from bank"); |
| |
| /* |
| * Move iaddr up into the range for this MC and insert any |
| * node interleave selection bits. |
| */ |
| if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0) |
| return (-1); /* errno already set */ |
| |
| return (0); |
| } |
| |
| int |
| mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp) |
| { |
| uint_t csmode; |
| struct mcprops mcp; |
| const struct rct_bnkaddrmode *bamp; |
| |
| if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0) |
| return (-1); /* errno already set */ |
| |
| csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum); |
| |
| if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL, |
| "mcamd_cs_size") < 0) |
| return (-1); /* errno already set */ |
| |
| *szp = MC_CS_SIZE(bamp, mcp.width); |
| |
| return (0); |
| } |