]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/jedec_ts/jedec_ts.c
jedec_ts(4) uses a sysctl format specifier of "IK4", to indicate that it
[FreeBSD/stable/10.git] / sys / dev / jedec_ts / jedec_ts.c
1 /*-
2  * Copyright (c) 2016 Andriy Gapon <avg@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 WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/bus.h>
32 #include <sys/endian.h>
33 #include <sys/module.h>
34 #include <sys/rman.h>
35 #include <sys/sysctl.h>
36 #include <sys/systm.h>
37
38 #include <dev/smbus/smbconf.h>
39 #include <dev/smbus/smbus.h>
40
41 #include "smbus_if.h"
42
43
44 /*
45  * General device identification notes.
46  *
47  * The JEDEC TSE2004av specification defines the device ID that all compliant
48  * devices should use, but very few do in practice.  Maybe that's because
49  * TSE2002av was rather vague about that.
50  * Rare examples are IDT TSE2004GB2B0 and Atmel AT30TSE004A, not sure if
51  * they are TSE2004av compliant by design or by accident.
52  * Also, the specification mandates that PCI SIG manufacturer IDs are to be
53  * used, but in practice the JEDEC manufacturer IDs are often used.
54  */
55
56 const struct ts_dev {
57         uint16_t        vendor_id;
58         uint8_t         device_id;
59         const char      *description;
60 } known_devices[] = {
61         /*
62          * Analog Devices ADT7408.
63          * http://www.analog.com/media/en/technical-documentation/data-sheets/ADT7408.pdf
64          */
65         { 0x11d4, 0x08, "Analog Devices DIMM temperature sensor" },
66
67         /*
68          * Atmel AT30TSE002B, AT30TSE004A.
69          * http://www.atmel.com/images/doc8711.pdf
70          * http://www.atmel.com/images/atmel-8868-dts-at30tse004a-datasheet.pdf
71          * Note how one chip uses the JEDEC Manufacturer ID while the other
72          * uses the PCI SIG one.
73          */
74         { 0x001f, 0x82, "Atmel DIMM temperature sensor" },
75         { 0x1114, 0x22, "Atmel DIMM temperature sensor" },
76
77         /*
78          * Integrated Device Technology (IDT) TS3000B3A, TSE2002B3C,
79          * TSE2004GB2B0 chips and their variants.
80          * http://www.idt.com/sites/default/files/documents/IDT_TSE2002B3C_DST_20100512_120303152056.pdf
81          * http://www.idt.com/sites/default/files/documents/IDT_TS3000B3A_DST_20101129_120303152013.pdf
82          * https://www.idt.com/document/dst/tse2004gb2b0-datasheet
83          */
84         { 0x00b3, 0x29, "IDT DIMM temperature sensor" },
85         { 0x00b3, 0x22, "IDT DIMM temperature sensor" },
86
87         /*
88          * Maxim Integrated MAX6604.
89          * Different document revisions specify different Device IDs.
90          * Document 19-3837; Rev 0; 10/05 has 0x3e00 while
91          * 19-3837; Rev 3; 10/11 has 0x5400.
92          * http://datasheets.maximintegrated.com/en/ds/MAX6604.pdf
93          */
94         { 0x004d, 0x3e, "Maxim Integrated DIMM temperature sensor" },
95         { 0x004d, 0x54, "Maxim Integrated DIMM temperature sensor" },
96
97         /*
98          * Microchip Technology MCP9805, MCP9843, MCP98242, MCP98243
99          * and their variants.
100          * http://ww1.microchip.com/downloads/en/DeviceDoc/21977b.pdf
101          * Microchip Technology EMC1501.
102          * http://ww1.microchip.com/downloads/en/DeviceDoc/00001605A.pdf
103          */
104         { 0x0054, 0x00, "Microchip DIMM temperature sensor" },
105         { 0x0054, 0x20, "Microchip DIMM temperature sensor" },
106         { 0x0054, 0x21, "Microchip DIMM temperature sensor" },
107         { 0x1055, 0x08, "Microchip DIMM temperature sensor" },
108
109         /*
110          * NXP Semiconductors SE97 and SE98.
111          * http://www.nxp.com/docs/en/data-sheet/SE97B.pdf
112          */
113         { 0x1131, 0xa1, "NXP DIMM temperature sensor" },
114         { 0x1131, 0xa2, "NXP DIMM temperature sensor" },
115
116         /*
117          * ON Semiconductor CAT34TS02 revisions B and C, CAT6095 and compatible.
118          * https://www.onsemi.com/pub/Collateral/CAT34TS02-D.PDF
119          * http://www.onsemi.com/pub/Collateral/CAT6095-D.PDF
120          */
121         { 0x1b09, 0x08, "ON Semiconductor DIMM temperature sensor" },
122         { 0x1b09, 0x0a, "ON Semiconductor DIMM temperature sensor" },
123
124         /*
125          * ST[Microelectronics] STTS424E02, STTS2002 and others.
126          * http://www.st.com/resource/en/datasheet/cd00157558.pdf
127          * http://www.st.com/resource/en/datasheet/stts2002.pdf
128          */
129         { 0x104a, 0x00, "ST DIMM temperature sensor" },
130         { 0x104a, 0x03, "ST DIMM temperature sensor" },
131 };
132
133 static const char *
134 ts_match_device(uint16_t vid, uint16_t did)
135 {
136         const struct ts_dev *d;
137         int i;
138
139         for (i = 0; i < nitems(known_devices); i++) {
140                 d = &known_devices[i];
141                 if (vid == d->vendor_id && (did >> 8) == d->device_id)
142                         return (d->description);
143         }
144
145         /*
146          * If no match for a specific device, then check
147          * for a generic TSE2004av compliant device.
148          */
149         if ((did >> 8) == 0x22)
150                 return ("TSE2004av compliant DIMM temperature sensor");
151         return (NULL);
152 }
153
154 /*
155  * SMBus specification defines little-endian byte order,
156  * but it seems that the JEDEC devices expect it to
157  * be big-endian.
158  */
159 static int
160 ts_readw_be(device_t dev, uint8_t reg, uint16_t *val)
161 {
162         device_t bus = device_get_parent(dev);
163         uint8_t addr = smbus_get_addr(dev);
164         int err;
165
166         err = smbus_readw(bus, addr, reg, val);
167         if (err != 0)
168                 return (err);
169         *val = be16toh(*val);
170         return (0);
171 }
172
173 static int
174 ts_temp_sysctl(SYSCTL_HANDLER_ARGS)
175 {
176         device_t dev = arg1;
177         int err;
178         int temp;
179         uint16_t val;
180
181         err = ts_readw_be(dev, 5, &val);
182         if (err != 0)
183                 return (EIO);
184
185         /*
186          * Convert the reading to temperature in 0.0001 Kelvins.
187          * Three most significant bits are flags, the next
188          * most significant bit is a sign bit.
189          * Each step is 0.0625 degrees.
190          */
191         temp = val & 0xfff;
192         if ((val & 0x1000) != 0)
193                 temp = -temp;
194         temp = temp * 625 + 2731500;
195
196         /* sysctl(8) reports deciKelvin, so round accordingly. */
197         temp = (temp + 500) / 1000;
198
199         err = sysctl_handle_int(oidp, &temp, 0, req);
200         return (err);
201 }
202
203 static int
204 ts_probe(device_t dev)
205 {
206         const char *match;
207         int err;
208         uint16_t vendorid;
209         uint16_t devid;
210         uint8_t addr;
211
212         addr = smbus_get_addr(dev);
213         if ((addr & 0xf0) != 0x30) {
214                 /* Up to 8 slave devices starting at 0x30. */
215                 return (ENXIO);
216         }
217
218         err = ts_readw_be(dev, 6, &vendorid);
219         if (err != 0) {
220                 device_printf(dev, "failed to read Manufacturer ID\n");
221                 return (ENXIO);
222         }
223         err = ts_readw_be(dev, 7, &devid);
224         if (err != 0) {
225                 device_printf(dev, "failed to read Device ID\n");
226                 return (ENXIO);
227         }
228
229         match = ts_match_device(vendorid, devid);
230         if (match == NULL) {
231                 if (bootverbose) {
232                         device_printf(dev, "Unknown Manufacturer and Device IDs"
233                             ", 0x%x and 0x%x\n", vendorid, devid);
234                 }
235                 return (ENXIO);
236         }
237
238         device_set_desc(dev, match);
239         return (BUS_PROBE_DEFAULT);
240 }
241
242 static int
243 ts_attach(device_t dev)
244 {
245         struct sysctl_ctx_list *ctx;
246         struct sysctl_oid_list *tree;
247
248         ctx = device_get_sysctl_ctx(dev);
249         tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
250         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temp",
251             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
252             ts_temp_sysctl, "IK", "Current temperature");
253
254         return (0);
255 }
256
257 static int
258 ts_detach(device_t dev)
259 {
260         return (0);
261 }
262
263
264 static device_method_t jedec_ts_methods[] = {
265         /* Methods from the device interface */
266         DEVMETHOD(device_probe,         ts_probe),
267         DEVMETHOD(device_attach,        ts_attach),
268         DEVMETHOD(device_detach,        ts_detach),
269
270         /* Terminate method list */
271         { 0, 0 }
272 };
273
274 static driver_t jedec_ts_driver = {
275         "jedec_ts",
276         jedec_ts_methods,
277         0       /* no softc */
278 };
279
280 static devclass_t jedec_ts_devclass;
281
282 DRIVER_MODULE(jedec_ts, smbus, jedec_ts_driver, jedec_ts_devclass, 0, 0);
283 MODULE_DEPEND(jedec_ts, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
284 MODULE_VERSION(jedec_ts, 1);