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
check_serial_number()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
dimm_manufacture_data(smbios_hdl_t * shp,id_t id,nhm_dimm_t * dimmp)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
dimm_label(smbios_hdl_t * shp,const smbios_struct_t * sp,void * arg)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
nhm_smbios()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
dimm_prop(nhm_dimm_t * dimmp,uint32_t dod)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
nhm_scrubber_enable()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 uint32_t id;
238 int i;
239 int hw_scrub = 0;
240
241 if (ecc_enabled && (nhm_patrol_scrub || nhm_demand_scrub)) {
242 for (i = 0; i < MAX_MEMORY_CONTROLLERS; i++) {
243 id = MC_CPU_RAS_RD(i);
244 if ((id != NHM_CPU_RAS && id != NHM_JF_CPU_RAS &&
245 id != NHM_WM_CPU_RAS) || nhm_memory_on_ctl[i] == 0)
246 continue;
247 mc_ssrcontrol = MC_SSR_CONTROL_RD(i);
248 if (nhm_demand_scrub &&
249 (mc_ssrcontrol & DEMAND_SCRUB_ENABLE) == 0) {
250 mc_ssrcontrol |= DEMAND_SCRUB_ENABLE;
251 MC_SSR_CONTROL_WR(i, mc_ssrcontrol);
252 }
253 if (nhm_patrol_scrub == 0)
254 continue;
255 if (SSR_MODE(mc_ssrcontrol) == SSR_IDLE) {
256 mc_dimm_clk_ratio_status =
257 MC_DIMM_CLK_RATIO_STATUS(i);
258 cycle_time =
259 MAX_DIMM_CLK_RATIO(mc_dimm_clk_ratio_status)
260 * 80000000;
261 interval = (uint32_t)((36400ULL * cycle_time) /
262 (nhm_memory_on_ctl[i]/64));
263 MC_SCRUB_CONTROL_WR(i, STARTSCRUB | interval);
264 MC_SSR_CONTROL_WR(i, mc_ssrcontrol | SSR_SCRUB);
265 } else if (SSR_MODE(mc_ssrcontrol) == SSR_SPARE) {
266 hw_scrub = 0;
267 break;
268 }
269 hw_scrub = 1;
270 }
271 if (hw_scrub)
272 cmi_mc_sw_memscrub_disable();
273 }
274 }
275
276 void
init_dimms()277 init_dimms()
278 {
279 int i, j, k;
280 nhm_dimm_t **dimmpp;
281 nhm_dimm_t *dimmp;
282 uint32_t dod;
283 uint32_t did;
284
285 nhm_dimms = (nhm_dimm_t **)kmem_zalloc(sizeof (nhm_dimm_t *) *
286 MAX_MEMORY_CONTROLLERS * CHANNELS_PER_MEMORY_CONTROLLER *
287 MAX_DIMMS_PER_CHANNEL, KM_SLEEP);
288 dimmpp = nhm_dimms;
289 for (i = 0; i < MAX_MEMORY_CONTROLLERS; i++) {
290 did = CPU_ID_RD(i);
291 if (did != NHM_EP_CPU && did != NHM_WS_CPU &&
292 did != NHM_JF_CPU && did != NHM_WM_CPU) {
293 dimmpp += CHANNELS_PER_MEMORY_CONTROLLER *
294 MAX_DIMMS_PER_CHANNEL;
295 continue;
296 }
297 for (j = 0; j < CHANNELS_PER_MEMORY_CONTROLLER; j++) {
298 for (k = 0; k < MAX_DIMMS_PER_CHANNEL; k++) {
299 dod = MC_DOD_RD(i, j, k);
300 if (DIMMPRESENT(dod)) {
301 dimmp = (nhm_dimm_t *)
302 kmem_zalloc(sizeof (nhm_dimm_t),
303 KM_SLEEP);
304 dimm_prop(dimmp, dod);
305 (void) snprintf(dimmp->label,
306 sizeof (dimmp->label),
307 "Socket %d channel %d dimm %d",
308 i, j, k);
309 *dimmpp = dimmp;
310 nhm_memory_on_ctl[i] +=
311 dimmp->dimm_size;
312 }
313 dimmpp++;
314 }
315 }
316 }
317 }
318
319
320 int
nhm_init(void)321 nhm_init(void)
322 {
323 int slot;
324
325 /* return ENOTSUP if there is no PCI config space support. */
326 if (pci_getl_func == NULL)
327 return (ENOTSUP);
328 for (slot = 0; slot < MAX_CPU_NODES; slot++) {
329 nhm_chipset = CPU_ID_RD(slot);
330 if (nhm_chipset == NHM_EP_CPU || nhm_chipset == NHM_WS_CPU ||
331 nhm_chipset == NHM_JF_CPU || nhm_chipset == NHM_WM_CPU)
332 break;
333 }
334 if (slot == MAX_CPU_NODES) {
335 return (ENOTSUP);
336 }
337 mem_reg_init();
338 return (0);
339 }
340
341 int
nhm_reinit(void)342 nhm_reinit(void)
343 {
344 mem_reg_init();
345 return (0);
346 }
347
348 int
nhm_dev_init()349 nhm_dev_init()
350 {
351 return (0);
352 }
353
354 void
nhm_dev_reinit()355 nhm_dev_reinit()
356 {
357 }
358
359 void
nhm_unload()360 nhm_unload()
361 {
362 }
363