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 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/kmem.h>
29 #include <sys/mc.h>
30 #include <sys/nvpair.h>
31 #include <sys/fm/protocol.h>
32 #include <sys/cmn_err.h>
33 #include <sys/sunddi.h>
34 #include <sys/mc_intel.h>
35 #include "dimm_addr.h"
36 #include "nb_log.h"
37 #include "rank.h"
38 #include "dimm_phys.h"
39 #include "nb5000.h"
40 
41 struct dimm_geometry **dimm_geometry;
42 struct rank_base *rank_base;
43 
44 uint64_t
45 dimm_getphys(int branch, int rank, int bank, int ras, int cas)
46 {
47 	uint8_t i;
48 	uint64_t m;
49 	uint64_t pa;
50 	struct rank_base *rp;
51 	struct rank_geometry *rgp;
52 
53 	ASSERT(rank < nb_dimms_per_channel * 2);
54 	rp = &rank_base[(branch * nb_dimms_per_channel * 2) + rank];
55 	rgp = (struct rank_geometry *)rp->rank_geometry;
56 	if (rgp == NULL)
57 		return (-1LL);
58 	pa = rp->base;
59 
60 	for (i = 0, m = 1; bank; i++, m <<= 1) {
61 		if ((bank & m) != 0 && rgp->bank[i] != 0xff) {
62 			pa += 1 << rgp->bank[i];
63 			bank &= ~m;
64 		}
65 	}
66 	for (i = 0, m = 1; cas; i++, m <<= 1) {
67 		if ((cas & m) != 0 && rgp->col[i] != 0xff) {
68 			pa += 1 << rgp->col[i];
69 			cas &= ~m;
70 		}
71 	}
72 	for (i = 0, m = 1; ras; i++, m <<= 1) {
73 		if ((ras & m) != 0 && rgp->row[i] != 0xff) {
74 			pa += 1 << rgp->row[i];
75 			ras &= ~m;
76 		}
77 	}
78 	if (rp->interleave > 1) {
79 		i = 0;
80 		if (rp->branch_interleave) {
81 			if (branch) {
82 				pa += 1 << rgp->interleave[i];
83 			}
84 			i++;
85 		}
86 		if ((rp->way & 1) != 0)
87 			pa += 1 << rgp->interleave[i];
88 		i++;
89 		if ((rp->way & 2) != 0)
90 			pa += 1 << rgp->interleave[i];
91 	}
92 	if (rp->hole && pa >= rp->hole)
93 		pa += rp->hole_size;
94 	return (pa);
95 }
96 
97 uint64_t
98 dimm_getoffset(int branch, int rank, int bank, int ras, int cas)
99 {
100 	uint8_t i;
101 	uint64_t m;
102 	uint64_t offset;
103 	struct dimm_geometry *dgp;
104 	struct rank_geometry *rgp;
105 	struct rank_base *rp;
106 	uint64_t pa;
107 	uint64_t cal_pa;
108 
109 	ASSERT(rank < nb_dimms_per_channel * 2);
110 	rp = &rank_base[(branch * nb_dimms_per_channel * 2) + rank];
111 	dgp = dimm_geometry[(branch * nb_dimms_per_channel) + rank/2];
112 	if (dgp == NULL)
113 		return (TCODE_OFFSET(rank, bank, ras, cas));
114 	rgp = (struct rank_geometry *)&dgp->rank_geometry[0];
115 	offset = 0;
116 	pa = dimm_getphys(branch, rank, bank, ras, cas) & PAGEMASK;
117 
118 	for (i = 0, m = 1; bank; i++, m <<= 1) {
119 		if ((bank & m) != 0 && rgp->bank[i] != 0xff) {
120 			offset += 1 << rgp->bank[i];
121 			bank &= ~m;
122 		}
123 	}
124 	for (i = 0, m = 1; cas; i++, m <<= 1) {
125 		if ((cas & m) != 0 && rgp->col[i] != 0xff) {
126 			offset += 1 << rgp->col[i];
127 			cas &= ~m;
128 		}
129 	}
130 	for (i = 0, m = 1; ras; i++, m <<= 1) {
131 		if ((ras & m) != 0 && rgp->row[i] != 0xff) {
132 			offset += 1 << rgp->row[i];
133 			ras &= ~m;
134 		}
135 	}
136 	cal_pa = rp->base + (offset * rp->interleave);
137 	if (rp->hole && cal_pa >= rp->hole)
138 		cal_pa += rp->hole_size;
139 	cal_pa &= PAGEMASK;
140 
141 	if (pa != cal_pa) {
142 		return (-1LL);
143 	}
144 	return (offset & PAGEMASK);
145 }
146 
147 static int
148 fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
149 {
150 	int i;
151 	uint64_t offset;
152 	nvlist_t **hcl, *hcsp;
153 	uint_t npr;
154 
155 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) != 0 ||
156 	    (nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
157 	    &offset) != 0 && nvlist_lookup_uint64(hcsp,
158 	    FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0) ||
159 	    nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &npr) != 0)
160 		return (0);
161 
162 
163 	bzero(unump, sizeof (mc_unum_t));
164 	for (i = 0; i < MC_UNUM_NDIMM; i++)
165 		unump->unum_dimms[i] = MC_INVALNUM;
166 
167 	for (i = 0; i < npr; i++) {
168 		char *hcnm, *hcid;
169 		long v;
170 
171 		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 ||
172 		    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 ||
173 		    ddi_strtol(hcid, NULL, 0, &v) != 0)
174 			return (0);
175 
176 		if (strcmp(hcnm, "motherboard") == 0)
177 			unump->unum_board = (int)v;
178 		else if (strcmp(hcnm, "memory-controller") == 0)
179 			unump->unum_mc = (int)v;
180 		else if (strcmp(hcnm, "dram-channel") == 0)
181 			unump->unum_cs = (int)v;
182 		else if (strcmp(hcnm, "dimm") == 0)
183 			unump->unum_dimms[0] = (int)v;
184 		else if (strcmp(hcnm, "rank") == 0)
185 			unump->unum_rank = (int)v;
186 	}
187 
188 	unump->unum_offset = offset;
189 
190 	return (1);
191 }
192 
193 /*ARGSUSED*/
194 static cmi_errno_t
195 inb_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
196     uint32_t synd, int syndtype, mc_unum_t *unump)
197 {
198 	struct rank_base *rp;
199 	int i;
200 	int last;
201 	uint64_t offset;
202 	cmi_errno_t rt = CMIERR_UNKNOWN;
203 
204 	last = nb_dimms_per_channel * nb_number_memory_controllers;
205 	for (i = 0; i < last; i++) {
206 		rp = &rank_base[i];
207 		if (rp && pa >= rp->base && pa < rp->limit)
208 			break;
209 	}
210 	if (i < last) {
211 		offset = pa - rp->base;
212 		if (offset > rp->hole)
213 			offset -= rp->hole_size;
214 		unump->unum_offset = offset / rp->interleave;
215 		unump->unum_mc = i / nb_dimms_per_channel;
216 		unump->unum_cs = 0;
217 		unump->unum_rank = i % nb_dimms_per_channel;
218 		rt = CMI_SUCCESS;
219 	}
220 	return (rt);
221 }
222 
223 /*ARGSUSED*/
224 static cmi_errno_t
225 inb_unumtopa(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
226 {
227 	mc_unum_t unum;
228 	uint64_t pa;
229 	struct rank_base *rp;
230 
231 	if (unump == NULL) {
232 		if (!fmri2unum(nvl, &unum))
233 			return (CMI_SUCCESS);
234 		unump = &unum;
235 	}
236 	if (unump->unum_offset & OFFSET_ROW_BANK_COL) {
237 		pa = dimm_getphys(unump->unum_mc,
238 		    TCODE_OFFSET_RANK(unump->unum_offset),
239 		    TCODE_OFFSET_BANK(unump->unum_offset),
240 		    TCODE_OFFSET_RAS(unump->unum_offset),
241 		    TCODE_OFFSET_CAS(unump->unum_offset));
242 		if (pa == -1LL)
243 			return (CMIERR_MC_NOADDR);
244 		*pap = pa;
245 		return (CMI_SUCCESS);
246 	}
247 	rp = &rank_base[(unump->unum_mc * nb_dimms_per_channel * 2) +
248 	    unump->unum_rank];
249 	pa = rp->base + (unump->unum_offset * rp->interleave);
250 
251 	if (rp->hole && pa >= rp->hole)
252 		pa += rp->hole_size;
253 	*pap = pa;
254 	return (CMI_SUCCESS);
255 }
256 
257 void
258 dimm_init()
259 {
260 	dimm_geometry = kmem_zalloc(sizeof (void *) *
261 	    nb_number_memory_controllers * nb_dimms_per_channel, KM_SLEEP);
262 	rank_base = kmem_zalloc(sizeof (struct rank_base) *
263 	    nb_number_memory_controllers * nb_dimms_per_channel * 2, KM_SLEEP);
264 }
265 
266 void
267 dimm_fini()
268 {
269 	kmem_free(dimm_geometry, sizeof (void *) *
270 	    nb_number_memory_controllers * nb_dimms_per_channel);
271 	dimm_geometry = 0;
272 	kmem_free(rank_base, sizeof (struct rank_base) *
273 	    nb_number_memory_controllers * nb_dimms_per_channel * 2);
274 	rank_base = 0;
275 }
276 
277 void
278 dimm_add_geometry(int branch, int dimm, int nbanks, int width, int ncolumn,
279     int nrow)
280 {
281 	int i;
282 	for (i = 0; i < dimm_types; i++) {
283 		if (dimm_data[i].row_nbits == nrow &&
284 		    dimm_data[i].col_nbits == ncolumn &&
285 		    dimm_data[i].width == width &&
286 		    (1 << dimm_data[i].bank_nbits) == nbanks) {
287 			dimm_geometry[(branch * nb_dimms_per_channel) + dimm] =
288 			    &dimm_data[i];
289 			break;
290 		}
291 	}
292 }
293 
294 void
295 dimm_add_rank(int branch, int rank, int branch_interleave, int way,
296     uint64_t base, uint32_t hole, uint32_t hole_size, int interleave,
297     uint64_t limit)
298 {
299 	struct dimm_geometry *dimm;
300 	struct rank_base *rp;
301 	int interleave_nbits;
302 
303 	dimm = dimm_geometry[(branch * nb_dimms_per_channel) + (rank / 2)];
304 	rp = &rank_base[(branch * nb_dimms_per_channel * 2) + rank];
305 	if (interleave == 1)
306 		interleave_nbits = 0;
307 	else if (interleave == 2)
308 		interleave_nbits = 1;
309 	else if (interleave == 4)
310 		interleave_nbits = 2;
311 	else
312 		interleave_nbits = 3;
313 	rp->branch_interleave = branch_interleave;
314 	rp->way = way;
315 	rp->base = base;
316 	rp->hole = hole;
317 	rp->hole_size = hole_size;
318 	rp->interleave = interleave;
319 	rp->limit = limit;
320 	if (dimm)
321 		rp->rank_geometry = &dimm->rank_geometry[interleave_nbits];
322 	else
323 		rp->rank_geometry = 0;
324 }
325 
326 static const cmi_mc_ops_t inb_mc_ops = {
327 	inb_patounum,
328 	inb_unumtopa,
329 	nb_error_trap			/* cmi_mc_logout */
330 };
331 
332 /*ARGSUSED*/
333 int
334 inb_mc_register(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3)
335 {
336 	cmi_mc_register(hdl, &inb_mc_ops, NULL);
337 	return (CMI_HDL_WALK_NEXT);
338 }
339