xref: /illumos-gate/usr/src/uts/intel/io/intel_nhm/intel_nhmdrv.c (revision 27954b0d964ffcb749cf19296906e7fecdf3da1b)
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