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