1 /* $NetBSD: lm75.c,v 1.2 2004/08/03 13:40:20 scw 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/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 #include <sys/kernel.h> 42 43 #include <dev/sysmon/sysmonvar.h> 44 45 #include <dev/i2c/i2cvar.h> 46 #include <dev/i2c/lm75reg.h> 47 48 struct lmtemp_softc { 49 struct device sc_dev; 50 i2c_tag_t sc_tag; 51 int sc_address; 52 int sc_is_ds75; 53 54 struct envsys_tre_data sc_sensor[1]; 55 struct envsys_basic_info sc_info[1]; 56 57 struct sysmon_envsys sc_sysmon; 58 }; 59 60 static int lmtemp_match(struct device *, struct cfdata *, void *); 61 static void lmtemp_attach(struct device *, struct device *, void *); 62 63 CFATTACH_DECL(lmtemp, sizeof(struct lmtemp_softc), 64 lmtemp_match, lmtemp_attach, NULL, NULL); 65 66 static int lmtemp_gtredata(struct sysmon_envsys *, 67 struct envsys_tre_data *); 68 static int lmtemp_streinfo(struct sysmon_envsys *, 69 struct envsys_basic_info *); 70 71 static const struct envsys_range lmtemp_ranges[] = { 72 { 0, 1, ENVSYS_STEMP }, 73 { 1, 0, -1 }, 74 }; 75 76 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 77 static uint32_t lmtemp_decode_lm75(const uint8_t *); 78 static uint32_t lmtemp_decode_ds75(const uint8_t *); 79 80 static int 81 lmtemp_match(struct device *parent, struct cfdata *cf, void *aux) 82 { 83 struct i2c_attach_args *ia = aux; 84 85 if ((ia->ia_addr & LM75_ADDRMASK) == LM75_ADDR) 86 return (1); 87 88 return (0); 89 } 90 91 static void 92 lmtemp_attach(struct device *parent, struct device *self, void *aux) 93 { 94 struct lmtemp_softc *sc = (struct lmtemp_softc *)self; 95 struct i2c_attach_args *ia = aux; 96 int ptype; 97 98 sc->sc_tag = ia->ia_tag; 99 sc->sc_address = ia->ia_addr; 100 sc->sc_is_ds75 = sc->sc_dev.dv_cfdata->cf_flags & 1; 101 102 aprint_naive(": Temperature Sensor\n"); 103 aprint_normal(": %s Temperature Sensor\n", 104 (sc->sc_is_ds75) ? "DS75" : "LM75"); 105 106 /* Set the configuration of the LM75 to defaults. */ 107 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 108 if (lmtemp_config_write(sc, 0) != 0) { 109 aprint_error("%s: unable to write config register\n", 110 sc->sc_dev.dv_xname); 111 iic_release_bus(sc->sc_tag, I2C_F_POLL); 112 return; 113 } 114 iic_release_bus(sc->sc_tag, I2C_F_POLL); 115 116 /* Initialize sensor data. */ 117 sc->sc_sensor[0].sensor = sc->sc_info[0].sensor = 0; 118 sc->sc_sensor[0].validflags = ENVSYS_FVALID; 119 sc->sc_info[0].validflags = ENVSYS_FVALID; 120 sc->sc_sensor[0].warnflags = ENVSYS_WARN_OK; 121 122 sc->sc_sensor[0].units = sc->sc_info[0].units = ENVSYS_STEMP; 123 if (prop_get(dev_propdb, &sc->sc_dev, "description", 124 sc->sc_info[0].desc, sizeof(sc->sc_info[0].desc), 125 &ptype) < 1 || 126 ptype != PROP_STRING) 127 strcpy(sc->sc_info[0].desc, sc->sc_dev.dv_xname); 128 129 /* Hook info system monitor. */ 130 sc->sc_sysmon.sme_ranges = lmtemp_ranges; 131 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 132 sc->sc_sysmon.sme_sensor_data = sc->sc_sensor; 133 sc->sc_sysmon.sme_cookie = sc; 134 135 sc->sc_sysmon.sme_gtredata = lmtemp_gtredata; 136 sc->sc_sysmon.sme_streinfo = lmtemp_streinfo; 137 138 sc->sc_sysmon.sme_nsensors = 1; 139 sc->sc_sysmon.sme_envsys_version = 1000; 140 141 if (sysmon_envsys_register(&sc->sc_sysmon)) 142 aprint_error("%s: unable to register with sysmon\n", 143 sc->sc_dev.dv_xname); 144 } 145 146 static int 147 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 148 { 149 uint8_t cmdbuf[2]; 150 151 cmdbuf[0] = LM75_REG_CONFIG; 152 cmdbuf[1] = val; 153 154 return (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 155 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)); 156 } 157 158 static int 159 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp) 160 { 161 int error; 162 uint8_t cmdbuf[1]; 163 uint8_t buf[LM75_TEMP_LEN]; 164 165 cmdbuf[0] = which; 166 167 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 168 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 169 if (error) 170 return (error); 171 172 if (sc->sc_is_ds75) 173 *valp = lmtemp_decode_ds75(buf); 174 else 175 *valp = lmtemp_decode_lm75(buf); 176 177 return (0); 178 } 179 180 static void 181 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 182 { 183 uint32_t val; 184 int error; 185 186 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val); 187 if (error) { 188 #if 0 189 printf("%s: unable to read temperature, error = %d\n", 190 sc->sc_dev.dv_xname, error); 191 #endif 192 sc->sc_sensor[0].validflags &= ~ENVSYS_FCURVALID; 193 return; 194 } 195 196 sc->sc_sensor[0].cur.data_us = val; 197 sc->sc_sensor[0].validflags |= ENVSYS_FCURVALID; 198 } 199 200 static int 201 lmtemp_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 202 { 203 struct lmtemp_softc *sc = sme->sme_cookie; 204 205 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 206 207 lmtemp_refresh_sensor_data(sc); 208 *tred = sc->sc_sensor[tred->sensor]; 209 210 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 211 212 return (0); 213 } 214 215 static int 216 lmtemp_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 217 { 218 struct lmtemp_softc *sc = sme->sme_cookie; 219 220 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 221 222 memcpy(sc->sc_info[binfo->sensor].desc, binfo->desc, 223 sizeof(sc->sc_info[binfo->sensor].desc)); 224 sc->sc_info[binfo->sensor].desc[ 225 sizeof(sc->sc_info[binfo->sensor].desc) - 1] = '\0'; 226 227 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 228 229 binfo->validflags = ENVSYS_FVALID; 230 231 return (0); 232 } 233 234 static uint32_t 235 lmtemp_decode_lm75(const uint8_t *buf) 236 { 237 int neg, temp; 238 uint32_t val; 239 240 if (buf[0] & 1) { 241 /* Below 0C */ 242 temp = ~buf[1] + 1; 243 neg = 1; 244 } else { 245 temp = buf[1]; 246 neg = 0; 247 } 248 249 /* Temp is given in 1/2 deg. C, we convert to uK. */ 250 val = ((neg ? -temp : temp) / 2) * 1000000 + 273150000; 251 if (temp & 1) { 252 if (neg) 253 val -= 500000; 254 else 255 val += 500000; 256 } 257 258 return (val); 259 } 260 261 static uint32_t 262 lmtemp_decode_ds75(const uint8_t *buf) 263 { 264 int temp; 265 266 /* 267 * Sign-extend the MSB byte, and add in the fractions of a 268 * degree contained in the LSB (prescision 1/16th DegC). 269 */ 270 temp = (int8_t)buf[0]; 271 temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 272 273 /* 274 * Conversion to uK is simple. 275 */ 276 return (temp * 62500 + 273150000); 277 } 278 279