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