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: stable/10/sys/dev/jedec_ts/jedec_ts.c 329015 2018-02-08 09:24:23Z rpokala $");
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
56const 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
133static const char *
134ts_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 */
159static int
160ts_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
173static int
174ts_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
203static int
204ts_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
242static int
243ts_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
257static int
258ts_detach(device_t dev)
259{
260	return (0);
261}
262
263
264static 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
274static driver_t jedec_ts_driver = {
275	"jedec_ts",
276	jedec_ts_methods,
277	0	/* no softc */
278};
279
280static devclass_t jedec_ts_devclass;
281
282DRIVER_MODULE(jedec_ts, smbus, jedec_ts_driver, jedec_ts_devclass, 0, 0);
283MODULE_DEPEND(jedec_ts, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
284MODULE_VERSION(jedec_ts, 1);
285