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/time.h>
     29 #include <sys/nvpair.h>
     30 #include <sys/cmn_err.h>
     31 #include <sys/cred.h>
     32 #include <sys/open.h>
     33 #include <sys/ddi.h>
     34 #include <sys/sunddi.h>
     35 #include <sys/conf.h>
     36 #include <sys/modctl.h>
     37 #include <sys/cyclic.h>
     38 #include <sys/errorq.h>
     39 #include <sys/stat.h>
     40 #include <sys/cpuvar.h>
     41 #include <sys/mc_intel.h>
     42 #include <sys/mc.h>
     43 #include <sys/fm/protocol.h>
     44 #include "nhm_log.h"
     45 #include "intel_nhm.h"
     46 
     47 int max_bus_number = 0xff;
     48 
     49 nvlist_t *inhm_mc_nvl[MAX_CPU_NODES];
     50 krwlock_t inhm_mc_lock;
     51 
     52 char *inhm_mc_snapshot[MAX_CPU_NODES];
     53 uint_t nhm_config_gen;
     54 uint_t inhm_mc_snapshotgen;
     55 size_t inhm_mc_snapshotsz[MAX_CPU_NODES];
     56 static dev_info_t *inhm_dip;
     57 int nhm_allow_detach = 0;
     58 
     59 extern int nhm_patrol_scrub;
     60 extern int nhm_demand_scrub;
     61 extern int nhm_no_smbios;
     62 extern int nhm_smbios_serial;
     63 extern int nhm_smbios_manufacturer;
     64 extern int nhm_smbios_part_number;
     65 extern int nhm_smbios_version;
     66 extern int nhm_smbios_label;
     67 
     68 extern void inhm_create_nvl(int);
     69 extern char *inhm_mc_name(void);
     70 extern void init_dimms(void);
     71 extern void nhm_smbios();
     72 
     73 static void
     74 inhm_mc_snapshot_destroy()
     75 {
     76 	int i;
     77 
     78 	ASSERT(RW_LOCK_HELD(&inhm_mc_lock));
     79 
     80 	for (i = 0; i < MAX_CPU_NODES; i++) {
     81 		if (inhm_mc_snapshot[i] == NULL)
     82 			continue;
     83 
     84 		kmem_free(inhm_mc_snapshot[i], inhm_mc_snapshotsz[i]);
     85 			inhm_mc_snapshot[i] = NULL;
     86 			inhm_mc_snapshotsz[i] = 0;
     87 	}
     88 	inhm_mc_snapshotgen++;
     89 }
     90 
     91 static int
     92 inhm_mc_snapshot_update()
     93 {
     94 	int i;
     95 	int rt = 0;
     96 
     97 	ASSERT(RW_LOCK_HELD(&inhm_mc_lock));
     98 
     99 	for (i = 0; i < MAX_CPU_NODES; i++) {
    100 		if (inhm_mc_snapshot[i] != NULL)
    101 			continue;
    102 
    103 		if (nvlist_pack(inhm_mc_nvl[i], &inhm_mc_snapshot[i],
    104 		    &inhm_mc_snapshotsz[i], NV_ENCODE_XDR, KM_SLEEP) != 0)
    105 			rt = -1;
    106 	}
    107 
    108 	return (rt);
    109 }
    110 
    111 /*ARGSUSED*/
    112 static int
    113 inhm_mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
    114     int *rvalp)
    115 {
    116 	int rc = 0;
    117 	int chip;
    118 	mc_snapshot_info_t mcs;
    119 
    120 	if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT)
    121 		return (EINVAL);
    122 
    123 	rw_enter(&inhm_mc_lock, RW_READER);
    124 	chip = getminor(dev) % MAX_CPU_NODES;
    125 	if (inhm_mc_nvl[chip] == NULL ||
    126 	    inhm_mc_snapshotgen != nhm_config_gen) {
    127 		if (!rw_tryupgrade(&inhm_mc_lock)) {
    128 			rw_exit(&inhm_mc_lock);
    129 			return (EAGAIN);
    130 		}
    131 		if (inhm_mc_nvl[chip])
    132 			inhm_mc_snapshot_destroy();
    133 		inhm_create_nvl(chip);
    134 		nhm_config_gen = inhm_mc_snapshotgen;
    135 		(void) inhm_mc_snapshot_update();
    136 	}
    137 	switch (cmd) {
    138 	case MC_IOC_SNAPSHOT_INFO:
    139 		mcs.mcs_size = (uint32_t)inhm_mc_snapshotsz[chip];
    140 		mcs.mcs_gen = inhm_mc_snapshotgen;
    141 
    142 		if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t),
    143 		    mode) < 0)
    144 			rc = EFAULT;
    145 		break;
    146 	case MC_IOC_SNAPSHOT:
    147 		if (ddi_copyout(inhm_mc_snapshot[chip], (void *)arg,
    148 		    inhm_mc_snapshotsz[chip], mode) < 0)
    149 			rc = EFAULT;
    150 		break;
    151 	}
    152 	rw_exit(&inhm_mc_lock);
    153 	return (rc);
    154 }
    155 
    156 /*ARGSUSED*/
    157 static int
    158 inhm_mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
    159     void **result)
    160 {
    161 	if ((infocmd != DDI_INFO_DEVT2DEVINFO &&
    162 	    infocmd != DDI_INFO_DEVT2INSTANCE) || inhm_dip == NULL) {
    163 		*result = NULL;
    164 		return (DDI_FAILURE);
    165 	}
    166 	if (infocmd == DDI_INFO_DEVT2DEVINFO)
    167 		*result = inhm_dip;
    168 	else
    169 		*result = (void *)(uintptr_t)ddi_get_instance(inhm_dip);
    170 	return (0);
    171 }
    172 
    173 static int
    174 inhm_mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    175 {
    176 	int i;
    177 	char buf[64];
    178 
    179 	if (cmd == DDI_RESUME) {
    180 		nhm_dev_reinit();
    181 		nhm_scrubber_enable();
    182 		nhm_smbios();
    183 		return (DDI_SUCCESS);
    184 	}
    185 	if (cmd != DDI_ATTACH)
    186 		return (DDI_FAILURE);
    187 	if (inhm_dip == NULL) {
    188 		inhm_dip = dip;
    189 		nhm_pci_cfg_setup(dip);
    190 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
    191 		    inhm_mc_name());
    192 		if (nhm_dev_init()) {
    193 			nhm_pci_cfg_free();
    194 			inhm_dip = NULL;
    195 			return (DDI_FAILURE);
    196 		}
    197 		ddi_set_name_addr(dip, "1");
    198 		for (i = 0; i < MAX_CPU_NODES; i++) {
    199 			(void) snprintf(buf, sizeof (buf), "mc-intel-%d", i);
    200 			if (ddi_create_minor_node(dip, buf, S_IFCHR,
    201 			    i, "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
    202 				cmn_err(CE_WARN, "failed to create minor node"
    203 				    " for memory controller %d\n", i);
    204 			}
    205 		}
    206 		cmi_hdl_walk(inhm_mc_register, NULL, NULL, NULL);
    207 		nhm_patrol_scrub = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    208 		    DDI_PROP_DONTPASS, "patrol-scrub", 0);
    209 		nhm_demand_scrub = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    210 		    DDI_PROP_DONTPASS, "demand-scrub", 0);
    211 		nhm_no_smbios = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    212 		    DDI_PROP_DONTPASS, "no-smbios", 0);
    213 		nhm_smbios_serial = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    214 		    DDI_PROP_DONTPASS, "smbios-dimm-serial", 1);
    215 		nhm_smbios_manufacturer = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    216 		    DDI_PROP_DONTPASS, "smbios-dimm-manufacturer", 1);
    217 		nhm_smbios_part_number = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    218 		    DDI_PROP_DONTPASS, "smbios-dimm-part-number", 1);
    219 		nhm_smbios_version = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    220 		    DDI_PROP_DONTPASS, "smbios-dimme-version", 1);
    221 		nhm_smbios_label = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
    222 		    DDI_PROP_DONTPASS, "smbios-dimm-label", 1);
    223 		nhm_scrubber_enable();
    224 		nhm_smbios();
    225 	}
    226 
    227 	return (DDI_SUCCESS);
    228 }
    229 
    230 /*ARGSUSED*/
    231 static int
    232 inhm_mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    233 {
    234 	if (nhm_allow_detach && cmd == DDI_DETACH && dip == inhm_dip) {
    235 		rw_enter(&inhm_mc_lock, RW_WRITER);
    236 		inhm_mc_snapshot_destroy();
    237 		rw_exit(&inhm_mc_lock);
    238 		inhm_dip = NULL;
    239 		return (DDI_SUCCESS);
    240 	} else if (cmd == DDI_SUSPEND || cmd == DDI_PM_SUSPEND) {
    241 		return (DDI_SUCCESS);
    242 	} else {
    243 		return (DDI_FAILURE);
    244 	}
    245 }
    246 
    247 /*ARGSUSED*/
    248 static int
    249 inhm_mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
    250 {
    251 	if (otyp != OTYP_CHR)
    252 		return (EINVAL);
    253 
    254 	rw_enter(&inhm_mc_lock, RW_READER);
    255 	if (getminor(*devp) >= MAX_CPU_NODES) {
    256 		rw_exit(&inhm_mc_lock);
    257 		return (EINVAL);
    258 	}
    259 	rw_exit(&inhm_mc_lock);
    260 
    261 	return (0);
    262 }
    263 
    264 /*ARGSUSED*/
    265 static int
    266 inhm_mc_close(dev_t dev, int flag, int otyp, cred_t *credp)
    267 {
    268 	return (0);
    269 }
    270 
    271 
    272 static struct cb_ops inhm_mc_cb_ops = {
    273 	inhm_mc_open,
    274 	inhm_mc_close,
    275 	nodev,		/* not a block driver */
    276 	nodev,		/* no print routine */
    277 	nodev,		/* no dump routine */
    278 	nodev,		/* no read routine */
    279 	nodev,		/* no write routine */
    280 	inhm_mc_ioctl,
    281 	nodev,		/* no devmap routine */
    282 	nodev,		/* no mmap routine */
    283 	nodev,		/* no segmap routine */
    284 	nochpoll,	/* no chpoll routine */
    285 	ddi_prop_op,
    286 	0,		/* not a STREAMS driver */
    287 	D_NEW | D_MP,	/* safe for multi-thread/multi-processor */
    288 };
    289 
    290 static struct dev_ops inhm_mc_ops = {
    291 	DEVO_REV,		/* devo_rev */
    292 	0,			/* devo_refcnt */
    293 	inhm_mc_getinfo,		/* devo_getinfo */
    294 	nulldev,		/* devo_identify */
    295 	nulldev,		/* devo_probe */
    296 	inhm_mc_attach,		/* devo_attach */
    297 	inhm_mc_detach,		/* devo_detach */
    298 	nodev,			/* devo_reset */
    299 	&inhm_mc_cb_ops,		/* devo_cb_ops */
    300 	NULL,			/* devo_bus_ops */
    301 	NULL,			/* devo_power */
    302 	ddi_quiesce_not_needed,	/* devo_quiesce */
    303 };
    304 
    305 static struct modldrv modldrv = {
    306 	&mod_driverops,
    307 	"Intel QuickPath Memory Controller Hub Module",
    308 	&inhm_mc_ops
    309 };
    310 
    311 static struct modlinkage modlinkage = {
    312 	MODREV_1,
    313 	(void *)&modldrv,
    314 	NULL
    315 };
    316 
    317 int
    318 _init(void)
    319 {
    320 	int err;
    321 
    322 	err = nhm_init();
    323 	if (err == 0 && (err = mod_install(&modlinkage)) == 0) {
    324 		rw_init(&inhm_mc_lock, NULL, RW_DRIVER, NULL);
    325 		init_dimms();
    326 	}
    327 
    328 	return (err);
    329 }
    330 
    331 int
    332 _info(struct modinfo *modinfop)
    333 {
    334 	return (mod_info(&modlinkage, modinfop));
    335 }
    336 
    337 int
    338 _fini(void)
    339 {
    340 	int err;
    341 
    342 	if ((err = mod_remove(&modlinkage)) == 0) {
    343 		nhm_unload();
    344 		rw_destroy(&inhm_mc_lock);
    345 	}
    346 
    347 	return (err);
    348 }
    349