1 /* $NetBSD: lm75.c,v 1.26 2015/09/27 13:02:21 phx Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: lm75.c,v 1.26 2015/09/27 13:02:21 phx Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/device.h> 44 #include <sys/kernel.h> 45 #include <sys/sysctl.h> 46 47 #include <dev/sysmon/sysmonvar.h> 48 49 #include <dev/i2c/i2cvar.h> 50 #include <dev/i2c/lm75reg.h> 51 52 struct lmtemp_softc { 53 device_t sc_dev; 54 i2c_tag_t sc_tag; 55 int sc_address; 56 57 struct sysmon_envsys *sc_sme; 58 envsys_data_t sc_sensor; 59 int sc_tmax; 60 61 uint32_t (*sc_lmtemp_decode)(const uint8_t *); 62 }; 63 64 static int lmtemp_match(device_t, cfdata_t, void *); 65 static void lmtemp_attach(device_t, device_t, void *); 66 67 CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc), 68 lmtemp_match, lmtemp_attach, NULL, NULL); 69 70 static void lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 71 72 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 73 static int lmtemp_temp_write(struct lmtemp_softc *, int, uint16_t); 74 static uint32_t lmtemp_decode_lm75(const uint8_t *); 75 static uint32_t lmtemp_decode_ds75(const uint8_t *); 76 static uint32_t lmtemp_decode_lm77(const uint8_t *); 77 78 static void lmtemp_setup_sysctl(struct lmtemp_softc *); 79 static int sysctl_lm75_temp(SYSCTLFN_ARGS); 80 81 static const char * lmtemp_compats[] = { 82 "i2c-lm75", 83 /* 84 * see XXX in _attach() below: add code once non-lm75 matches are 85 * added here! 86 */ 87 NULL 88 }; 89 90 enum { 91 lmtemp_lm75 = 0, 92 lmtemp_ds75, 93 lmtemp_lm77, 94 }; 95 static const struct { 96 int lmtemp_type; 97 const char *lmtemp_name; 98 int lmtemp_addrmask; 99 int lmtemp_addr; 100 uint32_t (*lmtemp_decode)(const uint8_t *); 101 } lmtemptbl[] = { 102 { lmtemp_lm75, "LM75", 103 LM75_ADDRMASK, LM75_ADDR, lmtemp_decode_lm75 }, 104 { lmtemp_ds75, "DS75", 105 LM75_ADDRMASK, LM75_ADDR, lmtemp_decode_ds75 }, 106 { lmtemp_lm77, "LM77", 107 LM77_ADDRMASK, LM77_ADDR, lmtemp_decode_lm77 }, 108 109 { -1, NULL, 110 0, 0, NULL } 111 }; 112 113 static int 114 lmtemp_match(device_t parent, cfdata_t cf, void *aux) 115 { 116 struct i2c_attach_args *ia = aux; 117 int i; 118 119 if (ia->ia_name == NULL) { 120 /* 121 * Indirect config - not much we can do! 122 */ 123 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) 124 if (lmtemptbl[i].lmtemp_type == cf->cf_flags) 125 break; 126 if (lmtemptbl[i].lmtemp_type == -1) 127 return 0; 128 129 if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == 130 lmtemptbl[i].lmtemp_addr) 131 return 1; 132 } else { 133 /* 134 * Direct config - match via the list of compatible 135 * hardware or simply match the device name. 136 */ 137 if (ia->ia_ncompat > 0) { 138 if (iic_compat_match(ia, lmtemp_compats)) 139 return 1; 140 } else { 141 if (strcmp(ia->ia_name, "lmtemp") == 0) 142 return 1; 143 } 144 } 145 146 147 return 0; 148 } 149 150 static void 151 lmtemp_attach(device_t parent, device_t self, void *aux) 152 { 153 struct lmtemp_softc *sc = device_private(self); 154 struct i2c_attach_args *ia = aux; 155 int i; 156 157 sc->sc_dev = self; 158 if (ia->ia_name == NULL) { 159 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) 160 if (lmtemptbl[i].lmtemp_type == 161 device_cfdata(self)->cf_flags) 162 break; 163 } else { 164 /* XXX - add code when adding other direct matches! */ 165 i = 0; 166 } 167 168 sc->sc_tag = ia->ia_tag; 169 sc->sc_address = ia->ia_addr; 170 171 aprint_naive(": Temperature Sensor\n"); 172 if (ia->ia_name) { 173 aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name, 174 lmtemptbl[i].lmtemp_name); 175 } else { 176 aprint_normal(": %s Temperature Sensor\n", 177 lmtemptbl[i].lmtemp_name); 178 } 179 180 /* 181 * according to the LM75 data sheet 80C is the default, so leave it 182 * there to avoid unexpected behaviour 183 */ 184 sc->sc_tmax = 80; 185 if (i == lmtemp_lm75) 186 lmtemp_setup_sysctl(sc); 187 188 /* Set the configuration of the LM75 to defaults. */ 189 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 190 if (lmtemp_config_write(sc, LM75_CONFIG_FAULT_QUEUE_4) != 0) { 191 aprint_error_dev(self, "unable to write config register\n"); 192 iic_release_bus(sc->sc_tag, I2C_F_POLL); 193 return; 194 } 195 iic_release_bus(sc->sc_tag, I2C_F_POLL); 196 197 sc->sc_sme = sysmon_envsys_create(); 198 /* Initialize sensor data. */ 199 sc->sc_sensor.units = ENVSYS_STEMP; 200 sc->sc_sensor.state = ENVSYS_SINVALID; 201 (void)strlcpy(sc->sc_sensor.desc, 202 ia->ia_name? ia->ia_name : device_xname(self), 203 sizeof(sc->sc_sensor.desc)); 204 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 205 sysmon_envsys_destroy(sc->sc_sme); 206 return; 207 } 208 209 sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode; 210 211 /* Hook into system monitor. */ 212 sc->sc_sme->sme_name = device_xname(self); 213 sc->sc_sme->sme_cookie = sc; 214 sc->sc_sme->sme_refresh = lmtemp_refresh; 215 216 if (sysmon_envsys_register(sc->sc_sme)) { 217 aprint_error_dev(self, "unable to register with sysmon\n"); 218 sysmon_envsys_destroy(sc->sc_sme); 219 } 220 } 221 222 static int 223 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 224 { 225 uint8_t cmdbuf[2]; 226 227 cmdbuf[0] = LM75_REG_CONFIG; 228 cmdbuf[1] = val; 229 230 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 231 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL); 232 } 233 234 static int 235 lmtemp_temp_write(struct lmtemp_softc *sc, int reg, uint16_t val) 236 { 237 uint8_t cmdbuf[3]; 238 239 cmdbuf[0] = reg; 240 cmdbuf[1] = (val >> 1) & 0xff; 241 cmdbuf[2] = (val & 1) << 7; 242 243 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 244 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, I2C_F_POLL); 245 } 246 247 static int 248 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp) 249 { 250 int error; 251 uint8_t cmdbuf[1]; 252 uint8_t buf[LM75_TEMP_LEN]; 253 254 cmdbuf[0] = which; 255 256 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 257 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 258 if (error) 259 return error; 260 261 *valp = sc->sc_lmtemp_decode(buf); 262 return 0; 263 } 264 265 static void 266 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 267 { 268 uint32_t val; 269 int error; 270 271 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val); 272 if (error) { 273 #if 0 274 aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n", 275 error); 276 #endif 277 sc->sc_sensor.state = ENVSYS_SINVALID; 278 return; 279 } 280 281 sc->sc_sensor.value_cur = val; 282 sc->sc_sensor.state = ENVSYS_SVALID; 283 } 284 285 static void 286 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 287 { 288 struct lmtemp_softc *sc = sme->sme_cookie; 289 290 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 291 lmtemp_refresh_sensor_data(sc); 292 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 293 } 294 295 static uint32_t 296 lmtemp_decode_lm75(const uint8_t *buf) 297 { 298 int temp; 299 uint32_t val; 300 301 /* 302 * LM75 temps are the most-significant 9 bits of a 16-bit reg. 303 * sign-extend the MSB and add in the 0.5 from the LSB 304 */ 305 temp = (int8_t) buf[0]; 306 temp = (temp << 1) + ((buf[1] >> 7) & 0x1); 307 308 /* Temp is given in 1/2 deg. C, we convert to uK. */ 309 val = temp * 500000 + 273150000; 310 311 return val; 312 } 313 314 static uint32_t 315 lmtemp_decode_ds75(const uint8_t *buf) 316 { 317 int temp; 318 319 /* 320 * Sign-extend the MSB byte, and add in the fractions of a 321 * degree contained in the LSB (precision 1/16th DegC). 322 */ 323 temp = (int8_t)buf[0]; 324 temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 325 326 /* 327 * Conversion to uK is simple. 328 */ 329 return (temp * 62500 + 273150000); 330 } 331 332 static uint32_t 333 lmtemp_decode_lm77(const uint8_t *buf) 334 { 335 int temp; 336 uint32_t val; 337 338 /* 339 * Describe each bits of temperature registers on LM77. 340 * D15 - D12: Sign 341 * D11 - D3 : Bit8(MSB) - Bit0 342 */ 343 temp = (int8_t)buf[0]; 344 temp = (temp << 5) | ((buf[1] >> 3) & 0x1f); 345 346 /* Temp is given in 1/2 deg. C, we convert to uK. */ 347 val = temp * 500000 + 273150000; 348 349 return val; 350 } 351 352 static void 353 lmtemp_setup_sysctl(struct lmtemp_softc *sc) 354 { 355 const struct sysctlnode *me = NULL, *node = NULL; 356 357 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 358 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, (sc->sc_tmax - 5) * 2); 359 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, sc->sc_tmax * 2); 360 iic_release_bus(sc->sc_tag, I2C_F_POLL); 361 362 sysctl_createv(NULL, 0, NULL, &me, 363 CTLFLAG_READWRITE, 364 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 365 NULL, 0, NULL, 0, 366 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 367 368 sysctl_createv(NULL, 0, NULL, &node, 369 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 370 CTLTYPE_INT, "temp", "Threshold temperature", 371 sysctl_lm75_temp, 1, (void *)sc, 0, 372 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 373 } 374 375 static int 376 sysctl_lm75_temp(SYSCTLFN_ARGS) 377 { 378 struct sysctlnode node = *rnode; 379 struct lmtemp_softc *sc = node.sysctl_data; 380 int temp; 381 382 if (newp) { 383 384 /* we're asked to write */ 385 node.sysctl_data = &sc->sc_tmax; 386 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 387 388 temp = *(int *)node.sysctl_data; 389 sc->sc_tmax = temp; 390 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 391 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 392 (sc->sc_tmax - 5) * 2); 393 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, 394 sc->sc_tmax * 2); 395 iic_release_bus(sc->sc_tag, I2C_F_POLL); 396 return 0; 397 } 398 return EINVAL; 399 } else { 400 401 node.sysctl_data = &sc->sc_tmax; 402 node.sysctl_size = 4; 403 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 404 } 405 406 return 0; 407 } 408 409 SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup") 410 { 411 412 sysctl_createv(NULL, 0, NULL, NULL, 413 CTLFLAG_PERMANENT, 414 CTLTYPE_NODE, "machdep", NULL, 415 NULL, 0, NULL, 0, 416 CTL_MACHDEP, CTL_EOL); 417 } 418 419 420