xref: /illumos-gate/usr/src/uts/intel/io/pchtemp/pchtemp.c (revision bbf21555)
1dc90e123SRobert Mustacchi /*
2dc90e123SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3dc90e123SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4dc90e123SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5dc90e123SRobert Mustacchi  * 1.0 of the CDDL.
6dc90e123SRobert Mustacchi  *
7dc90e123SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8dc90e123SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9dc90e123SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10dc90e123SRobert Mustacchi  */
11dc90e123SRobert Mustacchi 
12dc90e123SRobert Mustacchi /*
13dc90e123SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
14c8be5715SRobert Mustacchi  * Copyright 2021 Oxide Computer Company
15dc90e123SRobert Mustacchi  */
16dc90e123SRobert Mustacchi 
17dc90e123SRobert Mustacchi /*
18dc90e123SRobert Mustacchi  * Intel Platform Controller Hub (PCH) Thermal Sensor Driver
19dc90e123SRobert Mustacchi  *
20dc90e123SRobert Mustacchi  * The Intel PCH is a chip that was introduced around the Nehalem generation
21dc90e123SRobert Mustacchi  * that provides many services for the broader system on a discrete chip from
22dc90e123SRobert Mustacchi  * the CPU. While it existed prior to the Nehalem generation, it was previously
23dc90e123SRobert Mustacchi  * two discrete chips called the Northbridge and Southbridge. Sometimes this
24dc90e123SRobert Mustacchi  * device is also called a 'chipset'.
25dc90e123SRobert Mustacchi  *
26dc90e123SRobert Mustacchi  * The PCH contains everything from a USB controller, to an AHCI controller, to
27dc90e123SRobert Mustacchi  * clocks, the Intel Management Engine, and more. Relevant to this driver is its
28dc90e123SRobert Mustacchi  * thermal sensor which gives us the ability to read the temperature sensor that
29dc90e123SRobert Mustacchi  * is embedded in the PCH.
30dc90e123SRobert Mustacchi  *
31dc90e123SRobert Mustacchi  * The format of this sensor varies based on the generation of the chipset. The
32dc90e123SRobert Mustacchi  * current driver supports the following chipsets organized by datasheet, which
33dc90e123SRobert Mustacchi  * corresponds with a change in format that was introduced in the Haswell
34dc90e123SRobert Mustacchi  * generation:
35dc90e123SRobert Mustacchi  *
36dc90e123SRobert Mustacchi  *  - Intel 8 Series PCH
37c8be5715SRobert Mustacchi  *  - Intel 9 Series and Broadwell Mobile Low Power PCH
38dc90e123SRobert Mustacchi  *  - Intel C610 Series and X99 PCH
39dc90e123SRobert Mustacchi  *  - Intel C620 Series PCH
40dc90e123SRobert Mustacchi  *  - Intel 100 Series PCH
41dc90e123SRobert Mustacchi  *  - Intel 200 Series and Z730 PCH
42dc90e123SRobert Mustacchi  *  - Intel Sunrise Point-LP (Kaby Lake-U) PCH
432c4824dbSRobert Mustacchi  *  - Intel Cannon Lake (Whiskey Lake-U) PCH
44dc90e123SRobert Mustacchi  *  - Intel 300 Series and C240 Chipset
45c8be5715SRobert Mustacchi  *  - Intel 400 Series and On-Package PCH
46dc90e123SRobert Mustacchi  *
47dc90e123SRobert Mustacchi  * The following chipsets use a different format and are not currently
48dc90e123SRobert Mustacchi  * supported:
49dc90e123SRobert Mustacchi  *
50dc90e123SRobert Mustacchi  *  - Intel 5 Series and Xeon 3400 PCH
51dc90e123SRobert Mustacchi  *  - Intel 6 Series PCH
52dc90e123SRobert Mustacchi  *  - Intel 7 Series PCH
53dc90e123SRobert Mustacchi  *  - Intel C600 Series and X79 PCH
54dc90e123SRobert Mustacchi  */
55dc90e123SRobert Mustacchi 
56dc90e123SRobert Mustacchi #include <sys/modctl.h>
57dc90e123SRobert Mustacchi #include <sys/conf.h>
58dc90e123SRobert Mustacchi #include <sys/devops.h>
59dc90e123SRobert Mustacchi #include <sys/types.h>
60dc90e123SRobert Mustacchi #include <sys/file.h>
61dc90e123SRobert Mustacchi #include <sys/open.h>
62dc90e123SRobert Mustacchi #include <sys/cred.h>
63dc90e123SRobert Mustacchi #include <sys/ddi.h>
64dc90e123SRobert Mustacchi #include <sys/sunddi.h>
65dc90e123SRobert Mustacchi #include <sys/cmn_err.h>
66dc90e123SRobert Mustacchi #include <sys/stat.h>
67dc90e123SRobert Mustacchi #include <sys/sensors.h>
68dc90e123SRobert Mustacchi 
69dc90e123SRobert Mustacchi /*
70dc90e123SRobert Mustacchi  * In all cases the data we care about is in the first PCI bar, bar 0. Per
71*bbf21555SRichard Lowe  * pci(5)/pcie(5), this is always going to be register number 1.
72dc90e123SRobert Mustacchi  */
73dc90e123SRobert Mustacchi #define	PCHTEMP_RNUMBER	1
74dc90e123SRobert Mustacchi 
75dc90e123SRobert Mustacchi /*
76dc90e123SRobert Mustacchi  * The PCH Temperature Sensor has a resolution of 1/2 a degree. This is a
77dc90e123SRobert Mustacchi  * resolution of 2 in our parlance. The register reads 50 C higher than it is.
78dc90e123SRobert Mustacchi  * Therefore our offset is 50 shifted over by one.
79dc90e123SRobert Mustacchi  */
80dc90e123SRobert Mustacchi #define	PCHTEMP_TEMP_RESOLUTION	2
81dc90e123SRobert Mustacchi #define	PCHTEMP_TEMP_OFFSET	(50 << 1)
82dc90e123SRobert Mustacchi 
83dc90e123SRobert Mustacchi /*
84dc90e123SRobert Mustacchi  * This register offset has the temperature that we want to read in the lower
85dc90e123SRobert Mustacchi  * 8-bits. The resolution and offset are described above.
86dc90e123SRobert Mustacchi  */
87dc90e123SRobert Mustacchi #define	PCHTEMP_REG_TEMP	0x00
88dc90e123SRobert Mustacchi #define	PCHTEMP_REG_TEMP_TSR	0x00ff
89dc90e123SRobert Mustacchi 
90dc90e123SRobert Mustacchi /*
91dc90e123SRobert Mustacchi  * Thermal Sensor Enable and Lock (TSEL) register. This register is a byte wide
92dc90e123SRobert Mustacchi  * and has two bits that we care about. The ETS bit, enable thermal sensor,
93dc90e123SRobert Mustacchi  * indicates whether or not the sensor is enabled. The control for this can be
94dc90e123SRobert Mustacchi  * locked which is the PLDB, Policy Lock-Down Bit, bit. Which restricts
95dc90e123SRobert Mustacchi  * additional control of this register.
96dc90e123SRobert Mustacchi  */
97dc90e123SRobert Mustacchi #define	PCHTEMP_REG_TSEL	0x08
98dc90e123SRobert Mustacchi #define	PCHTEMP_REG_TSEL_ETS	0x01
99dc90e123SRobert Mustacchi #define	PCHTEMP_REG_TSEL_PLDB	0x80
100dc90e123SRobert Mustacchi 
101dc90e123SRobert Mustacchi /*
102dc90e123SRobert Mustacchi  * Threshold registers for the thermal sensors. These indicate the catastrophic,
103dc90e123SRobert Mustacchi  * the high alert threshold, and the low alert threshold respectively.
104dc90e123SRobert Mustacchi  */
105dc90e123SRobert Mustacchi #define	PCHTEMP_REG_CTT		0x10
106dc90e123SRobert Mustacchi #define	PCHTEMP_REG_TAHV	0x14
107dc90e123SRobert Mustacchi #define	PCHTEMP_REG_TALV	0x18
108dc90e123SRobert Mustacchi 
109dc90e123SRobert Mustacchi typedef struct pchtemp {
110dc90e123SRobert Mustacchi 	dev_info_t		*pcht_dip;
111dc90e123SRobert Mustacchi 	int			pcht_fm_caps;
112dc90e123SRobert Mustacchi 	caddr_t			pcht_base;
113dc90e123SRobert Mustacchi 	ddi_acc_handle_t	pcht_handle;
11402674280SRobert Mustacchi 	id_t			pcht_ksensor;
115dc90e123SRobert Mustacchi 	kmutex_t		pcht_mutex;	/* Protects members below */
116dc90e123SRobert Mustacchi 	uint16_t		pcht_temp_raw;
117dc90e123SRobert Mustacchi 	uint8_t			pcht_tsel_raw;
118dc90e123SRobert Mustacchi 	uint16_t		pcht_ctt_raw;
119dc90e123SRobert Mustacchi 	uint16_t		pcht_tahv_raw;
120dc90e123SRobert Mustacchi 	uint16_t		pcht_talv_raw;
121dc90e123SRobert Mustacchi 	int64_t			pcht_temp;
122dc90e123SRobert Mustacchi } pchtemp_t;
123dc90e123SRobert Mustacchi 
124dc90e123SRobert Mustacchi void *pchtemp_state;
125dc90e123SRobert Mustacchi 
126dc90e123SRobert Mustacchi static int
pchtemp_read_check(pchtemp_t * pch)127dc90e123SRobert Mustacchi pchtemp_read_check(pchtemp_t *pch)
128dc90e123SRobert Mustacchi {
129dc90e123SRobert Mustacchi 	ddi_fm_error_t de;
130dc90e123SRobert Mustacchi 
131dc90e123SRobert Mustacchi 	if (!DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) {
132dc90e123SRobert Mustacchi 		return (DDI_FM_OK);
133dc90e123SRobert Mustacchi 	}
134dc90e123SRobert Mustacchi 
135dc90e123SRobert Mustacchi 	ddi_fm_acc_err_get(pch->pcht_handle, &de, DDI_FME_VERSION);
136dc90e123SRobert Mustacchi 	ddi_fm_acc_err_clear(pch->pcht_handle, DDI_FME_VERSION);
137dc90e123SRobert Mustacchi 	return (de.fme_status);
138dc90e123SRobert Mustacchi }
139dc90e123SRobert Mustacchi 
140dc90e123SRobert Mustacchi static int
pchtemp_read(void * arg,sensor_ioctl_scalar_t * scalar)1411045e13aSRobert Mustacchi pchtemp_read(void *arg, sensor_ioctl_scalar_t *scalar)
142dc90e123SRobert Mustacchi {
143dc90e123SRobert Mustacchi 	uint16_t temp, ctt, tahv, talv;
144dc90e123SRobert Mustacchi 	uint8_t tsel;
14502674280SRobert Mustacchi 	pchtemp_t *pch = arg;
146dc90e123SRobert Mustacchi 
14702674280SRobert Mustacchi 	mutex_enter(&pch->pcht_mutex);
148dc90e123SRobert Mustacchi 
149dc90e123SRobert Mustacchi 	temp = ddi_get16(pch->pcht_handle,
150dc90e123SRobert Mustacchi 	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TEMP));
151dc90e123SRobert Mustacchi 	tsel = ddi_get8(pch->pcht_handle,
152dc90e123SRobert Mustacchi 	    (uint8_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TSEL));
153dc90e123SRobert Mustacchi 	ctt = ddi_get16(pch->pcht_handle,
154dc90e123SRobert Mustacchi 	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_CTT));
155dc90e123SRobert Mustacchi 	tahv = ddi_get16(pch->pcht_handle,
156dc90e123SRobert Mustacchi 	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TAHV));
157dc90e123SRobert Mustacchi 	talv = ddi_get16(pch->pcht_handle,
158dc90e123SRobert Mustacchi 	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TALV));
159dc90e123SRobert Mustacchi 
160dc90e123SRobert Mustacchi 	if (pchtemp_read_check(pch) != DDI_FM_OK) {
16102674280SRobert Mustacchi 		mutex_exit(&pch->pcht_mutex);
162dc90e123SRobert Mustacchi 		dev_err(pch->pcht_dip, CE_WARN, "failed to read temperature "
163dc90e123SRobert Mustacchi 		    "data due to FM device error");
164dc90e123SRobert Mustacchi 		return (EIO);
165dc90e123SRobert Mustacchi 	}
166dc90e123SRobert Mustacchi 
167dc90e123SRobert Mustacchi 	pch->pcht_temp_raw = temp;
168dc90e123SRobert Mustacchi 	pch->pcht_tsel_raw = tsel;
169dc90e123SRobert Mustacchi 	pch->pcht_ctt_raw = ctt;
170dc90e123SRobert Mustacchi 	pch->pcht_tahv_raw = tahv;
171dc90e123SRobert Mustacchi 	pch->pcht_talv_raw = talv;
172dc90e123SRobert Mustacchi 
173dc90e123SRobert Mustacchi 	if ((tsel & PCHTEMP_REG_TSEL_ETS) == 0) {
17402674280SRobert Mustacchi 		mutex_exit(&pch->pcht_mutex);
175dc90e123SRobert Mustacchi 		return (ENXIO);
176dc90e123SRobert Mustacchi 	}
177dc90e123SRobert Mustacchi 
178dc90e123SRobert Mustacchi 	pch->pcht_temp = (temp & PCHTEMP_REG_TEMP_TSR) - PCHTEMP_TEMP_OFFSET;
1791045e13aSRobert Mustacchi 	scalar->sis_unit = SENSOR_UNIT_CELSIUS;
1801045e13aSRobert Mustacchi 	scalar->sis_gran = PCHTEMP_TEMP_RESOLUTION;
1811045e13aSRobert Mustacchi 	scalar->sis_value = pch->pcht_temp;
182dc90e123SRobert Mustacchi 	mutex_exit(&pch->pcht_mutex);
183dc90e123SRobert Mustacchi 
184dc90e123SRobert Mustacchi 	return (0);
185dc90e123SRobert Mustacchi }
186dc90e123SRobert Mustacchi 
18702674280SRobert Mustacchi static const ksensor_ops_t pchtemp_temp_ops = {
18802674280SRobert Mustacchi 	.kso_kind = ksensor_kind_temperature,
1891045e13aSRobert Mustacchi 	.kso_scalar = pchtemp_read
19002674280SRobert Mustacchi };
191dc90e123SRobert Mustacchi 
192dc90e123SRobert Mustacchi static void
pchtemp_cleanup(pchtemp_t * pch)193dc90e123SRobert Mustacchi pchtemp_cleanup(pchtemp_t *pch)
194dc90e123SRobert Mustacchi {
195dc90e123SRobert Mustacchi 	int inst;
196dc90e123SRobert Mustacchi 
197dc90e123SRobert Mustacchi 	ASSERT3P(pch->pcht_dip, !=, NULL);
198dc90e123SRobert Mustacchi 	inst = ddi_get_instance(pch->pcht_dip);
199dc90e123SRobert Mustacchi 
20002674280SRobert Mustacchi 	(void) ksensor_remove(pch->pcht_dip, KSENSOR_ALL_IDS);
201dc90e123SRobert Mustacchi 
202dc90e123SRobert Mustacchi 	if (pch->pcht_handle != NULL) {
203dc90e123SRobert Mustacchi 		ddi_regs_map_free(&pch->pcht_handle);
204dc90e123SRobert Mustacchi 	}
205dc90e123SRobert Mustacchi 
206dc90e123SRobert Mustacchi 	if (pch->pcht_fm_caps != DDI_FM_NOT_CAPABLE) {
207dc90e123SRobert Mustacchi 		ddi_fm_fini(pch->pcht_dip);
208dc90e123SRobert Mustacchi 	}
209dc90e123SRobert Mustacchi 
210dc90e123SRobert Mustacchi 	mutex_destroy(&pch->pcht_mutex);
211dc90e123SRobert Mustacchi 	ddi_soft_state_free(pchtemp_state, inst);
212dc90e123SRobert Mustacchi }
213dc90e123SRobert Mustacchi 
214dc90e123SRobert Mustacchi static int
pchtemp_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)215dc90e123SRobert Mustacchi pchtemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
216dc90e123SRobert Mustacchi {
217dc90e123SRobert Mustacchi 	int inst, ret;
218dc90e123SRobert Mustacchi 	pchtemp_t *pch;
219dc90e123SRobert Mustacchi 	off_t memsize;
220dc90e123SRobert Mustacchi 	ddi_device_acc_attr_t da;
221dc90e123SRobert Mustacchi 	ddi_iblock_cookie_t iblk;
222dc90e123SRobert Mustacchi 	char name[1024];
223dc90e123SRobert Mustacchi 
224dc90e123SRobert Mustacchi 	switch (cmd) {
225dc90e123SRobert Mustacchi 	case DDI_RESUME:
226dc90e123SRobert Mustacchi 		return (DDI_SUCCESS);
227dc90e123SRobert Mustacchi 	case DDI_ATTACH:
228dc90e123SRobert Mustacchi 		break;
229dc90e123SRobert Mustacchi 	default:
230dc90e123SRobert Mustacchi 		return (DDI_FAILURE);
231dc90e123SRobert Mustacchi 	}
232dc90e123SRobert Mustacchi 
233dc90e123SRobert Mustacchi 	inst = ddi_get_instance(dip);
234dc90e123SRobert Mustacchi 	if (ddi_soft_state_zalloc(pchtemp_state, inst) != DDI_SUCCESS) {
235dc90e123SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to allocate soft state entry %d",
236dc90e123SRobert Mustacchi 		    inst);
237dc90e123SRobert Mustacchi 		return (DDI_FAILURE);
238dc90e123SRobert Mustacchi 	}
239dc90e123SRobert Mustacchi 
240dc90e123SRobert Mustacchi 	pch = ddi_get_soft_state(pchtemp_state, inst);
241dc90e123SRobert Mustacchi 	if (pch == NULL) {
242dc90e123SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to retrieve soft state entry %d",
243dc90e123SRobert Mustacchi 		    inst);
244dc90e123SRobert Mustacchi 		return (DDI_FAILURE);
245dc90e123SRobert Mustacchi 	}
246dc90e123SRobert Mustacchi 	pch->pcht_dip = dip;
247dc90e123SRobert Mustacchi 
248dc90e123SRobert Mustacchi 	pch->pcht_fm_caps = DDI_FM_ACCCHK_CAPABLE;
249dc90e123SRobert Mustacchi 	ddi_fm_init(dip, &pch->pcht_fm_caps, &iblk);
250dc90e123SRobert Mustacchi 
251dc90e123SRobert Mustacchi 	mutex_init(&pch->pcht_mutex, NULL, MUTEX_DRIVER, NULL);
252dc90e123SRobert Mustacchi 
253dc90e123SRobert Mustacchi 	if (ddi_dev_regsize(dip, PCHTEMP_RNUMBER, &memsize) != DDI_SUCCESS) {
254dc90e123SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to obtain register size for "
255dc90e123SRobert Mustacchi 		    "register set %d", PCHTEMP_RNUMBER);
256dc90e123SRobert Mustacchi 		goto err;
257dc90e123SRobert Mustacchi 	}
258dc90e123SRobert Mustacchi 
259dc90e123SRobert Mustacchi 	bzero(&da, sizeof (ddi_device_acc_attr_t));
260dc90e123SRobert Mustacchi 	da.devacc_attr_version = DDI_DEVICE_ATTR_V0;
261dc90e123SRobert Mustacchi 	da.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
262dc90e123SRobert Mustacchi 	da.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
263dc90e123SRobert Mustacchi 
264dc90e123SRobert Mustacchi 	if (DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) {
265dc90e123SRobert Mustacchi 		da.devacc_attr_access = DDI_FLAGERR_ACC;
266dc90e123SRobert Mustacchi 	} else {
267dc90e123SRobert Mustacchi 		da.devacc_attr_access = DDI_DEFAULT_ACC;
268dc90e123SRobert Mustacchi 	}
269dc90e123SRobert Mustacchi 
270dc90e123SRobert Mustacchi 	if ((ret = ddi_regs_map_setup(dip, PCHTEMP_RNUMBER, &pch->pcht_base,
271dc90e123SRobert Mustacchi 	    0, memsize, &da, &pch->pcht_handle)) != DDI_SUCCESS) {
272dc90e123SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to map register set %d: %d",
273dc90e123SRobert Mustacchi 		    PCHTEMP_RNUMBER, ret);
274dc90e123SRobert Mustacchi 		goto err;
275dc90e123SRobert Mustacchi 	}
276dc90e123SRobert Mustacchi 
277dc90e123SRobert Mustacchi 	if (snprintf(name, sizeof (name), "ts.%d", inst) >= sizeof (name)) {
278dc90e123SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to construct minor node name, "
279dc90e123SRobert Mustacchi 		    "name too long");
280dc90e123SRobert Mustacchi 		goto err;
281dc90e123SRobert Mustacchi 	}
282dc90e123SRobert Mustacchi 
28302674280SRobert Mustacchi 	if ((ret = ksensor_create(pch->pcht_dip, &pchtemp_temp_ops, pch, name,
28402674280SRobert Mustacchi 	    DDI_NT_SENSOR_TEMP_PCH, &pch->pcht_ksensor)) != 0) {
285dc90e123SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to create minor node %s", name);
286dc90e123SRobert Mustacchi 		goto err;
287dc90e123SRobert Mustacchi 	}
288dc90e123SRobert Mustacchi 
289dc90e123SRobert Mustacchi 	return (DDI_SUCCESS);
290dc90e123SRobert Mustacchi 
291dc90e123SRobert Mustacchi err:
292dc90e123SRobert Mustacchi 	pchtemp_cleanup(pch);
293dc90e123SRobert Mustacchi 	return (DDI_FAILURE);
294dc90e123SRobert Mustacchi }
295dc90e123SRobert Mustacchi 
296dc90e123SRobert Mustacchi static int
pchtemp_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)297dc90e123SRobert Mustacchi pchtemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
298dc90e123SRobert Mustacchi {
299dc90e123SRobert Mustacchi 	int inst;
300dc90e123SRobert Mustacchi 	pchtemp_t *pch;
301dc90e123SRobert Mustacchi 
302dc90e123SRobert Mustacchi 	switch (cmd) {
303dc90e123SRobert Mustacchi 	case DDI_DETACH:
304dc90e123SRobert Mustacchi 		break;
305dc90e123SRobert Mustacchi 	case DDI_SUSPEND:
306dc90e123SRobert Mustacchi 		return (DDI_SUCCESS);
307dc90e123SRobert Mustacchi 	default:
308dc90e123SRobert Mustacchi 		return (DDI_FAILURE);
309dc90e123SRobert Mustacchi 	}
310dc90e123SRobert Mustacchi 
311dc90e123SRobert Mustacchi 	inst = ddi_get_instance(dip);
312dc90e123SRobert Mustacchi 	pch = ddi_get_soft_state(pchtemp_state, inst);
313dc90e123SRobert Mustacchi 	if (pch == NULL) {
314dc90e123SRobert Mustacchi 		dev_err(dip, CE_WARN, "asked to detached instance %d, but "
315dc90e123SRobert Mustacchi 		    "it does not exist in soft state", inst);
316dc90e123SRobert Mustacchi 		return (DDI_FAILURE);
317dc90e123SRobert Mustacchi 	}
318dc90e123SRobert Mustacchi 
319dc90e123SRobert Mustacchi 	pchtemp_cleanup(pch);
320dc90e123SRobert Mustacchi 	return (DDI_SUCCESS);
321dc90e123SRobert Mustacchi }
322dc90e123SRobert Mustacchi 
323dc90e123SRobert Mustacchi static struct dev_ops pchtemp_dev_ops = {
324dc90e123SRobert Mustacchi 	.devo_rev = DEVO_REV,
325dc90e123SRobert Mustacchi 	.devo_refcnt = 0,
32602674280SRobert Mustacchi 	.devo_getinfo = nodev,
327dc90e123SRobert Mustacchi 	.devo_identify = nulldev,
328dc90e123SRobert Mustacchi 	.devo_probe = nulldev,
329dc90e123SRobert Mustacchi 	.devo_attach = pchtemp_attach,
330dc90e123SRobert Mustacchi 	.devo_detach = pchtemp_detach,
331dc90e123SRobert Mustacchi 	.devo_reset = nodev,
33202674280SRobert Mustacchi 	.devo_quiesce = ddi_quiesce_not_needed
333dc90e123SRobert Mustacchi };
334dc90e123SRobert Mustacchi 
335dc90e123SRobert Mustacchi static struct modldrv pchtemp_modldrv = {
336dc90e123SRobert Mustacchi 	.drv_modops = &mod_driverops,
337dc90e123SRobert Mustacchi 	.drv_linkinfo = "Intel PCH Thermal Sensor",
338dc90e123SRobert Mustacchi 	.drv_dev_ops = &pchtemp_dev_ops
339dc90e123SRobert Mustacchi };
340dc90e123SRobert Mustacchi 
341dc90e123SRobert Mustacchi static struct modlinkage pchtemp_modlinkage = {
342dc90e123SRobert Mustacchi 	.ml_rev = MODREV_1,
343dc90e123SRobert Mustacchi 	.ml_linkage = { &pchtemp_modldrv, NULL }
344dc90e123SRobert Mustacchi };
345dc90e123SRobert Mustacchi 
346dc90e123SRobert Mustacchi int
_init(void)347dc90e123SRobert Mustacchi _init(void)
348dc90e123SRobert Mustacchi {
349dc90e123SRobert Mustacchi 	int ret;
350dc90e123SRobert Mustacchi 
351dc90e123SRobert Mustacchi 	if (ddi_soft_state_init(&pchtemp_state, sizeof (pchtemp_t), 1) !=
352dc90e123SRobert Mustacchi 	    DDI_SUCCESS) {
353dc90e123SRobert Mustacchi 		return (ENOMEM);
354dc90e123SRobert Mustacchi 	}
355dc90e123SRobert Mustacchi 
356dc90e123SRobert Mustacchi 	if ((ret = mod_install(&pchtemp_modlinkage)) != 0) {
357dc90e123SRobert Mustacchi 		ddi_soft_state_fini(&pchtemp_state);
358dc90e123SRobert Mustacchi 		return (ret);
359dc90e123SRobert Mustacchi 	}
360dc90e123SRobert Mustacchi 
361dc90e123SRobert Mustacchi 	return (ret);
362dc90e123SRobert Mustacchi }
363dc90e123SRobert Mustacchi 
364dc90e123SRobert Mustacchi int
_info(struct modinfo * modinfop)365dc90e123SRobert Mustacchi _info(struct modinfo *modinfop)
366dc90e123SRobert Mustacchi {
367dc90e123SRobert Mustacchi 	return (mod_info(&pchtemp_modlinkage, modinfop));
368dc90e123SRobert Mustacchi }
369dc90e123SRobert Mustacchi 
370dc90e123SRobert Mustacchi int
_fini(void)371dc90e123SRobert Mustacchi _fini(void)
372dc90e123SRobert Mustacchi {
373dc90e123SRobert Mustacchi 	int ret;
374dc90e123SRobert Mustacchi 
375dc90e123SRobert Mustacchi 	if ((ret = mod_remove(&pchtemp_modlinkage)) != 0) {
376dc90e123SRobert Mustacchi 		return (ret);
377dc90e123SRobert Mustacchi 	}
378dc90e123SRobert Mustacchi 
379dc90e123SRobert Mustacchi 	ddi_soft_state_fini(&pchtemp_state);
380dc90e123SRobert Mustacchi 	return (ret);
381dc90e123SRobert Mustacchi }
382