xref: /illumos-gate/usr/src/uts/intel/io/intel_nhm/nhm_init.c (revision 3afe87ebb25691cb6d158edaa34a6fb9b703a691)
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/cmn_err.h>
29 #include <sys/errno.h>
30 #include <sys/log.h>
31 #include <sys/systm.h>
32 #include <sys/modctl.h>
33 #include <sys/errorq.h>
34 #include <sys/controlregs.h>
35 #include <sys/fm/util.h>
36 #include <sys/fm/protocol.h>
37 #include <sys/sysevent.h>
38 #include <sys/pghw.h>
39 #include <sys/cyclic.h>
40 #include <sys/pci_cfgspace.h>
41 #include <sys/mc_intel.h>
42 #include <sys/cpu_module_impl.h>
43 #include <sys/smbios.h>
44 #include <sys/pci.h>
45 #include "intel_nhm.h"
46 #include "nhm_log.h"
47 
48 errorq_t *nhm_queue;
49 kmutex_t nhm_mutex;
50 uint32_t nhm_chipset;
51 
52 nhm_dimm_t **nhm_dimms;
53 
54 uint64_t nhm_memory_on_ctl[MAX_MEMORY_CONTROLLERS];
55 int nhm_patrol_scrub;
56 int nhm_demand_scrub;
57 int nhm_no_smbios;
58 int nhm_smbios_serial;
59 int nhm_smbios_manufacturer;
60 int nhm_smbios_part_number;
61 int nhm_smbios_version;
62 int nhm_smbios_label;
63 
64 extern char ecc_enabled;
65 extern void mem_reg_init(void);
66 
67 static void
68 check_serial_number()
69 {
70 	nhm_dimm_t *dimmp, *tp;
71 	nhm_dimm_t **dimmpp, **tpp;
72 	nhm_dimm_t **end;
73 	int not_unique;
74 
75 	end = &nhm_dimms[MAX_MEMORY_CONTROLLERS *
76 	    CHANNELS_PER_MEMORY_CONTROLLER * MAX_DIMMS_PER_CHANNEL];
77 	for (dimmpp = nhm_dimms; dimmpp < end; dimmpp++) {
78 		dimmp = *dimmpp;
79 		if (dimmp == NULL)
80 			continue;
81 		not_unique = 0;
82 		for (tpp = dimmpp + 1; tpp < end; tpp++) {
83 			tp = *tpp;
84 			if (tp == NULL)
85 				continue;
86 			if (strncmp(dimmp->serial_number, tp->serial_number,
87 			    sizeof (dimmp->serial_number)) == 0) {
88 				not_unique = 1;
89 				tp->serial_number[0] = 0;
90 			}
91 		}
92 		if (not_unique)
93 			dimmp->serial_number[0] = 0;
94 	}
95 }
96 
97 static void
98 dimm_manufacture_data(smbios_hdl_t *shp, id_t id, nhm_dimm_t *dimmp)
99 {
100 	smbios_info_t cd;
101 
102 	if (smbios_info_common(shp, id, &cd) == 0) {
103 		if (cd.smbi_serial && nhm_smbios_serial) {
104 			(void) strncpy(dimmp->serial_number, cd.smbi_serial,
105 			    sizeof (dimmp->serial_number));
106 		}
107 		if (cd.smbi_manufacturer && nhm_smbios_manufacturer) {
108 			(void) strncpy(dimmp->manufacturer,
109 			    cd.smbi_manufacturer,
110 			    sizeof (dimmp->manufacturer));
111 		}
112 		if (cd.smbi_part && nhm_smbios_part_number) {
113 			(void) strncpy(dimmp->part_number, cd.smbi_part,
114 			    sizeof (dimmp->part_number));
115 		}
116 		if (cd.smbi_version && nhm_smbios_version) {
117 			(void) strncpy(dimmp->revision, cd.smbi_version,
118 			    sizeof (dimmp->revision));
119 		}
120 	}
121 }
122 
123 struct dimm_slot {
124 	int controller;
125 	int channel;
126 	int dimm;
127 	int max_dimm;
128 };
129 
130 static int
131 dimm_label(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
132 {
133 	nhm_dimm_t *dimmp;
134 	smbios_memdevice_t md;
135 	int slot;
136 	int last_slot;
137 	struct dimm_slot *dsp = (struct dimm_slot *)arg;
138 
139 	slot = (dsp->controller * CHANNELS_PER_MEMORY_CONTROLLER *
140 	    MAX_DIMMS_PER_CHANNEL) + (dsp->channel * MAX_DIMMS_PER_CHANNEL) +
141 	    dsp->dimm;
142 	last_slot = MAX_MEMORY_CONTROLLERS * CHANNELS_PER_MEMORY_CONTROLLER *
143 	    MAX_DIMMS_PER_CHANNEL;
144 	if (slot >= last_slot)
145 		return (0);
146 	dimmp = nhm_dimms[slot];
147 	if (sp->smbstr_type == SMB_TYPE_MEMDEVICE) {
148 		if (smbios_info_memdevice(shp, sp->smbstr_id,
149 		    &md) == 0 && md.smbmd_dloc != NULL) {
150 			if (dimmp == NULL && md.smbmd_size) {
151 				/* skip non existent slot */
152 				dsp->channel++;
153 				if (dsp->dimm == 2)
154 					dsp->max_dimm = 2;
155 				dsp->dimm = 0;
156 				slot = (dsp->controller *
157 				    CHANNELS_PER_MEMORY_CONTROLLER *
158 				    MAX_DIMMS_PER_CHANNEL) +
159 				    (dsp->channel * MAX_DIMMS_PER_CHANNEL);
160 				if (slot >= last_slot)
161 					return (0);
162 
163 				dimmp = nhm_dimms[slot];
164 
165 				if (dimmp == NULL) {
166 					dsp->channel++;
167 					if (dsp->channel ==
168 					    CHANNELS_PER_MEMORY_CONTROLLER) {
169 						dsp->channel = 0;
170 						dsp->controller++;
171 					}
172 					slot = (dsp->controller *
173 					    CHANNELS_PER_MEMORY_CONTROLLER *
174 					    MAX_DIMMS_PER_CHANNEL) +
175 					    (dsp->channel *
176 					    MAX_DIMMS_PER_CHANNEL);
177 					if (slot >= last_slot)
178 						return (0);
179 					dimmp = nhm_dimms[slot];
180 				}
181 			}
182 			if (dimmp) {
183 				if (nhm_smbios_label)
184 					(void) snprintf(dimmp->label,
185 					    sizeof (dimmp->label), "%s",
186 					    md.smbmd_dloc);
187 				dimm_manufacture_data(shp, sp->smbstr_id,
188 				    dimmp);
189 			}
190 		}
191 		dsp->dimm++;
192 		if (dsp->dimm == dsp->max_dimm) {
193 			dsp->dimm = 0;
194 			dsp->channel++;
195 			if (dsp->channel == CHANNELS_PER_MEMORY_CONTROLLER) {
196 				dsp->channel = 0;
197 				dsp->controller++;
198 			}
199 		}
200 	}
201 	return (0);
202 }
203 
204 void
205 nhm_smbios()
206 {
207 	struct dimm_slot ds;
208 
209 	if (ksmbios != NULL && nhm_no_smbios == 0) {
210 		ds.dimm = 0;
211 		ds.channel = 0;
212 		ds.controller = 0;
213 		ds.max_dimm = MAX_DIMMS_PER_CHANNEL;
214 		(void) smbios_iter(ksmbios, dimm_label, &ds);
215 		check_serial_number();
216 	}
217 }
218 
219 static void
220 dimm_prop(nhm_dimm_t *dimmp, uint32_t dod)
221 {
222 	dimmp->dimm_size = DIMMSIZE(dod);
223 	dimmp->nranks = NUMRANK(dod);
224 	dimmp->nbanks = NUMBANK(dod);
225 	dimmp->ncolumn = NUMCOL(dod);
226 	dimmp->nrow = NUMROW(dod);
227 	dimmp->width = DIMMWIDTH;
228 }
229 
230 void
231 nhm_scrubber_enable()
232 {
233 	uint32_t mc_ssrcontrol;
234 	uint32_t mc_dimm_clk_ratio_status;
235 	uint64_t cycle_time;
236 	uint32_t interval;
237 	int i;
238 	int hw_scrub = 0;
239 
240 	if (ecc_enabled && (nhm_patrol_scrub || nhm_demand_scrub)) {
241 		for (i = 0; i < MAX_MEMORY_CONTROLLERS; i++) {
242 			if (nhm_memory_on_ctl[i] == 0)
243 				continue;
244 			mc_ssrcontrol = MC_SSR_CONTROL_RD(i);
245 			if (nhm_demand_scrub &&
246 			    (mc_ssrcontrol & DEMAND_SCRUB_ENABLE) == 0) {
247 				mc_ssrcontrol |= DEMAND_SCRUB_ENABLE;
248 				MC_SSR_CONTROL_WR(i, mc_ssrcontrol);
249 			}
250 			if (nhm_patrol_scrub == 0)
251 				continue;
252 			if (SSR_MODE(mc_ssrcontrol) == SSR_IDLE) {
253 				mc_dimm_clk_ratio_status =
254 				    MC_DIMM_CLK_RATIO_STATUS(i);
255 				cycle_time =
256 				    MAX_DIMM_CLK_RATIO(mc_dimm_clk_ratio_status)
257 				    * 80000000;
258 				interval = (uint32_t)((36400ULL * cycle_time) /
259 				    (nhm_memory_on_ctl[i]/64));
260 				MC_SCRUB_CONTROL_WR(i, STARTSCRUB | interval);
261 				MC_SSR_CONTROL_WR(i, mc_ssrcontrol | SSR_SCRUB);
262 			} else if (SSR_MODE(mc_ssrcontrol) == SSR_SPARE) {
263 				hw_scrub = 0;
264 				break;
265 			}
266 			hw_scrub = 1;
267 		}
268 		if (hw_scrub)
269 			cmi_mc_sw_memscrub_disable();
270 	}
271 }
272 
273 void
274 init_dimms()
275 {
276 	int i, j, k;
277 	nhm_dimm_t **dimmpp;
278 	nhm_dimm_t *dimmp;
279 	uint32_t dod;
280 
281 	nhm_dimms = (nhm_dimm_t **)kmem_zalloc(sizeof (nhm_dimm_t *) *
282 	    MAX_MEMORY_CONTROLLERS * CHANNELS_PER_MEMORY_CONTROLLER *
283 	    MAX_DIMMS_PER_CHANNEL, KM_SLEEP);
284 	dimmpp = nhm_dimms;
285 	for (i = 0; i < MAX_MEMORY_CONTROLLERS; i++) {
286 		if (CPU_ID_RD(i) != NHM_CPU) {
287 			dimmpp += CHANNELS_PER_MEMORY_CONTROLLER *
288 			    MAX_DIMMS_PER_CHANNEL;
289 			continue;
290 		}
291 		for (j = 0; j < CHANNELS_PER_MEMORY_CONTROLLER; j++) {
292 			for (k = 0; k < MAX_DIMMS_PER_CHANNEL; k++) {
293 				dod = MC_DOD_RD(i, j, k);
294 				if (DIMMPRESENT(dod)) {
295 					dimmp = (nhm_dimm_t *)
296 					    kmem_zalloc(sizeof (nhm_dimm_t),
297 					    KM_SLEEP);
298 					dimm_prop(dimmp, dod);
299 					(void) snprintf(dimmp->label,
300 					    sizeof (dimmp->label),
301 					    "Socket %d channel %d dimm %d",
302 					    i, j, k);
303 					*dimmpp = dimmp;
304 					nhm_memory_on_ctl[i] +=
305 					    dimmp->dimm_size;
306 				}
307 				dimmpp++;
308 			}
309 		}
310 	}
311 }
312 
313 
314 int
315 nhm_init(void)
316 {
317 	int slot;
318 
319 	/* return ENOTSUP if there is no PCI config space support. */
320 	if (pci_getl_func == NULL)
321 		return (ENOTSUP);
322 	for (slot = 0; slot < MAX_CPU_NODES; slot++) {
323 		nhm_chipset = CPU_ID_RD(slot);
324 		if (nhm_chipset == NHM_CPU)
325 			break;
326 	}
327 	if (nhm_chipset != NHM_CPU) {
328 		return (ENOTSUP);
329 	}
330 	mem_reg_init();
331 	return (0);
332 }
333 
334 int
335 nhm_reinit(void)
336 {
337 	mem_reg_init();
338 	return (0);
339 }
340 
341 int
342 nhm_dev_init()
343 {
344 	return (0);
345 }
346 
347 void
348 nhm_dev_reinit()
349 {
350 }
351 
352 void
353 nhm_unload()
354 {
355 }
356