]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/k8temp/k8temp.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / k8temp / k8temp.c
1 /*-
2  * Copyright (c) 2008 Rui Paulo <rpaulo@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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.
25  */
26
27 /*
28  * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the
29  * same name.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/systm.h>
38 #include <sys/types.h>
39 #include <sys/module.h>
40 #include <sys/conf.h>
41 #include <sys/kernel.h>
42 #include <sys/sysctl.h>
43
44 #include <machine/specialreg.h>
45 #include <machine/cpufunc.h>
46 #include <machine/md_var.h>
47
48 #include <dev/pci/pcireg.h>
49 #include <dev/pci/pcivar.h>
50
51 struct k8temp_softc {
52         device_t        sc_dev;
53         int             sc_temps[4];
54         int             sc_ntemps;
55         struct sysctl_oid *sc_oid;
56         struct sysctl_oid *sc_sysctl_cpu[2];
57         struct intr_config_hook sc_ich;
58 };
59
60 #define VENDORID_AMD            0x1022
61 #define DEVICEID_AMD_MISC       0x1103
62
63 static struct k8temp_product {
64         uint16_t        k8temp_vendorid;
65         uint16_t        k8temp_deviceid;
66 } k8temp_products[] = {
67         { VENDORID_AMD, DEVICEID_AMD_MISC },
68         { 0, 0 }
69 };
70
71 /*
72  * Register control
73  */
74 #define K8TEMP_REG              0xe4
75 #define K8TEMP_REG_SELSENSOR    0x40
76 #define K8TEMP_REG_SELCORE      0x04
77
78 #define K8TEMP_MINTEMP          49      /* -49 C is the mininum temperature */
79
80 typedef enum {
81         SENSOR0_CORE0,
82         SENSOR0_CORE1,
83         SENSOR1_CORE0,
84         SENSOR1_CORE1,
85         CORE0,
86         CORE1
87 } k8sensor_t;
88
89 /*
90  * Device methods.
91  */
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);
100
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),
107
108         {0, 0}
109 };
110
111 static driver_t k8temp_driver = {
112         "k8temp",
113         k8temp_methods,
114         sizeof(struct k8temp_softc),
115 };
116
117 static devclass_t k8temp_devclass;
118 DRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL);
119
120 static int
121 k8temp_match(device_t dev)
122 {
123         int i;
124         uint16_t vendor, devid;
125         
126         vendor = pci_get_vendor(dev);
127         devid = pci_get_device(dev);
128
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)
132                         return (1);
133         }
134
135         return (0);
136 }
137
138 static void
139 k8temp_identify(driver_t *driver, device_t parent)
140 {
141         device_t child;
142
143         /* Make sure we're not being doubly invoked. */
144         if (device_find_child(parent, "k8temp", -1) != NULL)
145                 return;
146         
147         if (k8temp_match(parent)) {
148                 child = device_add_child(parent, "k8temp", -1);
149                 if (child == NULL)
150                         device_printf(parent, "add k8temp child failed\n");
151         }
152     
153 }
154
155 static int
156 k8temp_probe(device_t dev)
157 {
158         uint32_t regs[4];
159         
160         if (resource_disabled("k8temp", 0))
161                 return (ENXIO);
162
163         do_cpuid(1, regs);
164         switch (regs[0]) {
165         case 0xf40:
166         case 0xf50:
167         case 0xf51:
168                 return (ENXIO);
169         }
170         device_set_desc(dev, "AMD K8 Thermal Sensors");
171         
172         return (BUS_PROBE_GENERIC);
173 }
174
175 static int
176 k8temp_attach(device_t dev)
177 {
178         struct k8temp_softc *sc = device_get_softc(dev);
179         struct sysctl_ctx_list *sysctlctx;
180         struct sysctl_oid *sysctlnode;
181
182
183         /*
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
186          * us.
187          */
188         sc->sc_ich.ich_func = k8temp_intrhook;
189         sc->sc_ich.ich_arg = dev;
190         config_intrhook_establish(&sc->sc_ich);
191         
192         /*
193          * dev.k8temp.N tree.
194          */
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");
199         
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");
205         
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");
211         
212         sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
213             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1",
214             CTLFLAG_RD, 0, "Sensor 1");
215         
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");
221         
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");
227
228         return (0);
229 }
230
231 void
232 k8temp_intrhook(void *arg)
233 {
234         int i;
235         device_t nexus, acpi, cpu;
236         device_t dev = (device_t) arg;
237         struct k8temp_softc *sc;
238         struct sysctl_ctx_list *sysctlctx;
239
240         sc = device_get_softc(dev);
241         
242         /*
243          * dev.cpu.N.temperature.
244          */
245         nexus = device_find_child(root_bus, "nexus", 0);
246         acpi = device_find_child(nexus, "acpi", 0);
247
248         for (i = 0; i < 2; i++) {
249                 cpu = device_find_child(acpi, "cpu",
250                     device_get_unit(dev) * 2 + i);
251                 if (cpu) {
252                         sysctlctx = device_get_sysctl_ctx(cpu);
253
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");
259                 }
260         }
261         config_intrhook_disestablish(&sc->sc_ich);
262 }
263
264 int
265 k8temp_detach(device_t dev)
266 {
267         int i;
268         struct k8temp_softc *sc = device_get_softc(dev);
269         
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);
273         }
274
275         /* NewBus removes the dev.k8temp.N tree by itself. */
276         
277         return (0);
278 }
279
280 static int
281 k8temp_sysctl(SYSCTL_HANDLER_ARGS)
282 {
283         device_t dev = (device_t) arg1;
284         int error;
285         int32_t temp, auxtemp[2];
286
287         switch (arg2) {
288         case CORE0:
289                 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0);
290                 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0);
291                 temp = imax(auxtemp[0], auxtemp[1]);
292                 break;
293         case CORE1:
294                 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1);
295                 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1);
296                 temp = imax(auxtemp[0], auxtemp[1]);
297                 break;
298         default:
299                 temp = k8temp_gettemp(dev, arg2);
300                 break;
301         }
302         error = sysctl_handle_int(oidp, &temp, 0, req);
303         
304         return (error);
305 }
306
307 static int32_t
308 k8temp_gettemp(device_t dev, k8sensor_t sensor)
309 {
310         uint8_t cfg;
311         uint32_t temp;
312         
313         cfg = pci_read_config(dev, K8TEMP_REG, 1);
314         switch (sensor) {
315         case SENSOR0_CORE0:
316                 cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
317                 break;
318         case SENSOR0_CORE1:
319                 cfg &= ~K8TEMP_REG_SELSENSOR;
320                 cfg |= K8TEMP_REG_SELCORE;
321                 break;
322         case SENSOR1_CORE0:
323                 cfg &= ~K8TEMP_REG_SELCORE;
324                 cfg |= K8TEMP_REG_SELSENSOR;
325                 break;
326         case SENSOR1_CORE1:
327                 cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
328                 break;
329         default:
330                 cfg = 0;
331                 break;
332         }
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;
336         
337         return (temp);
338 }