1309b1170SImre Vadász /*
2309b1170SImre Vadász  * Copyright (c) 2015 Imre Vadász <imre@vdsz.com>
3309b1170SImre Vadász  * All rights reserved.
4309b1170SImre Vadász  *
5309b1170SImre Vadász  * Redistribution and use in source and binary forms, with or without
6309b1170SImre Vadász  * modification, are permitted provided that the following conditions
7309b1170SImre Vadász  * are met:
8309b1170SImre Vadász  * 1. Redistributions of source code must retain the above copyright
9309b1170SImre Vadász  *    notice, this list of conditions and the following disclaimer.
10309b1170SImre Vadász  * 2. Redistributions in binary form must reproduce the above copyright
11309b1170SImre Vadász  *    notice, this list of conditions and the following disclaimer in the
12309b1170SImre Vadász  *    documentation and/or other materials provided with the distribution.
13309b1170SImre Vadász  *
14309b1170SImre Vadász  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15309b1170SImre Vadász  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16309b1170SImre Vadász  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17309b1170SImre Vadász  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18309b1170SImre Vadász  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19309b1170SImre Vadász  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20309b1170SImre Vadász  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21309b1170SImre Vadász  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22309b1170SImre Vadász  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23309b1170SImre Vadász  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24309b1170SImre Vadász  * POSSIBILITY OF SUCH DAMAGE.
25309b1170SImre Vadász  */
26309b1170SImre Vadász 
27309b1170SImre Vadász /*
28309b1170SImre Vadász  * Device driver for Intel's On Die power usage estimation via MSR.
29309b1170SImre Vadász  * Supported by Sandy Bridge and later CPUs, and also by Atom CPUs
30309b1170SImre Vadász  * of the Silvermont and later architectures.
31309b1170SImre Vadász  */
32309b1170SImre Vadász 
33309b1170SImre Vadász #include <sys/param.h>
34309b1170SImre Vadász #include <sys/bus.h>
35309b1170SImre Vadász #include <sys/systm.h>
36309b1170SImre Vadász #include <sys/module.h>
37309b1170SImre Vadász #include <sys/conf.h>
38309b1170SImre Vadász #include <sys/cpu_topology.h>
39309b1170SImre Vadász #include <sys/kernel.h>
40309b1170SImre Vadász #include <sys/sensors.h>
41309b1170SImre Vadász #include <sys/bitops.h>
42309b1170SImre Vadász 
43309b1170SImre Vadász #include <machine/specialreg.h>
44309b1170SImre Vadász #include <machine/cpufunc.h>
45309b1170SImre Vadász #include <machine/cputypes.h>
46309b1170SImre Vadász #include <machine/md_var.h>
47309b1170SImre Vadász 
48309b1170SImre Vadász #include "cpu_if.h"
49309b1170SImre Vadász 
50309b1170SImre Vadász #define MSR_RAPL_POWER_UNIT_POWER	__BITS64(0, 3)
51309b1170SImre Vadász #define MSR_RAPL_POWER_UNIT_ENERGY	__BITS64(8, 12)
52309b1170SImre Vadász #define MSR_RAPL_POWER_UNIT_TIME	__BITS64(16, 19)
53309b1170SImre Vadász 
54309b1170SImre Vadász struct corepower_sensor {
55309b1170SImre Vadász 	uint64_t	energy;
56309b1170SImre Vadász 	u_int		msr;
57309b1170SImre Vadász 	struct ksensor	sensor;
58309b1170SImre Vadász };
59309b1170SImre Vadász 
60309b1170SImre Vadász struct corepower_softc {
61309b1170SImre Vadász 	device_t		sc_dev;
62309b1170SImre Vadász 
63c13f7d35SImre Vadász 	uint32_t		sc_watt_unit;
64c13f7d35SImre Vadász 	uint32_t		sc_joule_unit;
65c13f7d35SImre Vadász 	uint32_t		sc_second_unit;
66309b1170SImre Vadász 
67309b1170SImre Vadász 	int			sc_have_sens;
68c13f7d35SImre Vadász 	int			sc_is_atom;
69309b1170SImre Vadász 
70309b1170SImre Vadász 	struct corepower_sensor	sc_pkg_sens;
71309b1170SImre Vadász 	struct corepower_sensor	sc_dram_sens;
72309b1170SImre Vadász 	struct corepower_sensor	sc_pp0_sens;
73309b1170SImre Vadász 	struct corepower_sensor	sc_pp1_sens;
74214ee9b5SImre Vadász 	struct corepower_sensor	sc_platform_sens;
75309b1170SImre Vadász 
76309b1170SImre Vadász 	struct ksensordev	sc_sensordev;
77309b1170SImre Vadász 	struct sensor_task	*sc_senstask;
78309b1170SImre Vadász };
79309b1170SImre Vadász 
80309b1170SImre Vadász /*
81309b1170SImre Vadász  * Device methods.
82309b1170SImre Vadász  */
83309b1170SImre Vadász static void	corepower_identify(driver_t *driver, device_t parent);
84309b1170SImre Vadász static int	corepower_probe(device_t dev);
85309b1170SImre Vadász static int	corepower_attach(device_t dev);
86309b1170SImre Vadász static int	corepower_detach(device_t dev);
87309b1170SImre Vadász static uint32_t	corepower_energy_to_uwatts(struct corepower_softc *sc,
88309b1170SImre Vadász 					   uint32_t units, uint32_t secs);
89309b1170SImre Vadász static void	corepower_refresh(void *arg);
90309b1170SImre Vadász static void	corepower_sens_init(struct corepower_sensor *sens,
91309b1170SImre Vadász 				    char *desc, u_int msr, int cpu);
92309b1170SImre Vadász static void	corepower_sens_update(struct corepower_softc *sc,
93309b1170SImre Vadász 				      struct corepower_sensor *sens);
94384c0addSImre Vadász static int	corepower_try(u_int msr, char *name);
95309b1170SImre Vadász 
96309b1170SImre Vadász static device_method_t corepower_methods[] = {
97309b1170SImre Vadász 	/* Device interface */
98309b1170SImre Vadász 	DEVMETHOD(device_identify,	corepower_identify),
99309b1170SImre Vadász 	DEVMETHOD(device_probe,		corepower_probe),
100309b1170SImre Vadász 	DEVMETHOD(device_attach,	corepower_attach),
101309b1170SImre Vadász 	DEVMETHOD(device_detach,	corepower_detach),
102309b1170SImre Vadász 
103309b1170SImre Vadász 	DEVMETHOD_END
104309b1170SImre Vadász };
105309b1170SImre Vadász 
106309b1170SImre Vadász static driver_t corepower_driver = {
107309b1170SImre Vadász 	"corepower",
108309b1170SImre Vadász 	corepower_methods,
109309b1170SImre Vadász 	sizeof(struct corepower_softc),
110309b1170SImre Vadász };
111309b1170SImre Vadász 
112309b1170SImre Vadász static devclass_t corepower_devclass;
113309b1170SImre Vadász DRIVER_MODULE(corepower, cpu, corepower_driver, corepower_devclass, NULL, NULL);
114309b1170SImre Vadász MODULE_VERSION(corepower, 1);
115309b1170SImre Vadász 
116309b1170SImre Vadász static void
corepower_identify(driver_t * driver,device_t parent)117309b1170SImre Vadász corepower_identify(driver_t *driver, device_t parent)
118309b1170SImre Vadász {
119309b1170SImre Vadász 	device_t child;
120309b1170SImre Vadász 	const struct cpu_node *node;
121309b1170SImre Vadász 	int cpu, master_cpu;
122309b1170SImre Vadász 
123309b1170SImre Vadász 	/* Make sure we're not being doubly invoked. */
124309b1170SImre Vadász 	if (device_find_child(parent, "corepower", -1) != NULL)
125309b1170SImre Vadász 		return;
126309b1170SImre Vadász 
127309b1170SImre Vadász 	/* Check that the vendor is Intel. */
128309b1170SImre Vadász 	if (cpu_vendor_id != CPU_VENDOR_INTEL)
129309b1170SImre Vadász 		return;
130309b1170SImre Vadász 
131309b1170SImre Vadász 	/* We only want one child per CPU package */
132309b1170SImre Vadász 	cpu = device_get_unit(parent);
133309b1170SImre Vadász 	node = get_cpu_node_by_cpuid(cpu);
134309b1170SImre Vadász 	while (node != NULL) {
1356191bf2cSSepherosa Ziehau 		if (node->type == CHIP_LEVEL) {
136309b1170SImre Vadász 			if (node->child_no == 0)
137309b1170SImre Vadász 				node = NULL;
138309b1170SImre Vadász 			break;
139309b1170SImre Vadász 		}
140309b1170SImre Vadász 		node = node->parent_node;
141309b1170SImre Vadász 	}
142309b1170SImre Vadász 	if (node == NULL)
143309b1170SImre Vadász 		return;
144309b1170SImre Vadász 
145309b1170SImre Vadász 	master_cpu = BSRCPUMASK(node->members);
146309b1170SImre Vadász 	if (cpu != master_cpu)
147309b1170SImre Vadász 		return;
148309b1170SImre Vadász 
149309b1170SImre Vadász 	child = device_add_child(parent, "corepower", -1);
150309b1170SImre Vadász 	if (child == NULL)
151309b1170SImre Vadász 		device_printf(parent, "add corepower child failed\n");
152309b1170SImre Vadász }
153309b1170SImre Vadász 
154309b1170SImre Vadász static int
corepower_probe(device_t dev)155309b1170SImre Vadász corepower_probe(device_t dev)
156309b1170SImre Vadász {
157309b1170SImre Vadász 	int cpu_family, cpu_model;
158309b1170SImre Vadász 
159309b1170SImre Vadász 	if (resource_disabled("corepower", 0))
160309b1170SImre Vadász 		return (ENXIO);
161309b1170SImre Vadász 
162309b1170SImre Vadász 	cpu_model = CPUID_TO_MODEL(cpu_id);
163309b1170SImre Vadász 	cpu_family = CPUID_TO_FAMILY(cpu_id);
164309b1170SImre Vadász 
165309b1170SImre Vadász 	if (cpu_family == 0x06) {
166309b1170SImre Vadász 		switch (cpu_model) {
167309b1170SImre Vadász 		/* Core CPUs */
168309b1170SImre Vadász 		case 0x2a:
169309b1170SImre Vadász 		case 0x3a:
170309b1170SImre Vadász 		/* Xeon CPUs */
171309b1170SImre Vadász 		case 0x2d:
172309b1170SImre Vadász 		case 0x3e:
173309b1170SImre Vadász 		case 0x3f:
174309b1170SImre Vadász 		case 0x4f:
175309b1170SImre Vadász 		case 0x56:
1765ee2603dSSascha Wildner 		/* Haswell, Broadwell, Skylake, Kaby Lake, Coffee Lake */
177309b1170SImre Vadász 		case 0x3c:
178309b1170SImre Vadász 		case 0x3d:
179309b1170SImre Vadász 		case 0x45:
180309b1170SImre Vadász 		case 0x46:
181309b1170SImre Vadász 		case 0x47:
182309b1170SImre Vadász 		case 0x4e:
183309b1170SImre Vadász 		case 0x5e:
184*9200010eSSascha Wildner 		case 0x8e:	/* Kaby Lake, Coffee Lake */
185*9200010eSSascha Wildner 		case 0x9e:	/* dito */
186309b1170SImre Vadász 		/* Atom CPUs */
187309b1170SImre Vadász 		case 0x37:
188309b1170SImre Vadász 		case 0x4a:
189309b1170SImre Vadász 		case 0x4c:
190c13f7d35SImre Vadász 		case 0x4d:
191309b1170SImre Vadász 		case 0x5a:
192309b1170SImre Vadász 		case 0x5d:
193309b1170SImre Vadász 			break;
194309b1170SImre Vadász 		default:
195309b1170SImre Vadász 			return (ENXIO);
196309b1170SImre Vadász 		}
197309b1170SImre Vadász 	}
198309b1170SImre Vadász 
199384c0addSImre Vadász 	if (corepower_try(MSR_RAPL_POWER_UNIT, "MSR_RAPL_POWER_UNIT") == 0)
200384c0addSImre Vadász 		return (ENXIO);
201384c0addSImre Vadász 
202309b1170SImre Vadász 	device_set_desc(dev, "CPU On-Die Power Usage Estimation");
203309b1170SImre Vadász 
204309b1170SImre Vadász 	return (BUS_PROBE_GENERIC);
205309b1170SImre Vadász }
206309b1170SImre Vadász 
207309b1170SImre Vadász static int
corepower_attach(device_t dev)208309b1170SImre Vadász corepower_attach(device_t dev)
209309b1170SImre Vadász {
210309b1170SImre Vadász 	struct corepower_softc *sc = device_get_softc(dev);
211309b1170SImre Vadász 	uint64_t val;
212309b1170SImre Vadász 	uint32_t power_units;
213309b1170SImre Vadász 	uint32_t energy_units;
214309b1170SImre Vadász 	uint32_t time_units;
215309b1170SImre Vadász 	int cpu_family, cpu_model;
216309b1170SImre Vadász 	int cpu;
217309b1170SImre Vadász 
218309b1170SImre Vadász 	sc->sc_dev = dev;
219309b1170SImre Vadász 	sc->sc_have_sens = 0;
220c13f7d35SImre Vadász 	sc->sc_is_atom = 0;
221309b1170SImre Vadász 
222309b1170SImre Vadász 	cpu_family = CPUID_TO_FAMILY(cpu_id);
223309b1170SImre Vadász 	cpu_model = CPUID_TO_MODEL(cpu_id);
224309b1170SImre Vadász 
225c13f7d35SImre Vadász 	/* Check CPU model */
226309b1170SImre Vadász 	if (cpu_family == 0x06) {
227309b1170SImre Vadász 		switch (cpu_model) {
228309b1170SImre Vadász 		/* Core CPUs */
229309b1170SImre Vadász 		case 0x2a:
230309b1170SImre Vadász 		case 0x3a:
231309b1170SImre Vadász 			sc->sc_have_sens = 0xd;
232309b1170SImre Vadász 			break;
233309b1170SImre Vadász 		/* Xeon CPUs */
234309b1170SImre Vadász 		case 0x2d: /* Only Xeon branded, Core i version should probably be 0x5 */
235309b1170SImre Vadász 		case 0x3e:
236309b1170SImre Vadász 		case 0x3f:
237309b1170SImre Vadász 		case 0x4f:
238309b1170SImre Vadász 		case 0x56:
239309b1170SImre Vadász 			sc->sc_have_sens = 0x7;
240309b1170SImre Vadász 			break;
241214ee9b5SImre Vadász 		/* Haswell, Broadwell */
242309b1170SImre Vadász 		case 0x3c:
243309b1170SImre Vadász 		case 0x3d:
244309b1170SImre Vadász 		case 0x45:
245309b1170SImre Vadász 		case 0x46:
246309b1170SImre Vadász 		case 0x47:
247214ee9b5SImre Vadász 			/* Check if Core or Xeon (Xeon CPUs might be 0x7) */
248214ee9b5SImre Vadász 			sc->sc_have_sens = 0xf;
249214ee9b5SImre Vadász 			break;
25023553b39SSascha Wildner 		/* Skylake, Kaby Lake, Coffee Lake */
251309b1170SImre Vadász 		case 0x4e:
252309b1170SImre Vadász 		case 0x5e:
253*9200010eSSascha Wildner 		case 0x8e:	/* Kaby Lake, Coffee Lake */
254*9200010eSSascha Wildner 		case 0x9e:	/* dito */
255214ee9b5SImre Vadász 			sc->sc_have_sens = 0x1f;
256309b1170SImre Vadász 			break;
257309b1170SImre Vadász 		/* Atom CPUs */
258309b1170SImre Vadász 		case 0x37:
259309b1170SImre Vadász 		case 0x4a:
260309b1170SImre Vadász 		case 0x4c:
261c13f7d35SImre Vadász 		case 0x4d:
262309b1170SImre Vadász 		case 0x5a:
263309b1170SImre Vadász 		case 0x5d:
264309b1170SImre Vadász 			sc->sc_have_sens = 0x5;
265c13f7d35SImre Vadász 			/* use quirk for Valleyview Atom CPUs */
266c13f7d35SImre Vadász 			sc->sc_is_atom = 1;
267309b1170SImre Vadász 			break;
268309b1170SImre Vadász 		default:
269309b1170SImre Vadász 			return (ENXIO);
270309b1170SImre Vadász 		}
271309b1170SImre Vadász 	}
272309b1170SImre Vadász 
273309b1170SImre Vadász 	val = rdmsr(MSR_RAPL_POWER_UNIT);
274309b1170SImre Vadász 
275309b1170SImre Vadász 	power_units = __SHIFTOUT(val, MSR_RAPL_POWER_UNIT_POWER);
276309b1170SImre Vadász 	energy_units = __SHIFTOUT(val, MSR_RAPL_POWER_UNIT_ENERGY);
277309b1170SImre Vadász 	time_units = __SHIFTOUT(val, MSR_RAPL_POWER_UNIT_TIME);
278309b1170SImre Vadász 
279c13f7d35SImre Vadász 	sc->sc_watt_unit = (1 << power_units);
280c13f7d35SImre Vadász 	sc->sc_joule_unit = (1 << energy_units);
281c13f7d35SImre Vadász 	sc->sc_second_unit = (1 << time_units);
282309b1170SImre Vadász 
283309b1170SImre Vadász 	/*
284309b1170SImre Vadász 	 * Add hw.sensors.cpu_nodeN MIB.
285309b1170SImre Vadász 	 */
286309b1170SImre Vadász 	cpu = device_get_unit(device_get_parent(dev));
287309b1170SImre Vadász 	ksnprintf(sc->sc_sensordev.xname, sizeof(sc->sc_sensordev.xname),
288309b1170SImre Vadász 	    "cpu_node%d", get_chip_ID(cpu));
289384c0addSImre Vadász 	if ((sc->sc_have_sens & 1) &&
290384c0addSImre Vadász 	    corepower_try(MSR_PKG_ENERGY_STATUS, "MSR_PKG_ENERGY_STATUS")) {
291309b1170SImre Vadász 		corepower_sens_init(&sc->sc_pkg_sens, "Package Power",
292309b1170SImre Vadász 		    MSR_PKG_ENERGY_STATUS, cpu);
293309b1170SImre Vadász 		sensor_attach(&sc->sc_sensordev, &sc->sc_pkg_sens.sensor);
294384c0addSImre Vadász 	} else {
295384c0addSImre Vadász 		sc->sc_have_sens &= ~1;
296309b1170SImre Vadász 	}
297384c0addSImre Vadász 	if ((sc->sc_have_sens & 2) &&
298384c0addSImre Vadász 	    corepower_try(MSR_DRAM_ENERGY_STATUS, "MSR_DRAM_ENERGY_STATUS")) {
299309b1170SImre Vadász 		corepower_sens_init(&sc->sc_dram_sens, "DRAM Power",
300309b1170SImre Vadász 		    MSR_DRAM_ENERGY_STATUS, cpu);
301309b1170SImre Vadász 		sensor_attach(&sc->sc_sensordev, &sc->sc_dram_sens.sensor);
302384c0addSImre Vadász 	} else {
303384c0addSImre Vadász 		sc->sc_have_sens &= ~2;
304309b1170SImre Vadász 	}
305384c0addSImre Vadász 	if ((sc->sc_have_sens & 4) &&
306384c0addSImre Vadász 	    corepower_try(MSR_PP0_ENERGY_STATUS, "MSR_PP0_ENERGY_STATUS")) {
307309b1170SImre Vadász 		corepower_sens_init(&sc->sc_pp0_sens, "Cores Power",
308309b1170SImre Vadász 		    MSR_PP0_ENERGY_STATUS, cpu);
309309b1170SImre Vadász 		sensor_attach(&sc->sc_sensordev, &sc->sc_pp0_sens.sensor);
310384c0addSImre Vadász 	} else {
311384c0addSImre Vadász 		sc->sc_have_sens &= ~4;
312309b1170SImre Vadász 	}
313384c0addSImre Vadász 	if ((sc->sc_have_sens & 8) &&
314384c0addSImre Vadász 	    corepower_try(MSR_PP1_ENERGY_STATUS, "MSR_PP1_ENERGY_STATUS")) {
315309b1170SImre Vadász 		corepower_sens_init(&sc->sc_pp1_sens, "Graphics Power",
316309b1170SImre Vadász 		    MSR_PP1_ENERGY_STATUS, cpu);
317309b1170SImre Vadász 		sensor_attach(&sc->sc_sensordev, &sc->sc_pp1_sens.sensor);
318384c0addSImre Vadász 	} else {
319384c0addSImre Vadász 		sc->sc_have_sens &= ~8;
320309b1170SImre Vadász 	}
321214ee9b5SImre Vadász 	if ((sc->sc_have_sens & 0x10) &&
322214ee9b5SImre Vadász 	    corepower_try(MSR_PLATFORM_ENERGY_COUNTER, "MSR_PLATFORM_ENERGY_COUNTER") &&
323214ee9b5SImre Vadász 	    (rdmsr(MSR_PLATFORM_ENERGY_COUNTER) & 0xffffffffU) != 0) {
324214ee9b5SImre Vadász 		corepower_sens_init(&sc->sc_platform_sens, "Platform Power",
325214ee9b5SImre Vadász 		    MSR_PLATFORM_ENERGY_COUNTER, cpu);
326214ee9b5SImre Vadász 		sensor_attach(&sc->sc_sensordev, &sc->sc_platform_sens.sensor);
327214ee9b5SImre Vadász 	} else {
328214ee9b5SImre Vadász 		sc->sc_have_sens &= ~0x10;
329214ee9b5SImre Vadász 	}
330309b1170SImre Vadász 
331384c0addSImre Vadász 	if (sc->sc_have_sens == 0)
332384c0addSImre Vadász 		return (ENXIO);
333384c0addSImre Vadász 
334309b1170SImre Vadász 	sc->sc_senstask = sensor_task_register2(sc, corepower_refresh, 1, cpu);
335309b1170SImre Vadász 
336309b1170SImre Vadász 	sensordev_install(&sc->sc_sensordev);
337309b1170SImre Vadász 
338309b1170SImre Vadász 	return (0);
339309b1170SImre Vadász }
340309b1170SImre Vadász 
341309b1170SImre Vadász static int
corepower_detach(device_t dev)342309b1170SImre Vadász corepower_detach(device_t dev)
343309b1170SImre Vadász {
344309b1170SImre Vadász 	struct corepower_softc *sc = device_get_softc(dev);
345309b1170SImre Vadász 
346309b1170SImre Vadász 	sensordev_deinstall(&sc->sc_sensordev);
347309b1170SImre Vadász 	sensor_task_unregister2(sc->sc_senstask);
348309b1170SImre Vadász 
349309b1170SImre Vadász 	return (0);
350309b1170SImre Vadász }
351309b1170SImre Vadász 
352309b1170SImre Vadász static uint32_t
corepower_energy_to_uwatts(struct corepower_softc * sc,uint32_t units,uint32_t secs)353309b1170SImre Vadász corepower_energy_to_uwatts(struct corepower_softc *sc, uint32_t units,
354309b1170SImre Vadász     uint32_t secs)
355309b1170SImre Vadász {
356309b1170SImre Vadász 	uint64_t val;
357309b1170SImre Vadász 
358c13f7d35SImre Vadász 	if (sc->sc_is_atom) {
359c13f7d35SImre Vadász 		val = ((uint64_t)units) * sc->sc_joule_unit;
360c13f7d35SImre Vadász 	} else {
361309b1170SImre Vadász 		val = ((uint64_t)units) * 1000ULL * 1000ULL;
362c13f7d35SImre Vadász 		val /= sc->sc_joule_unit;
363c13f7d35SImre Vadász 	}
364309b1170SImre Vadász 
365309b1170SImre Vadász 	return val / secs;
366309b1170SImre Vadász }
367309b1170SImre Vadász 
368309b1170SImre Vadász static void
corepower_refresh(void * arg)369309b1170SImre Vadász corepower_refresh(void *arg)
370309b1170SImre Vadász {
371309b1170SImre Vadász 	struct corepower_softc *sc = (struct corepower_softc *)arg;
372309b1170SImre Vadász 
373309b1170SImre Vadász 	if (sc->sc_have_sens & 1)
374309b1170SImre Vadász 		corepower_sens_update(sc, &sc->sc_pkg_sens);
375309b1170SImre Vadász 	if (sc->sc_have_sens & 2)
376309b1170SImre Vadász 		corepower_sens_update(sc, &sc->sc_dram_sens);
377309b1170SImre Vadász 	if (sc->sc_have_sens & 4)
378309b1170SImre Vadász 		corepower_sens_update(sc, &sc->sc_pp0_sens);
379309b1170SImre Vadász 	if (sc->sc_have_sens & 8)
380309b1170SImre Vadász 		corepower_sens_update(sc, &sc->sc_pp1_sens);
381214ee9b5SImre Vadász 	if (sc->sc_have_sens & 0x10)
382214ee9b5SImre Vadász 		corepower_sens_update(sc, &sc->sc_platform_sens);
383309b1170SImre Vadász }
384309b1170SImre Vadász 
385309b1170SImre Vadász static void
corepower_sens_init(struct corepower_sensor * sens,char * desc,u_int msr,int cpu)386309b1170SImre Vadász corepower_sens_init(struct corepower_sensor *sens, char *desc, u_int msr,
387309b1170SImre Vadász     int cpu)
388309b1170SImre Vadász {
389309b1170SImre Vadász 	ksnprintf(sens->sensor.desc, sizeof(sens->sensor.desc), "node%d %s",
390309b1170SImre Vadász 	    get_chip_ID(cpu), desc);
391309b1170SImre Vadász 	sens->sensor.type = SENSOR_WATTS;
392309b1170SImre Vadász 	sens->msr = msr;
393309b1170SImre Vadász 	sens->energy = rdmsr(sens->msr) & 0xffffffffU;
394309b1170SImre Vadász }
395309b1170SImre Vadász 
396309b1170SImre Vadász static void
corepower_sens_update(struct corepower_softc * sc,struct corepower_sensor * sens)397309b1170SImre Vadász corepower_sens_update(struct corepower_softc *sc,
398309b1170SImre Vadász     struct corepower_sensor *sens)
399309b1170SImre Vadász {
400309b1170SImre Vadász 	uint64_t a, res;
401309b1170SImre Vadász 
402309b1170SImre Vadász 	a = rdmsr(sens->msr) & 0xffffffffU;
403309b1170SImre Vadász 	if (sens->energy > a) {
404309b1170SImre Vadász 		res = (0x100000000ULL - sens->energy) + a;
405309b1170SImre Vadász 	} else {
406309b1170SImre Vadász 		res = a - sens->energy;
407309b1170SImre Vadász 	}
408309b1170SImre Vadász 	sens->energy = a;
409309b1170SImre Vadász 	sens->sensor.value = corepower_energy_to_uwatts(sc, res, 1);
410309b1170SImre Vadász }
411384c0addSImre Vadász 
412384c0addSImre Vadász static int
corepower_try(u_int msr,char * name)413384c0addSImre Vadász corepower_try(u_int msr, char *name)
414384c0addSImre Vadász {
415384c0addSImre Vadász 	uint64_t val;
416384c0addSImre Vadász 
417384c0addSImre Vadász 	if (rdmsr_safe(msr, &val) != 0) {
418384c0addSImre Vadász 		kprintf("msr %s (0x%08x) not available\n", name, msr);
419384c0addSImre Vadász 		return 0;
420384c0addSImre Vadász 	}
421384c0addSImre Vadász 	return 1;
422384c0addSImre Vadász }
423