1dc90e12Robert Mustacchi/*
2dc90e12Robert Mustacchi * This file and its contents are supplied under the terms of the
3dc90e12Robert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4dc90e12Robert Mustacchi * You may only use this file in accordance with the terms of version
5dc90e12Robert Mustacchi * 1.0 of the CDDL.
6dc90e12Robert Mustacchi *
7dc90e12Robert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8dc90e12Robert Mustacchi * source.  A copy of the CDDL is also available via the Internet at
9dc90e12Robert Mustacchi * http://www.illumos.org/license/CDDL.
10dc90e12Robert Mustacchi */
11dc90e12Robert Mustacchi
12dc90e12Robert Mustacchi/*
13dc90e12Robert Mustacchi * Copyright 2019 Joyent, Inc.
14dc90e12Robert Mustacchi */
15dc90e12Robert Mustacchi
16dc90e12Robert Mustacchi/*
17dc90e12Robert Mustacchi * Intel Platform Controller Hub (PCH) Thermal Sensor Driver
18dc90e12Robert Mustacchi *
19dc90e12Robert Mustacchi * The Intel PCH is a chip that was introduced around the Nehalem generation
20dc90e12Robert Mustacchi * that provides many services for the broader system on a discrete chip from
21dc90e12Robert Mustacchi * the CPU. While it existed prior to the Nehalem generation, it was previously
22dc90e12Robert Mustacchi * two discrete chips called the Northbridge and Southbridge. Sometimes this
23dc90e12Robert Mustacchi * device is also called a 'chipset'.
24dc90e12Robert Mustacchi *
25dc90e12Robert Mustacchi * The PCH contains everything from a USB controller, to an AHCI controller, to
26dc90e12Robert Mustacchi * clocks, the Intel Management Engine, and more. Relevant to this driver is its
27dc90e12Robert Mustacchi * thermal sensor which gives us the ability to read the temperature sensor that
28dc90e12Robert Mustacchi * is embedded in the PCH.
29dc90e12Robert Mustacchi *
30dc90e12Robert Mustacchi * The format of this sensor varies based on the generation of the chipset. The
31dc90e12Robert Mustacchi * current driver supports the following chipsets organized by datasheet, which
32dc90e12Robert Mustacchi * corresponds with a change in format that was introduced in the Haswell
33dc90e12Robert Mustacchi * generation:
34dc90e12Robert Mustacchi *
35dc90e12Robert Mustacchi *  - Intel 8 Series PCH
36dc90e12Robert Mustacchi *  - Intel 9 Series PCH
37dc90e12Robert Mustacchi *  - Intel C610 Series and X99 PCH
38dc90e12Robert Mustacchi *  - Intel C620 Series PCH
39dc90e12Robert Mustacchi *  - Intel 100 Series PCH
40dc90e12Robert Mustacchi *  - Intel 200 Series and Z730 PCH
41dc90e12Robert Mustacchi *  - Intel Sunrise Point-LP (Kaby Lake-U) PCH
42dc90e12Robert Mustacchi *  - Intel 300 Series and C240 Chipset
43dc90e12Robert Mustacchi *
44dc90e12Robert Mustacchi * The following chipsets use a different format and are not currently
45dc90e12Robert Mustacchi * supported:
46dc90e12Robert Mustacchi *
47dc90e12Robert Mustacchi *  - Intel 5 Series and Xeon 3400 PCH
48dc90e12Robert Mustacchi *  - Intel 6 Series PCH
49dc90e12Robert Mustacchi *  - Intel 7 Series PCH
50dc90e12Robert Mustacchi *  - Intel C600 Series and X79 PCH
51dc90e12Robert Mustacchi */
52dc90e12Robert Mustacchi
53dc90e12Robert Mustacchi#include <sys/modctl.h>
54dc90e12Robert Mustacchi#include <sys/conf.h>
55dc90e12Robert Mustacchi#include <sys/devops.h>
56dc90e12Robert Mustacchi#include <sys/types.h>
57dc90e12Robert Mustacchi#include <sys/file.h>
58dc90e12Robert Mustacchi#include <sys/open.h>
59dc90e12Robert Mustacchi#include <sys/cred.h>
60dc90e12Robert Mustacchi#include <sys/ddi.h>
61dc90e12Robert Mustacchi#include <sys/sunddi.h>
62dc90e12Robert Mustacchi#include <sys/cmn_err.h>
63dc90e12Robert Mustacchi#include <sys/stat.h>
64dc90e12Robert Mustacchi#include <sys/sensors.h>
65dc90e12Robert Mustacchi
66dc90e12Robert Mustacchi/*
67dc90e12Robert Mustacchi * In all cases the data we care about is in the first PCI bar, bar 0. Per
68dc90e12Robert Mustacchi * pci(4)/pcie(4), this is always going to be register number 1.
69dc90e12Robert Mustacchi */
70dc90e12Robert Mustacchi#define	PCHTEMP_RNUMBER	1
71dc90e12Robert Mustacchi
72dc90e12Robert Mustacchi/*
73dc90e12Robert Mustacchi * The PCH Temperature Sensor has a resolution of 1/2 a degree. This is a
74dc90e12Robert Mustacchi * resolution of 2 in our parlance. The register reads 50 C higher than it is.
75dc90e12Robert Mustacchi * Therefore our offset is 50 shifted over by one.
76dc90e12Robert Mustacchi */
77dc90e12Robert Mustacchi#define	PCHTEMP_TEMP_RESOLUTION	2
78dc90e12Robert Mustacchi#define	PCHTEMP_TEMP_OFFSET	(50 << 1)
79dc90e12Robert Mustacchi
80dc90e12Robert Mustacchi/*
81dc90e12Robert Mustacchi * This register offset has the temperature that we want to read in the lower
82dc90e12Robert Mustacchi * 8-bits. The resolution and offset are described above.
83dc90e12Robert Mustacchi */
84dc90e12Robert Mustacchi#define	PCHTEMP_REG_TEMP	0x00
85dc90e12Robert Mustacchi#define	PCHTEMP_REG_TEMP_TSR	0x00ff
86dc90e12Robert Mustacchi
87dc90e12Robert Mustacchi/*
88dc90e12Robert Mustacchi * Thermal Sensor Enable and Lock (TSEL) register. This register is a byte wide
89dc90e12Robert Mustacchi * and has two bits that we care about. The ETS bit, enable thermal sensor,
90dc90e12Robert Mustacchi * indicates whether or not the sensor is enabled. The control for this can be
91dc90e12Robert Mustacchi * locked which is the PLDB, Policy Lock-Down Bit, bit. Which restricts
92dc90e12Robert Mustacchi * additional control of this register.
93dc90e12Robert Mustacchi */
94dc90e12Robert Mustacchi#define	PCHTEMP_REG_TSEL	0x08
95dc90e12Robert Mustacchi#define	PCHTEMP_REG_TSEL_ETS	0x01
96dc90e12Robert Mustacchi#define	PCHTEMP_REG_TSEL_PLDB	0x80
97dc90e12Robert Mustacchi
98dc90e12Robert Mustacchi/*
99dc90e12Robert Mustacchi * Threshold registers for the thermal sensors. These indicate the catastrophic,
100dc90e12Robert Mustacchi * the high alert threshold, and the low alert threshold respectively.
101dc90e12Robert Mustacchi */
102dc90e12Robert Mustacchi#define	PCHTEMP_REG_CTT		0x10
103dc90e12Robert Mustacchi#define	PCHTEMP_REG_TAHV	0x14
104dc90e12Robert Mustacchi#define	PCHTEMP_REG_TALV	0x18
105dc90e12Robert Mustacchi
106dc90e12Robert Mustacchitypedef struct pchtemp {
107dc90e12Robert Mustacchi	dev_info_t		*pcht_dip;
108dc90e12Robert Mustacchi	int			pcht_fm_caps;
109dc90e12Robert Mustacchi	caddr_t			pcht_base;
110dc90e12Robert Mustacchi	ddi_acc_handle_t	pcht_handle;
111dc90e12Robert Mustacchi	kmutex_t		pcht_mutex;	/* Protects members below */
112dc90e12Robert Mustacchi	uint16_t		pcht_temp_raw;
113dc90e12Robert Mustacchi	uint8_t			pcht_tsel_raw;
114dc90e12Robert Mustacchi	uint16_t		pcht_ctt_raw;
115dc90e12Robert Mustacchi	uint16_t		pcht_tahv_raw;
116dc90e12Robert Mustacchi	uint16_t		pcht_talv_raw;
117dc90e12Robert Mustacchi	int64_t			pcht_temp;
118dc90e12Robert Mustacchi} pchtemp_t;
119dc90e12Robert Mustacchi
120dc90e12Robert Mustacchivoid *pchtemp_state;
121dc90e12Robert Mustacchi
122dc90e12Robert Mustacchistatic pchtemp_t *
123dc90e12Robert Mustacchipchtemp_find_by_dev(dev_t dev)
124dc90e12Robert Mustacchi{
125dc90e12Robert Mustacchi	return (ddi_get_soft_state(pchtemp_state, getminor(dev)));
126dc90e12Robert Mustacchi}
127dc90e12Robert Mustacchi
128dc90e12Robert Mustacchistatic int
129dc90e12Robert Mustacchipchtemp_read_check(pchtemp_t *pch)
130dc90e12Robert Mustacchi{
131dc90e12Robert Mustacchi	ddi_fm_error_t de;
132dc90e12Robert Mustacchi
133dc90e12Robert Mustacchi	if (!DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) {
134dc90e12Robert Mustacchi		return (DDI_FM_OK);
135dc90e12Robert Mustacchi	}
136dc90e12Robert Mustacchi
137dc90e12Robert Mustacchi	ddi_fm_acc_err_get(pch->pcht_handle, &de, DDI_FME_VERSION);
138dc90e12Robert Mustacchi	ddi_fm_acc_err_clear(pch->pcht_handle, DDI_FME_VERSION);
139dc90e12Robert Mustacchi	return (de.fme_status);
140dc90e12Robert Mustacchi}
141dc90e12Robert Mustacchi
142dc90e12Robert Mustacchistatic int
143dc90e12Robert Mustacchipchtemp_read(pchtemp_t *pch)
144dc90e12Robert Mustacchi{
145dc90e12Robert Mustacchi	uint16_t temp, ctt, tahv, talv;
146dc90e12Robert Mustacchi	uint8_t tsel;
147dc90e12Robert Mustacchi
148dc90e12Robert Mustacchi	ASSERT(MUTEX_HELD(&pch->pcht_mutex));
149dc90e12Robert Mustacchi
150dc90e12Robert Mustacchi	temp = ddi_get16(pch->pcht_handle,
151dc90e12Robert Mustacchi	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TEMP));
152dc90e12Robert Mustacchi	tsel = ddi_get8(pch->pcht_handle,
153dc90e12Robert Mustacchi	    (uint8_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TSEL));
154dc90e12Robert Mustacchi	ctt = ddi_get16(pch->pcht_handle,
155dc90e12Robert Mustacchi	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_CTT));
156dc90e12Robert Mustacchi	tahv = ddi_get16(pch->pcht_handle,
157dc90e12Robert Mustacchi	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TAHV));
158dc90e12Robert Mustacchi	talv = ddi_get16(pch->pcht_handle,
159dc90e12Robert Mustacchi	    (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TALV));
160dc90e12Robert Mustacchi
161dc90e12Robert Mustacchi	if (pchtemp_read_check(pch) != DDI_FM_OK) {
162dc90e12Robert Mustacchi		dev_err(pch->pcht_dip, CE_WARN, "failed to read temperature "
163dc90e12Robert Mustacchi		    "data due to FM device error");
164dc90e12Robert Mustacchi		return (EIO);
165dc90e12Robert Mustacchi	}
166dc90e12Robert Mustacchi
167dc90e12Robert Mustacchi	pch->pcht_temp_raw = temp;
168dc90e12Robert Mustacchi	pch->pcht_tsel_raw = tsel;
169dc90e12Robert Mustacchi	pch->pcht_ctt_raw = ctt;
170dc90e12Robert Mustacchi	pch->pcht_tahv_raw = tahv;
171dc90e12Robert Mustacchi	pch->pcht_talv_raw = talv;
172dc90e12Robert Mustacchi
173dc90e12Robert Mustacchi	if ((tsel & PCHTEMP_REG_TSEL_ETS) == 0) {
174dc90e12Robert Mustacchi		return (ENXIO);
175dc90e12Robert Mustacchi	}
176dc90e12Robert Mustacchi
177dc90e12Robert Mustacchi	pch->pcht_temp = (temp & PCHTEMP_REG_TEMP_TSR) - PCHTEMP_TEMP_OFFSET;
178dc90e12Robert Mustacchi
179dc90e12Robert Mustacchi	return (0);
180dc90e12Robert Mustacchi}
181dc90e12Robert Mustacchi
182dc90e12Robert Mustacchistatic int
183dc90e12Robert Mustacchipchtemp_open(dev_t *devp, int flags, int otype, cred_t *credp)
184dc90e12Robert Mustacchi{
185dc90e12Robert Mustacchi	pchtemp_t *pch;
186dc90e12Robert Mustacchi
187dc90e12Robert Mustacchi	if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp)) {
188dc90e12Robert Mustacchi		return (EPERM);
189dc90e12Robert Mustacchi	}
190dc90e12Robert Mustacchi
191dc90e12Robert Mustacchi	if ((flags & (FEXCL | FNDELAY | FWRITE)) != 0) {
192dc90e12Robert Mustacchi		return (EINVAL);
193dc90e12Robert Mustacchi	}
194dc90e12Robert Mustacchi
195dc90e12Robert Mustacchi	if (otype != OTYP_CHR) {
196dc90e12Robert Mustacchi		return (EINVAL);
197dc90e12Robert Mustacchi	}
198dc90e12Robert Mustacchi
199dc90e12Robert Mustacchi	pch = pchtemp_find_by_dev(*devp);
200dc90e12Robert Mustacchi	if (pch == NULL) {
201dc90e12Robert Mustacchi		return (ENXIO);
202dc90e12Robert Mustacchi	}
203dc90e12Robert Mustacchi
204dc90e12Robert Mustacchi	return (0);
205dc90e12Robert Mustacchi}
206dc90e12Robert Mustacchi
207dc90e12Robert Mustacchistatic int
208dc90e12Robert Mustacchipchtemp_ioctl_kind(intptr_t arg, int mode)
209dc90e12Robert Mustacchi{
210dc90e12Robert Mustacchi	sensor_ioctl_kind_t kind;
211dc90e12Robert Mustacchi
212dc90e12Robert Mustacchi	bzero(&kind, sizeof (sensor_ioctl_kind_t));
213dc90e12Robert Mustacchi	kind.sik_kind = SENSOR_KIND_TEMPERATURE;
214dc90e12Robert Mustacchi
215dc90e12Robert Mustacchi	if (ddi_copyout((void *)&kind, (void *)arg, sizeof (kind),
216dc90e12Robert Mustacchi	    mode & FKIOCTL) != 0) {
217dc90e12Robert Mustacchi		return (EFAULT);
218dc90e12Robert Mustacchi	}
219dc90e12Robert Mustacchi
220dc90e12Robert Mustacchi	return (0);
221dc90e12Robert Mustacchi}
222dc90e12Robert Mustacchi
223dc90e12Robert Mustacchistatic int
224dc90e12Robert Mustacchipchtemp_ioctl_temp(pchtemp_t *pch, intptr_t arg, int mode)
225dc90e12Robert Mustacchi{
226dc90e12Robert Mustacchi	int ret;
227dc90e12Robert Mustacchi	sensor_ioctl_temperature_t temp;
228dc90e12Robert Mustacchi
229dc90e12Robert Mustacchi	bzero(&temp, sizeof (temp));
230dc90e12Robert Mustacchi
231dc90e12Robert Mustacchi	mutex_enter(&pch->pcht_mutex);
232dc90e12Robert Mustacchi	if ((ret = pchtemp_read(pch)) != 0) {
233dc90e12Robert Mustacchi		mutex_exit(&pch->pcht_mutex);
234dc90e12Robert Mustacchi		return (ret);
235dc90e12Robert Mustacchi	}
236dc90e12Robert Mustacchi
237dc90e12Robert Mustacchi	temp.sit_unit = SENSOR_UNIT_CELSIUS;
238dc90e12Robert Mustacchi	temp.sit_gran = PCHTEMP_TEMP_RESOLUTION;
239dc90e12Robert Mustacchi	temp.sit_temp = pch->pcht_temp;
240dc90e12Robert Mustacchi	mutex_exit(&pch->pcht_mutex);
241dc90e12Robert Mustacchi
242dc90e12Robert Mustacchi	if (ddi_copyout(&temp, (void *)arg, sizeof (temp),
243dc90e12Robert Mustacchi	    mode & FKIOCTL) != 0) {
244dc90e12Robert Mustacchi		return (EFAULT);
245dc90e12Robert Mustacchi	}
246dc90e12Robert Mustacchi
247dc90e12Robert Mustacchi	return (0);
248dc90e12Robert Mustacchi}
249dc90e12Robert Mustacchi
250dc90e12Robert Mustacchistatic int
251dc90e12Robert Mustacchipchtemp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
252dc90e12Robert Mustacchi    int *rvalp)
253dc90e12Robert Mustacchi{
254dc90e12Robert Mustacchi	pchtemp_t *pch;
255dc90e12Robert Mustacchi
256dc90e12Robert Mustacchi	pch = pchtemp_find_by_dev(dev);
257dc90e12Robert Mustacchi	if (pch == NULL) {
258dc90e12Robert Mustacchi		return (ENXIO);
259dc90e12Robert Mustacchi	}
260dc90e12Robert Mustacchi
261dc90e12Robert Mustacchi	if ((mode & FREAD) == 0) {
262dc90e12Robert Mustacchi		return (EINVAL);
263dc90e12Robert Mustacchi	}
264dc90e12Robert Mustacchi
265dc90e12Robert Mustacchi	switch (cmd) {
266dc90e12Robert Mustacchi	case SENSOR_IOCTL_TYPE:
267dc90e12Robert Mustacchi		return (pchtemp_ioctl_kind(arg, mode));
268dc90e12Robert Mustacchi	case SENSOR_IOCTL_TEMPERATURE:
269dc90e12Robert Mustacchi		return (pchtemp_ioctl_temp(pch, arg, mode));
270dc90e12Robert Mustacchi	default:
271dc90e12Robert Mustacchi		return (ENOTTY);
272dc90e12Robert Mustacchi	}
273dc90e12Robert Mustacchi}
274dc90e12Robert Mustacchi
275dc90e12Robert Mustacchistatic int
276dc90e12Robert Mustacchipchtemp_close(dev_t dev, int flags, int otype, cred_t *credp)
277dc90e12Robert Mustacchi{
278dc90e12Robert Mustacchi	return (0);
279dc90e12Robert Mustacchi}
280dc90e12Robert Mustacchi
281dc90e12Robert Mustacchistatic void
282dc90e12Robert Mustacchipchtemp_cleanup(pchtemp_t *pch)
283dc90e12Robert Mustacchi{
284dc90e12Robert Mustacchi	int inst;
285dc90e12Robert Mustacchi
286dc90e12Robert Mustacchi	ASSERT3P(pch->pcht_dip, !=, NULL);
287dc90e12Robert Mustacchi	inst = ddi_get_instance(pch->pcht_dip);
288dc90e12Robert Mustacchi
289dc90e12Robert Mustacchi	ddi_remove_minor_node(pch->pcht_dip, NULL);
290dc90e12Robert Mustacchi
291dc90e12Robert Mustacchi	if (pch->pcht_handle != NULL) {
292dc90e12Robert Mustacchi		ddi_regs_map_free(&pch->pcht_handle);
293dc90e12Robert Mustacchi	}
294dc90e12Robert Mustacchi
295dc90e12Robert Mustacchi	if (pch->pcht_fm_caps != DDI_FM_NOT_CAPABLE) {
296dc90e12Robert Mustacchi		ddi_fm_fini(pch->pcht_dip);
297dc90e12Robert Mustacchi	}
298dc90e12Robert Mustacchi
299dc90e12Robert Mustacchi	mutex_destroy(&pch->pcht_mutex);
300dc90e12Robert Mustacchi	ddi_soft_state_free(pchtemp_state, inst);
301dc90e12Robert Mustacchi}
302dc90e12Robert Mustacchi
303dc90e12Robert Mustacchistatic int
304dc90e12Robert Mustacchipchtemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
305dc90e12Robert Mustacchi{
306dc90e12Robert Mustacchi	int inst, ret;
307dc90e12Robert Mustacchi	pchtemp_t *pch;
308dc90e12Robert Mustacchi	off_t memsize;
309dc90e12Robert Mustacchi	ddi_device_acc_attr_t da;
310dc90e12Robert Mustacchi	ddi_iblock_cookie_t iblk;
311dc90e12Robert Mustacchi	char name[1024];
312dc90e12Robert Mustacchi
313dc90e12Robert Mustacchi	switch (cmd) {
314dc90e12Robert Mustacchi	case DDI_RESUME:
315dc90e12Robert Mustacchi		return (DDI_SUCCESS);
316dc90e12Robert Mustacchi	case DDI_ATTACH:
317dc90e12Robert Mustacchi		break;
318dc90e12Robert Mustacchi	default:
319dc90e12Robert Mustacchi		return (DDI_FAILURE);
320dc90e12Robert Mustacchi	}
321dc90e12Robert Mustacchi
322dc90e12Robert Mustacchi	inst = ddi_get_instance(dip);
323dc90e12Robert Mustacchi	if (ddi_soft_state_zalloc(pchtemp_state, inst) != DDI_SUCCESS) {
324dc90e12Robert Mustacchi		dev_err(dip, CE_WARN, "failed to allocate soft state entry %d",
325dc90e12Robert Mustacchi		    inst);
326dc90e12Robert Mustacchi		return (DDI_FAILURE);
327dc90e12Robert Mustacchi	}
328dc90e12Robert Mustacchi
329dc90e12Robert Mustacchi	pch = ddi_get_soft_state(pchtemp_state, inst);
330dc90e12Robert Mustacchi	if (pch == NULL) {
331dc90e12Robert Mustacchi		dev_err(dip, CE_WARN, "failed to retrieve soft state entry %d",
332dc90e12Robert Mustacchi		    inst);
333dc90e12Robert Mustacchi		return (DDI_FAILURE);
334dc90e12Robert Mustacchi	}
335dc90e12Robert Mustacchi	pch->pcht_dip = dip;
336dc90e12Robert Mustacchi
337dc90e12Robert Mustacchi	pch->pcht_fm_caps = DDI_FM_ACCCHK_CAPABLE;
338dc90e12Robert Mustacchi	ddi_fm_init(dip, &pch->pcht_fm_caps, &iblk);
339dc90e12Robert Mustacchi
340dc90e12Robert Mustacchi	mutex_init(&pch->pcht_mutex, NULL, MUTEX_DRIVER, NULL);
341dc90e12Robert Mustacchi
342dc90e12Robert Mustacchi	if (ddi_dev_regsize(dip, PCHTEMP_RNUMBER, &memsize) != DDI_SUCCESS) {
343dc90e12Robert Mustacchi		dev_err(dip, CE_WARN, "failed to obtain register size for "
344dc90e12Robert Mustacchi		    "register set %d", PCHTEMP_RNUMBER);
345dc90e12Robert Mustacchi		goto err;
346dc90e12Robert Mustacchi	}
347dc90e12Robert Mustacchi
348dc90e12Robert Mustacchi	bzero(&da, sizeof (ddi_device_acc_attr_t));
349dc90e12Robert Mustacchi	da.devacc_attr_version = DDI_DEVICE_ATTR_V0;
350dc90e12Robert Mustacchi	da.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
351dc90e12Robert Mustacchi	da.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
352dc90e12Robert Mustacchi
353dc90e12Robert Mustacchi	if (DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) {
354dc90e12Robert Mustacchi		da.devacc_attr_access = DDI_FLAGERR_ACC;
355dc90e12Robert Mustacchi	} else {
356dc90e12Robert Mustacchi		da.devacc_attr_access = DDI_DEFAULT_ACC;
357dc90e12Robert Mustacchi	}
358dc90e12Robert Mustacchi
359dc90e12Robert Mustacchi	if ((ret = ddi_regs_map_setup(dip, PCHTEMP_RNUMBER, &pch->pcht_base,
360dc90e12Robert Mustacchi	    0, memsize, &da, &pch->pcht_handle)) != DDI_SUCCESS) {
361dc90e12Robert Mustacchi		dev_err(dip, CE_WARN, "failed to map register set %d: %d",
362dc90e12Robert Mustacchi		    PCHTEMP_RNUMBER, ret);
363dc90e12Robert Mustacchi		goto err;
364dc90e12Robert Mustacchi	}
365dc90e12Robert Mustacchi
366dc90e12Robert Mustacchi	if (snprintf(name, sizeof (name), "ts.%d", inst) >= sizeof (name)) {
367dc90e12Robert Mustacchi		dev_err(dip, CE_WARN, "failed to construct minor node name, "
368dc90e12Robert Mustacchi		    "name too long");
369dc90e12Robert Mustacchi		goto err;
370dc90e12Robert Mustacchi	}
371dc90e12Robert Mustacchi
372dc90e12Robert Mustacchi	if (ddi_create_minor_node(pch->pcht_dip, name, S_IFCHR, (minor_t)inst,
373dc90e12Robert Mustacchi	    DDI_NT_SENSOR_TEMP_PCH, 0) != DDI_SUCCESS) {
374dc90e12Robert Mustacchi		dev_err(dip, CE_WARN, "failed to create minor node %s", name);
375dc90e12Robert Mustacchi		goto err;
376dc90e12Robert Mustacchi	}
377dc90e12Robert Mustacchi
378dc90e12Robert Mustacchi	/*
379dc90e12Robert Mustacchi	 * Attempt a single read to lock in the temperature. We don't mind if
380dc90e12Robert Mustacchi	 * this fails for some reason.
381dc90e12Robert Mustacchi	 */
382dc90e12Robert Mustacchi	mutex_enter(&pch->pcht_mutex);
383dc90e12Robert Mustacchi	(void) pchtemp_read(pch);
384dc90e12Robert Mustacchi	mutex_exit(&pch->pcht_mutex);
385dc90e12Robert Mustacchi
386dc90e12Robert Mustacchi	return (DDI_SUCCESS);
387dc90e12Robert Mustacchi
388dc90e12Robert Mustacchierr:
389dc90e12Robert Mustacchi	pchtemp_cleanup(pch);
390dc90e12Robert Mustacchi	return (DDI_FAILURE);
391dc90e12Robert Mustacchi}
392dc90e12Robert Mustacchi
393dc90e12Robert Mustacchistatic int
394dc90e12Robert Mustacchipchtemp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
395dc90e12Robert Mustacchi    void **resultp)
396dc90e12Robert Mustacchi{
397dc90e12Robert Mustacchi	pchtemp_t *pch;
398dc90e12Robert Mustacchi
399dc90e12Robert Mustacchi	switch (cmd) {
400dc90e12Robert Mustacchi	case DDI_INFO_DEVT2DEVINFO:
401dc90e12Robert Mustacchi		pch = pchtemp_find_by_dev((dev_t)arg);
402dc90e12Robert Mustacchi		if (pch == NULL) {
403dc90e12Robert Mustacchi			return (DDI_FAILURE);
404dc90e12Robert Mustacchi		}
405dc90e12Robert Mustacchi
406dc90e12Robert Mustacchi		*resultp = pch->pcht_dip;
407dc90e12Robert Mustacchi		break;
408dc90e12Robert Mustacchi	case DDI_INFO_DEVT2INSTANCE:
409dc90e12Robert Mustacchi		*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
410dc90e12Robert Mustacchi		break;
411dc90e12Robert Mustacchi	default:
412dc90e12Robert Mustacchi		return (DDI_FAILURE);
413dc90e12Robert Mustacchi	}
414dc90e12Robert Mustacchi
415dc90e12Robert Mustacchi	return (DDI_SUCCESS);
416dc90e12Robert Mustacchi}
417dc90e12Robert Mustacchi
418dc90e12Robert Mustacchistatic int
419dc90e12Robert Mustacchipchtemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
420dc90e12Robert Mustacchi{
421dc90e12Robert Mustacchi	int inst;
422dc90e12Robert Mustacchi	pchtemp_t *pch;
423dc90e12Robert Mustacchi
424dc90e12Robert Mustacchi	switch (cmd) {
425dc90e12Robert Mustacchi	case DDI_DETACH:
426dc90e12Robert Mustacchi		break;
427dc90e12Robert Mustacchi	case DDI_SUSPEND:
428dc90e12Robert Mustacchi		return (DDI_SUCCESS);
429dc90e12Robert Mustacchi	default:
430dc90e12Robert Mustacchi		return (DDI_FAILURE);
431dc90e12Robert Mustacchi	}
432dc90e12Robert Mustacchi
433dc90e12Robert Mustacchi	inst = ddi_get_instance(dip);
434dc90e12Robert Mustacchi	pch = ddi_get_soft_state(pchtemp_state, inst);
435dc90e12Robert Mustacchi	if (pch == NULL) {
436dc90e12Robert Mustacchi		dev_err(dip, CE_WARN, "asked to detached instance %d, but "
437dc90e12Robert Mustacchi		    "it does not exist in soft state", inst);
438dc90e12Robert Mustacchi		return (DDI_FAILURE);
439dc90e12Robert Mustacchi	}
440dc90e12Robert Mustacchi
441dc90e12Robert Mustacchi	pchtemp_cleanup(pch);
442dc90e12Robert Mustacchi	return (DDI_SUCCESS);
443dc90e12Robert Mustacchi}
444dc90e12Robert Mustacchi
445dc90e12Robert Mustacchistatic struct cb_ops pchtemp_cb_ops = {
446dc90e12Robert Mustacchi	.cb_open = pchtemp_open,
447dc90e12Robert Mustacchi	.cb_close = pchtemp_close,
448dc90e12Robert Mustacchi	.cb_strategy = nodev,
449dc90e12Robert Mustacchi	.cb_print = nodev,
450dc90e12Robert Mustacchi	.cb_dump = nodev,
451dc90e12Robert Mustacchi	.cb_read = nodev,
452dc90e12Robert Mustacchi	.cb_write = nodev,
453dc90e12Robert Mustacchi	.cb_ioctl = pchtemp_ioctl,
454dc90e12Robert Mustacchi	.cb_devmap = nodev,
455dc90e12Robert Mustacchi	.cb_mmap = nodev,
456dc90e12Robert Mustacchi	.cb_segmap = nodev,
457dc90e12Robert Mustacchi	.cb_chpoll = nochpoll,
458dc90e12Robert Mustacchi	.cb_prop_op = ddi_prop_op,
459dc90e12Robert Mustacchi	.cb_flag = D_MP,
460dc90e12Robert Mustacchi	.cb_rev = CB_REV,
461dc90e12Robert Mustacchi	.cb_aread = nodev,
462dc90e12Robert Mustacchi	.cb_awrite = nodev
463dc90e12Robert Mustacchi};
464dc90e12Robert Mustacchi
465dc90e12Robert Mustacchistatic struct dev_ops pchtemp_dev_ops = {
466dc90e12Robert Mustacchi	.devo_rev = DEVO_REV,
467dc90e12Robert Mustacchi	.devo_refcnt = 0,
468dc90e12Robert Mustacchi	.devo_getinfo = pchtemp_getinfo,
469dc90e12Robert Mustacchi	.devo_identify = nulldev,
470dc90e12Robert Mustacchi	.devo_probe = nulldev,
471dc90e12Robert Mustacchi	.devo_attach = pchtemp_attach,
472dc90e12Robert Mustacchi	.devo_detach = pchtemp_detach,
473dc90e12Robert Mustacchi	.devo_reset = nodev,
474dc90e12Robert Mustacchi	.devo_power = ddi_power,
475dc90e12Robert Mustacchi	.devo_quiesce = ddi_quiesce_not_needed,
476dc90e12Robert Mustacchi	.devo_cb_ops = &pchtemp_cb_ops
477dc90e12Robert Mustacchi};
478dc90e12Robert Mustacchi
479dc90e12Robert Mustacchistatic struct modldrv pchtemp_modldrv = {
480dc90e12Robert Mustacchi	.drv_modops = &mod_driverops,
481dc90e12Robert Mustacchi	.drv_linkinfo = "Intel PCH Thermal Sensor",
482dc90e12Robert Mustacchi	.drv_dev_ops = &pchtemp_dev_ops
483dc90e12Robert Mustacchi};
484dc90e12Robert Mustacchi
485dc90e12Robert Mustacchistatic struct modlinkage pchtemp_modlinkage = {
486dc90e12Robert Mustacchi	.ml_rev = MODREV_1,
487dc90e12Robert Mustacchi	.ml_linkage = { &pchtemp_modldrv, NULL }
488dc90e12Robert Mustacchi};
489dc90e12Robert Mustacchi
490dc90e12Robert Mustacchiint
491dc90e12Robert Mustacchi_init(void)
492dc90e12Robert Mustacchi{
493dc90e12Robert Mustacchi	int ret;
494dc90e12Robert Mustacchi
495dc90e12Robert Mustacchi	if (ddi_soft_state_init(&pchtemp_state, sizeof (pchtemp_t), 1) !=
496dc90e12Robert Mustacchi	    DDI_SUCCESS) {
497dc90e12Robert Mustacchi		return (ENOMEM);
498dc90e12Robert Mustacchi	}
499dc90e12Robert Mustacchi
500dc90e12Robert Mustacchi	if ((ret = mod_install(&pchtemp_modlinkage)) != 0) {
501dc90e12Robert Mustacchi		ddi_soft_state_fini(&pchtemp_state);
502dc90e12Robert Mustacchi		return (ret);
503dc90e12Robert Mustacchi	}
504dc90e12Robert Mustacchi
505dc90e12Robert Mustacchi	return (ret);
506dc90e12Robert Mustacchi}
507dc90e12Robert Mustacchi
508dc90e12Robert Mustacchiint
509dc90e12Robert Mustacchi_info(struct modinfo *modinfop)
510dc90e12Robert Mustacchi{
511dc90e12Robert Mustacchi	return (mod_info(&pchtemp_modlinkage, modinfop));
512dc90e12Robert Mustacchi}
513dc90e12Robert Mustacchi
514dc90e12Robert Mustacchiint
515dc90e12Robert Mustacchi_fini(void)
516dc90e12Robert Mustacchi{
517dc90e12Robert Mustacchi	int ret;
518dc90e12Robert Mustacchi
519dc90e12Robert Mustacchi	if ((ret = mod_remove(&pchtemp_modlinkage)) != 0) {
520dc90e12Robert Mustacchi		return (ret);
521dc90e12Robert Mustacchi	}
522dc90e12Robert Mustacchi
523dc90e12Robert Mustacchi	ddi_soft_state_fini(&pchtemp_state);
524dc90e12Robert Mustacchi	return (ret);
525dc90e12Robert Mustacchi}