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.
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.
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]
21 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
25 #pragma ident "%Z%%M% %I% %E% SMI"
27 #include <mcamd_api.h>
28 #include <mcamd_err.h>
29 #include <mcamd_rowcol_impl.h>
32 * Convenience structures to stash MC and CS properties in.
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 */
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) */
58 getmcprops(struct mcamd_hdl
*hdl
, mcamd_node_t
*mc
, const char *caller
,
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
,
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
));
84 getcsprops(struct mcamd_hdl
*hdl
, mcamd_node_t
*cs
, const char *caller
,
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
,
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
));
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
,
108 uint_t rev
= (uint_t
)mcpp
->rev
;
109 int width
= (int)mcpp
->width
;
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
));
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
,
121 return (mcamd_set_errno(hdl
, EMCAMD_NOTSUP
));
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
));
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
,
140 (int)mcpp
->csintlvfctr
);
141 return (mcamd_set_errno(hdl
, EMCAMD_NOTSUP
));
144 csid
->csi_factor
= 0;
152 iaddr_add(struct mcamd_hdl
*hdl
, uint64_t in
, uint64_t add
, const char *what
)
154 uint64_t new = in
| add
;
156 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "%s: 0x%llx | 0x%llx --> 0x%llx",
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.
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
)
173 int nbits
= bamp
->bam_nrows
;
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
);
182 if (BITVAL(iaddr
, ibitno
) != 0)
183 SETBIT(addr
, abitno
);
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
);
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
)
199 int nbits
= bamp
->bam_nrows
;
201 for (abitno
= 0; abitno
< nbits
; abitno
++) {
202 if (BIT(rowaddr
, abitno
) == 0)
204 ibitno
= rcbm
->rcb_rowbit
[abitno
];
205 if (MC_RC_CSI_SWAPPED_BIT(csid
, ibitno
)) {
206 ibitno
= MC_RC_CSI_BITSWAP(csid
, ibitno
);
208 SETBIT(iaddr
, ibitno
);
216 iaddr_to_col(struct mcamd_hdl
*hdl
, const struct rct_bnkaddrmode
*bamp
,
217 const struct rct_rcbmap
*rcbm
, uint64_t iaddr
)
220 int abitno
, ibitno
, bias
= 0;
221 int nbits
= bamp
->bam_ncols
;
224 * Knock off a column bit if the numbers are ambiguous
229 for (abitno
= 0; abitno
< nbits
; abitno
++) {
230 if (abitno
== MC_PC_COLADDRBIT
)
233 ibitno
= rcbm
->rcb_colbit
[abitno
+ bias
];
235 if (BITVAL(iaddr
, ibitno
) != 0)
236 SETBIT(addr
, abitno
);
239 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "iaddr_to_col: iaddr 0x%llx --> "
240 "col 0x%x\n", iaddr
, addr
);
247 col_to_iaddr(struct mcamd_hdl
*hdl
, const struct rct_bnkaddrmode
*bamp
,
248 const struct rct_rcbmap
*rcbm
, uint32_t coladdr
)
251 int abitno
, ibitno
, bias
= 0;
252 int nbits
= bamp
->bam_ncols
;
255 * Knock off a column bit if the numbers are ambiguous
260 for (abitno
= 0; abitno
< nbits
; abitno
++) {
261 if (BIT(coladdr
, abitno
) == 0)
264 if (abitno
== MC_PC_COLADDRBIT
)
267 ibitno
= rcbm
->rcb_colbit
[abitno
+ bias
];
268 SETBIT(iaddr
, ibitno
);
275 * Extract bank bit arguments and swizzle if requested.
278 iaddr_to_bank(struct mcamd_hdl
*hdl
, const struct rct_rcbmap
*rcbm
,
279 const struct rct_bnkswzlinfo
*swzlp
, uint64_t iaddr
)
282 int abitno
, ibitno
, i
;
284 for (abitno
= 0; abitno
< rcbm
->rcb_nbankbits
; abitno
++) {
288 * rcb_bankbit[abitno] tells us which iaddr bit number
289 * will form bit abitno of the bank address
291 ibitno
= rcbm
->rcb_bankbit
[abitno
];
292 val
= BITVAL(iaddr
, ibitno
);
295 * If bank swizzling is in operation then xor the bit value
296 * obtained above with other iaddr bits.
299 for (i
= 0; i
< MC_RC_SWZLBITS
; i
++) {
300 ibitno
= swzlp
->bswz_rowbits
[abitno
][i
];
301 val
^= BITVAL(iaddr
, ibitno
);
306 SETBIT(addr
, abitno
);
309 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "iaddr_to_bank: iaddr 0x%llx --> "
310 "bank 0x%x\n", iaddr
, addr
);
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).
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
)
329 int abitno
, pibitno
, i
;
331 for (abitno
= 0; abitno
< rcbm
->rcb_nbankbits
; abitno
++) {
332 uint32_t val
= BITVAL(bankaddr
, abitno
);
334 for (i
= 0; i
< MC_RC_SWZLBITS
; i
++) {
335 pibitno
= swzlp
->bswz_rowbits
[abitno
][i
];
336 val
^= BITVAL(partiaddr
, pibitno
);
340 SETBIT(iaddr
, rcbm
->rcb_bankbit
[abitno
]);
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
)
350 const struct rct_bnkaddrmode
*bamp
;
351 const struct rct_rcbmap
*rcbmp
;
352 const struct rct_bnkswzlinfo
*swzlp
= NULL
;
353 struct rct_csintlv csi
;
355 if (gettbls(hdl
, csmode
, mcpp
, &bamp
, &rcbmp
,
356 mcpp
->bnkswzl
? &swzlp
: NULL
, &csi
,
358 return (-1); /* errno already set */
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
);
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.
373 iaddr_unnormalize(struct mcamd_hdl
*hdl
, struct mcprops
*mcpp
, uint64_t iaddr
,
379 switch (mcpp
->intlven
) {
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
));
399 if (intlvbits
!= 0) {
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
410 * Note that the DRAM controller InputAddr is still 36 bits
413 dramaddr
= (BITS(iaddr
, 35, 12) << intlvbits
) |
414 (mcpp
->intlvsel
<< 12) | BITS(iaddr
, 11, 0);
416 dramaddr
= iaddr
+ mcpp
->base
;
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
,
430 mc_pa_to_offset(struct mcamd_hdl
*hdl
, mcamd_node_t
*mc
, mcamd_node_t
*cs
,
431 uint64_t iaddr
, uint64_t *offsetp
)
433 mcamd_dimm_offset_un_t offset_un
;
435 uint32_t bankaddr
, rowaddr
, coladdr
;
439 *offsetp
= MCAMD_RC_INVALID_OFFSET
;
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 */
445 csmode
= MC_CS_MODE(mcp
.csbnkmap_reg
, csp
.num
);
447 if (iaddr_to_rcb(hdl
, csmode
, &mcp
, iaddr
, &rowaddr
,
448 &coladdr
, &bankaddr
) < 0)
449 return (-1); /* errno already set */
451 offset_un
.do_offset
= 0;
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
;
460 *offsetp
= offset_un
.do_offset
;
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.
477 mc_offset_to_pa(struct mcamd_hdl
*hdl
, mcamd_node_t
*mc
, mcamd_node_t
*dimm
,
478 uint64_t offset
, uint64_t *pap
)
481 mcamd_dimm_offset_un_t off_un
;
482 uint32_t rank
, rowaddr
, bankaddr
, coladdr
;
484 const struct rct_bnkaddrmode
*bamp
;
485 const struct rct_rcbmap
*rcbmp
;
486 const struct rct_bnkswzlinfo
*swzlp
= NULL
;
487 struct rct_csintlv csi
;
491 int maskhi_hi
, maskhi_lo
, masklo_hi
, masklo_lo
;
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
;
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
);
503 if (getmcprops(hdl
, mc
, "mc_offset_to_pa", &mcp
) < 0)
504 return (-1); /* errno already set */
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
);
512 * Find the chip-select on this dimm using the given rank.
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 */
519 if (csp
.dimmrank
== rank
)
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",
527 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
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.
535 * If the cs# failed BIOS test it is not in the address map.
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#.
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
) {
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
552 for (cs
= mcamd_cs_next(hdl
, mc
, NULL
); cs
!= NULL
;
553 cs
= mcamd_cs_next(hdl
, mc
, cs
)) {
556 if (!mcamd_get_numprop(hdl
, cs
, MCAMD_PROP_NUM
,
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
));
564 if (csnum
== mcp
.badcs
)
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
));
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 */
580 csmode
= MC_CS_MODE(mcp
.csbnkmap_reg
, csp
.num
);
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 */
588 * If there are umaskable DRAM InputAddr bits the add those bits
589 * to iaddr from the cs base address.
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");
598 * basehi bits not meing masked pass straight through to the
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");
607 * if cs interleaving is active then baselo address bit are being
608 * masked - pass the rest through.
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");
618 * Reconstruct iaddr bits from known row address
620 iaddr
|= iaddr_add(hdl
, iaddr
,
621 row_to_iaddr(hdl
, bamp
, rcbmp
, &csi
, rowaddr
),
622 "add iaddr bits from row");
625 * Reconstruct iaddr bits from known column address
627 iaddr
|= iaddr_add(hdl
, iaddr
,
628 col_to_iaddr(hdl
, bamp
, rcbmp
, coladdr
),
629 "add iaddr bits from col");
632 * Reconstruct iaddr bits from known internal banksel address
634 iaddr
|= iaddr_add(hdl
, iaddr
,
635 bank_to_iaddr(hdl
, rcbmp
, swzlp
, iaddr
, bankaddr
),
636 "add iaddr bits from bank");
639 * Move iaddr up into the range for this MC and insert any
640 * node interleave selection bits.
642 if (iaddr_unnormalize(hdl
, &mcp
, iaddr
, pap
) < 0)
643 return (-1); /* errno already set */
649 mcamd_cs_size(struct mcamd_hdl
*hdl
, mcamd_node_t
*mc
, int csnum
, size_t *szp
)
653 const struct rct_bnkaddrmode
*bamp
;
655 if (getmcprops(hdl
, mc
, "mcamd_cs_size", &mcp
) < 0)
656 return (-1); /* errno already set */
658 csmode
= MC_CS_MODE(mcp
.csbnkmap_reg
, csnum
);
660 if (gettbls(hdl
, csmode
, &mcp
, &bamp
, NULL
, NULL
, NULL
,
661 "mcamd_cs_size") < 0)
662 return (-1); /* errno already set */
664 *szp
= MC_CS_SIZE(bamp
, mcp
.width
);