1 /* $NetBSD: lm75.c,v 1.25 2012/10/27 17:18:17 chs 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.25 2012/10/27 17:18:17 chs 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. 136 */ 137 if (iic_compat_match(ia, lmtemp_compats)) 138 return 1; 139 } 140 141 142 return 0; 143 } 144 145 static void 146 lmtemp_attach(device_t parent, device_t self, void *aux) 147 { 148 struct lmtemp_softc *sc = device_private(self); 149 struct i2c_attach_args *ia = aux; 150 int i; 151 152 sc->sc_dev = self; 153 if (ia->ia_name == NULL) { 154 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) 155 if (lmtemptbl[i].lmtemp_type == 156 device_cfdata(self)->cf_flags) 157 break; 158 } else { 159 /* XXX - add code when adding other direct matches! */ 160 i = 0; 161 } 162 163 sc->sc_tag = ia->ia_tag; 164 sc->sc_address = ia->ia_addr; 165 166 aprint_naive(": Temperature Sensor\n"); 167 if (ia->ia_name) { 168 aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name, 169 lmtemptbl[i].lmtemp_name); 170 } else { 171 aprint_normal(": %s Temperature Sensor\n", 172 lmtemptbl[i].lmtemp_name); 173 } 174 175 /* 176 * according to the LM75 data sheet 80C is the default, so leave it 177 * there to avoid unexpected behaviour 178 */ 179 sc->sc_tmax = 80; 180 if (i == lmtemp_lm75) 181 lmtemp_setup_sysctl(sc); 182 183 /* Set the configuration of the LM75 to defaults. */ 184 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 185 if (lmtemp_config_write(sc, LM75_CONFIG_FAULT_QUEUE_4) != 0) { 186 aprint_error_dev(self, "unable to write config register\n"); 187 iic_release_bus(sc->sc_tag, I2C_F_POLL); 188 return; 189 } 190 iic_release_bus(sc->sc_tag, I2C_F_POLL); 191 192 sc->sc_sme = sysmon_envsys_create(); 193 /* Initialize sensor data. */ 194 sc->sc_sensor.units = ENVSYS_STEMP; 195 sc->sc_sensor.state = ENVSYS_SINVALID; 196 (void)strlcpy(sc->sc_sensor.desc, 197 ia->ia_name? ia->ia_name : device_xname(self), 198 sizeof(sc->sc_sensor.desc)); 199 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 200 sysmon_envsys_destroy(sc->sc_sme); 201 return; 202 } 203 204 sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode; 205 206 /* Hook into system monitor. */ 207 sc->sc_sme->sme_name = device_xname(self); 208 sc->sc_sme->sme_cookie = sc; 209 sc->sc_sme->sme_refresh = lmtemp_refresh; 210 211 if (sysmon_envsys_register(sc->sc_sme)) { 212 aprint_error_dev(self, "unable to register with sysmon\n"); 213 sysmon_envsys_destroy(sc->sc_sme); 214 } 215 } 216 217 static int 218 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 219 { 220 uint8_t cmdbuf[2]; 221 222 cmdbuf[0] = LM75_REG_CONFIG; 223 cmdbuf[1] = val; 224 225 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 226 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL); 227 } 228 229 static int 230 lmtemp_temp_write(struct lmtemp_softc *sc, int reg, uint16_t val) 231 { 232 uint8_t cmdbuf[3]; 233 234 cmdbuf[0] = reg; 235 cmdbuf[1] = (val >> 1) & 0xff; 236 cmdbuf[2] = (val & 1) << 7; 237 238 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 239 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, I2C_F_POLL); 240 } 241 242 static int 243 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp) 244 { 245 int error; 246 uint8_t cmdbuf[1]; 247 uint8_t buf[LM75_TEMP_LEN]; 248 249 cmdbuf[0] = which; 250 251 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 252 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 253 if (error) 254 return error; 255 256 *valp = sc->sc_lmtemp_decode(buf); 257 return 0; 258 } 259 260 static void 261 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 262 { 263 uint32_t val; 264 int error; 265 266 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val); 267 if (error) { 268 #if 0 269 aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n", 270 error); 271 #endif 272 sc->sc_sensor.state = ENVSYS_SINVALID; 273 return; 274 } 275 276 sc->sc_sensor.value_cur = val; 277 sc->sc_sensor.state = ENVSYS_SVALID; 278 } 279 280 static void 281 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 282 { 283 struct lmtemp_softc *sc = sme->sme_cookie; 284 285 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 286 lmtemp_refresh_sensor_data(sc); 287 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 288 } 289 290 static uint32_t 291 lmtemp_decode_lm75(const uint8_t *buf) 292 { 293 int temp; 294 uint32_t val; 295 296 /* 297 * LM75 temps are the most-significant 9 bits of a 16-bit reg. 298 * sign-extend the MSB and add in the 0.5 from the LSB 299 */ 300 temp = (int8_t) buf[0]; 301 temp = (temp << 1) + ((buf[1] >> 7) & 0x1); 302 303 /* Temp is given in 1/2 deg. C, we convert to uK. */ 304 val = temp * 500000 + 273150000; 305 306 return val; 307 } 308 309 static uint32_t 310 lmtemp_decode_ds75(const uint8_t *buf) 311 { 312 int temp; 313 314 /* 315 * Sign-extend the MSB byte, and add in the fractions of a 316 * degree contained in the LSB (precision 1/16th DegC). 317 */ 318 temp = (int8_t)buf[0]; 319 temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 320 321 /* 322 * Conversion to uK is simple. 323 */ 324 return (temp * 62500 + 273150000); 325 } 326 327 static uint32_t 328 lmtemp_decode_lm77(const uint8_t *buf) 329 { 330 int temp; 331 uint32_t val; 332 333 /* 334 * Describe each bits of temperature registers on LM77. 335 * D15 - D12: Sign 336 * D11 - D3 : Bit8(MSB) - Bit0 337 */ 338 temp = (int8_t)buf[0]; 339 temp = (temp << 5) | ((buf[1] >> 3) & 0x1f); 340 341 /* Temp is given in 1/2 deg. C, we convert to uK. */ 342 val = temp * 500000 + 273150000; 343 344 return val; 345 } 346 347 static void 348 lmtemp_setup_sysctl(struct lmtemp_softc *sc) 349 { 350 const struct sysctlnode *me = NULL, *node = NULL; 351 352 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 353 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, (sc->sc_tmax - 5) * 2); 354 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, sc->sc_tmax * 2); 355 iic_release_bus(sc->sc_tag, I2C_F_POLL); 356 357 sysctl_createv(NULL, 0, NULL, &me, 358 CTLFLAG_READWRITE, 359 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 360 NULL, 0, NULL, 0, 361 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 362 363 sysctl_createv(NULL, 0, NULL, &node, 364 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 365 CTLTYPE_INT, "temp", "Threshold temperature", 366 sysctl_lm75_temp, 1, (void *)sc, 0, 367 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 368 } 369 370 static int 371 sysctl_lm75_temp(SYSCTLFN_ARGS) 372 { 373 struct sysctlnode node = *rnode; 374 struct lmtemp_softc *sc = node.sysctl_data; 375 int temp; 376 377 if (newp) { 378 379 /* we're asked to write */ 380 node.sysctl_data = &sc->sc_tmax; 381 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 382 383 temp = *(int *)node.sysctl_data; 384 sc->sc_tmax = temp; 385 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 386 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 387 (sc->sc_tmax - 5) * 2); 388 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, 389 sc->sc_tmax * 2); 390 iic_release_bus(sc->sc_tag, I2C_F_POLL); 391 return 0; 392 } 393 return EINVAL; 394 } else { 395 396 node.sysctl_data = &sc->sc_tmax; 397 node.sysctl_size = 4; 398 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 399 } 400 401 return 0; 402 } 403 404 SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup") 405 { 406 407 sysctl_createv(NULL, 0, NULL, NULL, 408 CTLFLAG_PERMANENT, 409 CTLTYPE_NODE, "machdep", NULL, 410 NULL, 0, NULL, 0, 411 CTL_MACHDEP, CTL_EOL); 412 } 413 414 415