1/*
2 * Copyright (c) 2010 Mike Larkin <mlarkin@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/*
18 * Intel 3400 thermal sensor controller driver
19 */
20
21#include <sys/param.h>
22#include <sys/systm.h>
23#include <sys/device.h>
24#include <sys/sensors.h>
25
26#include <dev/pci/pcireg.h>
27#include <dev/pci/pcivar.h>
28#include <dev/pci/pcidevs.h>
29
30/*
31 * Intel 5 series (3400) Thermal Sensor Data
32 * See Intel document 322169-004 (January 2012)
33 */
34#define	ITHERM_NUM_SENSORS		12
35#define	ITHERM_SENSOR_THERMOMETER	0
36#define	ITHERM_SENSOR_CORETEMP1		1
37#define	ITHERM_SENSOR_CORETEMP2		2
38#define	ITHERM_SENSOR_COREENERGY	3
39#define	ITHERM_SENSOR_GPUTEMP		4
40#define	ITHERM_SENSOR_MAXPROCTEMP	5
41#define	ITHERM_SENSOR_DIMMTEMP1		6
42#define	ITHERM_SENSOR_DIMMTEMP2		7
43#define	ITHERM_SENSOR_DIMMTEMP3		8
44#define	ITHERM_SENSOR_DIMMTEMP4		9
45#define	ITHERM_SENSOR_GPUTEMP_ABSOLUTE	10
46#define	ITHERM_SENSOR_PCHTEMP_ABSOLUTE	11
47
48/* Section 22.2 of datasheet */
49#define	ITHERM_TSE	0x1	/* TS enable */
50#define	ITHERM_TSTR	0x3	/* TS thermometer read */
51#define	ITHERM_TRC	0x1A	/* TS reporting control */
52#define	ITHERM_CTV1	0x30	/* TS core temp value 1 */
53#define	ITHERM_CTV2	0x32	/* TS core temp value 2 */
54#define	ITHERM_CEV1	0x34	/* TS core energy value 1 */
55#define	ITHERM_MGTV	0x58	/* mem/GPU temp value */
56#define	ITHERM_PTV	0x60	/* TS CPU temp value */
57#define	ITHERM_DTV	0xAC	/* DIMM temp values */
58#define	ITHERM_ITV	0xD8	/* Internal temp values */
59
60#define	ITHERM_TEMP_READ_ENABLE		0xFF
61#define	ITHERM_TDR_ENABLE		0x1000
62#define	ITHERM_SECOND_CORE_ENABLE	0x8000
63
64#define	ITHERM_TSE_ENABLE	0xB8	/* magic number in datasheet */
65
66#define	ITHERM_CTV_INVALID	0x8000
67#define	ITHERM_CTV_INT_MASK	0x3FC0	/* higher 8 bits */
68#define	ITHERM_CTV_FRAC_MASK	0x003F	/* lower 6 bits */
69
70#define	ITHERM_REFRESH_INTERVAL 5
71
72struct itherm_softc {
73	struct device		sc_dev;
74
75	bus_addr_t		sc_addr;
76	bus_space_tag_t		iot;
77	bus_space_handle_t	ioh;
78	bus_size_t		size;
79
80	int64_t			energy_prev;
81
82	struct ksensor sensors[ITHERM_NUM_SENSORS];
83	struct ksensordev sensordev;
84	void (*refresh_sensor_data)(struct itherm_softc *);
85};
86
87#define IREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
88#define IREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (a))
89#define IREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (a))
90#define IWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (a), (x))
91#define IWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (a), (x))
92
93int  itherm_probe(struct device *, void *, void *);
94void itherm_attach(struct device *, struct device *, void *);
95void itherm_refresh(void *);
96void itherm_enable(struct itherm_softc *);
97void itherm_refresh_sensor_data(struct itherm_softc *);
98int  itherm_activate(struct device *, int);
99void itherm_bias_temperature_sensor(struct ksensor *);
100
101const struct pci_matchid itherm_devices[] = {
102	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3400_THERMAL }
103};
104
105struct cfdriver itherm_cd = {
106	NULL, "itherm", DV_DULL
107};
108
109const struct cfattach itherm_ca = {
110	sizeof(struct itherm_softc), itherm_probe, itherm_attach, NULL,
111	itherm_activate
112};
113
114int
115itherm_probe(struct device *parent, void *match, void *aux)
116{
117	return (pci_matchbyid((struct pci_attach_args *)aux, itherm_devices,
118	    sizeof(itherm_devices)/sizeof(itherm_devices[0])));
119}
120
121void
122itherm_attach(struct device *parent, struct device *self, void *aux)
123{
124	struct itherm_softc *sc = (struct itherm_softc *)self;
125	struct pci_attach_args *pa = aux;
126	int i;
127	pcireg_t v;
128
129	v = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START);
130	v &= PCI_MAPREG_TYPE_MASK | PCI_MAPREG_MEM_TYPE_MASK;
131	if (pci_mapreg_map(pa, PCI_MAPREG_START,
132	    v, 0, &sc->iot, &sc->ioh, NULL, &sc->size, 0)) {
133		printf(": can't map mem space\n");
134		return;
135	}
136
137	sc->sensors[ITHERM_SENSOR_THERMOMETER].type = SENSOR_TEMP;
138	sc->sensors[ITHERM_SENSOR_CORETEMP1].type = SENSOR_TEMP;
139	sc->sensors[ITHERM_SENSOR_CORETEMP2].type = SENSOR_TEMP;
140	sc->sensors[ITHERM_SENSOR_COREENERGY].type = SENSOR_WATTS;
141	sc->sensors[ITHERM_SENSOR_GPUTEMP].type = SENSOR_TEMP;
142	sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].type = SENSOR_TEMP;
143	sc->sensors[ITHERM_SENSOR_DIMMTEMP1].type = SENSOR_TEMP;
144	sc->sensors[ITHERM_SENSOR_DIMMTEMP2].type = SENSOR_TEMP;
145	sc->sensors[ITHERM_SENSOR_DIMMTEMP3].type = SENSOR_TEMP;
146	sc->sensors[ITHERM_SENSOR_DIMMTEMP4].type = SENSOR_TEMP;
147	sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].type = SENSOR_TEMP;
148	sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].type = SENSOR_TEMP;
149
150	strlcpy(sc->sensors[ITHERM_SENSOR_THERMOMETER].desc,
151	    "Thermometer",
152	    sizeof(sc->sensors[ITHERM_SENSOR_THERMOMETER].desc));
153
154	strlcpy(sc->sensors[ITHERM_SENSOR_CORETEMP1].desc,
155	    "Core 1",
156	    sizeof(sc->sensors[ITHERM_SENSOR_CORETEMP1].desc));
157
158	strlcpy(sc->sensors[ITHERM_SENSOR_CORETEMP2].desc,
159	    "Core 2",
160	    sizeof(sc->sensors[ITHERM_SENSOR_CORETEMP2].desc));
161
162	strlcpy(sc->sensors[ITHERM_SENSOR_COREENERGY].desc,
163	    "CPU power consumption",
164	    sizeof(sc->sensors[ITHERM_SENSOR_COREENERGY].desc));
165
166	strlcpy(sc->sensors[ITHERM_SENSOR_GPUTEMP].desc,
167	    "GPU/Memory Controller Temp",
168	    sizeof(sc->sensors[ITHERM_SENSOR_GPUTEMP].desc));
169
170	strlcpy(sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].desc,
171	    "CPU/GPU Max temp",
172	    sizeof(sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].desc));
173
174	strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP1].desc,
175	    "DIMM 1",
176	    sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP1].desc));
177
178	strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP2].desc,
179	    "DIMM 2",
180	    sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP2].desc));
181
182	strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP3].desc,
183	    "DIMM 3",
184	    sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP3].desc));
185
186	strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP4].desc,
187	    "DIMM 4",
188	    sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP4].desc));
189
190	strlcpy(sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].desc,
191	    "GPU/Memory controller abs.",
192	    sizeof(sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].desc));
193
194	strlcpy(sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].desc,
195	    "PCH abs.",
196	    sizeof(sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].desc));
197
198	strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
199	    sizeof(sc->sensordev.xname));
200
201	itherm_enable(sc);
202
203	for (i = 0; i < ITHERM_NUM_SENSORS; i++)
204		sensor_attach(&sc->sensordev, &sc->sensors[i]);
205
206	sensordev_install(&sc->sensordev);
207	sensor_task_register(sc, itherm_refresh, ITHERM_REFRESH_INTERVAL);
208
209	printf("\n");
210
211	return;
212}
213
214void
215itherm_enable(struct itherm_softc *sc)
216{
217	sc->energy_prev = 0;
218
219	/* Enable thermal sensor */
220	IWRITE1(sc, ITHERM_TSE, ITHERM_TSE_ENABLE);
221
222	/* Enable thermal reporting */
223	IWRITE2(sc, ITHERM_TRC, (ITHERM_TEMP_READ_ENABLE |
224	    ITHERM_TDR_ENABLE | ITHERM_SECOND_CORE_ENABLE));
225}
226
227int
228itherm_activate(struct device *self, int act)
229{
230	struct itherm_softc *sc = (struct itherm_softc *)self;
231
232	switch (act) {
233	case DVACT_RESUME:
234		itherm_enable(sc);
235		break;
236	}
237
238	return (0);
239}
240
241void
242itherm_refresh_sensor_data(struct itherm_softc *sc)
243{
244	u_int16_t data;
245	int64_t energy;
246	u_int32_t i;
247
248	/* Thermometer sensor */
249	sc->sensors[ITHERM_SENSOR_THERMOMETER].value =
250	    IREAD1(sc, ITHERM_TSTR);
251
252	itherm_bias_temperature_sensor(
253	    &sc->sensors[ITHERM_SENSOR_THERMOMETER]);
254
255	/*
256	 * The Intel 3400 Thermal Sensor has separate sensors for each
257	 * core, reported as a 16 bit value. Bits 13:6 are the integer
258	 * part of the temperature in C and bits 5:0 are the fractional
259	 * part of the temperature, in 1/64 degree C intervals.
260	 * Bit 15 is used to indicate an invalid temperature
261	 */
262
263	/* Core 1 temperature */
264    	data = IREAD2(sc, ITHERM_CTV1);
265	if (data & ITHERM_CTV_INVALID)
266		sc->sensors[ITHERM_SENSOR_CORETEMP1].flags |=
267		    SENSOR_FINVALID;
268	else {
269		sc->sensors[ITHERM_SENSOR_CORETEMP1].flags &=
270		    ~SENSOR_FINVALID;
271		sc->sensors[ITHERM_SENSOR_CORETEMP1].value =
272		    (data & ITHERM_CTV_INT_MASK) >> 6;
273		sc->sensors[ITHERM_SENSOR_CORETEMP1].value *=
274		    1000000;
275		data &= ITHERM_CTV_FRAC_MASK;
276		data *= 1000000 / 64;
277		sc->sensors[ITHERM_SENSOR_CORETEMP1].value +=
278		    data;
279		itherm_bias_temperature_sensor(
280		    &sc->sensors[ITHERM_SENSOR_CORETEMP1]);
281	}
282
283	/* Core 2 temperature */
284    	data = IREAD2(sc, ITHERM_CTV2);
285	if (data & ITHERM_CTV_INVALID)
286		sc->sensors[ITHERM_SENSOR_CORETEMP2].flags |=
287		    SENSOR_FINVALID;
288	else {
289		sc->sensors[ITHERM_SENSOR_CORETEMP2].flags &=
290		    ~SENSOR_FINVALID;
291		sc->sensors[ITHERM_SENSOR_CORETEMP2].value =
292		    (data & ITHERM_CTV_INT_MASK) >> 6;
293		sc->sensors[ITHERM_SENSOR_CORETEMP2].value *=
294		    1000000;
295		data &= ITHERM_CTV_FRAC_MASK;
296		data *= 1000000 / 64;
297		sc->sensors[ITHERM_SENSOR_CORETEMP2].value +=
298		    data;
299		itherm_bias_temperature_sensor(
300		    &sc->sensors[ITHERM_SENSOR_CORETEMP2]);
301	}
302
303	/*
304	 * The core energy sensor reports the number of Joules
305	 * of energy consumed by the processor since powerup.
306	 * This number is scaled by 65535 and is continually
307	 * increasing, so we save the old value and compute
308	 * the difference for the Watt sensor value.
309	 */
310
311	i = IREAD4(sc, ITHERM_CEV1);
312	/* Convert to Joules per interval */
313	energy = (i / 65535);
314	energy = energy - sc->energy_prev;
315	sc->energy_prev = (i / 65535);
316	/* Convert to Joules per second */
317	energy = energy / ITHERM_REFRESH_INTERVAL;
318	/* Convert to micro Joules per second (micro Watts) */
319	energy = energy * 1000 * 1000;
320
321	sc->sensors[ITHERM_SENSOR_COREENERGY].value = energy;
322
323	/*
324	 * XXX - the GPU temp is reported as a 64 bit value with no
325	 * documented structure. Disabled for now
326	 */
327	sc->sensors[ITHERM_SENSOR_GPUTEMP].flags |= SENSOR_FINVALID;
328#if 0
329	bus_space_read_multi_4(sc->iot, sc->ioh, ITHERM_MGTV,
330	    (u_int32_t *)&sc->sensors[ITHERM_SENSOR_GPUTEMP].value, 2);
331	sc->sensors[ITHERM_SENSOR_GPUTEMP].value *= 1000000;
332	sc->sensors[ITHERM_SENSOR_GPUTEMP].value += 273150000;
333#endif
334
335	/* Max processor temperature */
336	sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].value =
337	    IREAD1(sc, ITHERM_PTV) * 1000000;
338	itherm_bias_temperature_sensor(
339	    &sc->sensors[ITHERM_SENSOR_MAXPROCTEMP]);
340
341	/* DIMM 1 */
342	sc->sensors[ITHERM_SENSOR_DIMMTEMP1].value =
343	    IREAD1(sc, ITHERM_DTV) * 1000000;
344	itherm_bias_temperature_sensor(
345	    &sc->sensors[ITHERM_SENSOR_DIMMTEMP1]);
346
347	/* DIMM 2 */
348	sc->sensors[ITHERM_SENSOR_DIMMTEMP2].value =
349	    IREAD1(sc, ITHERM_DTV+1) * 1000000;
350	itherm_bias_temperature_sensor(
351	    &sc->sensors[ITHERM_SENSOR_DIMMTEMP2]);
352
353	/* DIMM 3 */
354	sc->sensors[ITHERM_SENSOR_DIMMTEMP3].value =
355	    IREAD1(sc, ITHERM_DTV+2) * 1000000;
356	itherm_bias_temperature_sensor(
357	    &sc->sensors[ITHERM_SENSOR_DIMMTEMP3]);
358
359	/* DIMM 4 */
360	sc->sensors[ITHERM_SENSOR_DIMMTEMP4].value =
361	    IREAD1(sc, ITHERM_DTV+3) * 1000000;
362	itherm_bias_temperature_sensor(
363	    &sc->sensors[ITHERM_SENSOR_DIMMTEMP4]);
364
365	/* GPU Temperature */
366	sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].value =
367	    IREAD1(sc, ITHERM_ITV+1) * 1000000;
368	itherm_bias_temperature_sensor(
369	    &sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE]);
370
371	/* PCH Temperature */
372	sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].value =
373	    IREAD1(sc, ITHERM_ITV) * 1000000;
374	itherm_bias_temperature_sensor(
375	    &sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE]);
376}
377
378void
379itherm_bias_temperature_sensor(struct ksensor *sensor)
380{
381	if (sensor->value == 0 || sensor->value == 0xff)
382		sensor->flags |= SENSOR_FINVALID;
383	else
384		sensor->flags &= ~SENSOR_FINVALID;
385
386	/* Bias anyway from degC to degK, even if invalid */
387	sensor->value += 273150000;
388}
389
390void
391itherm_refresh(void *arg)
392{
393	struct itherm_softc *sc = (struct itherm_softc *)arg;
394
395	itherm_refresh_sensor_data(sc);
396}
397