1f657cd55SCheng Sean Ye /*
2f657cd55SCheng Sean Ye  * CDDL HEADER START
3f657cd55SCheng Sean Ye  *
4f657cd55SCheng Sean Ye  * The contents of this file are subject to the terms of the
5f657cd55SCheng Sean Ye  * Common Development and Distribution License (the "License").
6f657cd55SCheng Sean Ye  * You may not use this file except in compliance with the License.
7f657cd55SCheng Sean Ye  *
8f657cd55SCheng Sean Ye  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f657cd55SCheng Sean Ye  * or http://www.opensolaris.org/os/licensing.
10f657cd55SCheng Sean Ye  * See the License for the specific language governing permissions
11f657cd55SCheng Sean Ye  * and limitations under the License.
12f657cd55SCheng Sean Ye  *
13f657cd55SCheng Sean Ye  * When distributing Covered Code, include this CDDL HEADER in each
14f657cd55SCheng Sean Ye  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f657cd55SCheng Sean Ye  * If applicable, add the following below this CDDL HEADER, with the
16f657cd55SCheng Sean Ye  * fields enclosed by brackets "[]" replaced with your own identifying
17f657cd55SCheng Sean Ye  * information: Portions Copyright [yyyy] [name of copyright owner]
18f657cd55SCheng Sean Ye  *
19f657cd55SCheng Sean Ye  * CDDL HEADER END
20f657cd55SCheng Sean Ye  */
21f657cd55SCheng Sean Ye 
22f657cd55SCheng Sean Ye /*
23*e8ee2240SAdrian Frost  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24f657cd55SCheng Sean Ye  * Use is subject to license terms.
25f657cd55SCheng Sean Ye  */
26f657cd55SCheng Sean Ye 
27f657cd55SCheng Sean Ye #include <sys/types.h>
28f657cd55SCheng Sean Ye #include <sys/time.h>
29f657cd55SCheng Sean Ye #include <sys/fm/protocol.h>
30f657cd55SCheng Sean Ye #include <sys/cpu_module_impl.h>
31f899e573SVuong Nguyen #include <sys/mc_intel.h>
32f657cd55SCheng Sean Ye #include "intel_nhm.h"
33f657cd55SCheng Sean Ye #include "nhm_log.h"
34f899e573SVuong Nguyen #include "mem_addr.h"
35f657cd55SCheng Sean Ye 
36f657cd55SCheng Sean Ye char closed_page;
37f657cd55SCheng Sean Ye char ecc_enabled;
38f899e573SVuong Nguyen char divby3_enabled;
39f657cd55SCheng Sean Ye char lockstep[2];
40f657cd55SCheng Sean Ye char mirror_mode[2];
41f657cd55SCheng Sean Ye char spare_channel[2];
42f899e573SVuong Nguyen sad_t sad[MAX_SAD_DRAM_RULE];
43f899e573SVuong Nguyen tad_t tad[MAX_CPU_NODES][MAX_TAD_DRAM_RULE];
44f899e573SVuong Nguyen sag_ch_t sag_ch[MAX_CPU_NODES][CHANNELS_PER_MEMORY_CONTROLLER]
45f899e573SVuong Nguyen 	[MAX_TAD_DRAM_RULE];
46f899e573SVuong Nguyen rir_t rir[MAX_CPU_NODES][CHANNELS_PER_MEMORY_CONTROLLER]
47f899e573SVuong Nguyen 	[MAX_TAD_DRAM_RULE];
48f899e573SVuong Nguyen dod_t dod_reg[MAX_CPU_NODES][CHANNELS_PER_MEMORY_CONTROLLER]
49f899e573SVuong Nguyen 	[MAX_DIMMS_PER_CHANNEL];
50f657cd55SCheng Sean Ye 
51f657cd55SCheng Sean Ye static int
channel_in_interleave(int node,int channel,int rule,int * way_p,int * no_interleave_p)52f657cd55SCheng Sean Ye channel_in_interleave(int node, int channel, int rule, int *way_p,
53f657cd55SCheng Sean Ye     int *no_interleave_p)
54f657cd55SCheng Sean Ye {
55f657cd55SCheng Sean Ye 	int way;
56f657cd55SCheng Sean Ye 	int c;
57f657cd55SCheng Sean Ye 	int i;
58f657cd55SCheng Sean Ye 	uint32_t mc_channel_mapper;
59f657cd55SCheng Sean Ye 	int lc;
60f657cd55SCheng Sean Ye 	int rt = 0;
61f657cd55SCheng Sean Ye 	int start = 0;
62f657cd55SCheng Sean Ye 
63f657cd55SCheng Sean Ye 	if (lockstep[node] || mirror_mode[node]) {
64f657cd55SCheng Sean Ye 		*no_interleave_p = 0;
65f657cd55SCheng Sean Ye 		if (channel > 1)
66f657cd55SCheng Sean Ye 			return (0);
67f657cd55SCheng Sean Ye 		else
68f657cd55SCheng Sean Ye 			return (1);
69f657cd55SCheng Sean Ye 	}
70f657cd55SCheng Sean Ye 	mc_channel_mapper = MC_CHANNEL_MAPPER_RD(node);
71f657cd55SCheng Sean Ye 	lc = -1;
72f657cd55SCheng Sean Ye 	c = 1 << channel;
73f657cd55SCheng Sean Ye 	for (i = 0; i < CHANNELS_PER_MEMORY_CONTROLLER; i++) {
74f657cd55SCheng Sean Ye 		if ((CHANNEL_MAP(mc_channel_mapper, i, 0) & c) != 0) {
75f657cd55SCheng Sean Ye 			lc = i;
76f657cd55SCheng Sean Ye 			break;
77f657cd55SCheng Sean Ye 		}
78f657cd55SCheng Sean Ye 	}
79f657cd55SCheng Sean Ye 	if (lc == -1) {
80f657cd55SCheng Sean Ye 		for (i = 0; i < CHANNELS_PER_MEMORY_CONTROLLER; i++) {
81f657cd55SCheng Sean Ye 			if ((CHANNEL_MAP(mc_channel_mapper, i, 1) & c) != 0) {
82f657cd55SCheng Sean Ye 				lc = i;
83f657cd55SCheng Sean Ye 				break;
84f657cd55SCheng Sean Ye 			}
85f657cd55SCheng Sean Ye 		}
86f657cd55SCheng Sean Ye 	}
87f657cd55SCheng Sean Ye 	if (lc == -1) {
88f657cd55SCheng Sean Ye 		return (0);
89f657cd55SCheng Sean Ye 	}
90f657cd55SCheng Sean Ye 	*way_p = 0;
91f657cd55SCheng Sean Ye 	*no_interleave_p = 0;
92f657cd55SCheng Sean Ye 	if (node && tad[node][rule].mode == 2)
93f657cd55SCheng Sean Ye 		start = 4;
94f657cd55SCheng Sean Ye 	for (way = start; way < INTERLEAVE_NWAY; way++) {
95f657cd55SCheng Sean Ye 		if (lc == TAD_INTERLEAVE(tad[node][rule].pkg_list, way)) {
96f657cd55SCheng Sean Ye 			*way_p = way;
97f657cd55SCheng Sean Ye 			if (way == 0) {
98f657cd55SCheng Sean Ye 				for (i = way + 1; i < INTERLEAVE_NWAY; i++) {
99f657cd55SCheng Sean Ye 					c = TAD_INTERLEAVE(
100f657cd55SCheng Sean Ye 					    tad[node][rule].pkg_list, i);
101f657cd55SCheng Sean Ye 					if (lc != c) {
102f657cd55SCheng Sean Ye 						break;
103f657cd55SCheng Sean Ye 					}
104f657cd55SCheng Sean Ye 				}
105f657cd55SCheng Sean Ye 				if (i == INTERLEAVE_NWAY)
106f657cd55SCheng Sean Ye 					*no_interleave_p = 1;
107f657cd55SCheng Sean Ye 			}
108f657cd55SCheng Sean Ye 			rt = 1;
109f657cd55SCheng Sean Ye 			break;
110f657cd55SCheng Sean Ye 		}
111f657cd55SCheng Sean Ye 	}
112f657cd55SCheng Sean Ye 	return (rt);
113f657cd55SCheng Sean Ye }
114f657cd55SCheng Sean Ye 
115f657cd55SCheng Sean Ye int
address_to_node(uint64_t addr,int * interleave_p)116f657cd55SCheng Sean Ye address_to_node(uint64_t addr, int *interleave_p)
117f657cd55SCheng Sean Ye {
118f657cd55SCheng Sean Ye 	int i;
119f657cd55SCheng Sean Ye 	int node = -1;
120f657cd55SCheng Sean Ye 	uint64_t base;
121f657cd55SCheng Sean Ye 	int way;
122f657cd55SCheng Sean Ye 	uchar_t package;
123f657cd55SCheng Sean Ye 
124f657cd55SCheng Sean Ye 	base = 0;
125f657cd55SCheng Sean Ye 	for (i = 0; i < MAX_SAD_DRAM_RULE; i++) {
126f657cd55SCheng Sean Ye 		if (sad[i].enable && addr >= base && addr < sad[i].limit) {
127f657cd55SCheng Sean Ye 			switch (sad[i].mode) {
128f657cd55SCheng Sean Ye 			case 0:
129f657cd55SCheng Sean Ye 				way = (addr >> 6) & 7;
130f657cd55SCheng Sean Ye 				break;
131f657cd55SCheng Sean Ye 			case 1:
132f657cd55SCheng Sean Ye 				way = ((addr >> 6) & 7) ^ ((addr >> 16) & 7);
133f657cd55SCheng Sean Ye 				break;
134f657cd55SCheng Sean Ye 			case 2:
135f657cd55SCheng Sean Ye 				way = ((addr >> 4) & 4) |
136f657cd55SCheng Sean Ye 				    (((addr >> 6) & 0x3ffffffff) % 3);
137f657cd55SCheng Sean Ye 				break;
138f657cd55SCheng Sean Ye 			default:
139f657cd55SCheng Sean Ye 				return (-1);
140f657cd55SCheng Sean Ye 			}
141f657cd55SCheng Sean Ye 			package = SAD_INTERLEAVE(sad[i].node_list, way);
142f657cd55SCheng Sean Ye 			if (interleave_p)
143f657cd55SCheng Sean Ye 				*interleave_p = sad[i].interleave;
144f657cd55SCheng Sean Ye 			if (package == 1)
145f657cd55SCheng Sean Ye 				node = 0;
146f657cd55SCheng Sean Ye 			else if (package == 2)
147f657cd55SCheng Sean Ye 				node = 1;
148f657cd55SCheng Sean Ye 			else
149f657cd55SCheng Sean Ye 				node = -1;
150f657cd55SCheng Sean Ye 			break;
151f657cd55SCheng Sean Ye 		}
152f657cd55SCheng Sean Ye 		base = sad[i].limit;
153f657cd55SCheng Sean Ye 	}
154f657cd55SCheng Sean Ye 	return (node);
155f657cd55SCheng Sean Ye }
156f657cd55SCheng Sean Ye 
157f657cd55SCheng Sean Ye static uint64_t
channel_address(int node,int channel,int rule,uint64_t addr)158f657cd55SCheng Sean Ye channel_address(int node, int channel, int rule, uint64_t addr)
159f657cd55SCheng Sean Ye {
160f657cd55SCheng Sean Ye 	uint64_t caddr;
161f657cd55SCheng Sean Ye 
162f657cd55SCheng Sean Ye 	if (lockstep[node] || mirror_mode[node])
163f657cd55SCheng Sean Ye 		channel = 0;
164f657cd55SCheng Sean Ye 	caddr = (((addr >> 16) +
165f899e573SVuong Nguyen 	    (int64_t)sag_ch[node][channel][rule].soffset) << 16) |
166f657cd55SCheng Sean Ye 	    (addr & 0xffc0);
167f657cd55SCheng Sean Ye 	if (sag_ch[node][channel][rule].remove8) {
168f657cd55SCheng Sean Ye 		caddr = ((caddr >> 1) & ~0xff) | (caddr & 0xff);
169f657cd55SCheng Sean Ye 	}
170f657cd55SCheng Sean Ye 	if (sag_ch[node][channel][rule].remove7) {
171f657cd55SCheng Sean Ye 		caddr = ((caddr >> 1) & ~0x7f) | (caddr & 0x7f);
172f657cd55SCheng Sean Ye 	}
173f657cd55SCheng Sean Ye 	if (sag_ch[node][channel][rule].remove6) {
174f657cd55SCheng Sean Ye 		caddr = ((caddr >> 1) & ~0x3f) | (caddr & 0x3f);
175f657cd55SCheng Sean Ye 	}
176f657cd55SCheng Sean Ye 	caddr = caddr & 0x1fffffffff;
177f657cd55SCheng Sean Ye 	if (sag_ch[node][channel][rule].divby3) {
178f657cd55SCheng Sean Ye 		caddr = ((((caddr >> 6) / 3) << 6) & 0x1fffffffc0) |
179f657cd55SCheng Sean Ye 		    (caddr & 0x3f);
180f657cd55SCheng Sean Ye 	}
181f657cd55SCheng Sean Ye 	return (caddr);
182f657cd55SCheng Sean Ye }
183f657cd55SCheng Sean Ye 
184f657cd55SCheng Sean Ye int
address_to_channel(int node,uint64_t addr,int write,int * log_chan,uint64_t * channel_addrp,int * interleave_p)185f899e573SVuong Nguyen address_to_channel(int node, uint64_t addr, int write,
186f899e573SVuong Nguyen     int *log_chan, uint64_t *channel_addrp, int *interleave_p)
187f657cd55SCheng Sean Ye {
188f657cd55SCheng Sean Ye 	int i;
189f657cd55SCheng Sean Ye 	int channel = -1;
190f657cd55SCheng Sean Ye 	uint64_t base;
191f657cd55SCheng Sean Ye 	uint32_t mapper;
192f657cd55SCheng Sean Ye 	uint32_t lc;
193f657cd55SCheng Sean Ye 	int way;
194f657cd55SCheng Sean Ye 
195f657cd55SCheng Sean Ye 	base = 0;
196f657cd55SCheng Sean Ye 	for (i = 0; i < MAX_TAD_DRAM_RULE; i++) {
197f657cd55SCheng Sean Ye 		if (tad[node][i].enable && addr >= base &&
198f657cd55SCheng Sean Ye 		    addr < tad[node][i].limit) {
199f657cd55SCheng Sean Ye 			switch (tad[node][i].mode) {
200f657cd55SCheng Sean Ye 			case 0:
201f657cd55SCheng Sean Ye 				way = (addr >> 6) & 7;
202f657cd55SCheng Sean Ye 				break;
203f657cd55SCheng Sean Ye 			case 1:
204f657cd55SCheng Sean Ye 				way = ((addr >> 6) & 7) ^ ((addr >> 16) & 7);
205f657cd55SCheng Sean Ye 				break;
206f657cd55SCheng Sean Ye 			case 2:
207f657cd55SCheng Sean Ye 				way = ((addr >> 4) & 4) |
208f657cd55SCheng Sean Ye 				    (((addr >> 6) & 0x3ffffffff) % 3);
209f657cd55SCheng Sean Ye 				break;
210f657cd55SCheng Sean Ye 			default:
211f657cd55SCheng Sean Ye 				return (-1);
212f657cd55SCheng Sean Ye 			}
213f899e573SVuong Nguyen 			/* get logical channel number */
214f657cd55SCheng Sean Ye 			channel = TAD_INTERLEAVE(tad[node][i].pkg_list, way);
215f899e573SVuong Nguyen 			if (log_chan)
216f899e573SVuong Nguyen 				*log_chan = channel;
217f899e573SVuong Nguyen 
218f657cd55SCheng Sean Ye 			if (channel_addrp) {
219f899e573SVuong Nguyen 				*channel_addrp = channel_address(node,
220f899e573SVuong Nguyen 				    channel, i, addr);
221f657cd55SCheng Sean Ye 			}
222f657cd55SCheng Sean Ye 			if (interleave_p)
223f657cd55SCheng Sean Ye 				*interleave_p = tad[node][i].interleave;
224f657cd55SCheng Sean Ye 			break;
225f657cd55SCheng Sean Ye 		}
226f657cd55SCheng Sean Ye 		base = tad[node][i].limit;
227f657cd55SCheng Sean Ye 	}
228f657cd55SCheng Sean Ye 	if (!lockstep[node] && channel != -1) {
229f657cd55SCheng Sean Ye 		mapper = MC_CHANNEL_MAPPER_RD(node);
230f657cd55SCheng Sean Ye 		lc = CHANNEL_MAP(mapper, channel, write);
231f657cd55SCheng Sean Ye 		switch (lc) {
232f657cd55SCheng Sean Ye 		case 1:
233f657cd55SCheng Sean Ye 			channel = 0;
234f657cd55SCheng Sean Ye 			break;
235f657cd55SCheng Sean Ye 		case 2:
236f657cd55SCheng Sean Ye 			channel = 1;
237f657cd55SCheng Sean Ye 			break;
238f657cd55SCheng Sean Ye 		case 4:
239f657cd55SCheng Sean Ye 			channel = 2;
240f657cd55SCheng Sean Ye 			break;
241f657cd55SCheng Sean Ye 		case 3:		/* mirror PCH0 and PCH1 */
242f657cd55SCheng Sean Ye 			if (!write) {
243f657cd55SCheng Sean Ye 				if (((addr >> 24) & 1) ^ ((addr >> 12) & 1) ^
244f657cd55SCheng Sean Ye 				    ((addr >> 6) & 1))
245f657cd55SCheng Sean Ye 					channel = 1;
246f657cd55SCheng Sean Ye 				else
247f657cd55SCheng Sean Ye 					channel = 0;
248f657cd55SCheng Sean Ye 			}
249f657cd55SCheng Sean Ye 			break;
250f657cd55SCheng Sean Ye 		case 5:		/* sparing PCH0 to PCH2 */
251f657cd55SCheng Sean Ye 			channel = 0;
252f657cd55SCheng Sean Ye 			break;
253f657cd55SCheng Sean Ye 		case 6:		/* sparing PCH1 to PCH2 */
254f657cd55SCheng Sean Ye 			channel = 1;
255f657cd55SCheng Sean Ye 			break;
256f657cd55SCheng Sean Ye 		}
257f657cd55SCheng Sean Ye 	}
258f657cd55SCheng Sean Ye 	return (channel);
259f657cd55SCheng Sean Ye }
260f657cd55SCheng Sean Ye 
261f657cd55SCheng Sean Ye int
channels_interleave(uint64_t addr)262f657cd55SCheng Sean Ye channels_interleave(uint64_t addr)
263f657cd55SCheng Sean Ye {
264f657cd55SCheng Sean Ye 	int node;
265f657cd55SCheng Sean Ye 	int sinterleave;
266f657cd55SCheng Sean Ye 	int channels, channels1;
267f657cd55SCheng Sean Ye 
268f657cd55SCheng Sean Ye 	node = address_to_node(addr, &sinterleave);
269f657cd55SCheng Sean Ye 	if (sinterleave == 1) {
270f657cd55SCheng Sean Ye 		channels = 0;
271f899e573SVuong Nguyen 		(void) address_to_channel(node, addr, 0, 0, 0, &channels);
272f657cd55SCheng Sean Ye 	} else {
273f657cd55SCheng Sean Ye 		channels = 0;
274f657cd55SCheng Sean Ye 		channels1 = 0;
275f899e573SVuong Nguyen 		(void) address_to_channel(0, addr, 0, 0, 0, &channels);
276f899e573SVuong Nguyen 		(void) address_to_channel(1, addr, 0, 0, 0, &channels1);
277f657cd55SCheng Sean Ye 		channels += channels1;
278f657cd55SCheng Sean Ye 	}
279f657cd55SCheng Sean Ye 	return (channels);
280f657cd55SCheng Sean Ye }
281f657cd55SCheng Sean Ye 
282f657cd55SCheng Sean Ye int
channel_addr_to_dimm(int node,int channel,uint64_t caddr,int * rank_p,uint64_t * rank_addr_p)283f899e573SVuong Nguyen channel_addr_to_dimm(int node, int channel, uint64_t caddr, int *rank_p,
284f657cd55SCheng Sean Ye     uint64_t *rank_addr_p)
285f657cd55SCheng Sean Ye {
286f657cd55SCheng Sean Ye 	int i;
287f657cd55SCheng Sean Ye 	uint64_t base;
288f657cd55SCheng Sean Ye 	uint64_t rank_addr;
289f657cd55SCheng Sean Ye 	int rank;
290f657cd55SCheng Sean Ye 	int dimm;
291f657cd55SCheng Sean Ye 	int way;
292f657cd55SCheng Sean Ye 
293f657cd55SCheng Sean Ye 	dimm = -1;
294f657cd55SCheng Sean Ye 	rank = -1;
295f657cd55SCheng Sean Ye 	base = 0;
296f657cd55SCheng Sean Ye 	rank_addr = -1ULL;
297f657cd55SCheng Sean Ye 	for (i = 0; i < MAX_TAD_DRAM_RULE; i++) {
298f657cd55SCheng Sean Ye 		if (caddr >= base && caddr < rir[node][channel][i].limit) {
299f657cd55SCheng Sean Ye 			if (closed_page) {
300f657cd55SCheng Sean Ye 				way = (caddr >> 6) & 3;
301f657cd55SCheng Sean Ye 				rank_addr = (((caddr + (int64_t)
302f657cd55SCheng Sean Ye 				    rir[node][channel][i].way[way].offset *
303f657cd55SCheng Sean Ye 				    VRANK_SZ) /
304f657cd55SCheng Sean Ye 				    rir[node][channel][i].interleave) &
305f657cd55SCheng Sean Ye 				    ~0x3f) + (caddr & 0x3f);
306f657cd55SCheng Sean Ye 			} else {
307f657cd55SCheng Sean Ye 				way = (caddr >> 12) & 3;
308f657cd55SCheng Sean Ye 				rank_addr = (((caddr + (int64_t)
309f657cd55SCheng Sean Ye 				    rir[node][channel][i].way[way].offset *
310f657cd55SCheng Sean Ye 				    VRANK_SZ) /
311f657cd55SCheng Sean Ye 				    rir[node][channel][i].interleave) &
312f657cd55SCheng Sean Ye 				    ~0xfff) + (caddr & 0xfff);
313f657cd55SCheng Sean Ye 			}
314f657cd55SCheng Sean Ye 			rank = rir[node][channel][i].way[way].rank;
315f657cd55SCheng Sean Ye 			dimm = rank >> 2;
316f657cd55SCheng Sean Ye 			break;
317f657cd55SCheng Sean Ye 		}
318f657cd55SCheng Sean Ye 		base = rir[node][channel][i].limit;
319f657cd55SCheng Sean Ye 	}
320f657cd55SCheng Sean Ye 	*rank_p = rank;
321f657cd55SCheng Sean Ye 	*rank_addr_p = rank_addr;
322f657cd55SCheng Sean Ye 	return (dimm);
323f657cd55SCheng Sean Ye }
324f657cd55SCheng Sean Ye 
325f657cd55SCheng Sean Ye static int
socket_interleave(uint64_t addr,int node,int channel,int rule,int * way_p)326f657cd55SCheng Sean Ye socket_interleave(uint64_t addr, int node, int channel, int rule,
327f657cd55SCheng Sean Ye     int *way_p)
328f657cd55SCheng Sean Ye {
329f657cd55SCheng Sean Ye 	int i, j;
330f657cd55SCheng Sean Ye 	uint64_t base;
331f657cd55SCheng Sean Ye 	uchar_t package;
332f657cd55SCheng Sean Ye 	uchar_t xp;
333f657cd55SCheng Sean Ye 	uchar_t xc;
334f657cd55SCheng Sean Ye 	int ot = 0;
335f657cd55SCheng Sean Ye 	int mode;
336f657cd55SCheng Sean Ye 	int start;
337f657cd55SCheng Sean Ye 	int rt = 1;
338f657cd55SCheng Sean Ye 	int found = 0;
339f657cd55SCheng Sean Ye 
340f657cd55SCheng Sean Ye 	if (mirror_mode[node] || lockstep[node])
341f657cd55SCheng Sean Ye 		channel = 0;
342f657cd55SCheng Sean Ye 	package = node + 1;
343f657cd55SCheng Sean Ye 	mode = tad[node][rule].mode;
344f657cd55SCheng Sean Ye 	base = 0;
345f657cd55SCheng Sean Ye 	for (i = 0; i < MAX_SAD_DRAM_RULE; i++) {
346f657cd55SCheng Sean Ye 		if (sad[i].enable && addr >= base && addr < sad[i].limit) {
347f657cd55SCheng Sean Ye 			if (mode == 2) {
348f657cd55SCheng Sean Ye 				for (j = 0; j < INTERLEAVE_NWAY; j++) {
349f657cd55SCheng Sean Ye 					xp = SAD_INTERLEAVE(sad[i].node_list,
350f657cd55SCheng Sean Ye 					    j);
351f657cd55SCheng Sean Ye 					if (package != xp) {
352f657cd55SCheng Sean Ye 						ot++;
353f657cd55SCheng Sean Ye 						if (found) {
354f657cd55SCheng Sean Ye 							rt = 2;
355f657cd55SCheng Sean Ye 							break;
356f657cd55SCheng Sean Ye 						}
357f657cd55SCheng Sean Ye 					} else {
358f657cd55SCheng Sean Ye 						found = 1;
359f657cd55SCheng Sean Ye 						if (ot) {
360f657cd55SCheng Sean Ye 							rt = 2;
361f657cd55SCheng Sean Ye 							break;
362f657cd55SCheng Sean Ye 						}
363f657cd55SCheng Sean Ye 					}
364f657cd55SCheng Sean Ye 				}
365f657cd55SCheng Sean Ye 			} else {
366f657cd55SCheng Sean Ye 				if (mode == 2)
367f657cd55SCheng Sean Ye 					start = *way_p;
368f657cd55SCheng Sean Ye 				else
369f657cd55SCheng Sean Ye 					start = 0;
370f657cd55SCheng Sean Ye 				for (j = start; j < INTERLEAVE_NWAY; j++) {
371f657cd55SCheng Sean Ye 					xp = SAD_INTERLEAVE(sad[i].node_list,
372f657cd55SCheng Sean Ye 					    j);
373f657cd55SCheng Sean Ye 					if (package != xp) {
374f657cd55SCheng Sean Ye 						ot++;
375f657cd55SCheng Sean Ye 						if (found) {
376f657cd55SCheng Sean Ye 							rt = 2;
377f657cd55SCheng Sean Ye 							break;
378f657cd55SCheng Sean Ye 						}
379f657cd55SCheng Sean Ye 					} else if (!found) {
380f657cd55SCheng Sean Ye 						xc = TAD_INTERLEAVE(
381f657cd55SCheng Sean Ye 						    tad[node][rule].pkg_list,
382f657cd55SCheng Sean Ye 						    j);
383f657cd55SCheng Sean Ye 						if (channel == xc) {
384f657cd55SCheng Sean Ye 							*way_p = j;
385f657cd55SCheng Sean Ye 							if (ot) {
386f657cd55SCheng Sean Ye 								rt = 2;
387f657cd55SCheng Sean Ye 								break;
388f657cd55SCheng Sean Ye 							}
389f657cd55SCheng Sean Ye 							found = 1;
390f657cd55SCheng Sean Ye 						}
391f657cd55SCheng Sean Ye 					}
392f657cd55SCheng Sean Ye 				}
393f657cd55SCheng Sean Ye 			}
394f657cd55SCheng Sean Ye 			break;
395f657cd55SCheng Sean Ye 		}
396f657cd55SCheng Sean Ye 		base = sad[i].limit;
397f657cd55SCheng Sean Ye 	}
398f657cd55SCheng Sean Ye 	return (rt);
399f657cd55SCheng Sean Ye }
400f657cd55SCheng Sean Ye 
401f657cd55SCheng Sean Ye uint64_t
dimm_to_addr(int node,int channel,int rank,uint64_t rank_addr,uint64_t * rank_base_p,uint64_t * rank_sz_p,uint32_t * socket_interleave_p,uint32_t * channel_interleave_p,uint32_t * rank_interleave_p,uint32_t * socket_way_p,uint32_t * channel_way_p,uint32_t * rank_way_p)402f657cd55SCheng Sean Ye dimm_to_addr(int node, int channel, int rank, uint64_t rank_addr,
403f657cd55SCheng Sean Ye     uint64_t *rank_base_p, uint64_t *rank_sz_p, uint32_t *socket_interleave_p,
404f657cd55SCheng Sean Ye     uint32_t *channel_interleave_p, uint32_t *rank_interleave_p,
405f657cd55SCheng Sean Ye     uint32_t *socket_way_p, uint32_t *channel_way_p, uint32_t *rank_way_p)
406f657cd55SCheng Sean Ye {
407f657cd55SCheng Sean Ye 	int i;
408f657cd55SCheng Sean Ye 	int way, xway;
409f657cd55SCheng Sean Ye 	uint64_t addr;
410f657cd55SCheng Sean Ye 	uint64_t caddr;
411f657cd55SCheng Sean Ye 	uint64_t cbaddr;
412f657cd55SCheng Sean Ye 	uint64_t baddr;
413f657cd55SCheng Sean Ye 	uint64_t rlimit;
414f657cd55SCheng Sean Ye 	uint64_t rank_sz;
415f657cd55SCheng Sean Ye 	uint64_t base;
416f657cd55SCheng Sean Ye 	int lchannel;
417f657cd55SCheng Sean Ye 	int bits;
418f657cd55SCheng Sean Ye 	int no_interleave;
419f657cd55SCheng Sean Ye 	int sinterleave;
420f657cd55SCheng Sean Ye 	int cinterleave;
421f657cd55SCheng Sean Ye 	int rinterleave;
422f657cd55SCheng Sean Ye 	int found = 0;
423f657cd55SCheng Sean Ye 
424f657cd55SCheng Sean Ye 	if (lockstep[node] || mirror_mode[node])
425f657cd55SCheng Sean Ye 		lchannel = 0;
426f657cd55SCheng Sean Ye 	else
427f657cd55SCheng Sean Ye 		lchannel = channel;
428f657cd55SCheng Sean Ye 	addr = -1;
429f657cd55SCheng Sean Ye 	base = 0;
430f657cd55SCheng Sean Ye 	for (i = 0; i < MAX_TAD_DRAM_RULE && found == 0; i++) {
431f657cd55SCheng Sean Ye 		for (way = 0; way < MAX_RIR_WAY; way++) {
432f899e573SVuong Nguyen 			if (rir[node][channel][i].way[way].dimm_rank == rank) {
433f657cd55SCheng Sean Ye 				rlimit = rir[node][channel][i].way[way].rlimit;
434f657cd55SCheng Sean Ye 				if (rlimit && rank_addr >= rlimit)
435f657cd55SCheng Sean Ye 					continue;
436*e8ee2240SAdrian Frost 				cbaddr = base;
437f657cd55SCheng Sean Ye 				if (closed_page) {
438f657cd55SCheng Sean Ye 					caddr = (rank_addr & ~0x3f) *
439f657cd55SCheng Sean Ye 					    rir[node][channel][i].interleave -
440f657cd55SCheng Sean Ye 					    (int64_t)rir[node][channel][i].
441f899e573SVuong Nguyen 					    way[way].soffset * VRANK_SZ;
442f657cd55SCheng Sean Ye 					caddr += way << 6;
443f657cd55SCheng Sean Ye 					caddr |= rank_addr & 0x3f;
444f657cd55SCheng Sean Ye 				} else {
445f657cd55SCheng Sean Ye 					caddr = (rank_addr & ~0xfff) *
446f657cd55SCheng Sean Ye 					    rir[node][channel][i].interleave -
447f657cd55SCheng Sean Ye 					    (int64_t)rir[node][channel][i].
448f899e573SVuong Nguyen 					    way[way].soffset * VRANK_SZ;
449f657cd55SCheng Sean Ye 					caddr += way << 12;
450f657cd55SCheng Sean Ye 					caddr |= rank_addr & 0xfff;
451f657cd55SCheng Sean Ye 				}
452f657cd55SCheng Sean Ye 				if (caddr < rir[node][channel][i].limit) {
453f657cd55SCheng Sean Ye 					rinterleave =
454f657cd55SCheng Sean Ye 					    rir[node][channel][i].interleave;
455f657cd55SCheng Sean Ye 					rank_sz = (rir[node][channel][i].limit -
456f657cd55SCheng Sean Ye 					    base) / rinterleave;
457f657cd55SCheng Sean Ye 					found = 1;
458f657cd55SCheng Sean Ye 					if (rank_interleave_p) {
459f657cd55SCheng Sean Ye 						*rank_interleave_p =
460f657cd55SCheng Sean Ye 						    rinterleave;
461f657cd55SCheng Sean Ye 					}
462f657cd55SCheng Sean Ye 					if (rank_way_p)
463f657cd55SCheng Sean Ye 						*rank_way_p = way;
464f657cd55SCheng Sean Ye 					break;
465f657cd55SCheng Sean Ye 				}
466f657cd55SCheng Sean Ye 			}
467f657cd55SCheng Sean Ye 		}
468f657cd55SCheng Sean Ye 		base = rir[node][channel][i].limit;
469f657cd55SCheng Sean Ye 	}
470f657cd55SCheng Sean Ye 	if (!found)
471f657cd55SCheng Sean Ye 		return (-1ULL);
472f657cd55SCheng Sean Ye 	base = 0;
473f657cd55SCheng Sean Ye 	for (i = 0; i < MAX_TAD_DRAM_RULE; i++) {
474f657cd55SCheng Sean Ye 		way = 0;
475f657cd55SCheng Sean Ye 		if (tad[node][i].enable &&
476f657cd55SCheng Sean Ye 		    channel_in_interleave(node, channel, i, &way,
477f657cd55SCheng Sean Ye 		    &no_interleave)) {
478f657cd55SCheng Sean Ye 			bits = 0;
479f657cd55SCheng Sean Ye 			addr = caddr;
480f657cd55SCheng Sean Ye 			baddr = cbaddr;
481f657cd55SCheng Sean Ye 			if (sag_ch[node][lchannel][i].divby3) {
482f657cd55SCheng Sean Ye 				addr = (((addr >> 6) * 3) << 6) +
483f657cd55SCheng Sean Ye 				    (addr & 0x3f);
484f657cd55SCheng Sean Ye 				baddr = (((baddr >> 6) * 3) << 6);
485f657cd55SCheng Sean Ye 			}
486f657cd55SCheng Sean Ye 			if (sag_ch[node][lchannel][i].remove6) {
487f657cd55SCheng Sean Ye 				bits = 1;
488f657cd55SCheng Sean Ye 				addr = ((addr & ~0x3f) << 1) | (addr & 0x3f);
489f657cd55SCheng Sean Ye 				baddr = (baddr & ~0x3f) << 1;
490f657cd55SCheng Sean Ye 			}
491f657cd55SCheng Sean Ye 			if (sag_ch[node][lchannel][i].remove7) {
492f657cd55SCheng Sean Ye 				bits =  bits | 2;
493f657cd55SCheng Sean Ye 				addr = ((addr & ~0x7f) << 1) | (addr & 0x7f);
494f657cd55SCheng Sean Ye 				baddr = ((baddr & ~0x7f) << 1) | (baddr & 0x40);
495f657cd55SCheng Sean Ye 			}
496f657cd55SCheng Sean Ye 			if (sag_ch[node][lchannel][i].remove8) {
497f657cd55SCheng Sean Ye 				bits =  bits | 4;
498f657cd55SCheng Sean Ye 				addr = ((addr & ~0xff) << 1) | (addr & 0xff);
499f657cd55SCheng Sean Ye 				baddr = ((baddr & ~0xff) << 1) | (baddr & 0xc0);
500f657cd55SCheng Sean Ye 			}
501f899e573SVuong Nguyen 			addr -= (int64_t)sag_ch[node][lchannel][i].soffset <<
502f899e573SVuong Nguyen 			    16;
503f657cd55SCheng Sean Ye 			baddr -= (int64_t)
504f899e573SVuong Nguyen 			    sag_ch[node][lchannel][i].soffset << 16;
505f657cd55SCheng Sean Ye 			if (addr < tad[node][i].limit) {
506*e8ee2240SAdrian Frost 				/*
507*e8ee2240SAdrian Frost 				 * this is the target address descripter to use
508*e8ee2240SAdrian Frost 				 */
509f657cd55SCheng Sean Ye 				sinterleave = socket_interleave(addr,
510f657cd55SCheng Sean Ye 				    node, channel, i, &way);
511f657cd55SCheng Sean Ye 				if (socket_interleave_p) {
512f657cd55SCheng Sean Ye 					*socket_interleave_p = sinterleave;
513f657cd55SCheng Sean Ye 				}
514f657cd55SCheng Sean Ye 				if (socket_way_p)
515f657cd55SCheng Sean Ye 					*socket_way_p = way;
516f657cd55SCheng Sean Ye 				if ((no_interleave && sinterleave == 1) ||
517f657cd55SCheng Sean Ye 				    mirror_mode[node] || lockstep[node]) {
518f657cd55SCheng Sean Ye 					cinterleave = 1;
519f657cd55SCheng Sean Ye 				} else {
520f657cd55SCheng Sean Ye 					cinterleave = channels_interleave(addr);
521f657cd55SCheng Sean Ye 				}
522f657cd55SCheng Sean Ye 				if (channel_interleave_p) {
523f657cd55SCheng Sean Ye 					*channel_interleave_p = cinterleave;
524f657cd55SCheng Sean Ye 				}
525*e8ee2240SAdrian Frost 				if (baddr + (rank_sz * rinterleave *
526*e8ee2240SAdrian Frost 				    cinterleave * sinterleave) >
527f657cd55SCheng Sean Ye 				    tad[node][i].limit) {
528*e8ee2240SAdrian Frost 					/*
529*e8ee2240SAdrian Frost 					 * The system address mapped to this
530*e8ee2240SAdrian Frost 					 * rank is not contiguous or has
531*e8ee2240SAdrian Frost 					 * different socket/channel interleave
532*e8ee2240SAdrian Frost 					 * adjust vitual rank to address where
533*e8ee2240SAdrian Frost 					 * change or break occures
534*e8ee2240SAdrian Frost 					 */
535f657cd55SCheng Sean Ye 					rank_sz = (tad[node][i].limit - baddr) /
536f657cd55SCheng Sean Ye 					    (cinterleave * sinterleave *
537f657cd55SCheng Sean Ye 					    rinterleave);
538f657cd55SCheng Sean Ye 				}
539f657cd55SCheng Sean Ye 				if (rank_sz_p) {
540f657cd55SCheng Sean Ye 					*rank_sz_p = rank_sz;
541f657cd55SCheng Sean Ye 				}
542f657cd55SCheng Sean Ye 				if (rank_base_p)
543f657cd55SCheng Sean Ye 					*rank_base_p = baddr;
544f657cd55SCheng Sean Ye 				if (channel_way_p)
545f657cd55SCheng Sean Ye 					*channel_way_p = way;
546f657cd55SCheng Sean Ye 				if (sinterleave == 1 && no_interleave) {
547f657cd55SCheng Sean Ye 					break;
548f657cd55SCheng Sean Ye 				}
549f657cd55SCheng Sean Ye 				switch (tad[node][i].mode) {
550f657cd55SCheng Sean Ye 				case 0:
551f657cd55SCheng Sean Ye 					addr += way * 0x40;
552f657cd55SCheng Sean Ye 					break;
553f657cd55SCheng Sean Ye 				case 1:
554f657cd55SCheng Sean Ye 					way = (way ^ (addr >> 16)) & bits;
555f657cd55SCheng Sean Ye 					addr += way * 0x40;
556f657cd55SCheng Sean Ye 					break;
557f657cd55SCheng Sean Ye 				case 2:
558f657cd55SCheng Sean Ye 					if (sinterleave == 1) {
559f657cd55SCheng Sean Ye 						xway = ((addr >> 4) & 4) |
560f657cd55SCheng Sean Ye 						    (((addr >> 6) &
561f657cd55SCheng Sean Ye 						    0x3ffffffff) % 3);
562f657cd55SCheng Sean Ye 						if (((way - xway) & 3) == 3)
563f657cd55SCheng Sean Ye 							xway = (way - xway) & 4;
564f657cd55SCheng Sean Ye 						else
565f657cd55SCheng Sean Ye 							xway = way - xway;
566f657cd55SCheng Sean Ye 						switch (xway) {
567f657cd55SCheng Sean Ye 						case 0:
568f657cd55SCheng Sean Ye 							way = 0;
569f657cd55SCheng Sean Ye 							break;
570f657cd55SCheng Sean Ye 						case 5:
571f657cd55SCheng Sean Ye 							way = 1;
572f657cd55SCheng Sean Ye 							break;
573f657cd55SCheng Sean Ye 						case 2:
574f657cd55SCheng Sean Ye 							way = 2;
575f657cd55SCheng Sean Ye 							break;
576f657cd55SCheng Sean Ye 						case 4:
577f657cd55SCheng Sean Ye 							way = 3;
578f657cd55SCheng Sean Ye 							break;
579f657cd55SCheng Sean Ye 						case 1:
580f657cd55SCheng Sean Ye 							way = 4;
581f657cd55SCheng Sean Ye 							break;
582f657cd55SCheng Sean Ye 						case 6:
583f657cd55SCheng Sean Ye 							way = 5;
584f657cd55SCheng Sean Ye 							break;
585f657cd55SCheng Sean Ye 						}
586f657cd55SCheng Sean Ye 					} else {
587f657cd55SCheng Sean Ye 						xway = (way & 3) -
588f657cd55SCheng Sean Ye 						    (((addr >> 6) &
589f657cd55SCheng Sean Ye 						    0x3ffffffff) % 3);
590f657cd55SCheng Sean Ye 						if (xway < 0)
591f657cd55SCheng Sean Ye 							xway += 3;
592f657cd55SCheng Sean Ye 						switch (xway) {
593f657cd55SCheng Sean Ye 						case 0:
594f657cd55SCheng Sean Ye 							way = 0;
595f657cd55SCheng Sean Ye 							break;
596f657cd55SCheng Sean Ye 						case 1:
597f657cd55SCheng Sean Ye 							way = 1;
598f657cd55SCheng Sean Ye 							break;
599f657cd55SCheng Sean Ye 						case 2:
600f657cd55SCheng Sean Ye 							way = 2;
601f657cd55SCheng Sean Ye 							break;
602f657cd55SCheng Sean Ye 						}
603f657cd55SCheng Sean Ye 					}
604f657cd55SCheng Sean Ye 					addr += way * 0x40;
605f657cd55SCheng Sean Ye 					break;
606f657cd55SCheng Sean Ye 				}
607f657cd55SCheng Sean Ye 				break;
608*e8ee2240SAdrian Frost 			} else if (baddr < tad[node][i].limit) {
609*e8ee2240SAdrian Frost 				/*
610*e8ee2240SAdrian Frost 				 * the channel address is not contiguous or
611*e8ee2240SAdrian Frost 				 * socket/channel interleave changes in the
612*e8ee2240SAdrian Frost 				 * middle of the rank adjust base and size for
613*e8ee2240SAdrian Frost 				 * virtual rank to where the break occurs
614*e8ee2240SAdrian Frost 				 */
615*e8ee2240SAdrian Frost 				sinterleave = socket_interleave(baddr,
616*e8ee2240SAdrian Frost 				    node, channel, i, &way);
617*e8ee2240SAdrian Frost 				if ((no_interleave && sinterleave == 1) ||
618*e8ee2240SAdrian Frost 				    mirror_mode[node] || lockstep[node]) {
619*e8ee2240SAdrian Frost 					cinterleave = 1;
620*e8ee2240SAdrian Frost 				} else {
621*e8ee2240SAdrian Frost 					cinterleave =
622*e8ee2240SAdrian Frost 					    channels_interleave(baddr);
623*e8ee2240SAdrian Frost 				}
624*e8ee2240SAdrian Frost 				rank_sz -= (tad[node][i].limit - baddr) /
625*e8ee2240SAdrian Frost 				    (cinterleave * sinterleave * rinterleave);
626*e8ee2240SAdrian Frost 				cbaddr += (tad[node][i].limit - baddr) /
627*e8ee2240SAdrian Frost 				    (cinterleave * sinterleave);
628f657cd55SCheng Sean Ye 			}
629f657cd55SCheng Sean Ye 		}
630f657cd55SCheng Sean Ye 		base = tad[node][i].limit;
631f657cd55SCheng Sean Ye 	}
632f657cd55SCheng Sean Ye 	return (addr);
633f657cd55SCheng Sean Ye }
634f657cd55SCheng Sean Ye /*ARGSUSED*/
635f657cd55SCheng Sean Ye static cmi_errno_t
nhm_patounum(void * arg,uint64_t pa,uint8_t valid_hi,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)636f657cd55SCheng Sean Ye nhm_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
637f657cd55SCheng Sean Ye     uint32_t synd, int syndtype, mc_unum_t *unump)
638f657cd55SCheng Sean Ye {
639f657cd55SCheng Sean Ye 	int node;
640f657cd55SCheng Sean Ye 	int channel;
641f657cd55SCheng Sean Ye 	int dimm;
642f657cd55SCheng Sean Ye 	int rank;
643f899e573SVuong Nguyen 	int log_chan;
644f899e573SVuong Nguyen 	uint64_t bank, row, column;
645f657cd55SCheng Sean Ye 	uint64_t caddr, raddr;
646f657cd55SCheng Sean Ye 
647f657cd55SCheng Sean Ye 	node = address_to_node(pa, 0);
648f899e573SVuong Nguyen 	if (node == -1) {
649f657cd55SCheng Sean Ye 		return (CMIERR_UNKNOWN);
650f899e573SVuong Nguyen 	}
651f899e573SVuong Nguyen 	channel = address_to_channel(node, pa, syndtype, &log_chan, &caddr, 0);
652f899e573SVuong Nguyen 	if (channel == -1) {
653f657cd55SCheng Sean Ye 		return (CMIERR_UNKNOWN);
654f899e573SVuong Nguyen 	}
655f899e573SVuong Nguyen 	/*
656f899e573SVuong Nguyen 	 * If driver was built with closed tree present then we will have Intel
657f899e573SVuong Nguyen 	 * proprietary functions caddr_to_dimm and rankaddr_to_dimm for finding
658f899e573SVuong Nguyen 	 * dimm/bank/row/column address otherwise we just locate dimm and
659f899e573SVuong Nguyen 	 * offset.
660f899e573SVuong Nguyen 	 */
661f899e573SVuong Nguyen 	if (&caddr_to_dimm)
662f899e573SVuong Nguyen 		dimm = caddr_to_dimm(node, log_chan, caddr, &rank, &raddr);
663f899e573SVuong Nguyen 	else
664f899e573SVuong Nguyen 		dimm = channel_addr_to_dimm(node, log_chan, caddr, &rank,
665f899e573SVuong Nguyen 		    &raddr);
666f899e573SVuong Nguyen 	if (dimm == -1) {
667f657cd55SCheng Sean Ye 		return (CMIERR_UNKNOWN);
668f657cd55SCheng Sean Ye 
669f899e573SVuong Nguyen 	}
670f657cd55SCheng Sean Ye 	unump->unum_board = 0;
671f657cd55SCheng Sean Ye 	unump->unum_chip = node;
672f657cd55SCheng Sean Ye 	unump->unum_mc = 0;
673f657cd55SCheng Sean Ye 	unump->unum_chan = channel;
674f657cd55SCheng Sean Ye 	unump->unum_cs = dimm;
675f657cd55SCheng Sean Ye 	unump->unum_rank = rank;
676f899e573SVuong Nguyen 
677f899e573SVuong Nguyen 	if (&rankaddr_to_dimm) {
678f899e573SVuong Nguyen 		if (rankaddr_to_dimm(raddr, node, channel, dimm, 0, &bank, &row,
679f899e573SVuong Nguyen 		    &column) != DDI_SUCCESS) {
680f899e573SVuong Nguyen 			return (CMIERR_UNKNOWN);
681f899e573SVuong Nguyen 		};
682f899e573SVuong Nguyen 		unump->unum_offset = TCODE_OFFSET(rank, bank, row, column);
683f899e573SVuong Nguyen 	} else {
684f657cd55SCheng Sean Ye 		unump->unum_offset = raddr;
685f899e573SVuong Nguyen 	}
686f657cd55SCheng Sean Ye 
687f657cd55SCheng Sean Ye 	return (CMI_SUCCESS);
688f657cd55SCheng Sean Ye }
689f657cd55SCheng Sean Ye 
690f657cd55SCheng Sean Ye /*ARGSUSED*/
691f657cd55SCheng Sean Ye static cmi_errno_t
nhm_unumtopa(void * arg,mc_unum_t * unump,nvlist_t * nvl,uint64_t * pap)692f657cd55SCheng Sean Ye nhm_unumtopa(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
693f657cd55SCheng Sean Ye {
694f657cd55SCheng Sean Ye 	uint64_t pa;
695f657cd55SCheng Sean Ye 	cmi_errno_t rt;
696f657cd55SCheng Sean Ye 	int node;
697f657cd55SCheng Sean Ye 	int channel;
698f899e573SVuong Nguyen 	int log_chan;
699f657cd55SCheng Sean Ye 	int rank;
700f657cd55SCheng Sean Ye 	int i;
701f657cd55SCheng Sean Ye 	nvlist_t **hcl, *hcsp;
702f657cd55SCheng Sean Ye 	uint_t npr;
703f899e573SVuong Nguyen 	uint64_t offset;
704f657cd55SCheng Sean Ye 	char *hcnm, *hcid;
705f657cd55SCheng Sean Ye 	long v;
706f899e573SVuong Nguyen 	uint64_t row, bank, col;
707f899e573SVuong Nguyen 	int dimm;
708f899e573SVuong Nguyen 	uint64_t rank_addr;
709f657cd55SCheng Sean Ye 
710f657cd55SCheng Sean Ye 	if (unump == NULL) {
711f657cd55SCheng Sean Ye 		if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC,
712f657cd55SCheng Sean Ye 		    &hcsp) != 0)
713f657cd55SCheng Sean Ye 			return (CMIERR_UNKNOWN);
714f657cd55SCheng Sean Ye 		if (nvlist_lookup_uint64(hcsp,
715f899e573SVuong Nguyen 		    "asru-" FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0 &&
716f657cd55SCheng Sean Ye 		    nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET,
717f899e573SVuong Nguyen 		    &offset) != 0) {
718f657cd55SCheng Sean Ye 			if (nvlist_lookup_uint64(hcsp,
719f657cd55SCheng Sean Ye 			    "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0 ||
720f657cd55SCheng Sean Ye 			    nvlist_lookup_uint64(hcsp,
721f657cd55SCheng Sean Ye 			    FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0) {
722f657cd55SCheng Sean Ye 				*pap = pa;
723f657cd55SCheng Sean Ye 				return (CMI_SUCCESS);
724f657cd55SCheng Sean Ye 			}
725f657cd55SCheng Sean Ye 			return (CMIERR_UNKNOWN);
726f657cd55SCheng Sean Ye 		}
727f657cd55SCheng Sean Ye 		if (nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST,
728f657cd55SCheng Sean Ye 		    &hcl, &npr) != 0)
729f657cd55SCheng Sean Ye 			return (CMIERR_UNKNOWN);
730f657cd55SCheng Sean Ye 		node = -1;
731f657cd55SCheng Sean Ye 		channel = -1;
732f899e573SVuong Nguyen 		dimm = -1;
733f657cd55SCheng Sean Ye 		rank = -1;
734f657cd55SCheng Sean Ye 		for (i = 0; i < npr; i++) {
735f657cd55SCheng Sean Ye 			if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME,
736f657cd55SCheng Sean Ye 			    &hcnm) != 0 ||
737f657cd55SCheng Sean Ye 			    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID,
738f657cd55SCheng Sean Ye 			    &hcid) != 0 ||
739f657cd55SCheng Sean Ye 			    ddi_strtol(hcid, NULL, 0, &v) != 0)
740f657cd55SCheng Sean Ye 				return (CMIERR_UNKNOWN);
741f657cd55SCheng Sean Ye 			if (strcmp(hcnm, "chip") == 0)
742f657cd55SCheng Sean Ye 				node = (int)v;
743f657cd55SCheng Sean Ye 			else if (strcmp(hcnm, "dram-channel") == 0)
744f657cd55SCheng Sean Ye 				channel = (int)v;
745f899e573SVuong Nguyen 			else if (strcmp(hcnm, "dimm") == 0)
746f899e573SVuong Nguyen 				dimm = (int)v;
747f657cd55SCheng Sean Ye 			else if (strcmp(hcnm, "rank") == 0)
748f657cd55SCheng Sean Ye 				rank = (int)v;
749f657cd55SCheng Sean Ye 		}
750f899e573SVuong Nguyen 		if (node == -1 || channel == -1 || dimm == -1 || rank == -1)
751f657cd55SCheng Sean Ye 			return (CMIERR_UNKNOWN);
752f657cd55SCheng Sean Ye 	} else {
753f657cd55SCheng Sean Ye 		node = unump->unum_chip;
754f657cd55SCheng Sean Ye 		channel = unump->unum_chan;
755f657cd55SCheng Sean Ye 		rank = unump->unum_rank;
756f899e573SVuong Nguyen 		dimm = unump->unum_cs;
757f899e573SVuong Nguyen 		offset = unump->unum_offset;
758f657cd55SCheng Sean Ye 	}
759f899e573SVuong Nguyen 
760f899e573SVuong Nguyen 	/*
761f899e573SVuong Nguyen 	 * If driver was built with closed tree present then we will have Intel
762f899e573SVuong Nguyen 	 * proprietary functions dimm_to_rankaddr for finding
763f899e573SVuong Nguyen 	 * physical address.
764f899e573SVuong Nguyen 	 */
765f899e573SVuong Nguyen 	if (&dimm_to_rankaddr && (offset & OFFSET_ROW_BANK_COL) != 0) {
766f899e573SVuong Nguyen 		row = TCODE_OFFSET_RAS(offset);
767f899e573SVuong Nguyen 		bank = TCODE_OFFSET_BANK(offset);
768f899e573SVuong Nguyen 		col = TCODE_OFFSET_CAS(offset);
769f899e573SVuong Nguyen 		rank_addr = dimm_to_rankaddr(node, channel, dimm, row,
770f899e573SVuong Nguyen 		    bank, col, &log_chan);
771f899e573SVuong Nguyen 		pa = rankaddr_to_phyaddr(node, log_chan, dimm, rank,
772f899e573SVuong Nguyen 		    rank_addr);
773f899e573SVuong Nguyen 	} else if ((offset & OFFSET_ROW_BANK_COL) == 0) {
774f899e573SVuong Nguyen 		pa = dimm_to_addr(node, channel, rank, offset, 0, 0, 0, 0, 0,
775f899e573SVuong Nguyen 		    0, 0, 0);
776f899e573SVuong Nguyen 	} else {
777f899e573SVuong Nguyen 		pa = -1LL;
778f899e573SVuong Nguyen 	}
779f899e573SVuong Nguyen 
780f657cd55SCheng Sean Ye 	if (pa == -1) {
781f657cd55SCheng Sean Ye 		rt = CMIERR_UNKNOWN;
782f657cd55SCheng Sean Ye 	} else {
783f657cd55SCheng Sean Ye 		rt = CMI_SUCCESS;
784f657cd55SCheng Sean Ye 		*pap = pa;
785f657cd55SCheng Sean Ye 	}
786f657cd55SCheng Sean Ye 	return (rt);
787f657cd55SCheng Sean Ye }
788f657cd55SCheng Sean Ye 
789f657cd55SCheng Sean Ye static const cmi_mc_ops_t nhm_mc_ops = {
790f657cd55SCheng Sean Ye 	nhm_patounum,
791f657cd55SCheng Sean Ye 	nhm_unumtopa,
792f657cd55SCheng Sean Ye 	nhm_error_trap	/* cmi_mc_logout */
793f657cd55SCheng Sean Ye };
794f657cd55SCheng Sean Ye 
795f657cd55SCheng Sean Ye /*ARGSUSED*/
796f657cd55SCheng Sean Ye int
inhm_mc_register(cmi_hdl_t hdl,void * arg1,void * arg2,void * arg3)797f657cd55SCheng Sean Ye inhm_mc_register(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3)
798f657cd55SCheng Sean Ye {
799f657cd55SCheng Sean Ye 	cmi_mc_register(hdl, &nhm_mc_ops, NULL);
800f657cd55SCheng Sean Ye 	return (CMI_HDL_WALK_NEXT);
801f657cd55SCheng Sean Ye }
802f657cd55SCheng Sean Ye 
803f657cd55SCheng Sean Ye static int
choose_cpu(int * lastslot_p)804f657cd55SCheng Sean Ye choose_cpu(int *lastslot_p)
805f657cd55SCheng Sean Ye {
806f657cd55SCheng Sean Ye 	uint32_t id;
807f657cd55SCheng Sean Ye 	int first;
808f657cd55SCheng Sean Ye 	int last;
809f657cd55SCheng Sean Ye 
810f657cd55SCheng Sean Ye 	first = 0;
811f657cd55SCheng Sean Ye 	last = MAX_CPU_NODES;
812f657cd55SCheng Sean Ye 	id = CPU_ID_RD(0);
81335366b93SAdrian Frost 	if (id == NHM_EP_CPU || id == NHM_WS_CPU || id == NHM_JF_CPU ||
81435366b93SAdrian Frost 	    id == NHM_WM_CPU) {
815f657cd55SCheng Sean Ye 		id = CPU_ID_RD(1);
81635366b93SAdrian Frost 		if (id != NHM_EP_CPU && id != NHM_WS_CPU && id != NHM_JF_CPU &&
81735366b93SAdrian Frost 		    id != NHM_WM_CPU) {
818f657cd55SCheng Sean Ye 			last = 1;
819f657cd55SCheng Sean Ye 		}
820f657cd55SCheng Sean Ye 	} else {
821f657cd55SCheng Sean Ye 		first = 1;
822f657cd55SCheng Sean Ye 	}
823f657cd55SCheng Sean Ye 	*lastslot_p = last;
824f657cd55SCheng Sean Ye 	return (first);
825f657cd55SCheng Sean Ye }
826f657cd55SCheng Sean Ye 
827f657cd55SCheng Sean Ye static int
sad_interleave(uint32_t list)828f657cd55SCheng Sean Ye sad_interleave(uint32_t list)
829f657cd55SCheng Sean Ye {
830f657cd55SCheng Sean Ye 	int rt = 1;
831f657cd55SCheng Sean Ye 	int i, j;
832f657cd55SCheng Sean Ye 	int p;
833f657cd55SCheng Sean Ye 
834f657cd55SCheng Sean Ye 	for (i = 1; i < INTERLEAVE_NWAY; i++) {
835f657cd55SCheng Sean Ye 		p = SAD_INTERLEAVE(list, i);
836f657cd55SCheng Sean Ye 		for (j = 0; j < i; j++) {
837f657cd55SCheng Sean Ye 			if (p == SAD_INTERLEAVE(list, j))
838f657cd55SCheng Sean Ye 				break;
839f657cd55SCheng Sean Ye 		}
840f657cd55SCheng Sean Ye 		if (i == j)
841f657cd55SCheng Sean Ye 			rt++;
842f657cd55SCheng Sean Ye 	}
843f657cd55SCheng Sean Ye 	return (rt);
844f657cd55SCheng Sean Ye }
845f657cd55SCheng Sean Ye 
846f657cd55SCheng Sean Ye static int
tad_interleave(uint32_t list)847f657cd55SCheng Sean Ye tad_interleave(uint32_t list)
848f657cd55SCheng Sean Ye {
849f657cd55SCheng Sean Ye 	int rt = 1;
850f657cd55SCheng Sean Ye 	int i, j;
851f657cd55SCheng Sean Ye 	int c;
852f657cd55SCheng Sean Ye 
853f657cd55SCheng Sean Ye 	for (i = 1; i < INTERLEAVE_NWAY; i++) {
854f657cd55SCheng Sean Ye 		c = TAD_INTERLEAVE(list, i);
855f657cd55SCheng Sean Ye 		for (j = 0; j < i; j++) {
856f657cd55SCheng Sean Ye 			if (c == TAD_INTERLEAVE(list, j))
857f657cd55SCheng Sean Ye 				break;
858f657cd55SCheng Sean Ye 		}
859f657cd55SCheng Sean Ye 		if (i == j)
860f657cd55SCheng Sean Ye 			rt++;
861f657cd55SCheng Sean Ye 	}
862f657cd55SCheng Sean Ye 	return (rt);
863f657cd55SCheng Sean Ye }
864f657cd55SCheng Sean Ye 
865f657cd55SCheng Sean Ye static void
set_rank(int socket,int channel,int rule,int way,int rank,uint64_t rank_addr)866f657cd55SCheng Sean Ye set_rank(int socket, int channel, int rule, int way, int rank,
867f657cd55SCheng Sean Ye     uint64_t rank_addr)
868f657cd55SCheng Sean Ye {
869f657cd55SCheng Sean Ye 	int k, l;
870f657cd55SCheng Sean Ye 	if (rank_addr == 0)
871f657cd55SCheng Sean Ye 		return;
872*e8ee2240SAdrian Frost 	/*
873*e8ee2240SAdrian Frost 	 * set limit on any rules which have virtual rank in current rank and
874*e8ee2240SAdrian Frost 	 * are not already limited by earlier rule
875*e8ee2240SAdrian Frost 	 */
876*e8ee2240SAdrian Frost 	for (k = 0; k < rule; k++) {
877*e8ee2240SAdrian Frost 		for (l = 0; l < MAX_RIR_WAY; l++) {
878f899e573SVuong Nguyen 			if (rir[socket][channel][k].way[l].dimm_rank == rank &&
879f657cd55SCheng Sean Ye 			    rir[socket][channel][k].way[l].rlimit == 0) {
880f657cd55SCheng Sean Ye 				rir[socket][channel][k].way[l].rlimit =
881f657cd55SCheng Sean Ye 				    rank_addr;
882f657cd55SCheng Sean Ye 			}
883f657cd55SCheng Sean Ye 		}
884f657cd55SCheng Sean Ye 	}
885*e8ee2240SAdrian Frost 	/*
886*e8ee2240SAdrian Frost 	 * set limit if this rule supplies more than 1 virtual rank from current
887*e8ee2240SAdrian Frost 	 * rank
888*e8ee2240SAdrian Frost 	 */
889*e8ee2240SAdrian Frost 	for (l = 0; l < way; l++) {
890*e8ee2240SAdrian Frost 		if (rir[socket][channel][k].way[l].dimm_rank == rank &&
891*e8ee2240SAdrian Frost 		    rir[socket][channel][k].way[l].rlimit == 0) {
892*e8ee2240SAdrian Frost 			rir[socket][channel][k].way[l].rlimit = rank_addr;
893*e8ee2240SAdrian Frost 		}
894*e8ee2240SAdrian Frost 	}
895f657cd55SCheng Sean Ye }
896f657cd55SCheng Sean Ye 
897f657cd55SCheng Sean Ye void
mem_reg_init()898f657cd55SCheng Sean Ye mem_reg_init()
899f657cd55SCheng Sean Ye {
900f657cd55SCheng Sean Ye 	int i, j, k, l, m;
901f657cd55SCheng Sean Ye 	uint32_t sad_dram_rule;
902f657cd55SCheng Sean Ye 	uint32_t tad_dram_rule;
903f657cd55SCheng Sean Ye 	uint32_t mc_ras_enables;
904f657cd55SCheng Sean Ye 	uint32_t mc_channel_mapping;
905f657cd55SCheng Sean Ye 	uint32_t sagch;
906f657cd55SCheng Sean Ye 	uint32_t rir_limit;
907f657cd55SCheng Sean Ye 	uint32_t rir_way;
908f657cd55SCheng Sean Ye 	uint32_t mc_control;
90935366b93SAdrian Frost 	uint32_t id;
910f657cd55SCheng Sean Ye 	int nhm_slot;
911f657cd55SCheng Sean Ye 	int nhm_lastslot;
912f657cd55SCheng Sean Ye 	uint8_t	rank;
913f657cd55SCheng Sean Ye 	uint64_t base;
91435366b93SAdrian Frost 	int ras_dev = 0;
915f899e573SVuong Nguyen 	uint32_t dod_value;
916f657cd55SCheng Sean Ye 
917f657cd55SCheng Sean Ye 	nhm_slot = choose_cpu(&nhm_lastslot);
918f657cd55SCheng Sean Ye 
919f657cd55SCheng Sean Ye 	for (i = 0; i < MAX_SAD_DRAM_RULE; i++) {
920f657cd55SCheng Sean Ye 		sad_dram_rule = SAD_DRAM_RULE_RD(nhm_slot, i);
921f657cd55SCheng Sean Ye 		sad[i].enable = SAD_DRAM_RULE_ENABLE(sad_dram_rule);
922f657cd55SCheng Sean Ye 		sad[i].limit = SAD_DRAM_LIMIT(sad_dram_rule);
923f657cd55SCheng Sean Ye 		sad[i].mode = SAD_DRAM_MODE(sad_dram_rule);
924f657cd55SCheng Sean Ye 		sad[i].node_list = SAD_INTERLEAVE_LIST_RD(nhm_slot, i);
925f657cd55SCheng Sean Ye 		sad[i].interleave = sad_interleave(sad[i].node_list);
926f899e573SVuong Nguyen 		for (j = 0; j < INTERLEAVE_NWAY; j++) {
927f899e573SVuong Nguyen 			sad[i].node_tgt[j] = (sad[i].node_list >>
928f899e573SVuong Nguyen 			    (j * 4)) & 0x3;
929f899e573SVuong Nguyen 		}
930f657cd55SCheng Sean Ye 	}
931f657cd55SCheng Sean Ye 
932f657cd55SCheng Sean Ye 	for (i = nhm_slot; i < nhm_lastslot; i++) {
93335366b93SAdrian Frost 		id = MC_CPU_RAS_RD(i);
93435366b93SAdrian Frost 		if (id == NHM_CPU_RAS || id == NHM_JF_CPU_RAS ||
93535366b93SAdrian Frost 		    id == NHM_WM_CPU_RAS) {
93635366b93SAdrian Frost 			ras_dev = 1;
937f657cd55SCheng Sean Ye 			mc_ras_enables = MC_RAS_ENABLES_RD(i);
938f657cd55SCheng Sean Ye 			if (RAS_LOCKSTEP_ENABLE(mc_ras_enables))
939f657cd55SCheng Sean Ye 				lockstep[i] = 1;
940f657cd55SCheng Sean Ye 			if (RAS_MIRROR_MEM_ENABLE(mc_ras_enables))
941f657cd55SCheng Sean Ye 				mirror_mode[i] = 1;
942ee9ef9e5SAdrian Frost 		}
943f657cd55SCheng Sean Ye 		mc_channel_mapping = MC_CHANNEL_MAPPER_RD(i);
944f657cd55SCheng Sean Ye 		if (CHANNEL_MAP(mc_channel_mapping, 2, 0) == 0 &&
945f657cd55SCheng Sean Ye 		    CHANNEL_MAP(mc_channel_mapping, 2, 1) == 0)
946f657cd55SCheng Sean Ye 			spare_channel[i] = 1;
947f657cd55SCheng Sean Ye 		for (j = 0; j < MAX_TAD_DRAM_RULE; j++) {
948f657cd55SCheng Sean Ye 			tad_dram_rule = TAD_DRAM_RULE_RD(i, j);
949f657cd55SCheng Sean Ye 			tad[i][j].enable = TAD_DRAM_RULE_ENABLE(tad_dram_rule);
950f657cd55SCheng Sean Ye 			tad[i][j].limit = TAD_DRAM_LIMIT(tad_dram_rule);
951f657cd55SCheng Sean Ye 			tad[i][j].mode = TAD_DRAM_MODE(tad_dram_rule);
952f657cd55SCheng Sean Ye 			tad[i][j].pkg_list =
953f657cd55SCheng Sean Ye 			    TAD_INTERLEAVE_LIST_RD(i, j);
954f899e573SVuong Nguyen 			for (k = 0; k < INTERLEAVE_NWAY; k++) {
955f899e573SVuong Nguyen 				tad[i][j].pkg_tgt[k] = ((tad[i][j].pkg_list >>
956f899e573SVuong Nguyen 				    (k * 4)) & 0x3);
957f899e573SVuong Nguyen 			}
958f657cd55SCheng Sean Ye 			if (mirror_mode[i] || lockstep[i]) {
959f657cd55SCheng Sean Ye 				tad[i][j].interleave = 1;
960f657cd55SCheng Sean Ye 			} else {
961f657cd55SCheng Sean Ye 				tad[i][j].interleave =
962f657cd55SCheng Sean Ye 				    tad_interleave(tad[i][j].pkg_list);
963f657cd55SCheng Sean Ye 				if (spare_channel[i] &&
964f657cd55SCheng Sean Ye 				    tad[i][j].interleave ==
965f657cd55SCheng Sean Ye 				    CHANNELS_PER_MEMORY_CONTROLLER)
966f657cd55SCheng Sean Ye 					tad[i][j].interleave--;
967f657cd55SCheng Sean Ye 			}
968f657cd55SCheng Sean Ye 		}
969f657cd55SCheng Sean Ye 		for (j = 0; j < CHANNELS_PER_MEMORY_CONTROLLER; j++) {
970f657cd55SCheng Sean Ye 			m = 0;
971f657cd55SCheng Sean Ye 			base = 0;
972f657cd55SCheng Sean Ye 			for (k = 0; k < MAX_TAD_DRAM_RULE; k++) {
973f657cd55SCheng Sean Ye 				sagch = MC_SAG_RD(i, j, k);
974f657cd55SCheng Sean Ye 				sag_ch[i][j][k].offset =
975f657cd55SCheng Sean Ye 				    CH_ADDRESS_OFFSET(sagch);
976f899e573SVuong Nguyen 				sag_ch[i][j][k].soffset =
977f899e573SVuong Nguyen 				    CH_ADDRESS_SOFFSET(sagch);
978f657cd55SCheng Sean Ye 				sag_ch[i][j][k].divby3 = DIVBY3(sagch);
979f657cd55SCheng Sean Ye 				sag_ch[i][j][k].remove6 = REMOVE_6(sagch);
980f657cd55SCheng Sean Ye 				sag_ch[i][j][k].remove7 = REMOVE_7(sagch);
981f657cd55SCheng Sean Ye 				sag_ch[i][j][k].remove8 = REMOVE_8(sagch);
982f657cd55SCheng Sean Ye 
983f657cd55SCheng Sean Ye 				rir_limit = MC_RIR_LIMIT_RD(i, j, k);
984f657cd55SCheng Sean Ye 				rir[i][j][k].limit = RIR_LIMIT(rir_limit);
985f657cd55SCheng Sean Ye 				for (l = 0; l < MAX_RIR_WAY; l++) {
986f657cd55SCheng Sean Ye 					rir_way = MC_RIR_WAY_RD(i, j, m);
987f657cd55SCheng Sean Ye 					rir[i][j][k].way[l].offset =
988f657cd55SCheng Sean Ye 					    RIR_OFFSET(rir_way);
989f899e573SVuong Nguyen 					rir[i][j][k].way[l].soffset =
990f899e573SVuong Nguyen 					    RIR_SOFFSET(rir_way);
991f657cd55SCheng Sean Ye 					rir[i][j][k].way[l].rank =
992f657cd55SCheng Sean Ye 					    RIR_RANK(rir_way);
993f899e573SVuong Nguyen 					rir[i][j][k].way[l].dimm =
994f899e573SVuong Nguyen 					    RIR_DIMM(rir_way);
995f899e573SVuong Nguyen 					rir[i][j][k].way[l].dimm_rank =
996f899e573SVuong Nguyen 					    RIR_DIMM_RANK(rir_way);
997f657cd55SCheng Sean Ye 					rir[i][j][k].way[l].rlimit = 0;
998f657cd55SCheng Sean Ye 					m++;
999f657cd55SCheng Sean Ye 				}
1000f899e573SVuong Nguyen 				rank = rir[i][j][k].way[0].dimm_rank;
1001f899e573SVuong Nguyen 				if (rank == rir[i][j][k].way[1].dimm_rank &&
1002f899e573SVuong Nguyen 				    rank == rir[i][j][k].way[2].dimm_rank &&
1003f899e573SVuong Nguyen 				    rank == rir[i][j][k].way[3].dimm_rank) {
1004f657cd55SCheng Sean Ye 					rir[i][j][k].interleave = 1;
1005f899e573SVuong Nguyen 				} else if
1006f899e573SVuong Nguyen 				    (rank == rir[i][j][k].way[1].dimm_rank ||
1007f899e573SVuong Nguyen 				    rank == rir[i][j][k].way[2].dimm_rank ||
1008f899e573SVuong Nguyen 				    rank == rir[i][j][k].way[3].dimm_rank) {
1009f657cd55SCheng Sean Ye 					rir[i][j][k].interleave = 2;
1010f657cd55SCheng Sean Ye 				} else {
1011f657cd55SCheng Sean Ye 					rir[i][j][k].interleave = 4;
1012f657cd55SCheng Sean Ye 				}
1013f657cd55SCheng Sean Ye 				for (l = 0; l < MAX_RIR_WAY; l++) {
1014f657cd55SCheng Sean Ye 					set_rank(i, j, k, l,
1015f899e573SVuong Nguyen 					    rir[i][j][k].way[l].dimm_rank,
1016f899e573SVuong Nguyen 					    ((rir[i][j][k].way[l].soffset +
1017f657cd55SCheng Sean Ye 					    base) /
1018f657cd55SCheng Sean Ye 					    rir[i][j][k].interleave));
1019f657cd55SCheng Sean Ye 				}
1020f657cd55SCheng Sean Ye 				base = rir[i][j][k].limit;
1021f657cd55SCheng Sean Ye 			}
1022f899e573SVuong Nguyen 			for (k = 0; k < MAX_DIMMS_PER_CHANNEL; k++) {
1023f899e573SVuong Nguyen 				dod_value = MC_DOD_RD(i, j, k);
1024f899e573SVuong Nguyen 				dod_reg[i][j][k].NUMCol = NUMCOL(dod_value);
1025f899e573SVuong Nguyen 				dod_reg[i][j][k].NUMRow = NUMROW(dod_value);
1026f899e573SVuong Nguyen 				dod_reg[i][j][k].NUMBank = NUMBANK(dod_value);
1027f899e573SVuong Nguyen 				dod_reg[i][j][k].NUMRank = NUMRANK(dod_value);
1028f899e573SVuong Nguyen 				dod_reg[i][j][k].DIMMPresent =
1029f899e573SVuong Nguyen 				    DIMMPRESENT(dod_value);
1030f899e573SVuong Nguyen 				dod_reg[i][j][k].RankOffset =
1031f899e573SVuong Nguyen 				    RANKOFFSET(dod_value);
1032f899e573SVuong Nguyen 			}
1033f657cd55SCheng Sean Ye 		}
1034f657cd55SCheng Sean Ye 	}
1035f657cd55SCheng Sean Ye 	mc_control = MC_CONTROL_RD(nhm_slot);
1036f657cd55SCheng Sean Ye 	closed_page = MC_CONTROL_CLOSED_PAGE(mc_control);
103735366b93SAdrian Frost 	if (ras_dev)
1038f657cd55SCheng Sean Ye 		ecc_enabled = MC_CONTROL_ECCEN(mc_control);
1039ee9ef9e5SAdrian Frost 	else if ((MC_STATUS_RD(nhm_slot) & WS_ECC_ENABLED) != 0)
1040ee9ef9e5SAdrian Frost 		ecc_enabled = 1;
1041f899e573SVuong Nguyen 	divby3_enabled = MC_CONTROL_DIVBY3(mc_control);
1042f657cd55SCheng Sean Ye }
1043