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
getmcprops(struct mcamd_hdl * hdl,mcamd_node_t * mc,const char * caller,struct mcprops * pp)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
getcsprops(struct mcamd_hdl * hdl,mcamd_node_t * cs,const char * caller,struct csprops * csp)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
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)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
iaddr_add(struct mcamd_hdl * hdl,uint64_t in,uint64_t add,const char * what)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
iaddr_to_row(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,struct rct_csintlv * csid,uint64_t iaddr)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
row_to_iaddr(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,struct rct_csintlv * csid,uint32_t rowaddr)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
iaddr_to_col(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,uint64_t iaddr)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
col_to_iaddr(struct mcamd_hdl * hdl,const struct rct_bnkaddrmode * bamp,const struct rct_rcbmap * rcbm,uint32_t coladdr)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
iaddr_to_bank(struct mcamd_hdl * hdl,const struct rct_rcbmap * rcbm,const struct rct_bnkswzlinfo * swzlp,uint64_t iaddr)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
bank_to_iaddr(struct mcamd_hdl * hdl,const struct rct_rcbmap * rcbm,const struct rct_bnkswzlinfo * swzlp,uint64_t partiaddr,uint32_t bankaddr)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
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)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
iaddr_unnormalize(struct mcamd_hdl * hdl,struct mcprops * mcpp,uint64_t iaddr,uint64_t * rsltp)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
mc_pa_to_offset(struct mcamd_hdl * hdl,mcamd_node_t * mc,mcamd_node_t * cs,uint64_t iaddr,uint64_t * offsetp)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
mc_offset_to_pa(struct mcamd_hdl * hdl,mcamd_node_t * mc,mcamd_node_t * dimm,uint64_t offset,uint64_t * pap)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
mcamd_cs_size(struct mcamd_hdl * hdl,mcamd_node_t * mc,int csnum,size_t * szp)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 }
668