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