1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/kmem.h>
29 #include <sys/mc.h>
30 #include <sys/nvpair.h>
31 #include <sys/fm/protocol.h>
32 #include <sys/cmn_err.h>
33 #include <sys/sunddi.h>
34 #include <sys/mc_intel.h>
35 #include "nb_log.h"
36 #include "rank.h"
37 #include "dimm_phys.h"
38 #include "nb5000.h"
39 
40 struct rank_base *rank_base;
41 
42 static int
fmri2unum(nvlist_t * nvl,mc_unum_t * unump)43 fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
44 {
45 	int i;
46 	uint64_t offset;
47 	nvlist_t **hcl, *hcsp;
48 	uint_t npr;
49 
50 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) != 0 ||
51 	    (nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
52 	    &offset) != 0 && nvlist_lookup_uint64(hcsp,
53 	    FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0) ||
54 	    nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &npr) != 0)
55 		return (0);
56 
57 
58 	bzero(unump, sizeof (mc_unum_t));
59 	for (i = 0; i < MC_UNUM_NDIMM; i++)
60 		unump->unum_dimms[i] = MC_INVALNUM;
61 
62 	for (i = 0; i < npr; i++) {
63 		char *hcnm, *hcid;
64 		long v;
65 
66 		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 ||
67 		    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 ||
68 		    ddi_strtol(hcid, NULL, 0, &v) != 0)
69 			return (0);
70 
71 		if (strcmp(hcnm, "motherboard") == 0)
72 			unump->unum_board = (int)v;
73 		else if (strcmp(hcnm, "memory-controller") == 0)
74 			unump->unum_mc = (int)v;
75 		else if (strcmp(hcnm, "dram-channel") == 0)
76 			unump->unum_cs = (int)v;
77 		else if (strcmp(hcnm, "dimm") == 0)
78 			unump->unum_dimms[0] = (int)v;
79 		else if (strcmp(hcnm, "rank") == 0)
80 			unump->unum_rank = (int)v;
81 	}
82 
83 	return (1);
84 }
85 
86 /*ARGSUSED*/
87 static cmi_errno_t
inb_patounum(void * arg,uint64_t pa,uint8_t valid_hi,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)88 inb_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
89     uint32_t synd, int syndtype, mc_unum_t *unump)
90 {
91 	struct rank_base *rp;
92 	int i;
93 	int last;
94 	uint64_t offset;
95 	cmi_errno_t rt = CMIERR_UNKNOWN;
96 
97 	last = nb_dimms_per_channel * nb_number_memory_controllers;
98 	for (i = 0; i < last; i++) {
99 		rp = &rank_base[i];
100 		if (rp && pa >= rp->base && pa < rp->limit)
101 			break;
102 	}
103 	if (i < last) {
104 		offset = pa - rp->base;
105 		if (offset > rp->hole)
106 			offset -= rp->hole_size;
107 		unump->unum_offset = offset / rp->interleave;
108 		unump->unum_mc = i / nb_dimms_per_channel;
109 		unump->unum_cs = 0;
110 		unump->unum_rank = i % nb_dimms_per_channel;
111 		rt = CMI_SUCCESS;
112 	}
113 	return (rt);
114 }
115 
116 /*ARGSUSED*/
117 static cmi_errno_t
inb_unumtopa(void * arg,mc_unum_t * unump,nvlist_t * nvl,uint64_t * pap)118 inb_unumtopa(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
119 {
120 	int num_ranks_per_branch;
121 	mc_unum_t unum;
122 	uint64_t pa;
123 	struct rank_base *rp;
124 
125 	if (unump == NULL) {
126 		if (!fmri2unum(nvl, &unum))
127 			return (CMI_SUCCESS);
128 		unump = &unum;
129 	}
130 	if ((unump->unum_offset & OFFSET_ROW_BANK_COL)) {
131 		if (&dimm_getphys) {
132 			pa = dimm_getphys(unump->unum_mc,
133 			    TCODE_OFFSET_RANK(unump->unum_offset),
134 			    TCODE_OFFSET_BANK(unump->unum_offset),
135 			    TCODE_OFFSET_RAS(unump->unum_offset),
136 			    TCODE_OFFSET_CAS(unump->unum_offset));
137 			if (pa >= MAXPHYS_ADDR)
138 				return (CMIERR_MC_NOADDR);
139 		} else {
140 			return (CMIERR_MC_NOADDR);
141 		}
142 		*pap = pa;
143 		return (CMI_SUCCESS);
144 	}
145 
146 
147 	/* max number of ranks per branch */
148 	num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
149 	    NB_5100_RANKS_PER_CHANNEL :
150 	    nb_dimms_per_channel * nb_channels_per_branch;
151 	rp = &rank_base[(unump->unum_mc * num_ranks_per_branch) +
152 	    unump->unum_rank];
153 	pa = rp->base + (unump->unum_offset * rp->interleave);
154 
155 	if (rp->hole && pa >= rp->hole)
156 		pa += rp->hole_size;
157 	*pap = pa;
158 	return (CMI_SUCCESS);
159 }
160 
161 void
dimm_init()162 dimm_init()
163 {
164 	int num_ranks_per_branch;
165 
166 
167 	/* max number of ranks per branch */
168 	num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
169 	    NB_5100_RANKS_PER_CHANNEL :
170 	    nb_dimms_per_channel * nb_channels_per_branch;
171 
172 	rank_base = kmem_zalloc(sizeof (struct rank_base) *
173 	    nb_number_memory_controllers * num_ranks_per_branch, KM_SLEEP);
174 }
175 
176 void
dimm_fini()177 dimm_fini()
178 {
179 	int num_ranks_per_branch;
180 
181 
182 	/* max number of ranks per branch */
183 	num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
184 	    NB_5100_RANKS_PER_CHANNEL :
185 	    nb_dimms_per_channel * nb_channels_per_branch;
186 
187 	kmem_free(rank_base, sizeof (struct rank_base) *
188 	    nb_number_memory_controllers * num_ranks_per_branch);
189 	rank_base = 0;
190 }
191 
192 void
dimm_add_rank(int branch,int rank,int branch_interleave,int way,uint64_t base,uint32_t hole,uint32_t hole_size,int interleave,uint64_t limit)193 dimm_add_rank(int branch, int rank, int branch_interleave, int way,
194     uint64_t base, uint32_t hole, uint32_t hole_size, int interleave,
195     uint64_t limit)
196 {
197 	struct rank_base *rp;
198 	int num_ranks_per_branch;
199 
200 	/* max number of ranks per branch */
201 	num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
202 	    NB_5100_RANKS_PER_CHANNEL :
203 	    nb_dimms_per_channel * nb_channels_per_branch;
204 	rp = &rank_base[(branch * num_ranks_per_branch) + rank];
205 	rp->branch_interleave = branch_interleave;
206 	rp->way = way;
207 	rp->base = base;
208 	rp->hole = hole;
209 	rp->hole_size = hole_size;
210 	rp->interleave = interleave;
211 	rp->limit = limit;
212 }
213 
214 static const cmi_mc_ops_t inb_mc_ops = {
215 	inb_patounum,
216 	inb_unumtopa,
217 	nb_error_trap			/* cmi_mc_logout */
218 };
219 
220 /*ARGSUSED*/
221 int
inb_mc_register(cmi_hdl_t hdl,void * arg1,void * arg2,void * arg3)222 inb_mc_register(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3)
223 {
224 	cmi_mc_register(hdl, &inb_mc_ops, NULL);
225 	return (CMI_HDL_WALK_NEXT);
226 }
227