1 /* $NetBSD: lm75.c,v 1.21 2010/02/28 11:36:27 martin 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.21 2010/02/28 11:36:27 martin Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/device.h> 44 #include <sys/kernel.h> 45 46 #include <dev/sysmon/sysmonvar.h> 47 48 #include <dev/i2c/i2cvar.h> 49 #include <dev/i2c/lm75reg.h> 50 51 struct lmtemp_softc { 52 i2c_tag_t sc_tag; 53 int sc_address; 54 55 struct sysmon_envsys *sc_sme; 56 envsys_data_t sc_sensor; 57 58 uint32_t (*sc_lmtemp_decode)(const uint8_t *); 59 }; 60 61 static int lmtemp_match(device_t, cfdata_t, void *); 62 static void lmtemp_attach(device_t, device_t, void *); 63 64 CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc), 65 lmtemp_match, lmtemp_attach, NULL, NULL); 66 67 static void lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 68 69 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 70 static uint32_t lmtemp_decode_lm75(const uint8_t *); 71 static uint32_t lmtemp_decode_ds75(const uint8_t *); 72 static uint32_t lmtemp_decode_lm77(const uint8_t *); 73 74 75 static const char * lmtemp_compats[] = { 76 "i2c-lm75", 77 /* 78 * see XXX in _attach() below: add code once non-lm75 matches are 79 * added here! 80 */ 81 NULL 82 }; 83 84 enum { 85 lmtemp_lm75 = 0, 86 lmtemp_ds75, 87 lmtemp_lm77, 88 }; 89 static const struct { 90 int lmtemp_type; 91 const char *lmtemp_name; 92 int lmtemp_addrmask; 93 int lmtemp_addr; 94 uint32_t (*lmtemp_decode)(const uint8_t *); 95 } lmtemptbl[] = { 96 { lmtemp_lm75, "LM75", 97 LM75_ADDRMASK, LM75_ADDR, lmtemp_decode_lm75 }, 98 { lmtemp_ds75, "DS75", 99 LM75_ADDRMASK, LM75_ADDR, lmtemp_decode_ds75 }, 100 { lmtemp_lm77, "LM77", 101 LM77_ADDRMASK, LM77_ADDR, lmtemp_decode_lm77 }, 102 103 { -1, NULL, 104 0, 0, NULL } 105 }; 106 107 static int 108 lmtemp_match(device_t parent, cfdata_t cf, void *aux) 109 { 110 struct i2c_attach_args *ia = aux; 111 int i; 112 113 if (ia->ia_name == NULL) { 114 /* 115 * Indirect config - not much we can do! 116 */ 117 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) 118 if (lmtemptbl[i].lmtemp_type == cf->cf_flags) 119 break; 120 if (lmtemptbl[i].lmtemp_type == -1) 121 return 0; 122 123 if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == 124 lmtemptbl[i].lmtemp_addr) 125 return 1; 126 } else { 127 /* 128 * Direct config - match via the list of compatible 129 * hardware. 130 */ 131 if (iic_compat_match(ia, lmtemp_compats)) 132 return 1; 133 } 134 135 136 return 0; 137 } 138 139 static void 140 lmtemp_attach(device_t parent, device_t self, void *aux) 141 { 142 struct lmtemp_softc *sc = device_private(self); 143 struct i2c_attach_args *ia = aux; 144 int i; 145 146 if (ia->ia_name == NULL) { 147 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) 148 if (lmtemptbl[i].lmtemp_type == 149 device_cfdata(self)->cf_flags) 150 break; 151 } else { 152 /* XXX - add code when adding other direct matches! */ 153 i = 0; 154 } 155 156 sc->sc_tag = ia->ia_tag; 157 sc->sc_address = ia->ia_addr; 158 159 aprint_naive(": Temperature Sensor\n"); 160 if (ia->ia_name) { 161 aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name, 162 lmtemptbl[i].lmtemp_name); 163 } else { 164 aprint_normal(": %s Temperature Sensor\n", 165 lmtemptbl[i].lmtemp_name); 166 } 167 168 /* Set the configuration of the LM75 to defaults. */ 169 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 170 if (lmtemp_config_write(sc, 0) != 0) { 171 aprint_error_dev(self, "unable to write config register\n"); 172 iic_release_bus(sc->sc_tag, I2C_F_POLL); 173 return; 174 } 175 iic_release_bus(sc->sc_tag, I2C_F_POLL); 176 177 sc->sc_sme = sysmon_envsys_create(); 178 /* Initialize sensor data. */ 179 sc->sc_sensor.units = ENVSYS_STEMP; 180 (void)strlcpy(sc->sc_sensor.desc, 181 ia->ia_name? ia->ia_name : device_xname(self), 182 sizeof(sc->sc_sensor.desc)); 183 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 184 sysmon_envsys_destroy(sc->sc_sme); 185 return; 186 } 187 188 sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode; 189 190 /* Hook into system monitor. */ 191 sc->sc_sme->sme_name = device_xname(self); 192 sc->sc_sme->sme_cookie = sc; 193 sc->sc_sme->sme_refresh = lmtemp_refresh; 194 195 if (sysmon_envsys_register(sc->sc_sme)) { 196 aprint_error_dev(self, "unable to register with sysmon\n"); 197 sysmon_envsys_destroy(sc->sc_sme); 198 } 199 } 200 201 static int 202 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 203 { 204 uint8_t cmdbuf[2]; 205 206 cmdbuf[0] = LM75_REG_CONFIG; 207 cmdbuf[1] = val; 208 209 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 210 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL); 211 } 212 213 static int 214 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp) 215 { 216 int error; 217 uint8_t cmdbuf[1]; 218 uint8_t buf[LM75_TEMP_LEN]; 219 220 cmdbuf[0] = which; 221 222 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 223 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 224 if (error) 225 return error; 226 227 *valp = sc->sc_lmtemp_decode(buf); 228 return 0; 229 } 230 231 static void 232 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 233 { 234 uint32_t val; 235 int error; 236 237 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val); 238 if (error) { 239 #if 0 240 aprint_error_dev(&sc->sc_dev, "unable to read temperature, error = %d\n", 241 error); 242 #endif 243 sc->sc_sensor.state = ENVSYS_SINVALID; 244 return; 245 } 246 247 sc->sc_sensor.value_cur = val; 248 sc->sc_sensor.state = ENVSYS_SVALID; 249 } 250 251 static void 252 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 253 { 254 struct lmtemp_softc *sc = sme->sme_cookie; 255 256 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 257 lmtemp_refresh_sensor_data(sc); 258 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 259 } 260 261 static uint32_t 262 lmtemp_decode_lm75(const uint8_t *buf) 263 { 264 int temp; 265 uint32_t val; 266 267 /* 268 * LM75 temps are the most-significant 9 bits of a 16-bit reg. 269 * sign-extend the MSB and add in the 0.5 from the LSB 270 */ 271 temp = (int8_t) buf[0]; 272 temp = (temp << 1) + ((buf[1] >> 7) & 0x1); 273 274 /* Temp is given in 1/2 deg. C, we convert to uK. */ 275 val = temp * 500000 + 273150000; 276 277 return val; 278 } 279 280 static uint32_t 281 lmtemp_decode_ds75(const uint8_t *buf) 282 { 283 int temp; 284 285 /* 286 * Sign-extend the MSB byte, and add in the fractions of a 287 * degree contained in the LSB (precision 1/16th DegC). 288 */ 289 temp = (int8_t)buf[0]; 290 temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 291 292 /* 293 * Conversion to uK is simple. 294 */ 295 return (temp * 62500 + 273150000); 296 } 297 298 static uint32_t 299 lmtemp_decode_lm77(const uint8_t *buf) 300 { 301 int temp; 302 uint32_t val; 303 304 /* 305 * Describe each bits of temperature registers on LM77. 306 * D15 - D12: Sign 307 * D11 - D3 : Bit8(MSB) - Bit0 308 */ 309 temp = (int8_t)buf[0]; 310 temp = (temp << 5) | ((buf[1] >> 3) & 0x1f); 311 312 /* Temp is given in 1/2 deg. C, we convert to uK. */ 313 val = temp * 500000 + 273150000; 314 315 return val; 316 } 317 318