1 /* $NetBSD: lm75.c,v 1.17 2007/12/11 12:09:22 lukem 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.17 2007/12/11 12:09:22 lukem 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 struct device sc_dev; 53 i2c_tag_t sc_tag; 54 int sc_address; 55 56 envsys_data_t sc_sensor; 57 struct sysmon_envsys *sc_sme; 58 59 uint32_t (*sc_lmtemp_decode)(const uint8_t *); 60 }; 61 62 static int lmtemp_match(struct device *, struct cfdata *, void *); 63 static void lmtemp_attach(struct device *, struct device *, void *); 64 65 CFATTACH_DECL(lmtemp, sizeof(struct lmtemp_softc), 66 lmtemp_match, lmtemp_attach, NULL, NULL); 67 68 static void lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 69 70 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 71 static uint32_t lmtemp_decode_lm75(const uint8_t *); 72 static uint32_t lmtemp_decode_ds75(const uint8_t *); 73 static uint32_t lmtemp_decode_lm77(const uint8_t *); 74 75 enum { 76 lmtemp_lm75 = 0, 77 lmtemp_ds75, 78 lmtemp_lm77, 79 }; 80 static const struct { 81 int lmtemp_type; 82 const char *lmtemp_name; 83 int lmtemp_addrmask; 84 int lmtemp_addr; 85 uint32_t (*lmtemp_decode)(const uint8_t *); 86 } lmtemptbl[] = { 87 { lmtemp_lm75, "LM75", 88 LM75_ADDRMASK, LM75_ADDR, lmtemp_decode_lm75 }, 89 { lmtemp_ds75, "DS75", 90 LM75_ADDRMASK, LM75_ADDR, lmtemp_decode_ds75 }, 91 { lmtemp_lm77, "LM77", 92 LM77_ADDRMASK, LM77_ADDR, lmtemp_decode_lm77 }, 93 94 { -1, NULL, 95 0, 0, NULL } 96 }; 97 98 static int 99 lmtemp_match(struct device *parent, struct cfdata *cf, void *aux) 100 { 101 struct i2c_attach_args *ia = aux; 102 int i; 103 104 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) 105 if (lmtemptbl[i].lmtemp_type == cf->cf_flags) 106 break; 107 if (lmtemptbl[i].lmtemp_type == -1) 108 return (0); 109 110 if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == 111 lmtemptbl[i].lmtemp_addr) 112 return (1); 113 114 return (0); 115 } 116 117 static void 118 lmtemp_attach(struct device *parent, struct device *self, void *aux) 119 { 120 struct lmtemp_softc *sc = device_private(self); 121 struct i2c_attach_args *ia = aux; 122 int i; 123 124 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) 125 if (lmtemptbl[i].lmtemp_type == 126 device_cfdata(&sc->sc_dev)->cf_flags) 127 break; 128 129 sc->sc_tag = ia->ia_tag; 130 sc->sc_address = ia->ia_addr; 131 132 aprint_naive(": Temperature Sensor\n"); 133 aprint_normal(": %s Temperature Sensor\n", lmtemptbl[i].lmtemp_name); 134 135 /* Set the configuration of the LM75 to defaults. */ 136 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 137 if (lmtemp_config_write(sc, 0) != 0) { 138 aprint_error("%s: unable to write config register\n", 139 sc->sc_dev.dv_xname); 140 iic_release_bus(sc->sc_tag, I2C_F_POLL); 141 return; 142 } 143 iic_release_bus(sc->sc_tag, I2C_F_POLL); 144 145 sc->sc_sme = sysmon_envsys_create(); 146 /* Initialize sensor data. */ 147 sc->sc_sensor.units = ENVSYS_STEMP; 148 (void)strlcpy(sc->sc_sensor.desc, 149 sc->sc_dev.dv_xname, sizeof(sc->sc_sensor.desc)); 150 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 151 sysmon_envsys_destroy(sc->sc_sme); 152 return; 153 } 154 155 sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode; 156 157 /* Hook into system monitor. */ 158 sc->sc_sme->sme_name = sc->sc_dev.dv_xname; 159 sc->sc_sme->sme_cookie = sc; 160 sc->sc_sme->sme_refresh = lmtemp_refresh; 161 162 if (sysmon_envsys_register(sc->sc_sme)) { 163 aprint_error("%s: unable to register with sysmon\n", 164 sc->sc_dev.dv_xname); 165 sysmon_envsys_destroy(sc->sc_sme); 166 } 167 } 168 169 static int 170 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 171 { 172 uint8_t cmdbuf[2]; 173 174 cmdbuf[0] = LM75_REG_CONFIG; 175 cmdbuf[1] = val; 176 177 return (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 178 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)); 179 } 180 181 static int 182 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp) 183 { 184 int error; 185 uint8_t cmdbuf[1]; 186 uint8_t buf[LM75_TEMP_LEN]; 187 188 cmdbuf[0] = which; 189 190 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 191 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 192 if (error) 193 return (error); 194 195 *valp = sc->sc_lmtemp_decode(buf); 196 return (0); 197 } 198 199 static void 200 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 201 { 202 uint32_t val; 203 int error; 204 205 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val); 206 if (error) { 207 #if 0 208 printf("%s: unable to read temperature, error = %d\n", 209 sc->sc_dev.dv_xname, error); 210 #endif 211 sc->sc_sensor.state = ENVSYS_SINVALID; 212 return; 213 } 214 215 sc->sc_sensor.value_cur = val; 216 sc->sc_sensor.state = ENVSYS_SVALID; 217 } 218 219 static void 220 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 221 { 222 struct lmtemp_softc *sc = sme->sme_cookie; 223 224 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 225 lmtemp_refresh_sensor_data(sc); 226 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 227 } 228 229 static uint32_t 230 lmtemp_decode_lm75(const uint8_t *buf) 231 { 232 int neg, temp; 233 uint32_t val; 234 235 if (buf[0] & 1) { 236 /* Below 0C */ 237 temp = ~buf[1] + 1; 238 neg = 1; 239 } else { 240 temp = buf[1]; 241 neg = 0; 242 } 243 244 /* Temp is given in 1/2 deg. C, we convert to uK. */ 245 val = ((neg ? -temp : temp) / 2) * 1000000 + 273150000; 246 if (temp & 1) { 247 if (neg) 248 val -= 500000; 249 else 250 val += 500000; 251 } 252 253 return (val); 254 } 255 256 static uint32_t 257 lmtemp_decode_ds75(const uint8_t *buf) 258 { 259 int temp; 260 261 /* 262 * Sign-extend the MSB byte, and add in the fractions of a 263 * degree contained in the LSB (precision 1/16th DegC). 264 */ 265 temp = (int8_t)buf[0]; 266 temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 267 268 /* 269 * Conversion to uK is simple. 270 */ 271 return (temp * 62500 + 273150000); 272 } 273 274 static uint32_t 275 lmtemp_decode_lm77(const uint8_t *buf) 276 { 277 int temp; 278 uint32_t val; 279 280 /* 281 * Describe each bits of temperature registers on LM77. 282 * D15 - D12: Sign 283 * D11 - D3 : Bit8(MSB) - Bit0 284 */ 285 temp = (int8_t)buf[0]; 286 temp = (temp << 5) | ((buf[1] >> 3) & 0x1f); 287 288 /* Temp is given in 1/2 deg. C, we convert to uK. */ 289 val = temp * 500000 + 273150000; 290 291 return (val); 292 } 293 294