2 * Copyright (c) 2008 Rui Paulo <rpaulo@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
28 * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/types.h>
39 #include <sys/module.h>
41 #include <sys/kernel.h>
42 #include <sys/sysctl.h>
44 #include <machine/specialreg.h>
45 #include <machine/cpufunc.h>
46 #include <machine/md_var.h>
48 #include <dev/pci/pcireg.h>
49 #include <dev/pci/pcivar.h>
55 struct sysctl_oid *sc_oid;
56 struct sysctl_oid *sc_sysctl_cpu[2];
57 struct intr_config_hook sc_ich;
60 #define VENDORID_AMD 0x1022
61 #define DEVICEID_AMD_MISC 0x1103
63 static struct k8temp_product {
64 uint16_t k8temp_vendorid;
65 uint16_t k8temp_deviceid;
66 } k8temp_products[] = {
67 { VENDORID_AMD, DEVICEID_AMD_MISC },
74 #define K8TEMP_REG 0xe4
75 #define K8TEMP_REG_SELSENSOR 0x40
76 #define K8TEMP_REG_SELCORE 0x04
78 #define K8TEMP_MINTEMP 49 /* -49 C is the mininum temperature */
92 static void k8temp_identify(driver_t *driver, device_t parent);
93 static int k8temp_probe(device_t dev);
94 static int k8temp_attach(device_t dev);
95 static void k8temp_intrhook(void *arg);
96 static int k8temp_detach(device_t dev);
97 static int k8temp_match(device_t dev);
98 static int32_t k8temp_gettemp(device_t dev, k8sensor_t sensor);
99 static int k8temp_sysctl(SYSCTL_HANDLER_ARGS);
101 static device_method_t k8temp_methods[] = {
102 /* Device interface */
103 DEVMETHOD(device_identify, k8temp_identify),
104 DEVMETHOD(device_probe, k8temp_probe),
105 DEVMETHOD(device_attach, k8temp_attach),
106 DEVMETHOD(device_detach, k8temp_detach),
111 static driver_t k8temp_driver = {
114 sizeof(struct k8temp_softc),
117 static devclass_t k8temp_devclass;
118 DRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL);
121 k8temp_match(device_t dev)
124 uint16_t vendor, devid;
126 vendor = pci_get_vendor(dev);
127 devid = pci_get_device(dev);
129 for (i = 0; k8temp_products[i].k8temp_vendorid != 0; i++) {
130 if (vendor == k8temp_products[i].k8temp_vendorid &&
131 devid == k8temp_products[i].k8temp_deviceid)
139 k8temp_identify(driver_t *driver, device_t parent)
143 /* Make sure we're not being doubly invoked. */
144 if (device_find_child(parent, "k8temp", -1) != NULL)
147 if (k8temp_match(parent)) {
148 child = device_add_child(parent, "k8temp", -1);
150 device_printf(parent, "add k8temp child failed\n");
156 k8temp_probe(device_t dev)
160 if (resource_disabled("k8temp", 0))
170 device_set_desc(dev, "AMD K8 Thermal Sensors");
172 return (BUS_PROBE_GENERIC);
176 k8temp_attach(device_t dev)
178 struct k8temp_softc *sc = device_get_softc(dev);
179 struct sysctl_ctx_list *sysctlctx;
180 struct sysctl_oid *sysctlnode;
184 * Setup intrhook function to create dev.cpu sysctl entries. This is
185 * needed because the cpu driver may be loaded late on boot, after
188 sc->sc_ich.ich_func = k8temp_intrhook;
189 sc->sc_ich.ich_arg = dev;
190 config_intrhook_establish(&sc->sc_ich);
195 sysctlctx = device_get_sysctl_ctx(dev);
196 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
197 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0",
198 CTLFLAG_RD, 0, "Sensor 0");
200 SYSCTL_ADD_PROC(sysctlctx,
201 SYSCTL_CHILDREN(sysctlnode),
202 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
203 dev, SENSOR0_CORE0, k8temp_sysctl, "I",
204 "Sensor 0 / Core 0 temperature");
206 SYSCTL_ADD_PROC(sysctlctx,
207 SYSCTL_CHILDREN(sysctlnode),
208 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
209 dev, SENSOR0_CORE1, k8temp_sysctl, "I",
210 "Sensor 0 / Core 1 temperature");
212 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
213 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1",
214 CTLFLAG_RD, 0, "Sensor 1");
216 SYSCTL_ADD_PROC(sysctlctx,
217 SYSCTL_CHILDREN(sysctlnode),
218 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
219 dev, SENSOR1_CORE0, k8temp_sysctl, "I",
220 "Sensor 1 / Core 0 temperature");
222 SYSCTL_ADD_PROC(sysctlctx,
223 SYSCTL_CHILDREN(sysctlnode),
224 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
225 dev, SENSOR1_CORE1, k8temp_sysctl, "I",
226 "Sensor 1 / Core 1 temperature");
232 k8temp_intrhook(void *arg)
235 device_t nexus, acpi, cpu;
236 device_t dev = (device_t) arg;
237 struct k8temp_softc *sc;
238 struct sysctl_ctx_list *sysctlctx;
240 sc = device_get_softc(dev);
243 * dev.cpu.N.temperature.
245 nexus = device_find_child(root_bus, "nexus", 0);
246 acpi = device_find_child(nexus, "acpi", 0);
248 for (i = 0; i < 2; i++) {
249 cpu = device_find_child(acpi, "cpu",
250 device_get_unit(dev) * 2 + i);
252 sysctlctx = device_get_sysctl_ctx(cpu);
254 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
255 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
256 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
257 dev, CORE0, k8temp_sysctl, "I",
258 "Max of sensor 0 / 1");
261 config_intrhook_disestablish(&sc->sc_ich);
265 k8temp_detach(device_t dev)
268 struct k8temp_softc *sc = device_get_softc(dev);
270 for (i = 0; i < 2; i++) {
271 if (sc->sc_sysctl_cpu[i])
272 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
275 /* NewBus removes the dev.k8temp.N tree by itself. */
281 k8temp_sysctl(SYSCTL_HANDLER_ARGS)
283 device_t dev = (device_t) arg1;
285 int32_t temp, auxtemp[2];
289 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0);
290 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0);
291 temp = imax(auxtemp[0], auxtemp[1]);
294 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1);
295 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1);
296 temp = imax(auxtemp[0], auxtemp[1]);
299 temp = k8temp_gettemp(dev, arg2);
302 error = sysctl_handle_int(oidp, &temp, 0, req);
308 k8temp_gettemp(device_t dev, k8sensor_t sensor)
313 cfg = pci_read_config(dev, K8TEMP_REG, 1);
316 cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
319 cfg &= ~K8TEMP_REG_SELSENSOR;
320 cfg |= K8TEMP_REG_SELCORE;
323 cfg &= ~K8TEMP_REG_SELCORE;
324 cfg |= K8TEMP_REG_SELSENSOR;
327 cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
333 pci_write_config(dev, K8TEMP_REG, cfg, 1);
334 temp = pci_read_config(dev, K8TEMP_REG, 4);
335 temp = ((temp >> 16) & 0xff) - K8TEMP_MINTEMP;