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