Home | History | Annotate | Download | only in intel_nhm
      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 	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
    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
    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
    342 nhm_reinit(void)
    343 {
    344 	mem_reg_init();
    345 	return (0);
    346 }
    347 
    348 int
    349 nhm_dev_init()
    350 {
    351 	return (0);
    352 }
    353 
    354 void
    355 nhm_dev_reinit()
    356 {
    357 }
    358 
    359 void
    360 nhm_unload()
    361 {
    362 }
    363