xref: /onnv-gate/usr/src/uts/intel/io/intel_nhm/mem_addr.c (revision 11516:dea3f81dcab2)
18472SSean.Ye@Sun.COM /*
28472SSean.Ye@Sun.COM  * CDDL HEADER START
38472SSean.Ye@Sun.COM  *
48472SSean.Ye@Sun.COM  * The contents of this file are subject to the terms of the
58472SSean.Ye@Sun.COM  * Common Development and Distribution License (the "License").
68472SSean.Ye@Sun.COM  * You may not use this file except in compliance with the License.
78472SSean.Ye@Sun.COM  *
88472SSean.Ye@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98472SSean.Ye@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108472SSean.Ye@Sun.COM  * See the License for the specific language governing permissions
118472SSean.Ye@Sun.COM  * and limitations under the License.
128472SSean.Ye@Sun.COM  *
138472SSean.Ye@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148472SSean.Ye@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158472SSean.Ye@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168472SSean.Ye@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178472SSean.Ye@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188472SSean.Ye@Sun.COM  *
198472SSean.Ye@Sun.COM  * CDDL HEADER END
208472SSean.Ye@Sun.COM  */
218472SSean.Ye@Sun.COM 
228472SSean.Ye@Sun.COM /*
23*11516SAdrian.Frost@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
248472SSean.Ye@Sun.COM  * Use is subject to license terms.
258472SSean.Ye@Sun.COM  */
268472SSean.Ye@Sun.COM 
278472SSean.Ye@Sun.COM #include <sys/types.h>
288472SSean.Ye@Sun.COM #include <sys/time.h>
298472SSean.Ye@Sun.COM #include <sys/fm/protocol.h>
308472SSean.Ye@Sun.COM #include <sys/cpu_module_impl.h>
3110650SVuong.Nguyen@Sun.COM #include <sys/mc_intel.h>
328472SSean.Ye@Sun.COM #include "intel_nhm.h"
338472SSean.Ye@Sun.COM #include "nhm_log.h"
3410650SVuong.Nguyen@Sun.COM #include "mem_addr.h"
358472SSean.Ye@Sun.COM 
368472SSean.Ye@Sun.COM char closed_page;
378472SSean.Ye@Sun.COM char ecc_enabled;
3810650SVuong.Nguyen@Sun.COM char divby3_enabled;
398472SSean.Ye@Sun.COM char lockstep[2];
408472SSean.Ye@Sun.COM char mirror_mode[2];
418472SSean.Ye@Sun.COM char spare_channel[2];
4210650SVuong.Nguyen@Sun.COM sad_t sad[MAX_SAD_DRAM_RULE];
4310650SVuong.Nguyen@Sun.COM tad_t tad[MAX_CPU_NODES][MAX_TAD_DRAM_RULE];
4410650SVuong.Nguyen@Sun.COM sag_ch_t sag_ch[MAX_CPU_NODES][CHANNELS_PER_MEMORY_CONTROLLER]
4510650SVuong.Nguyen@Sun.COM 	[MAX_TAD_DRAM_RULE];
4610650SVuong.Nguyen@Sun.COM rir_t rir[MAX_CPU_NODES][CHANNELS_PER_MEMORY_CONTROLLER]
4710650SVuong.Nguyen@Sun.COM 	[MAX_TAD_DRAM_RULE];
4810650SVuong.Nguyen@Sun.COM dod_t dod_reg[MAX_CPU_NODES][CHANNELS_PER_MEMORY_CONTROLLER]
4910650SVuong.Nguyen@Sun.COM 	[MAX_DIMMS_PER_CHANNEL];
508472SSean.Ye@Sun.COM 
518472SSean.Ye@Sun.COM static int
channel_in_interleave(int node,int channel,int rule,int * way_p,int * no_interleave_p)528472SSean.Ye@Sun.COM channel_in_interleave(int node, int channel, int rule, int *way_p,
538472SSean.Ye@Sun.COM     int *no_interleave_p)
548472SSean.Ye@Sun.COM {
558472SSean.Ye@Sun.COM 	int way;
568472SSean.Ye@Sun.COM 	int c;
578472SSean.Ye@Sun.COM 	int i;
588472SSean.Ye@Sun.COM 	uint32_t mc_channel_mapper;
598472SSean.Ye@Sun.COM 	int lc;
608472SSean.Ye@Sun.COM 	int rt = 0;
618472SSean.Ye@Sun.COM 	int start = 0;
628472SSean.Ye@Sun.COM 
638472SSean.Ye@Sun.COM 	if (lockstep[node] || mirror_mode[node]) {
648472SSean.Ye@Sun.COM 		*no_interleave_p = 0;
658472SSean.Ye@Sun.COM 		if (channel > 1)
668472SSean.Ye@Sun.COM 			return (0);
678472SSean.Ye@Sun.COM 		else
688472SSean.Ye@Sun.COM 			return (1);
698472SSean.Ye@Sun.COM 	}
708472SSean.Ye@Sun.COM 	mc_channel_mapper = MC_CHANNEL_MAPPER_RD(node);
718472SSean.Ye@Sun.COM 	lc = -1;
728472SSean.Ye@Sun.COM 	c = 1 << channel;
738472SSean.Ye@Sun.COM 	for (i = 0; i < CHANNELS_PER_MEMORY_CONTROLLER; i++) {
748472SSean.Ye@Sun.COM 		if ((CHANNEL_MAP(mc_channel_mapper, i, 0) & c) != 0) {
758472SSean.Ye@Sun.COM 			lc = i;
768472SSean.Ye@Sun.COM 			break;
778472SSean.Ye@Sun.COM 		}
788472SSean.Ye@Sun.COM 	}
798472SSean.Ye@Sun.COM 	if (lc == -1) {
808472SSean.Ye@Sun.COM 		for (i = 0; i < CHANNELS_PER_MEMORY_CONTROLLER; i++) {
818472SSean.Ye@Sun.COM 			if ((CHANNEL_MAP(mc_channel_mapper, i, 1) & c) != 0) {
828472SSean.Ye@Sun.COM 				lc = i;
838472SSean.Ye@Sun.COM 				break;
848472SSean.Ye@Sun.COM 			}
858472SSean.Ye@Sun.COM 		}
868472SSean.Ye@Sun.COM 	}
878472SSean.Ye@Sun.COM 	if (lc == -1) {
888472SSean.Ye@Sun.COM 		return (0);
898472SSean.Ye@Sun.COM 	}
908472SSean.Ye@Sun.COM 	*way_p = 0;
918472SSean.Ye@Sun.COM 	*no_interleave_p = 0;
928472SSean.Ye@Sun.COM 	if (node && tad[node][rule].mode == 2)
938472SSean.Ye@Sun.COM 		start = 4;
948472SSean.Ye@Sun.COM 	for (way = start; way < INTERLEAVE_NWAY; way++) {
958472SSean.Ye@Sun.COM 		if (lc == TAD_INTERLEAVE(tad[node][rule].pkg_list, way)) {
968472SSean.Ye@Sun.COM 			*way_p = way;
978472SSean.Ye@Sun.COM 			if (way == 0) {
988472SSean.Ye@Sun.COM 				for (i = way + 1; i < INTERLEAVE_NWAY; i++) {
998472SSean.Ye@Sun.COM 					c = TAD_INTERLEAVE(
1008472SSean.Ye@Sun.COM 					    tad[node][rule].pkg_list, i);
1018472SSean.Ye@Sun.COM 					if (lc != c) {
1028472SSean.Ye@Sun.COM 						break;
1038472SSean.Ye@Sun.COM 					}
1048472SSean.Ye@Sun.COM 				}
1058472SSean.Ye@Sun.COM 				if (i == INTERLEAVE_NWAY)
1068472SSean.Ye@Sun.COM 					*no_interleave_p = 1;
1078472SSean.Ye@Sun.COM 			}
1088472SSean.Ye@Sun.COM 			rt = 1;
1098472SSean.Ye@Sun.COM 			break;
1108472SSean.Ye@Sun.COM 		}
1118472SSean.Ye@Sun.COM 	}
1128472SSean.Ye@Sun.COM 	return (rt);
1138472SSean.Ye@Sun.COM }
1148472SSean.Ye@Sun.COM 
1158472SSean.Ye@Sun.COM int
address_to_node(uint64_t addr,int * interleave_p)1168472SSean.Ye@Sun.COM address_to_node(uint64_t addr, int *interleave_p)
1178472SSean.Ye@Sun.COM {
1188472SSean.Ye@Sun.COM 	int i;
1198472SSean.Ye@Sun.COM 	int node = -1;
1208472SSean.Ye@Sun.COM 	uint64_t base;
1218472SSean.Ye@Sun.COM 	int way;
1228472SSean.Ye@Sun.COM 	uchar_t package;
1238472SSean.Ye@Sun.COM 
1248472SSean.Ye@Sun.COM 	base = 0;
1258472SSean.Ye@Sun.COM 	for (i = 0; i < MAX_SAD_DRAM_RULE; i++) {
1268472SSean.Ye@Sun.COM 		if (sad[i].enable && addr >= base && addr < sad[i].limit) {
1278472SSean.Ye@Sun.COM 			switch (sad[i].mode) {
1288472SSean.Ye@Sun.COM 			case 0:
1298472SSean.Ye@Sun.COM 				way = (addr >> 6) & 7;
1308472SSean.Ye@Sun.COM 				break;
1318472SSean.Ye@Sun.COM 			case 1:
1328472SSean.Ye@Sun.COM 				way = ((addr >> 6) & 7) ^ ((addr >> 16) & 7);
1338472SSean.Ye@Sun.COM 				break;
1348472SSean.Ye@Sun.COM 			case 2:
1358472SSean.Ye@Sun.COM 				way = ((addr >> 4) & 4) |
1368472SSean.Ye@Sun.COM 				    (((addr >> 6) & 0x3ffffffff) % 3);
1378472SSean.Ye@Sun.COM 				break;
1388472SSean.Ye@Sun.COM 			default:
1398472SSean.Ye@Sun.COM 				return (-1);
1408472SSean.Ye@Sun.COM 			}
1418472SSean.Ye@Sun.COM 			package = SAD_INTERLEAVE(sad[i].node_list, way);
1428472SSean.Ye@Sun.COM 			if (interleave_p)
1438472SSean.Ye@Sun.COM 				*interleave_p = sad[i].interleave;
1448472SSean.Ye@Sun.COM 			if (package == 1)
1458472SSean.Ye@Sun.COM 				node = 0;
1468472SSean.Ye@Sun.COM 			else if (package == 2)
1478472SSean.Ye@Sun.COM 				node = 1;
1488472SSean.Ye@Sun.COM 			else
1498472SSean.Ye@Sun.COM 				node = -1;
1508472SSean.Ye@Sun.COM 			break;
1518472SSean.Ye@Sun.COM 		}
1528472SSean.Ye@Sun.COM 		base = sad[i].limit;
1538472SSean.Ye@Sun.COM 	}
1548472SSean.Ye@Sun.COM 	return (node);
1558472SSean.Ye@Sun.COM }
1568472SSean.Ye@Sun.COM 
1578472SSean.Ye@Sun.COM static uint64_t
channel_address(int node,int channel,int rule,uint64_t addr)1588472SSean.Ye@Sun.COM channel_address(int node, int channel, int rule, uint64_t addr)
1598472SSean.Ye@Sun.COM {
1608472SSean.Ye@Sun.COM 	uint64_t caddr;
1618472SSean.Ye@Sun.COM 
1628472SSean.Ye@Sun.COM 	if (lockstep[node] || mirror_mode[node])
1638472SSean.Ye@Sun.COM 		channel = 0;
1648472SSean.Ye@Sun.COM 	caddr = (((addr >> 16) +
16510650SVuong.Nguyen@Sun.COM 	    (int64_t)sag_ch[node][channel][rule].soffset) << 16) |
1668472SSean.Ye@Sun.COM 	    (addr & 0xffc0);
1678472SSean.Ye@Sun.COM 	if (sag_ch[node][channel][rule].remove8) {
1688472SSean.Ye@Sun.COM 		caddr = ((caddr >> 1) & ~0xff) | (caddr & 0xff);
1698472SSean.Ye@Sun.COM 	}
1708472SSean.Ye@Sun.COM 	if (sag_ch[node][channel][rule].remove7) {
1718472SSean.Ye@Sun.COM 		caddr = ((caddr >> 1) & ~0x7f) | (caddr & 0x7f);
1728472SSean.Ye@Sun.COM 	}
1738472SSean.Ye@Sun.COM 	if (sag_ch[node][channel][rule].remove6) {
1748472SSean.Ye@Sun.COM 		caddr = ((caddr >> 1) & ~0x3f) | (caddr & 0x3f);
1758472SSean.Ye@Sun.COM 	}
1768472SSean.Ye@Sun.COM 	caddr = caddr & 0x1fffffffff;
1778472SSean.Ye@Sun.COM 	if (sag_ch[node][channel][rule].divby3) {
1788472SSean.Ye@Sun.COM 		caddr = ((((caddr >> 6) / 3) << 6) & 0x1fffffffc0) |
1798472SSean.Ye@Sun.COM 		    (caddr & 0x3f);
1808472SSean.Ye@Sun.COM 	}
1818472SSean.Ye@Sun.COM 	return (caddr);
1828472SSean.Ye@Sun.COM }
1838472SSean.Ye@Sun.COM 
1848472SSean.Ye@Sun.COM int
address_to_channel(int node,uint64_t addr,int write,int * log_chan,uint64_t * channel_addrp,int * interleave_p)18510650SVuong.Nguyen@Sun.COM address_to_channel(int node, uint64_t addr, int write,
18610650SVuong.Nguyen@Sun.COM     int *log_chan, uint64_t *channel_addrp, int *interleave_p)
1878472SSean.Ye@Sun.COM {
1888472SSean.Ye@Sun.COM 	int i;
1898472SSean.Ye@Sun.COM 	int channel = -1;
1908472SSean.Ye@Sun.COM 	uint64_t base;
1918472SSean.Ye@Sun.COM 	uint32_t mapper;
1928472SSean.Ye@Sun.COM 	uint32_t lc;
1938472SSean.Ye@Sun.COM 	int way;
1948472SSean.Ye@Sun.COM 
1958472SSean.Ye@Sun.COM 	base = 0;
1968472SSean.Ye@Sun.COM 	for (i = 0; i < MAX_TAD_DRAM_RULE; i++) {
1978472SSean.Ye@Sun.COM 		if (tad[node][i].enable && addr >= base &&
1988472SSean.Ye@Sun.COM 		    addr < tad[node][i].limit) {
1998472SSean.Ye@Sun.COM 			switch (tad[node][i].mode) {
2008472SSean.Ye@Sun.COM 			case 0:
2018472SSean.Ye@Sun.COM 				way = (addr >> 6) & 7;
2028472SSean.Ye@Sun.COM 				break;
2038472SSean.Ye@Sun.COM 			case 1:
2048472SSean.Ye@Sun.COM 				way = ((addr >> 6) & 7) ^ ((addr >> 16) & 7);
2058472SSean.Ye@Sun.COM 				break;
2068472SSean.Ye@Sun.COM 			case 2:
2078472SSean.Ye@Sun.COM 				way = ((addr >> 4) & 4) |
2088472SSean.Ye@Sun.COM 				    (((addr >> 6) & 0x3ffffffff) % 3);
2098472SSean.Ye@Sun.COM 				break;
2108472SSean.Ye@Sun.COM 			default:
2118472SSean.Ye@Sun.COM 				return (-1);
2128472SSean.Ye@Sun.COM 			}
21310650SVuong.Nguyen@Sun.COM 			/* get logical channel number */
2148472SSean.Ye@Sun.COM 			channel = TAD_INTERLEAVE(tad[node][i].pkg_list, way);
21510650SVuong.Nguyen@Sun.COM 			if (log_chan)
21610650SVuong.Nguyen@Sun.COM 				*log_chan = channel;
21710650SVuong.Nguyen@Sun.COM 
2188472SSean.Ye@Sun.COM 			if (channel_addrp) {
21910650SVuong.Nguyen@Sun.COM 				*channel_addrp = channel_address(node,
22010650SVuong.Nguyen@Sun.COM 				    channel, i, addr);
2218472SSean.Ye@Sun.COM 			}
2228472SSean.Ye@Sun.COM 			if (interleave_p)
2238472SSean.Ye@Sun.COM 				*interleave_p = tad[node][i].interleave;
2248472SSean.Ye@Sun.COM 			break;
2258472SSean.Ye@Sun.COM 		}
2268472SSean.Ye@Sun.COM 		base = tad[node][i].limit;
2278472SSean.Ye@Sun.COM 	}
2288472SSean.Ye@Sun.COM 	if (!lockstep[node] && channel != -1) {
2298472SSean.Ye@Sun.COM 		mapper = MC_CHANNEL_MAPPER_RD(node);
2308472SSean.Ye@Sun.COM 		lc = CHANNEL_MAP(mapper, channel, write);
2318472SSean.Ye@Sun.COM 		switch (lc) {
2328472SSean.Ye@Sun.COM 		case 1:
2338472SSean.Ye@Sun.COM 			channel = 0;
2348472SSean.Ye@Sun.COM 			break;
2358472SSean.Ye@Sun.COM 		case 2:
2368472SSean.Ye@Sun.COM 			channel = 1;
2378472SSean.Ye@Sun.COM 			break;
2388472SSean.Ye@Sun.COM 		case 4:
2398472SSean.Ye@Sun.COM 			channel = 2;
2408472SSean.Ye@Sun.COM 			break;
24110650SVuong.Nguyen@Sun.COM 		case 3:		/* mirror PCH0 and PCH1 */
2428472SSean.Ye@Sun.COM 			if (!write) {
2438472SSean.Ye@Sun.COM 				if (((addr >> 24) & 1) ^ ((addr >> 12) & 1) ^
2448472SSean.Ye@Sun.COM 				    ((addr >> 6) & 1))
2458472SSean.Ye@Sun.COM 					channel = 1;
2468472SSean.Ye@Sun.COM 				else
2478472SSean.Ye@Sun.COM 					channel = 0;
2488472SSean.Ye@Sun.COM 			}
2498472SSean.Ye@Sun.COM 			break;
25010650SVuong.Nguyen@Sun.COM 		case 5:		/* sparing PCH0 to PCH2 */
2518472SSean.Ye@Sun.COM 			channel = 0;
2528472SSean.Ye@Sun.COM 			break;
25310650SVuong.Nguyen@Sun.COM 		case 6:		/* sparing PCH1 to PCH2 */
2548472SSean.Ye@Sun.COM 			channel = 1;
2558472SSean.Ye@Sun.COM 			break;
2568472SSean.Ye@Sun.COM 		}
2578472SSean.Ye@Sun.COM 	}
2588472SSean.Ye@Sun.COM 	return (channel);
2598472SSean.Ye@Sun.COM }
2608472SSean.Ye@Sun.COM 
2618472SSean.Ye@Sun.COM int
channels_interleave(uint64_t addr)2628472SSean.Ye@Sun.COM channels_interleave(uint64_t addr)
2638472SSean.Ye@Sun.COM {
2648472SSean.Ye@Sun.COM 	int node;
2658472SSean.Ye@Sun.COM 	int sinterleave;
2668472SSean.Ye@Sun.COM 	int channels, channels1;
2678472SSean.Ye@Sun.COM 
2688472SSean.Ye@Sun.COM 	node = address_to_node(addr, &sinterleave);
2698472SSean.Ye@Sun.COM 	if (sinterleave == 1) {
2708472SSean.Ye@Sun.COM 		channels = 0;
27110650SVuong.Nguyen@Sun.COM 		(void) address_to_channel(node, addr, 0, 0, 0, &channels);
2728472SSean.Ye@Sun.COM 	} else {
2738472SSean.Ye@Sun.COM 		channels = 0;
2748472SSean.Ye@Sun.COM 		channels1 = 0;
27510650SVuong.Nguyen@Sun.COM 		(void) address_to_channel(0, addr, 0, 0, 0, &channels);
27610650SVuong.Nguyen@Sun.COM 		(void) address_to_channel(1, addr, 0, 0, 0, &channels1);
2778472SSean.Ye@Sun.COM 		channels += channels1;
2788472SSean.Ye@Sun.COM 	}
2798472SSean.Ye@Sun.COM 	return (channels);
2808472SSean.Ye@Sun.COM }
2818472SSean.Ye@Sun.COM 
2828472SSean.Ye@Sun.COM int
channel_addr_to_dimm(int node,int channel,uint64_t caddr,int * rank_p,uint64_t * rank_addr_p)28310650SVuong.Nguyen@Sun.COM channel_addr_to_dimm(int node, int channel, uint64_t caddr, int *rank_p,
2848472SSean.Ye@Sun.COM     uint64_t *rank_addr_p)
2858472SSean.Ye@Sun.COM {
2868472SSean.Ye@Sun.COM 	int i;
2878472SSean.Ye@Sun.COM 	uint64_t base;
2888472SSean.Ye@Sun.COM 	uint64_t rank_addr;
2898472SSean.Ye@Sun.COM 	int rank;
2908472SSean.Ye@Sun.COM 	int dimm;
2918472SSean.Ye@Sun.COM 	int way;
2928472SSean.Ye@Sun.COM 
2938472SSean.Ye@Sun.COM 	dimm = -1;
2948472SSean.Ye@Sun.COM 	rank = -1;
2958472SSean.Ye@Sun.COM 	base = 0;
2968472SSean.Ye@Sun.COM 	rank_addr = -1ULL;
2978472SSean.Ye@Sun.COM 	for (i = 0; i < MAX_TAD_DRAM_RULE; i++) {
2988472SSean.Ye@Sun.COM 		if (caddr >= base && caddr < rir[node][channel][i].limit) {
2998472SSean.Ye@Sun.COM 			if (closed_page) {
3008472SSean.Ye@Sun.COM 				way = (caddr >> 6) & 3;
3018472SSean.Ye@Sun.COM 				rank_addr = (((caddr + (int64_t)
3028472SSean.Ye@Sun.COM 				    rir[node][channel][i].way[way].offset *
3038472SSean.Ye@Sun.COM 				    VRANK_SZ) /
3048472SSean.Ye@Sun.COM 				    rir[node][channel][i].interleave) &
3058472SSean.Ye@Sun.COM 				    ~0x3f) + (caddr & 0x3f);
3068472SSean.Ye@Sun.COM 			} else {
3078472SSean.Ye@Sun.COM 				way = (caddr >> 12) & 3;
3088472SSean.Ye@Sun.COM 				rank_addr = (((caddr + (int64_t)
3098472SSean.Ye@Sun.COM 				    rir[node][channel][i].way[way].offset *
3108472SSean.Ye@Sun.COM 				    VRANK_SZ) /
3118472SSean.Ye@Sun.COM 				    rir[node][channel][i].interleave) &
3128472SSean.Ye@Sun.COM 				    ~0xfff) + (caddr & 0xfff);
3138472SSean.Ye@Sun.COM 			}
3148472SSean.Ye@Sun.COM 			rank = rir[node][channel][i].way[way].rank;
3158472SSean.Ye@Sun.COM 			dimm = rank >> 2;
3168472SSean.Ye@Sun.COM 			break;
3178472SSean.Ye@Sun.COM 		}
3188472SSean.Ye@Sun.COM 		base = rir[node][channel][i].limit;
3198472SSean.Ye@Sun.COM 	}
3208472SSean.Ye@Sun.COM 	*rank_p = rank;
3218472SSean.Ye@Sun.COM 	*rank_addr_p = rank_addr;
3228472SSean.Ye@Sun.COM 	return (dimm);
3238472SSean.Ye@Sun.COM }
3248472SSean.Ye@Sun.COM 
3258472SSean.Ye@Sun.COM static int
socket_interleave(uint64_t addr,int node,int channel,int rule,int * way_p)3268472SSean.Ye@Sun.COM socket_interleave(uint64_t addr, int node, int channel, int rule,
3278472SSean.Ye@Sun.COM     int *way_p)
3288472SSean.Ye@Sun.COM {
3298472SSean.Ye@Sun.COM 	int i, j;
3308472SSean.Ye@Sun.COM 	uint64_t base;
3318472SSean.Ye@Sun.COM 	uchar_t package;
3328472SSean.Ye@Sun.COM 	uchar_t xp;
3338472SSean.Ye@Sun.COM 	uchar_t xc;
3348472SSean.Ye@Sun.COM 	int ot = 0;
3358472SSean.Ye@Sun.COM 	int mode;
3368472SSean.Ye@Sun.COM 	int start;
3378472SSean.Ye@Sun.COM 	int rt = 1;
3388472SSean.Ye@Sun.COM 	int found = 0;
3398472SSean.Ye@Sun.COM 
3408472SSean.Ye@Sun.COM 	if (mirror_mode[node] || lockstep[node])
3418472SSean.Ye@Sun.COM 		channel = 0;
3428472SSean.Ye@Sun.COM 	package = node + 1;
3438472SSean.Ye@Sun.COM 	mode = tad[node][rule].mode;
3448472SSean.Ye@Sun.COM 	base = 0;
3458472SSean.Ye@Sun.COM 	for (i = 0; i < MAX_SAD_DRAM_RULE; i++) {
3468472SSean.Ye@Sun.COM 		if (sad[i].enable && addr >= base && addr < sad[i].limit) {
3478472SSean.Ye@Sun.COM 			if (mode == 2) {
3488472SSean.Ye@Sun.COM 				for (j = 0; j < INTERLEAVE_NWAY; j++) {
3498472SSean.Ye@Sun.COM 					xp = SAD_INTERLEAVE(sad[i].node_list,
3508472SSean.Ye@Sun.COM 					    j);
3518472SSean.Ye@Sun.COM 					if (package != xp) {
3528472SSean.Ye@Sun.COM 						ot++;
3538472SSean.Ye@Sun.COM 						if (found) {
3548472SSean.Ye@Sun.COM 							rt = 2;
3558472SSean.Ye@Sun.COM 							break;
3568472SSean.Ye@Sun.COM 						}
3578472SSean.Ye@Sun.COM 					} else {
3588472SSean.Ye@Sun.COM 						found = 1;
3598472SSean.Ye@Sun.COM 						if (ot) {
3608472SSean.Ye@Sun.COM 							rt = 2;
3618472SSean.Ye@Sun.COM 							break;
3628472SSean.Ye@Sun.COM 						}
3638472SSean.Ye@Sun.COM 					}
3648472SSean.Ye@Sun.COM 				}
3658472SSean.Ye@Sun.COM 			} else {
3668472SSean.Ye@Sun.COM 				if (mode == 2)
3678472SSean.Ye@Sun.COM 					start = *way_p;
3688472SSean.Ye@Sun.COM 				else
3698472SSean.Ye@Sun.COM 					start = 0;
3708472SSean.Ye@Sun.COM 				for (j = start; j < INTERLEAVE_NWAY; j++) {
3718472SSean.Ye@Sun.COM 					xp = SAD_INTERLEAVE(sad[i].node_list,
3728472SSean.Ye@Sun.COM 					    j);
3738472SSean.Ye@Sun.COM 					if (package != xp) {
3748472SSean.Ye@Sun.COM 						ot++;
3758472SSean.Ye@Sun.COM 						if (found) {
3768472SSean.Ye@Sun.COM 							rt = 2;
3778472SSean.Ye@Sun.COM 							break;
3788472SSean.Ye@Sun.COM 						}
3798472SSean.Ye@Sun.COM 					} else if (!found) {
3808472SSean.Ye@Sun.COM 						xc = TAD_INTERLEAVE(
3818472SSean.Ye@Sun.COM 						    tad[node][rule].pkg_list,
3828472SSean.Ye@Sun.COM 						    j);
3838472SSean.Ye@Sun.COM 						if (channel == xc) {
3848472SSean.Ye@Sun.COM 							*way_p = j;
3858472SSean.Ye@Sun.COM 							if (ot) {
3868472SSean.Ye@Sun.COM 								rt = 2;
3878472SSean.Ye@Sun.COM 								break;
3888472SSean.Ye@Sun.COM 							}
3898472SSean.Ye@Sun.COM 							found = 1;
3908472SSean.Ye@Sun.COM 						}
3918472SSean.Ye@Sun.COM 					}
3928472SSean.Ye@Sun.COM 				}
3938472SSean.Ye@Sun.COM 			}
3948472SSean.Ye@Sun.COM 			break;
3958472SSean.Ye@Sun.COM 		}
3968472SSean.Ye@Sun.COM 		base = sad[i].limit;
3978472SSean.Ye@Sun.COM 	}
3988472SSean.Ye@Sun.COM 	return (rt);
3998472SSean.Ye@Sun.COM }
4008472SSean.Ye@Sun.COM 
4018472SSean.Ye@Sun.COM 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)4028472SSean.Ye@Sun.COM dimm_to_addr(int node, int channel, int rank, uint64_t rank_addr,
4038472SSean.Ye@Sun.COM     uint64_t *rank_base_p, uint64_t *rank_sz_p, uint32_t *socket_interleave_p,
4048472SSean.Ye@Sun.COM     uint32_t *channel_interleave_p, uint32_t *rank_interleave_p,
4058472SSean.Ye@Sun.COM     uint32_t *socket_way_p, uint32_t *channel_way_p, uint32_t *rank_way_p)
4068472SSean.Ye@Sun.COM {
4078472SSean.Ye@Sun.COM 	int i;
4088472SSean.Ye@Sun.COM 	int way, xway;
4098472SSean.Ye@Sun.COM 	uint64_t addr;
4108472SSean.Ye@Sun.COM 	uint64_t caddr;
4118472SSean.Ye@Sun.COM 	uint64_t cbaddr;
4128472SSean.Ye@Sun.COM 	uint64_t baddr;
4138472SSean.Ye@Sun.COM 	uint64_t rlimit;
4148472SSean.Ye@Sun.COM 	uint64_t rank_sz;
4158472SSean.Ye@Sun.COM 	uint64_t base;
4168472SSean.Ye@Sun.COM 	int lchannel;
4178472SSean.Ye@Sun.COM 	int bits;
4188472SSean.Ye@Sun.COM 	int no_interleave;
4198472SSean.Ye@Sun.COM 	int sinterleave;
4208472SSean.Ye@Sun.COM 	int cinterleave;
4218472SSean.Ye@Sun.COM 	int rinterleave;
4228472SSean.Ye@Sun.COM 	int found = 0;
4238472SSean.Ye@Sun.COM 
4248472SSean.Ye@Sun.COM 	if (lockstep[node] || mirror_mode[node])
4258472SSean.Ye@Sun.COM 		lchannel = 0;
4268472SSean.Ye@Sun.COM 	else
4278472SSean.Ye@Sun.COM 		lchannel = channel;
4288472SSean.Ye@Sun.COM 	addr = -1;
4298472SSean.Ye@Sun.COM 	base = 0;
4308472SSean.Ye@Sun.COM 	for (i = 0; i < MAX_TAD_DRAM_RULE && found == 0; i++) {
4318472SSean.Ye@Sun.COM 		for (way = 0; way < MAX_RIR_WAY; way++) {
43210650SVuong.Nguyen@Sun.COM 			if (rir[node][channel][i].way[way].dimm_rank == rank) {
4338472SSean.Ye@Sun.COM 				rlimit = rir[node][channel][i].way[way].rlimit;
4348472SSean.Ye@Sun.COM 				if (rlimit && rank_addr >= rlimit)
4358472SSean.Ye@Sun.COM 					continue;
436*11516SAdrian.Frost@Sun.COM 				cbaddr = base;
4378472SSean.Ye@Sun.COM 				if (closed_page) {
4388472SSean.Ye@Sun.COM 					caddr = (rank_addr & ~0x3f) *
4398472SSean.Ye@Sun.COM 					    rir[node][channel][i].interleave -
4408472SSean.Ye@Sun.COM 					    (int64_t)rir[node][channel][i].
44110650SVuong.Nguyen@Sun.COM 					    way[way].soffset * VRANK_SZ;
4428472SSean.Ye@Sun.COM 					caddr += way << 6;
4438472SSean.Ye@Sun.COM 					caddr |= rank_addr & 0x3f;
4448472SSean.Ye@Sun.COM 				} else {
4458472SSean.Ye@Sun.COM 					caddr = (rank_addr & ~0xfff) *
4468472SSean.Ye@Sun.COM 					    rir[node][channel][i].interleave -
4478472SSean.Ye@Sun.COM 					    (int64_t)rir[node][channel][i].
44810650SVuong.Nguyen@Sun.COM 					    way[way].soffset * VRANK_SZ;
4498472SSean.Ye@Sun.COM 					caddr += way << 12;
4508472SSean.Ye@Sun.COM 					caddr |= rank_addr & 0xfff;
4518472SSean.Ye@Sun.COM 				}
4528472SSean.Ye@Sun.COM 				if (caddr < rir[node][channel][i].limit) {
4538472SSean.Ye@Sun.COM 					rinterleave =
4548472SSean.Ye@Sun.COM 					    rir[node][channel][i].interleave;
4558472SSean.Ye@Sun.COM 					rank_sz = (rir[node][channel][i].limit -
4568472SSean.Ye@Sun.COM 					    base) / rinterleave;
4578472SSean.Ye@Sun.COM 					found = 1;
4588472SSean.Ye@Sun.COM 					if (rank_interleave_p) {
4598472SSean.Ye@Sun.COM 						*rank_interleave_p =
4608472SSean.Ye@Sun.COM 						    rinterleave;
4618472SSean.Ye@Sun.COM 					}
4628472SSean.Ye@Sun.COM 					if (rank_way_p)
4638472SSean.Ye@Sun.COM 						*rank_way_p = way;
4648472SSean.Ye@Sun.COM 					break;
4658472SSean.Ye@Sun.COM 				}
4668472SSean.Ye@Sun.COM 			}
4678472SSean.Ye@Sun.COM 		}
4688472SSean.Ye@Sun.COM 		base = rir[node][channel][i].limit;
4698472SSean.Ye@Sun.COM 	}
4708472SSean.Ye@Sun.COM 	if (!found)
4718472SSean.Ye@Sun.COM 		return (-1ULL);
4728472SSean.Ye@Sun.COM 	base = 0;
4738472SSean.Ye@Sun.COM 	for (i = 0; i < MAX_TAD_DRAM_RULE; i++) {
4748472SSean.Ye@Sun.COM 		way = 0;
4758472SSean.Ye@Sun.COM 		if (tad[node][i].enable &&
4768472SSean.Ye@Sun.COM 		    channel_in_interleave(node, channel, i, &way,
4778472SSean.Ye@Sun.COM 		    &no_interleave)) {
4788472SSean.Ye@Sun.COM 			bits = 0;
4798472SSean.Ye@Sun.COM 			addr = caddr;
4808472SSean.Ye@Sun.COM 			baddr = cbaddr;
4818472SSean.Ye@Sun.COM 			if (sag_ch[node][lchannel][i].divby3) {
4828472SSean.Ye@Sun.COM 				addr = (((addr >> 6) * 3) << 6) +
4838472SSean.Ye@Sun.COM 				    (addr & 0x3f);
4848472SSean.Ye@Sun.COM 				baddr = (((baddr >> 6) * 3) << 6);
4858472SSean.Ye@Sun.COM 			}
4868472SSean.Ye@Sun.COM 			if (sag_ch[node][lchannel][i].remove6) {
4878472SSean.Ye@Sun.COM 				bits = 1;
4888472SSean.Ye@Sun.COM 				addr = ((addr & ~0x3f) << 1) | (addr & 0x3f);
4898472SSean.Ye@Sun.COM 				baddr = (baddr & ~0x3f) << 1;
4908472SSean.Ye@Sun.COM 			}
4918472SSean.Ye@Sun.COM 			if (sag_ch[node][lchannel][i].remove7) {
4928472SSean.Ye@Sun.COM 				bits =  bits | 2;
4938472SSean.Ye@Sun.COM 				addr = ((addr & ~0x7f) << 1) | (addr & 0x7f);
4948472SSean.Ye@Sun.COM 				baddr = ((baddr & ~0x7f) << 1) | (baddr & 0x40);
4958472SSean.Ye@Sun.COM 			}
4968472SSean.Ye@Sun.COM 			if (sag_ch[node][lchannel][i].remove8) {
4978472SSean.Ye@Sun.COM 				bits =  bits | 4;
4988472SSean.Ye@Sun.COM 				addr = ((addr & ~0xff) << 1) | (addr & 0xff);
4998472SSean.Ye@Sun.COM 				baddr = ((baddr & ~0xff) << 1) | (baddr & 0xc0);
5008472SSean.Ye@Sun.COM 			}
50110650SVuong.Nguyen@Sun.COM 			addr -= (int64_t)sag_ch[node][lchannel][i].soffset <<
50210650SVuong.Nguyen@Sun.COM 			    16;
5038472SSean.Ye@Sun.COM 			baddr -= (int64_t)
50410650SVuong.Nguyen@Sun.COM 			    sag_ch[node][lchannel][i].soffset << 16;
5058472SSean.Ye@Sun.COM 			if (addr < tad[node][i].limit) {
506*11516SAdrian.Frost@Sun.COM 				/*
507*11516SAdrian.Frost@Sun.COM 				 * this is the target address descripter to use
508*11516SAdrian.Frost@Sun.COM 				 */
5098472SSean.Ye@Sun.COM 				sinterleave = socket_interleave(addr,
5108472SSean.Ye@Sun.COM 				    node, channel, i, &way);
5118472SSean.Ye@Sun.COM 				if (socket_interleave_p) {
5128472SSean.Ye@Sun.COM 					*socket_interleave_p = sinterleave;
5138472SSean.Ye@Sun.COM 				}
5148472SSean.Ye@Sun.COM 				if (socket_way_p)
5158472SSean.Ye@Sun.COM 					*socket_way_p = way;
5168472SSean.Ye@Sun.COM 				if ((no_interleave && sinterleave == 1) ||
5178472SSean.Ye@Sun.COM 				    mirror_mode[node] || lockstep[node]) {
5188472SSean.Ye@Sun.COM 					cinterleave = 1;
5198472SSean.Ye@Sun.COM 				} else {
5208472SSean.Ye@Sun.COM 					cinterleave = channels_interleave(addr);
5218472SSean.Ye@Sun.COM 				}
5228472SSean.Ye@Sun.COM 				if (channel_interleave_p) {
5238472SSean.Ye@Sun.COM 					*channel_interleave_p = cinterleave;
5248472SSean.Ye@Sun.COM 				}
525*11516SAdrian.Frost@Sun.COM 				if (baddr + (rank_sz * rinterleave *
526*11516SAdrian.Frost@Sun.COM 				    cinterleave * sinterleave) >
5278472SSean.Ye@Sun.COM 				    tad[node][i].limit) {
528*11516SAdrian.Frost@Sun.COM 					/*
529*11516SAdrian.Frost@Sun.COM 					 * The system address mapped to this
530*11516SAdrian.Frost@Sun.COM 					 * rank is not contiguous or has
531*11516SAdrian.Frost@Sun.COM 					 * different socket/channel interleave
532*11516SAdrian.Frost@Sun.COM 					 * adjust vitual rank to address where
533*11516SAdrian.Frost@Sun.COM 					 * change or break occures
534*11516SAdrian.Frost@Sun.COM 					 */
5358472SSean.Ye@Sun.COM 					rank_sz = (tad[node][i].limit - baddr) /
5368472SSean.Ye@Sun.COM 					    (cinterleave * sinterleave *
5378472SSean.Ye@Sun.COM 					    rinterleave);
5388472SSean.Ye@Sun.COM 				}
5398472SSean.Ye@Sun.COM 				if (rank_sz_p) {
5408472SSean.Ye@Sun.COM 					*rank_sz_p = rank_sz;
5418472SSean.Ye@Sun.COM 				}
5428472SSean.Ye@Sun.COM 				if (rank_base_p)
5438472SSean.Ye@Sun.COM 					*rank_base_p = baddr;
5448472SSean.Ye@Sun.COM 				if (channel_way_p)
5458472SSean.Ye@Sun.COM 					*channel_way_p = way;
5468472SSean.Ye@Sun.COM 				if (sinterleave == 1 && no_interleave) {
5478472SSean.Ye@Sun.COM 					break;
5488472SSean.Ye@Sun.COM 				}
5498472SSean.Ye@Sun.COM 				switch (tad[node][i].mode) {
5508472SSean.Ye@Sun.COM 				case 0:
5518472SSean.Ye@Sun.COM 					addr += way * 0x40;
5528472SSean.Ye@Sun.COM 					break;
5538472SSean.Ye@Sun.COM 				case 1:
5548472SSean.Ye@Sun.COM 					way = (way ^ (addr >> 16)) & bits;
5558472SSean.Ye@Sun.COM 					addr += way * 0x40;
5568472SSean.Ye@Sun.COM 					break;
5578472SSean.Ye@Sun.COM 				case 2:
5588472SSean.Ye@Sun.COM 					if (sinterleave == 1) {
5598472SSean.Ye@Sun.COM 						xway = ((addr >> 4) & 4) |
5608472SSean.Ye@Sun.COM 						    (((addr >> 6) &
5618472SSean.Ye@Sun.COM 						    0x3ffffffff) % 3);
5628472SSean.Ye@Sun.COM 						if (((way - xway) & 3) == 3)
5638472SSean.Ye@Sun.COM 							xway = (way - xway) & 4;
5648472SSean.Ye@Sun.COM 						else
5658472SSean.Ye@Sun.COM 							xway = way - xway;
5668472SSean.Ye@Sun.COM 						switch (xway) {
5678472SSean.Ye@Sun.COM 						case 0:
5688472SSean.Ye@Sun.COM 							way = 0;
5698472SSean.Ye@Sun.COM 							break;
5708472SSean.Ye@Sun.COM 						case 5:
5718472SSean.Ye@Sun.COM 							way = 1;
5728472SSean.Ye@Sun.COM 							break;
5738472SSean.Ye@Sun.COM 						case 2:
5748472SSean.Ye@Sun.COM 							way = 2;
5758472SSean.Ye@Sun.COM 							break;
5768472SSean.Ye@Sun.COM 						case 4:
5778472SSean.Ye@Sun.COM 							way = 3;
5788472SSean.Ye@Sun.COM 							break;
5798472SSean.Ye@Sun.COM 						case 1:
5808472SSean.Ye@Sun.COM 							way = 4;
5818472SSean.Ye@Sun.COM 							break;
5828472SSean.Ye@Sun.COM 						case 6:
5838472SSean.Ye@Sun.COM 							way = 5;
5848472SSean.Ye@Sun.COM 							break;
5858472SSean.Ye@Sun.COM 						}
5868472SSean.Ye@Sun.COM 					} else {
5878472SSean.Ye@Sun.COM 						xway = (way & 3) -
5888472SSean.Ye@Sun.COM 						    (((addr >> 6) &
5898472SSean.Ye@Sun.COM 						    0x3ffffffff) % 3);
5908472SSean.Ye@Sun.COM 						if (xway < 0)
5918472SSean.Ye@Sun.COM 							xway += 3;
5928472SSean.Ye@Sun.COM 						switch (xway) {
5938472SSean.Ye@Sun.COM 						case 0:
5948472SSean.Ye@Sun.COM 							way = 0;
5958472SSean.Ye@Sun.COM 							break;
5968472SSean.Ye@Sun.COM 						case 1:
5978472SSean.Ye@Sun.COM 							way = 1;
5988472SSean.Ye@Sun.COM 							break;
5998472SSean.Ye@Sun.COM 						case 2:
6008472SSean.Ye@Sun.COM 							way = 2;
6018472SSean.Ye@Sun.COM 							break;
6028472SSean.Ye@Sun.COM 						}
6038472SSean.Ye@Sun.COM 					}
6048472SSean.Ye@Sun.COM 					addr += way * 0x40;
6058472SSean.Ye@Sun.COM 					break;
6068472SSean.Ye@Sun.COM 				}
6078472SSean.Ye@Sun.COM 				break;
608*11516SAdrian.Frost@Sun.COM 			} else if (baddr < tad[node][i].limit) {
609*11516SAdrian.Frost@Sun.COM 				/*
610*11516SAdrian.Frost@Sun.COM 				 * the channel address is not contiguous or
611*11516SAdrian.Frost@Sun.COM 				 * socket/channel interleave changes in the
612*11516SAdrian.Frost@Sun.COM 				 * middle of the rank adjust base and size for
613*11516SAdrian.Frost@Sun.COM 				 * virtual rank to where the break occurs
614*11516SAdrian.Frost@Sun.COM 				 */
615*11516SAdrian.Frost@Sun.COM 				sinterleave = socket_interleave(baddr,
616*11516SAdrian.Frost@Sun.COM 				    node, channel, i, &way);
617*11516SAdrian.Frost@Sun.COM 				if ((no_interleave && sinterleave == 1) ||
618*11516SAdrian.Frost@Sun.COM 				    mirror_mode[node] || lockstep[node]) {
619*11516SAdrian.Frost@Sun.COM 					cinterleave = 1;
620*11516SAdrian.Frost@Sun.COM 				} else {
621*11516SAdrian.Frost@Sun.COM 					cinterleave =
622*11516SAdrian.Frost@Sun.COM 					    channels_interleave(baddr);
623*11516SAdrian.Frost@Sun.COM 				}
624*11516SAdrian.Frost@Sun.COM 				rank_sz -= (tad[node][i].limit - baddr) /
625*11516SAdrian.Frost@Sun.COM 				    (cinterleave * sinterleave * rinterleave);
626*11516SAdrian.Frost@Sun.COM 				cbaddr += (tad[node][i].limit - baddr) /
627*11516SAdrian.Frost@Sun.COM 				    (cinterleave * sinterleave);
6288472SSean.Ye@Sun.COM 			}
6298472SSean.Ye@Sun.COM 		}
6308472SSean.Ye@Sun.COM 		base = tad[node][i].limit;
6318472SSean.Ye@Sun.COM 	}
6328472SSean.Ye@Sun.COM 	return (addr);
6338472SSean.Ye@Sun.COM }
6348472SSean.Ye@Sun.COM /*ARGSUSED*/
6358472SSean.Ye@Sun.COM 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)6368472SSean.Ye@Sun.COM nhm_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
6378472SSean.Ye@Sun.COM     uint32_t synd, int syndtype, mc_unum_t *unump)
6388472SSean.Ye@Sun.COM {
6398472SSean.Ye@Sun.COM 	int node;
6408472SSean.Ye@Sun.COM 	int channel;
6418472SSean.Ye@Sun.COM 	int dimm;
6428472SSean.Ye@Sun.COM 	int rank;
64310650SVuong.Nguyen@Sun.COM 	int log_chan;
64410650SVuong.Nguyen@Sun.COM 	uint64_t bank, row, column;
6458472SSean.Ye@Sun.COM 	uint64_t caddr, raddr;
6468472SSean.Ye@Sun.COM 
6478472SSean.Ye@Sun.COM 	node = address_to_node(pa, 0);
64810650SVuong.Nguyen@Sun.COM 	if (node == -1) {
64910650SVuong.Nguyen@Sun.COM 		return (CMIERR_UNKNOWN);
65010650SVuong.Nguyen@Sun.COM 	}
65110650SVuong.Nguyen@Sun.COM 	channel = address_to_channel(node, pa, syndtype, &log_chan, &caddr, 0);
65210650SVuong.Nguyen@Sun.COM 	if (channel == -1) {
6538472SSean.Ye@Sun.COM 		return (CMIERR_UNKNOWN);
65410650SVuong.Nguyen@Sun.COM 	}
65510650SVuong.Nguyen@Sun.COM 	/*
65610650SVuong.Nguyen@Sun.COM 	 * If driver was built with closed tree present then we will have Intel
65710650SVuong.Nguyen@Sun.COM 	 * proprietary functions caddr_to_dimm and rankaddr_to_dimm for finding
65810650SVuong.Nguyen@Sun.COM 	 * dimm/bank/row/column address otherwise we just locate dimm and
65910650SVuong.Nguyen@Sun.COM 	 * offset.
66010650SVuong.Nguyen@Sun.COM 	 */
66110650SVuong.Nguyen@Sun.COM 	if (&caddr_to_dimm)
66210650SVuong.Nguyen@Sun.COM 		dimm = caddr_to_dimm(node, log_chan, caddr, &rank, &raddr);
66310650SVuong.Nguyen@Sun.COM 	else
66410650SVuong.Nguyen@Sun.COM 		dimm = channel_addr_to_dimm(node, log_chan, caddr, &rank,
66510650SVuong.Nguyen@Sun.COM 		    &raddr);
66610650SVuong.Nguyen@Sun.COM 	if (dimm == -1) {
6678472SSean.Ye@Sun.COM 		return (CMIERR_UNKNOWN);
6688472SSean.Ye@Sun.COM 
66910650SVuong.Nguyen@Sun.COM 	}
6708472SSean.Ye@Sun.COM 	unump->unum_board = 0;
6718472SSean.Ye@Sun.COM 	unump->unum_chip = node;
6728472SSean.Ye@Sun.COM 	unump->unum_mc = 0;
6738472SSean.Ye@Sun.COM 	unump->unum_chan = channel;
6748472SSean.Ye@Sun.COM 	unump->unum_cs = dimm;
6758472SSean.Ye@Sun.COM 	unump->unum_rank = rank;
67610650SVuong.Nguyen@Sun.COM 
67710650SVuong.Nguyen@Sun.COM 	if (&rankaddr_to_dimm) {
67810650SVuong.Nguyen@Sun.COM 		if (rankaddr_to_dimm(raddr, node, channel, dimm, 0, &bank, &row,
67910650SVuong.Nguyen@Sun.COM 		    &column) != DDI_SUCCESS) {
68010650SVuong.Nguyen@Sun.COM 			return (CMIERR_UNKNOWN);
68110650SVuong.Nguyen@Sun.COM 		};
68210650SVuong.Nguyen@Sun.COM 		unump->unum_offset = TCODE_OFFSET(rank, bank, row, column);
68310650SVuong.Nguyen@Sun.COM 	} else {
68410650SVuong.Nguyen@Sun.COM 		unump->unum_offset = raddr;
68510650SVuong.Nguyen@Sun.COM 	}
6868472SSean.Ye@Sun.COM 
6878472SSean.Ye@Sun.COM 	return (CMI_SUCCESS);
6888472SSean.Ye@Sun.COM }
6898472SSean.Ye@Sun.COM 
6908472SSean.Ye@Sun.COM /*ARGSUSED*/
6918472SSean.Ye@Sun.COM static cmi_errno_t
nhm_unumtopa(void * arg,mc_unum_t * unump,nvlist_t * nvl,uint64_t * pap)6928472SSean.Ye@Sun.COM nhm_unumtopa(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
6938472SSean.Ye@Sun.COM {
6948472SSean.Ye@Sun.COM 	uint64_t pa;
6958472SSean.Ye@Sun.COM 	cmi_errno_t rt;
6968472SSean.Ye@Sun.COM 	int node;
6978472SSean.Ye@Sun.COM 	int channel;
69810650SVuong.Nguyen@Sun.COM 	int log_chan;
6998472SSean.Ye@Sun.COM 	int rank;
7008472SSean.Ye@Sun.COM 	int i;
7018472SSean.Ye@Sun.COM 	nvlist_t **hcl, *hcsp;
7028472SSean.Ye@Sun.COM 	uint_t npr;
70310650SVuong.Nguyen@Sun.COM 	uint64_t offset;
7048472SSean.Ye@Sun.COM 	char *hcnm, *hcid;
7058472SSean.Ye@Sun.COM 	long v;
70610650SVuong.Nguyen@Sun.COM 	uint64_t row, bank, col;
70710650SVuong.Nguyen@Sun.COM 	int dimm;
70810650SVuong.Nguyen@Sun.COM 	uint64_t rank_addr;
7098472SSean.Ye@Sun.COM 
7108472SSean.Ye@Sun.COM 	if (unump == NULL) {
7118472SSean.Ye@Sun.COM 		if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC,
7128472SSean.Ye@Sun.COM 		    &hcsp) != 0)
7138472SSean.Ye@Sun.COM 			return (CMIERR_UNKNOWN);
7148472SSean.Ye@Sun.COM 		if (nvlist_lookup_uint64(hcsp,
71510650SVuong.Nguyen@Sun.COM 		    "asru-" FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0 &&
7168472SSean.Ye@Sun.COM 		    nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET,
71710650SVuong.Nguyen@Sun.COM 		    &offset) != 0) {
7188472SSean.Ye@Sun.COM 			if (nvlist_lookup_uint64(hcsp,
7198472SSean.Ye@Sun.COM 			    "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0 ||
7208472SSean.Ye@Sun.COM 			    nvlist_lookup_uint64(hcsp,
7218472SSean.Ye@Sun.COM 			    FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0) {
7228472SSean.Ye@Sun.COM 				*pap = pa;
7238472SSean.Ye@Sun.COM 				return (CMI_SUCCESS);
7248472SSean.Ye@Sun.COM 			}
7258472SSean.Ye@Sun.COM 			return (CMIERR_UNKNOWN);
7268472SSean.Ye@Sun.COM 		}
7278472SSean.Ye@Sun.COM 		if (nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST,
7288472SSean.Ye@Sun.COM 		    &hcl, &npr) != 0)
7298472SSean.Ye@Sun.COM 			return (CMIERR_UNKNOWN);
7308472SSean.Ye@Sun.COM 		node = -1;
7318472SSean.Ye@Sun.COM 		channel = -1;
73210650SVuong.Nguyen@Sun.COM 		dimm = -1;
7338472SSean.Ye@Sun.COM 		rank = -1;
7348472SSean.Ye@Sun.COM 		for (i = 0; i < npr; i++) {
7358472SSean.Ye@Sun.COM 			if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME,
7368472SSean.Ye@Sun.COM 			    &hcnm) != 0 ||
7378472SSean.Ye@Sun.COM 			    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID,
7388472SSean.Ye@Sun.COM 			    &hcid) != 0 ||
7398472SSean.Ye@Sun.COM 			    ddi_strtol(hcid, NULL, 0, &v) != 0)
7408472SSean.Ye@Sun.COM 				return (CMIERR_UNKNOWN);
7418472SSean.Ye@Sun.COM 			if (strcmp(hcnm, "chip") == 0)
7428472SSean.Ye@Sun.COM 				node = (int)v;
7438472SSean.Ye@Sun.COM 			else if (strcmp(hcnm, "dram-channel") == 0)
7448472SSean.Ye@Sun.COM 				channel = (int)v;
74510650SVuong.Nguyen@Sun.COM 			else if (strcmp(hcnm, "dimm") == 0)
74610650SVuong.Nguyen@Sun.COM 				dimm = (int)v;
7478472SSean.Ye@Sun.COM 			else if (strcmp(hcnm, "rank") == 0)
7488472SSean.Ye@Sun.COM 				rank = (int)v;
7498472SSean.Ye@Sun.COM 		}
75010650SVuong.Nguyen@Sun.COM 		if (node == -1 || channel == -1 || dimm == -1 || rank == -1)
7518472SSean.Ye@Sun.COM 			return (CMIERR_UNKNOWN);
7528472SSean.Ye@Sun.COM 	} else {
7538472SSean.Ye@Sun.COM 		node = unump->unum_chip;
7548472SSean.Ye@Sun.COM 		channel = unump->unum_chan;
7558472SSean.Ye@Sun.COM 		rank = unump->unum_rank;
75610650SVuong.Nguyen@Sun.COM 		dimm = unump->unum_cs;
75710650SVuong.Nguyen@Sun.COM 		offset = unump->unum_offset;
7588472SSean.Ye@Sun.COM 	}
75910650SVuong.Nguyen@Sun.COM 
76010650SVuong.Nguyen@Sun.COM 	/*
76110650SVuong.Nguyen@Sun.COM 	 * If driver was built with closed tree present then we will have Intel
76210650SVuong.Nguyen@Sun.COM 	 * proprietary functions dimm_to_rankaddr for finding
76310650SVuong.Nguyen@Sun.COM 	 * physical address.
76410650SVuong.Nguyen@Sun.COM 	 */
76510650SVuong.Nguyen@Sun.COM 	if (&dimm_to_rankaddr && (offset & OFFSET_ROW_BANK_COL) != 0) {
76610650SVuong.Nguyen@Sun.COM 		row = TCODE_OFFSET_RAS(offset);
76710650SVuong.Nguyen@Sun.COM 		bank = TCODE_OFFSET_BANK(offset);
76810650SVuong.Nguyen@Sun.COM 		col = TCODE_OFFSET_CAS(offset);
76910650SVuong.Nguyen@Sun.COM 		rank_addr = dimm_to_rankaddr(node, channel, dimm, row,
77010650SVuong.Nguyen@Sun.COM 		    bank, col, &log_chan);
77110650SVuong.Nguyen@Sun.COM 		pa = rankaddr_to_phyaddr(node, log_chan, dimm, rank,
77210650SVuong.Nguyen@Sun.COM 		    rank_addr);
77310650SVuong.Nguyen@Sun.COM 	} else if ((offset & OFFSET_ROW_BANK_COL) == 0) {
77410650SVuong.Nguyen@Sun.COM 		pa = dimm_to_addr(node, channel, rank, offset, 0, 0, 0, 0, 0,
77510650SVuong.Nguyen@Sun.COM 		    0, 0, 0);
77610650SVuong.Nguyen@Sun.COM 	} else {
77710650SVuong.Nguyen@Sun.COM 		pa = -1LL;
77810650SVuong.Nguyen@Sun.COM 	}
77910650SVuong.Nguyen@Sun.COM 
7808472SSean.Ye@Sun.COM 	if (pa == -1) {
7818472SSean.Ye@Sun.COM 		rt = CMIERR_UNKNOWN;
7828472SSean.Ye@Sun.COM 	} else {
7838472SSean.Ye@Sun.COM 		rt = CMI_SUCCESS;
7848472SSean.Ye@Sun.COM 		*pap = pa;
7858472SSean.Ye@Sun.COM 	}
7868472SSean.Ye@Sun.COM 	return (rt);
7878472SSean.Ye@Sun.COM }
7888472SSean.Ye@Sun.COM 
7898472SSean.Ye@Sun.COM static const cmi_mc_ops_t nhm_mc_ops = {
7908472SSean.Ye@Sun.COM 	nhm_patounum,
7918472SSean.Ye@Sun.COM 	nhm_unumtopa,
7928472SSean.Ye@Sun.COM 	nhm_error_trap	/* cmi_mc_logout */
7938472SSean.Ye@Sun.COM };
7948472SSean.Ye@Sun.COM 
7958472SSean.Ye@Sun.COM /*ARGSUSED*/
7968472SSean.Ye@Sun.COM int
inhm_mc_register(cmi_hdl_t hdl,void * arg1,void * arg2,void * arg3)7978472SSean.Ye@Sun.COM inhm_mc_register(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3)
7988472SSean.Ye@Sun.COM {
7998472SSean.Ye@Sun.COM 	cmi_mc_register(hdl, &nhm_mc_ops, NULL);
8008472SSean.Ye@Sun.COM 	return (CMI_HDL_WALK_NEXT);
8018472SSean.Ye@Sun.COM }
8028472SSean.Ye@Sun.COM 
8038472SSean.Ye@Sun.COM static int
choose_cpu(int * lastslot_p)8048472SSean.Ye@Sun.COM choose_cpu(int *lastslot_p)
8058472SSean.Ye@Sun.COM {
8068472SSean.Ye@Sun.COM 	uint32_t id;
8078472SSean.Ye@Sun.COM 	int first;
8088472SSean.Ye@Sun.COM 	int last;
8098472SSean.Ye@Sun.COM 
8108472SSean.Ye@Sun.COM 	first = 0;
8118472SSean.Ye@Sun.COM 	last = MAX_CPU_NODES;
8128472SSean.Ye@Sun.COM 	id = CPU_ID_RD(0);
81310556SAdrian.Frost@Sun.COM 	if (id == NHM_EP_CPU || id == NHM_WS_CPU || id == NHM_JF_CPU ||
81410556SAdrian.Frost@Sun.COM 	    id == NHM_WM_CPU) {
8158472SSean.Ye@Sun.COM 		id = CPU_ID_RD(1);
81610556SAdrian.Frost@Sun.COM 		if (id != NHM_EP_CPU && id != NHM_WS_CPU && id != NHM_JF_CPU &&
81710556SAdrian.Frost@Sun.COM 		    id != NHM_WM_CPU) {
8188472SSean.Ye@Sun.COM 			last = 1;
8198472SSean.Ye@Sun.COM 		}
8208472SSean.Ye@Sun.COM 	} else {
8218472SSean.Ye@Sun.COM 		first = 1;
8228472SSean.Ye@Sun.COM 	}
8238472SSean.Ye@Sun.COM 	*lastslot_p = last;
8248472SSean.Ye@Sun.COM 	return (first);
8258472SSean.Ye@Sun.COM }
8268472SSean.Ye@Sun.COM 
8278472SSean.Ye@Sun.COM static int
sad_interleave(uint32_t list)8288472SSean.Ye@Sun.COM sad_interleave(uint32_t list)
8298472SSean.Ye@Sun.COM {
8308472SSean.Ye@Sun.COM 	int rt = 1;
8318472SSean.Ye@Sun.COM 	int i, j;
8328472SSean.Ye@Sun.COM 	int p;
8338472SSean.Ye@Sun.COM 
8348472SSean.Ye@Sun.COM 	for (i = 1; i < INTERLEAVE_NWAY; i++) {
8358472SSean.Ye@Sun.COM 		p = SAD_INTERLEAVE(list, i);
8368472SSean.Ye@Sun.COM 		for (j = 0; j < i; j++) {
8378472SSean.Ye@Sun.COM 			if (p == SAD_INTERLEAVE(list, j))
8388472SSean.Ye@Sun.COM 				break;
8398472SSean.Ye@Sun.COM 		}
8408472SSean.Ye@Sun.COM 		if (i == j)
8418472SSean.Ye@Sun.COM 			rt++;
8428472SSean.Ye@Sun.COM 	}
8438472SSean.Ye@Sun.COM 	return (rt);
8448472SSean.Ye@Sun.COM }
8458472SSean.Ye@Sun.COM 
8468472SSean.Ye@Sun.COM static int
tad_interleave(uint32_t list)8478472SSean.Ye@Sun.COM tad_interleave(uint32_t list)
8488472SSean.Ye@Sun.COM {
8498472SSean.Ye@Sun.COM 	int rt = 1;
8508472SSean.Ye@Sun.COM 	int i, j;
8518472SSean.Ye@Sun.COM 	int c;
8528472SSean.Ye@Sun.COM 
8538472SSean.Ye@Sun.COM 	for (i = 1; i < INTERLEAVE_NWAY; i++) {
8548472SSean.Ye@Sun.COM 		c = TAD_INTERLEAVE(list, i);
8558472SSean.Ye@Sun.COM 		for (j = 0; j < i; j++) {
8568472SSean.Ye@Sun.COM 			if (c == TAD_INTERLEAVE(list, j))
8578472SSean.Ye@Sun.COM 				break;
8588472SSean.Ye@Sun.COM 		}
8598472SSean.Ye@Sun.COM 		if (i == j)
8608472SSean.Ye@Sun.COM 			rt++;
8618472SSean.Ye@Sun.COM 	}
8628472SSean.Ye@Sun.COM 	return (rt);
8638472SSean.Ye@Sun.COM }
8648472SSean.Ye@Sun.COM 
8658472SSean.Ye@Sun.COM static void
set_rank(int socket,int channel,int rule,int way,int rank,uint64_t rank_addr)8668472SSean.Ye@Sun.COM set_rank(int socket, int channel, int rule, int way, int rank,
8678472SSean.Ye@Sun.COM     uint64_t rank_addr)
8688472SSean.Ye@Sun.COM {
8698472SSean.Ye@Sun.COM 	int k, l;
8708472SSean.Ye@Sun.COM 	if (rank_addr == 0)
8718472SSean.Ye@Sun.COM 		return;
872*11516SAdrian.Frost@Sun.COM 	/*
873*11516SAdrian.Frost@Sun.COM 	 * set limit on any rules which have virtual rank in current rank and
874*11516SAdrian.Frost@Sun.COM 	 * are not already limited by earlier rule
875*11516SAdrian.Frost@Sun.COM 	 */
876*11516SAdrian.Frost@Sun.COM 	for (k = 0; k < rule; k++) {
877*11516SAdrian.Frost@Sun.COM 		for (l = 0; l < MAX_RIR_WAY; l++) {
87810650SVuong.Nguyen@Sun.COM 			if (rir[socket][channel][k].way[l].dimm_rank == rank &&
8798472SSean.Ye@Sun.COM 			    rir[socket][channel][k].way[l].rlimit == 0) {
8808472SSean.Ye@Sun.COM 				rir[socket][channel][k].way[l].rlimit =
8818472SSean.Ye@Sun.COM 				    rank_addr;
8828472SSean.Ye@Sun.COM 			}
8838472SSean.Ye@Sun.COM 		}
8848472SSean.Ye@Sun.COM 	}
885*11516SAdrian.Frost@Sun.COM 	/*
886*11516SAdrian.Frost@Sun.COM 	 * set limit if this rule supplies more than 1 virtual rank from current
887*11516SAdrian.Frost@Sun.COM 	 * rank
888*11516SAdrian.Frost@Sun.COM 	 */
889*11516SAdrian.Frost@Sun.COM 	for (l = 0; l < way; l++) {
890*11516SAdrian.Frost@Sun.COM 		if (rir[socket][channel][k].way[l].dimm_rank == rank &&
891*11516SAdrian.Frost@Sun.COM 		    rir[socket][channel][k].way[l].rlimit == 0) {
892*11516SAdrian.Frost@Sun.COM 			rir[socket][channel][k].way[l].rlimit = rank_addr;
893*11516SAdrian.Frost@Sun.COM 		}
894*11516SAdrian.Frost@Sun.COM 	}
8958472SSean.Ye@Sun.COM }
8968472SSean.Ye@Sun.COM 
8978472SSean.Ye@Sun.COM void
mem_reg_init()8988472SSean.Ye@Sun.COM mem_reg_init()
8998472SSean.Ye@Sun.COM {
9008472SSean.Ye@Sun.COM 	int i, j, k, l, m;
9018472SSean.Ye@Sun.COM 	uint32_t sad_dram_rule;
9028472SSean.Ye@Sun.COM 	uint32_t tad_dram_rule;
9038472SSean.Ye@Sun.COM 	uint32_t mc_ras_enables;
9048472SSean.Ye@Sun.COM 	uint32_t mc_channel_mapping;
9058472SSean.Ye@Sun.COM 	uint32_t sagch;
9068472SSean.Ye@Sun.COM 	uint32_t rir_limit;
9078472SSean.Ye@Sun.COM 	uint32_t rir_way;
9088472SSean.Ye@Sun.COM 	uint32_t mc_control;
90910556SAdrian.Frost@Sun.COM 	uint32_t id;
9108472SSean.Ye@Sun.COM 	int nhm_slot;
9118472SSean.Ye@Sun.COM 	int nhm_lastslot;
9128472SSean.Ye@Sun.COM 	uint8_t	rank;
9138472SSean.Ye@Sun.COM 	uint64_t base;
91410556SAdrian.Frost@Sun.COM 	int ras_dev = 0;
91510650SVuong.Nguyen@Sun.COM 	uint32_t dod_value;
9168472SSean.Ye@Sun.COM 
9178472SSean.Ye@Sun.COM 	nhm_slot = choose_cpu(&nhm_lastslot);
9188472SSean.Ye@Sun.COM 
9198472SSean.Ye@Sun.COM 	for (i = 0; i < MAX_SAD_DRAM_RULE; i++) {
9208472SSean.Ye@Sun.COM 		sad_dram_rule = SAD_DRAM_RULE_RD(nhm_slot, i);
9218472SSean.Ye@Sun.COM 		sad[i].enable = SAD_DRAM_RULE_ENABLE(sad_dram_rule);
9228472SSean.Ye@Sun.COM 		sad[i].limit = SAD_DRAM_LIMIT(sad_dram_rule);
9238472SSean.Ye@Sun.COM 		sad[i].mode = SAD_DRAM_MODE(sad_dram_rule);
9248472SSean.Ye@Sun.COM 		sad[i].node_list = SAD_INTERLEAVE_LIST_RD(nhm_slot, i);
9258472SSean.Ye@Sun.COM 		sad[i].interleave = sad_interleave(sad[i].node_list);
92610650SVuong.Nguyen@Sun.COM 		for (j = 0; j < INTERLEAVE_NWAY; j++) {
92710650SVuong.Nguyen@Sun.COM 			sad[i].node_tgt[j] = (sad[i].node_list >>
92810650SVuong.Nguyen@Sun.COM 			    (j * 4)) & 0x3;
92910650SVuong.Nguyen@Sun.COM 		}
9308472SSean.Ye@Sun.COM 	}
9318472SSean.Ye@Sun.COM 
9328472SSean.Ye@Sun.COM 	for (i = nhm_slot; i < nhm_lastslot; i++) {
93310556SAdrian.Frost@Sun.COM 		id = MC_CPU_RAS_RD(i);
93410556SAdrian.Frost@Sun.COM 		if (id == NHM_CPU_RAS || id == NHM_JF_CPU_RAS ||
93510556SAdrian.Frost@Sun.COM 		    id == NHM_WM_CPU_RAS) {
93610556SAdrian.Frost@Sun.COM 			ras_dev = 1;
9378977SAdrian.Frost@Sun.COM 			mc_ras_enables = MC_RAS_ENABLES_RD(i);
9388977SAdrian.Frost@Sun.COM 			if (RAS_LOCKSTEP_ENABLE(mc_ras_enables))
9398977SAdrian.Frost@Sun.COM 				lockstep[i] = 1;
9408977SAdrian.Frost@Sun.COM 			if (RAS_MIRROR_MEM_ENABLE(mc_ras_enables))
9418977SAdrian.Frost@Sun.COM 				mirror_mode[i] = 1;
9428977SAdrian.Frost@Sun.COM 		}
9438472SSean.Ye@Sun.COM 		mc_channel_mapping = MC_CHANNEL_MAPPER_RD(i);
9448472SSean.Ye@Sun.COM 		if (CHANNEL_MAP(mc_channel_mapping, 2, 0) == 0 &&
9458472SSean.Ye@Sun.COM 		    CHANNEL_MAP(mc_channel_mapping, 2, 1) == 0)
9468472SSean.Ye@Sun.COM 			spare_channel[i] = 1;
9478472SSean.Ye@Sun.COM 		for (j = 0; j < MAX_TAD_DRAM_RULE; j++) {
9488472SSean.Ye@Sun.COM 			tad_dram_rule = TAD_DRAM_RULE_RD(i, j);
9498472SSean.Ye@Sun.COM 			tad[i][j].enable = TAD_DRAM_RULE_ENABLE(tad_dram_rule);
9508472SSean.Ye@Sun.COM 			tad[i][j].limit = TAD_DRAM_LIMIT(tad_dram_rule);
9518472SSean.Ye@Sun.COM 			tad[i][j].mode = TAD_DRAM_MODE(tad_dram_rule);
9528472SSean.Ye@Sun.COM 			tad[i][j].pkg_list =
9538472SSean.Ye@Sun.COM 			    TAD_INTERLEAVE_LIST_RD(i, j);
95410650SVuong.Nguyen@Sun.COM 			for (k = 0; k < INTERLEAVE_NWAY; k++) {
95510650SVuong.Nguyen@Sun.COM 				tad[i][j].pkg_tgt[k] = ((tad[i][j].pkg_list >>
95610650SVuong.Nguyen@Sun.COM 				    (k * 4)) & 0x3);
95710650SVuong.Nguyen@Sun.COM 			}
9588472SSean.Ye@Sun.COM 			if (mirror_mode[i] || lockstep[i]) {
9598472SSean.Ye@Sun.COM 				tad[i][j].interleave = 1;
9608472SSean.Ye@Sun.COM 			} else {
9618472SSean.Ye@Sun.COM 				tad[i][j].interleave =
9628472SSean.Ye@Sun.COM 				    tad_interleave(tad[i][j].pkg_list);
9638472SSean.Ye@Sun.COM 				if (spare_channel[i] &&
9648472SSean.Ye@Sun.COM 				    tad[i][j].interleave ==
9658472SSean.Ye@Sun.COM 				    CHANNELS_PER_MEMORY_CONTROLLER)
9668472SSean.Ye@Sun.COM 					tad[i][j].interleave--;
9678472SSean.Ye@Sun.COM 			}
9688472SSean.Ye@Sun.COM 		}
9698472SSean.Ye@Sun.COM 		for (j = 0; j < CHANNELS_PER_MEMORY_CONTROLLER; j++) {
9708472SSean.Ye@Sun.COM 			m = 0;
9718472SSean.Ye@Sun.COM 			base = 0;
9728472SSean.Ye@Sun.COM 			for (k = 0; k < MAX_TAD_DRAM_RULE; k++) {
9738472SSean.Ye@Sun.COM 				sagch = MC_SAG_RD(i, j, k);
9748472SSean.Ye@Sun.COM 				sag_ch[i][j][k].offset =
9758472SSean.Ye@Sun.COM 				    CH_ADDRESS_OFFSET(sagch);
97610650SVuong.Nguyen@Sun.COM 				sag_ch[i][j][k].soffset =
97710650SVuong.Nguyen@Sun.COM 				    CH_ADDRESS_SOFFSET(sagch);
9788472SSean.Ye@Sun.COM 				sag_ch[i][j][k].divby3 = DIVBY3(sagch);
9798472SSean.Ye@Sun.COM 				sag_ch[i][j][k].remove6 = REMOVE_6(sagch);
9808472SSean.Ye@Sun.COM 				sag_ch[i][j][k].remove7 = REMOVE_7(sagch);
9818472SSean.Ye@Sun.COM 				sag_ch[i][j][k].remove8 = REMOVE_8(sagch);
9828472SSean.Ye@Sun.COM 
9838472SSean.Ye@Sun.COM 				rir_limit = MC_RIR_LIMIT_RD(i, j, k);
9848472SSean.Ye@Sun.COM 				rir[i][j][k].limit = RIR_LIMIT(rir_limit);
9858472SSean.Ye@Sun.COM 				for (l = 0; l < MAX_RIR_WAY; l++) {
9868472SSean.Ye@Sun.COM 					rir_way = MC_RIR_WAY_RD(i, j, m);
9878472SSean.Ye@Sun.COM 					rir[i][j][k].way[l].offset =
9888472SSean.Ye@Sun.COM 					    RIR_OFFSET(rir_way);
98910650SVuong.Nguyen@Sun.COM 					rir[i][j][k].way[l].soffset =
99010650SVuong.Nguyen@Sun.COM 					    RIR_SOFFSET(rir_way);
9918472SSean.Ye@Sun.COM 					rir[i][j][k].way[l].rank =
9928472SSean.Ye@Sun.COM 					    RIR_RANK(rir_way);
99310650SVuong.Nguyen@Sun.COM 					rir[i][j][k].way[l].dimm =
99410650SVuong.Nguyen@Sun.COM 					    RIR_DIMM(rir_way);
99510650SVuong.Nguyen@Sun.COM 					rir[i][j][k].way[l].dimm_rank =
99610650SVuong.Nguyen@Sun.COM 					    RIR_DIMM_RANK(rir_way);
9978472SSean.Ye@Sun.COM 					rir[i][j][k].way[l].rlimit = 0;
9988472SSean.Ye@Sun.COM 					m++;
9998472SSean.Ye@Sun.COM 				}
100010650SVuong.Nguyen@Sun.COM 				rank = rir[i][j][k].way[0].dimm_rank;
100110650SVuong.Nguyen@Sun.COM 				if (rank == rir[i][j][k].way[1].dimm_rank &&
100210650SVuong.Nguyen@Sun.COM 				    rank == rir[i][j][k].way[2].dimm_rank &&
100310650SVuong.Nguyen@Sun.COM 				    rank == rir[i][j][k].way[3].dimm_rank) {
10048472SSean.Ye@Sun.COM 					rir[i][j][k].interleave = 1;
100510650SVuong.Nguyen@Sun.COM 				} else if
100610650SVuong.Nguyen@Sun.COM 				    (rank == rir[i][j][k].way[1].dimm_rank ||
100710650SVuong.Nguyen@Sun.COM 				    rank == rir[i][j][k].way[2].dimm_rank ||
100810650SVuong.Nguyen@Sun.COM 				    rank == rir[i][j][k].way[3].dimm_rank) {
10098472SSean.Ye@Sun.COM 					rir[i][j][k].interleave = 2;
10108472SSean.Ye@Sun.COM 				} else {
10118472SSean.Ye@Sun.COM 					rir[i][j][k].interleave = 4;
10128472SSean.Ye@Sun.COM 				}
10138472SSean.Ye@Sun.COM 				for (l = 0; l < MAX_RIR_WAY; l++) {
10148472SSean.Ye@Sun.COM 					set_rank(i, j, k, l,
101510650SVuong.Nguyen@Sun.COM 					    rir[i][j][k].way[l].dimm_rank,
101610650SVuong.Nguyen@Sun.COM 					    ((rir[i][j][k].way[l].soffset +
10178472SSean.Ye@Sun.COM 					    base) /
10188472SSean.Ye@Sun.COM 					    rir[i][j][k].interleave));
10198472SSean.Ye@Sun.COM 				}
10208472SSean.Ye@Sun.COM 				base = rir[i][j][k].limit;
10218472SSean.Ye@Sun.COM 			}
102210650SVuong.Nguyen@Sun.COM 			for (k = 0; k < MAX_DIMMS_PER_CHANNEL; k++) {
102310650SVuong.Nguyen@Sun.COM 				dod_value = MC_DOD_RD(i, j, k);
102410650SVuong.Nguyen@Sun.COM 				dod_reg[i][j][k].NUMCol = NUMCOL(dod_value);
102510650SVuong.Nguyen@Sun.COM 				dod_reg[i][j][k].NUMRow = NUMROW(dod_value);
102610650SVuong.Nguyen@Sun.COM 				dod_reg[i][j][k].NUMBank = NUMBANK(dod_value);
102710650SVuong.Nguyen@Sun.COM 				dod_reg[i][j][k].NUMRank = NUMRANK(dod_value);
102810650SVuong.Nguyen@Sun.COM 				dod_reg[i][j][k].DIMMPresent =
102910650SVuong.Nguyen@Sun.COM 				    DIMMPRESENT(dod_value);
103010650SVuong.Nguyen@Sun.COM 				dod_reg[i][j][k].RankOffset =
103110650SVuong.Nguyen@Sun.COM 				    RANKOFFSET(dod_value);
103210650SVuong.Nguyen@Sun.COM 			}
10338472SSean.Ye@Sun.COM 		}
10348472SSean.Ye@Sun.COM 	}
10358472SSean.Ye@Sun.COM 	mc_control = MC_CONTROL_RD(nhm_slot);
10368472SSean.Ye@Sun.COM 	closed_page = MC_CONTROL_CLOSED_PAGE(mc_control);
103710556SAdrian.Frost@Sun.COM 	if (ras_dev)
10388977SAdrian.Frost@Sun.COM 		ecc_enabled = MC_CONTROL_ECCEN(mc_control);
10398977SAdrian.Frost@Sun.COM 	else if ((MC_STATUS_RD(nhm_slot) & WS_ECC_ENABLED) != 0)
10408977SAdrian.Frost@Sun.COM 		ecc_enabled = 1;
104110650SVuong.Nguyen@Sun.COM 	divby3_enabled = MC_CONTROL_DIVBY3(mc_control);
10428472SSean.Ye@Sun.COM }
1043