1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13 * Copyright 2019, Joyent, Inc.
14 */
15
16/*
17 * Intel CPU Thermal sensor driver
18 *
19 * These MSRs that were used were introduced with the 'Core' family processors
20 * and have since spread beyond there, even to the Atom line. Currently,
21 * temperature sensors exist on a per-core basis and optionally on a per-package
22 * basis. The temperature sensor exposes a reading that's relative to the
23 * processor's maximum junction temperature, often referred to as Tj. We
24 * currently only support models where we can determine that junction
25 * temperature programatically. For older processors, we would need to track
26 * down the datasheet. Unfortunately, the values here are often on a per-brand
27 * string basis. As in two CPUs with the same model and stepping, but have
28 * binned differently have different temperatures.
29 *
30 * The temperature is exposed through /dev and uses a semi-standard sensor
31 * framework. We expose one minor node per CPU core and one minor node per CPU
32 * package, if that is supported. Reads are rate-limited in the driver at 100ms
33 * by default per the global variable coretemp_cache_ms.
34 */
35
36#include <sys/modctl.h>
37#include <sys/conf.h>
38#include <sys/devops.h>
39#include <sys/types.h>
40#include <sys/file.h>
41#include <sys/open.h>
42#include <sys/stat.h>
43#include <sys/cred.h>
44#include <sys/ddi.h>
45#include <sys/sunddi.h>
46#include <sys/list.h>
47#include <sys/stddef.h>
48#include <sys/cmn_err.h>
49#include <sys/id_space.h>
50#include <sys/x86_archext.h>
51#include <sys/cpu_module.h>
52#include <sys/ontrap.h>
53#include <sys/cpuvar.h>
54#include <sys/x_call.h>
55#include <sys/sensors.h>
56
57#define	CORETEMP_MINOR_MIN	1
58#define	CORETEMP_MINOR_MAX	INT32_MAX
59
60typedef struct coretemp_core {
61	list_node_t		ctc_link;
62	id_t			ctc_core_minor;
63	id_t			ctc_pkg_minor;
64	enum cmi_hdl_class	ctc_class;
65	uint_t			ctc_chip;
66	uint_t			ctc_core;
67	uint_t			ctc_strand;
68	uint_t			ctc_tjmax;
69	hrtime_t		ctc_last_read;
70	uint64_t		ctc_core_status;
71	uint64_t		ctc_core_intr;
72	uint64_t		ctc_pkg_status;
73	uint64_t		ctc_pkg_intr;
74	uint64_t		ctc_invalid_reads;
75	/* The following fields are derived from above */
76	uint_t			ctc_temperature;
77	uint_t			ctc_resolution;
78	uint_t			ctc_pkg_temperature;
79} coretemp_core_t;
80
81typedef struct coretemp {
82	dev_info_t	*coretemp_dip;
83	id_space_t	*coretemp_ids;
84	cpuset_t	*coretemp_cpuset;
85	boolean_t	coretemp_pkg;
86	kmutex_t	coretemp_mutex;
87	list_t		coretemp_cores;
88} coretemp_t;
89
90coretemp_t *coretemp;
91
92/*
93 * This indicates a number of milliseconds that we should wait between reads.
94 * This is somewhat arbitrary, but the goal is to reduce cross call activity
95 * and reflect that the sensor may not update all the time.
96 */
97uint_t coretemp_cache_ms = 100;
98
99static int
100coretemp_rdmsr_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3)
101{
102	uint_t msr = (uint_t)arg1;
103	uint64_t *valp = (uint64_t *)arg2;
104	cmi_errno_t *errp = (cmi_errno_t *)arg3;
105
106	on_trap_data_t otd;
107
108	if (on_trap(&otd, OT_DATA_ACCESS) == 0) {
109		if (checked_rdmsr(msr, valp) == 0) {
110			*errp = CMI_SUCCESS;
111		} else {
112			*errp = CMIERR_NOTSUP;
113		}
114	} else {
115		*errp = CMIERR_MSRGPF;
116	}
117	no_trap();
118
119	return (0);
120}
121
122/*
123 * This really should just be a call to the CMI handle to provide us the MSR.
124 * However, that routine, cmi_hdl_rdmsr(), cannot be safely used until it is
125 * fixed for use outside of a panic-like context.
126 */
127static int
128coretemp_rdmsr(coretemp_t *ct, cmi_hdl_t hdl, uint_t msr, uint64_t *valp)
129{
130	id_t cpu = cmi_hdl_logical_id(hdl);
131	int ret = CMI_SUCCESS;
132
133	ASSERT(MUTEX_HELD(&ct->coretemp_mutex));
134	kpreempt_disable();
135	if (CPU->cpu_id == cpu) {
136		(void) coretemp_rdmsr_xc((xc_arg_t)msr, (xc_arg_t)valp,
137		    (xc_arg_t)&ret);
138	} else {
139		cpuset_only(ct->coretemp_cpuset, (uint_t)cpu);
140		xc_call((xc_arg_t)msr, (xc_arg_t)valp, (xc_arg_t)&ret,
141		    (ulong_t *)ct->coretemp_cpuset, coretemp_rdmsr_xc);
142	}
143	kpreempt_enable();
144
145	return (ret);
146}
147
148static int
149coretemp_cmi_errno(cmi_errno_t e)
150{
151	switch (e) {
152	case CMIERR_NOTSUP:
153		return (ENOTSUP);
154	default:
155		return (EIO);
156	}
157}
158
159/*
160 * Answer the question of whether or not the driver can support the CPU in
161 * question. Right now we have the following constraints for supporting the CPU:
162 *
163 *   o The CPU is made by Intel
164 *   o The CPU has the Digital Thermal Sensor
165 *   o The CPU family is 6, which is usually implicit from the above
166 *   o We can determine its junction temperature through an MSR
167 *
168 * If we can't determine the junction temperature programatically, then we need
169 * to set up tables of CPUs to do so. This can be fleshed out and improved.
170 */
171static boolean_t
172coretemp_supported(void)
173{
174	uint_t model;
175
176	if (cpuid_getvendor(CPU) != X86_VENDOR_Intel) {
177		return (B_FALSE);
178	}
179
180	if (!is_x86_feature(x86_featureset, X86FSET_CORE_THERMAL)) {
181		return (B_FALSE);
182	}
183
184	if (cpuid_getfamily(CPU) != 6) {
185		return (B_FALSE);
186	}
187
188	model = cpuid_getmodel(CPU);
189	if (model <= INTC_MODEL_PENRYN || model == INTC_MODEL_SILVERTHORNE ||
190	    model == INTC_MODEL_LINCROFT || model == INTC_MODEL_PENWELL ||
191	    model == INTC_MODEL_CLOVERVIEW || model == INTC_MODEL_CEDARVIEW) {
192		return (B_FALSE);
193	}
194
195	return (B_TRUE);
196}
197
198static coretemp_core_t *
199coretemp_lookup_core(coretemp_t *ct, minor_t minor)
200{
201	coretemp_core_t *ctc;
202
203	ASSERT(MUTEX_HELD(&ct->coretemp_mutex));
204
205	if (minor < CORETEMP_MINOR_MIN || minor > CORETEMP_MINOR_MAX) {
206		return (NULL);
207	}
208
209	for (ctc = list_head(&ct->coretemp_cores); ctc != NULL;
210	    ctc = list_next(&ct->coretemp_cores, ctc)) {
211		if (ctc->ctc_core_minor == (id_t)minor ||
212		    (ctc->ctc_pkg_minor >= CORETEMP_MINOR_MIN &&
213		    ctc->ctc_pkg_minor == (id_t)minor)) {
214			return (ctc);
215		}
216	}
217
218	return (NULL);
219}
220
221
222/*
223 * We need to determine the value of Tj Max as all temperature sensors are
224 * derived from this value. The ease of this depends on how old the processor in
225 * question is. The Core family processors after Penryn have support for an MSR
226 * that tells us what to go for. In the Atom family, processors starting with
227 * Silvermont have support for an MSR that documents this value. For older
228 * processors, one needs to track down the datasheet for a specific processor.
229 * Two processors in the same family/model may have different values of Tj Max.
230 * At the moment, we only support this on processors that have that MSR.
231 */
232static int
233coretemp_calculate_tjmax(coretemp_t *ct, coretemp_core_t *ctc, cmi_hdl_t hdl)
234{
235	cmi_errno_t e;
236	int err = 0;
237	uint64_t val = 0;
238
239	e = coretemp_rdmsr(ct, hdl, MSR_TEMPERATURE_TARGET, &val);
240	if (e == CMI_SUCCESS && val != 0) {
241		ctc->ctc_tjmax = MSR_TEMPERATURE_TARGET_TARGET(val);
242	} else if (val == 0) {
243		err = EINVAL;
244	} else {
245		err = coretemp_cmi_errno(e);
246	}
247
248	return (err);
249}
250
251static int
252coretemp_read(coretemp_t *ct, coretemp_core_t *ctc, cmi_hdl_t hdl)
253{
254	cmi_errno_t e;
255	int err = 0;
256	uint64_t val = 0;
257
258	ctc->ctc_last_read = gethrtime();
259
260	e = coretemp_rdmsr(ct, hdl, MSR_IA32_THERM_STATUS, &val);
261	if (e == CMI_SUCCESS) {
262		ctc->ctc_core_status = val;
263	} else {
264		err = coretemp_cmi_errno(e);
265		dev_err(ct->coretemp_dip, CE_WARN, "!failed to get core "
266		    "thermal status on %u/%u: %d", ctc->ctc_chip, ctc->ctc_core,
267		    err);
268		return (err);
269	}
270
271	e = coretemp_rdmsr(ct, hdl, MSR_IA32_THERM_INTERRUPT, &val);
272	if (e == CMI_SUCCESS) {
273		ctc->ctc_core_intr = val;
274	} else {
275		err = coretemp_cmi_errno(e);
276		dev_err(ct->coretemp_dip, CE_WARN, "!failed to get core "
277		    "thermal interrupt on %u/%u: %d", ctc->ctc_chip,
278		    ctc->ctc_core, err);
279		return (err);
280	}
281
282	/*
283	 * If the last read wasn't valid, then we should keep the current state.
284	 */
285	if ((ctc->ctc_core_status & IA32_THERM_STATUS_READ_VALID) != 0) {
286		uint_t diff;
287		diff = IA32_THERM_STATUS_READING(ctc->ctc_core_status);
288
289		if (diff >= ctc->ctc_tjmax) {
290			dev_err(ct->coretemp_dip, CE_WARN, "!found invalid "
291			    "core temperature on %u/%u: readout: %u, Tjmax: "
292			    "%u, raw: 0x%" PRIx64, ctc->ctc_chip,
293			    ctc->ctc_core, diff, ctc->ctc_tjmax,
294			    ctc->ctc_core_status);
295			ctc->ctc_invalid_reads++;
296		} else {
297			ctc->ctc_temperature = ctc->ctc_tjmax - diff;
298		}
299	} else {
300		ctc->ctc_invalid_reads++;
301	}
302
303	ctc->ctc_resolution =
304	    IA32_THERM_STATUS_RESOLUTION(ctc->ctc_core_status);
305
306	/*
307	 * If we have package support and this is core zero, then update the
308	 * package data.
309	 */
310	if (ct->coretemp_pkg && ctc->ctc_core == 0) {
311		uint_t diff;
312
313		e = coretemp_rdmsr(ct, hdl, MSR_IA32_PACKAGE_THERM_STATUS,
314		    &val);
315		if (e == CMI_SUCCESS) {
316			ctc->ctc_pkg_status = val;
317		} else {
318			err = coretemp_cmi_errno(e);
319			dev_err(ct->coretemp_dip, CE_WARN, "!failed to get "
320			    "package thermal status on %u: %d", ctc->ctc_chip,
321			    err);
322			return (err);
323		}
324
325		e = coretemp_rdmsr(ct, hdl, MSR_IA32_PACKAGE_THERM_INTERRUPT,
326		    &val);
327		if (e == CMI_SUCCESS) {
328			ctc->ctc_pkg_intr = val;
329		} else {
330			err = coretemp_cmi_errno(e);
331			dev_err(ct->coretemp_dip, CE_WARN, "!failed to get "
332			    "package thermal interrupt on %u: %d",
333			    ctc->ctc_chip, err);
334			return (err);
335		}
336
337		diff = IA32_PKG_THERM_STATUS_READING(ctc->ctc_pkg_status);
338		if (diff >= ctc->ctc_tjmax) {
339			dev_err(ct->coretemp_dip, CE_WARN, "!found invalid "
340			    "package temperature on %u: readout: %u, tjmax: "
341			    "%u, raw: 0x%" PRIx64, ctc->ctc_chip, diff,
342			    ctc->ctc_tjmax, ctc->ctc_pkg_status);
343			ctc->ctc_invalid_reads++;
344
345		} else {
346			ctc->ctc_pkg_temperature = ctc->ctc_tjmax - diff;
347		}
348	}
349
350	return (0);
351}
352
353static int
354coretemp_open(dev_t *devp, int flags, int otype, cred_t *credp)
355{
356	coretemp_t *ct = coretemp;
357
358	if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp)) {
359		return (EPERM);
360	}
361
362	if ((flags & (FEXCL | FNDELAY | FWRITE)) != 0) {
363		return (EINVAL);
364	}
365
366	if (otype != OTYP_CHR) {
367		return (EINVAL);
368	}
369
370	/*
371	 * Sanity check the minor
372	 */
373	mutex_enter(&ct->coretemp_mutex);
374	if (coretemp_lookup_core(ct, getminor(*devp)) == NULL) {
375		mutex_exit(&ct->coretemp_mutex);
376		return (ENXIO);
377	}
378	mutex_exit(&ct->coretemp_mutex);
379
380	return (0);
381}
382
383static int
384coretemp_ioctl_kind(intptr_t arg, int mode)
385{
386	sensor_ioctl_kind_t kind;
387
388	bzero(&kind, sizeof (kind));
389	kind.sik_kind = SENSOR_KIND_TEMPERATURE;
390
391	if (ddi_copyout((void *)&kind, (void *)arg, sizeof (kind),
392	    mode & FKIOCTL) != 0) {
393		return (EFAULT);
394	}
395
396	return (0);
397}
398
399static int
400coretemp_ioctl_temp(coretemp_t *ct, minor_t minor, intptr_t arg, int mode)
401{
402	coretemp_core_t *ctc;
403	hrtime_t diff;
404	sensor_ioctl_temperature_t temp;
405
406	bzero(&temp, sizeof (temp));
407
408	mutex_enter(&ct->coretemp_mutex);
409	ctc = coretemp_lookup_core(ct, minor);
410	if (ctc == NULL) {
411		mutex_exit(&ct->coretemp_mutex);
412		return (ENXIO);
413	}
414
415	diff = NSEC2MSEC(gethrtime() - ctc->ctc_last_read);
416	if (diff > 0 && diff > (hrtime_t)coretemp_cache_ms) {
417		int ret;
418		cmi_hdl_t hdl;
419
420		if ((hdl = cmi_hdl_lookup(ctc->ctc_class, ctc->ctc_chip,
421		    ctc->ctc_core, ctc->ctc_strand)) == NULL) {
422			mutex_exit(&ct->coretemp_mutex);
423			return (ENXIO);
424		}
425		ret = coretemp_read(ct, ctc, hdl);
426		cmi_hdl_rele(hdl);
427		if (ret != 0) {
428			mutex_exit(&ct->coretemp_mutex);
429			return (ret);
430		}
431	}
432
433	temp.sit_unit = SENSOR_UNIT_CELSIUS;
434	if ((id_t)minor == ctc->ctc_core_minor) {
435		temp.sit_temp = ctc->ctc_temperature;
436	} else {
437		temp.sit_temp = ctc->ctc_pkg_temperature;
438	}
439
440	/*
441	 * The resolution field is in whole units of degrees Celsius.
442	 */
443	temp.sit_gran = ctc->ctc_resolution;
444	if (ctc->ctc_resolution > 1) {
445		temp.sit_gran *= -1;
446	}
447	mutex_exit(&ct->coretemp_mutex);
448
449	if (ddi_copyout(&temp, (void *)arg, sizeof (temp),
450	    mode & FKIOCTL) != 0) {
451		return (EFAULT);
452	}
453
454	return (0);
455}
456
457static int
458coretemp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
459    int *rvalp)
460{
461	coretemp_t *ct = coretemp;
462
463	if ((mode & FREAD) == 0) {
464		return (EINVAL);
465	}
466
467	switch (cmd) {
468	case SENSOR_IOCTL_TYPE:
469		return (coretemp_ioctl_kind(arg, mode));
470	case SENSOR_IOCTL_TEMPERATURE:
471		return (coretemp_ioctl_temp(ct, getminor(dev), arg, mode));
472	default:
473		return (ENOTTY);
474	}
475}
476
477/*
478 * We don't really do any state tracking on close, so for now, just allow it to
479 * always succeed.
480 */
481static int
482coretemp_close(dev_t dev, int flags, int otype, cred_t *credp)
483{
484	return (0);
485}
486
487static void
488coretemp_fini_core(coretemp_t *ct, coretemp_core_t *ctc)
489{
490	if (ctc->ctc_core_minor > 0)
491		id_free(ct->coretemp_ids, ctc->ctc_core_minor);
492	if (ctc->ctc_pkg_minor > 0)
493		id_free(ct->coretemp_ids, ctc->ctc_pkg_minor);
494	kmem_free(ctc, sizeof (coretemp_core_t));
495}
496
497static void
498coretemp_destroy(coretemp_t *ct)
499{
500	coretemp_core_t *ctc;
501
502	ddi_remove_minor_node(ct->coretemp_dip, NULL);
503
504	while ((ctc = list_remove_head(&ct->coretemp_cores)) != NULL) {
505		coretemp_fini_core(ct, ctc);
506	}
507	list_destroy(&ct->coretemp_cores);
508
509	if (ct->coretemp_cpuset != NULL) {
510		cpuset_free(ct->coretemp_cpuset);
511	}
512
513	if (ct->coretemp_ids != NULL) {
514		id_space_destroy(ct->coretemp_ids);
515	}
516
517	mutex_destroy(&ct->coretemp_mutex);
518	kmem_free(ct, sizeof (coretemp_t));
519}
520
521static int
522coretemp_init_core(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3)
523{
524	coretemp_t *ct = arg1;
525	boolean_t *walkerr = arg2;
526	coretemp_core_t *ctc;
527	uint_t chip, core;
528	int err;
529
530	chip = cmi_hdl_chipid(hdl);
531	core = cmi_hdl_coreid(hdl);
532
533	/*
534	 * The temperature sensor only exists on a per-core basis. Therefore we
535	 * ignore any non-zero strand.
536	 */
537	if (cmi_hdl_strandid(hdl) != 0) {
538		return (CMI_HDL_WALK_NEXT);
539	}
540
541	ctc = kmem_zalloc(sizeof (coretemp_core_t), KM_SLEEP);
542	ctc->ctc_class = cmi_hdl_class(hdl);
543	ctc->ctc_chip = chip;
544	ctc->ctc_core = core;
545	ctc->ctc_strand = 0;
546	ctc->ctc_core_minor = id_alloc(ct->coretemp_ids);
547	if (ct->coretemp_pkg && ctc->ctc_core == 0) {
548		ctc->ctc_pkg_minor = id_alloc(ct->coretemp_ids);
549	}
550
551	if ((err = coretemp_calculate_tjmax(ct, ctc, hdl)) != 0) {
552		dev_err(ct->coretemp_dip, CE_WARN,
553		    "failed to read Tj Max on %u/%u: %d", chip, core, err);
554		*walkerr = B_TRUE;
555		coretemp_fini_core(ct, ctc);
556		return (CMI_HDL_WALK_DONE);
557	}
558
559	if ((err = coretemp_read(ct, ctc, hdl)) != 0) {
560		dev_err(ct->coretemp_dip, CE_WARN,
561		    "failed to take initial temperature reading on %u/%u: %d",
562		    chip, core, err);
563		*walkerr = B_TRUE;
564		coretemp_fini_core(ct, ctc);
565		return (CMI_HDL_WALK_DONE);
566	}
567
568	list_insert_tail(&ct->coretemp_cores, ctc);
569
570	return (CMI_HDL_WALK_NEXT);
571}
572
573static boolean_t
574coretemp_create_minors(coretemp_t *ct)
575{
576	coretemp_core_t *ctc;
577
578	for (ctc = list_head(&ct->coretemp_cores); ctc != NULL;
579	    ctc = list_next(&ct->coretemp_cores, ctc)) {
580		int ret;
581		char buf[128];
582
583		if (snprintf(buf, sizeof (buf), "chip%u.core%u", ctc->ctc_chip,
584		    ctc->ctc_core) >= sizeof (buf)) {
585			return (B_FALSE);
586		}
587		ret = ddi_create_minor_node(ct->coretemp_dip, buf, S_IFCHR,
588		    ctc->ctc_core_minor, DDI_NT_SENSOR_TEMP_CPU, 0);
589		if (ret != DDI_SUCCESS) {
590			dev_err(ct->coretemp_dip, CE_WARN, "!failed to create "
591			    "minor node %s", buf);
592			return (B_FALSE);
593		}
594
595		if (ctc->ctc_core != 0)
596			continue;
597
598		if (snprintf(buf, sizeof (buf), "chip%u", ctc->ctc_chip) >=
599		    sizeof (buf)) {
600			return (B_FALSE);
601		}
602
603		ret = ddi_create_minor_node(ct->coretemp_dip, buf, S_IFCHR,
604		    ctc->ctc_pkg_minor, DDI_NT_SENSOR_TEMP_CPU, 0);
605		if (ret != DDI_SUCCESS) {
606			dev_err(ct->coretemp_dip, CE_WARN, "!failed to create "
607			    "minor node %s", buf);
608			return (B_FALSE);
609		}
610	}
611
612	return (B_TRUE);
613}
614
615static int
616coretemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
617{
618	boolean_t walkerr;
619	coretemp_t *ct = NULL;
620
621	if (cmd == DDI_RESUME) {
622		/*
623		 * Currently suspend and resume for this driver are nops.
624		 */
625		return (DDI_SUCCESS);
626	}
627
628	if (cmd != DDI_ATTACH) {
629		return (DDI_FAILURE);
630	}
631
632	if (coretemp != NULL) {
633		return (DDI_FAILURE);
634	}
635
636	ct = kmem_zalloc(sizeof (coretemp_t), KM_SLEEP);
637	ct->coretemp_dip = dip;
638	ct->coretemp_pkg = is_x86_feature(x86_featureset, X86FSET_PKG_THERMAL);
639	list_create(&ct->coretemp_cores, sizeof (coretemp_core_t),
640	    offsetof(coretemp_core_t, ctc_link));
641	mutex_init(&ct->coretemp_mutex, NULL, MUTEX_DRIVER, NULL);
642	ct->coretemp_cpuset = cpuset_alloc(KM_SLEEP);
643	if ((ct->coretemp_ids = id_space_create("coretemp_minors", 1,
644	    INT32_MAX)) == NULL) {
645		goto fail;
646	}
647
648	mutex_enter(&ct->coretemp_mutex);
649	walkerr = B_FALSE;
650	cmi_hdl_walk(coretemp_init_core, ct, &walkerr, NULL);
651
652	if (walkerr) {
653		mutex_exit(&ct->coretemp_mutex);
654		goto fail;
655	}
656
657	if (!coretemp_create_minors(ct)) {
658		mutex_exit(&ct->coretemp_mutex);
659		goto fail;
660	}
661
662	coretemp = ct;
663	mutex_exit(&ct->coretemp_mutex);
664	return (DDI_SUCCESS);
665fail:
666	coretemp = NULL;
667	coretemp_destroy(ct);
668	return (DDI_FAILURE);
669
670}
671
672static int
673coretemp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
674    void **resultp)
675{
676	int ret;
677
678	switch (cmd) {
679	case DDI_INFO_DEVT2DEVINFO:
680		*resultp = coretemp->coretemp_dip;
681		ret = DDI_SUCCESS;
682		break;
683	case DDI_INFO_DEVT2INSTANCE:
684		*resultp = (void *)0;
685		ret = DDI_SUCCESS;
686		break;
687	default:
688		ret = DDI_FAILURE;
689		break;
690	}
691
692	return (ret);
693}
694
695static int
696coretemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
697{
698	coretemp_t *ct;
699
700	if (cmd == DDI_SUSPEND) {
701		return (DDI_SUCCESS);
702	}
703
704	if (cmd != DDI_DETACH) {
705		return (DDI_FAILURE);
706	}
707
708	if (coretemp == NULL) {
709		return (DDI_FAILURE);
710	}
711
712	ct = coretemp;
713	coretemp = NULL;
714	coretemp_destroy(ct);
715
716	return (DDI_SUCCESS);
717}
718
719static struct cb_ops coretemp_cb_ops = {
720	.cb_open = coretemp_open,
721	.cb_close = coretemp_close,
722	.cb_strategy = nodev,
723	.cb_print = nodev,
724	.cb_dump = nodev,
725	.cb_read = nodev,
726	.cb_write = nodev,
727	.cb_ioctl = coretemp_ioctl,
728	.cb_devmap = nodev,
729	.cb_mmap = nodev,
730	.cb_segmap = nodev,
731	.cb_chpoll = nochpoll,
732	.cb_prop_op = ddi_prop_op,
733	.cb_flag = D_MP,
734	.cb_rev = CB_REV,
735	.cb_aread = nodev,
736	.cb_awrite = nodev
737};
738
739static struct dev_ops coretemp_dev_ops = {
740	.devo_rev = DEVO_REV,
741	.devo_refcnt = 0,
742	.devo_getinfo = coretemp_getinfo,
743	.devo_identify = nulldev,
744	.devo_probe = nulldev,
745	.devo_attach = coretemp_attach,
746	.devo_detach = coretemp_detach,
747	.devo_reset = nodev,
748	.devo_power = ddi_power,
749	.devo_quiesce = ddi_quiesce_not_needed,
750	.devo_cb_ops = &coretemp_cb_ops
751};
752
753static struct modldrv coretemp_modldrv = {
754	.drv_modops = &mod_driverops,
755	.drv_linkinfo = "Intel CPU/Package thermal sensor",
756	.drv_dev_ops = &coretemp_dev_ops
757};
758
759static struct modlinkage coretemp_modlinkage = {
760	.ml_rev = MODREV_1,
761	.ml_linkage = { &coretemp_modldrv, NULL }
762};
763
764int
765_init(void)
766{
767	if (!coretemp_supported()) {
768		return (ENOTSUP);
769	}
770
771	return (mod_install(&coretemp_modlinkage));
772}
773
774int
775_info(struct modinfo *modinfop)
776{
777	return (mod_info(&coretemp_modlinkage, modinfop));
778}
779
780int
781_fini(void)
782{
783	return (mod_remove(&coretemp_modlinkage));
784}
785