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