1 /* $NetBSD: lm75.c,v 1.1 2003/09/30 00:35:31 thorpej 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 struct envsys_tre_data sc_sensor[1]; 54 struct envsys_basic_info sc_info[1]; 55 56 struct sysmon_envsys sc_sysmon; 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 *, 66 struct envsys_tre_data *); 67 static int lmtemp_streinfo(struct sysmon_envsys *, 68 struct envsys_basic_info *); 69 70 static const struct envsys_range lmtemp_ranges[] = { 71 { 0, 1, ENVSYS_STEMP }, 72 { 1, 0, -1 }, 73 }; 74 75 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 76 77 static int 78 lmtemp_match(struct device *parent, struct cfdata *cf, void *aux) 79 { 80 struct i2c_attach_args *ia = aux; 81 82 if ((ia->ia_addr & LM75_ADDRMASK) == LM75_ADDR) 83 return (1); 84 85 return (0); 86 } 87 88 static void 89 lmtemp_attach(struct device *parent, struct device *self, void *aux) 90 { 91 struct lmtemp_softc *sc = (struct lmtemp_softc *)self; 92 struct i2c_attach_args *ia = aux; 93 int ptype; 94 95 sc->sc_tag = ia->ia_tag; 96 sc->sc_address = ia->ia_addr; 97 98 aprint_naive(": Temperature Sensor\n"); 99 aprint_normal(": LM75 Temperature Sensor\n"); 100 101 /* Set the configuration of the LM75 to defaults. */ 102 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 103 if (lmtemp_config_write(sc, 0) != 0) { 104 aprint_error("%s: unable to write config register\n", 105 sc->sc_dev.dv_xname); 106 iic_release_bus(sc->sc_tag, I2C_F_POLL); 107 return; 108 } 109 iic_release_bus(sc->sc_tag, I2C_F_POLL); 110 111 /* Initialize sensor data. */ 112 sc->sc_sensor[0].sensor = sc->sc_info[0].sensor = 0; 113 sc->sc_sensor[0].validflags = ENVSYS_FVALID; 114 sc->sc_info[0].validflags = ENVSYS_FVALID; 115 sc->sc_sensor[0].warnflags = ENVSYS_WARN_OK; 116 117 sc->sc_sensor[0].units = sc->sc_info[0].units = ENVSYS_STEMP; 118 if (prop_get(dev_propdb, &sc->sc_dev, "description", 119 sc->sc_info[0].desc, sizeof(sc->sc_info[0].desc), 120 &ptype) < 1 || 121 ptype != PROP_STRING) 122 strcpy(sc->sc_info[0].desc, sc->sc_dev.dv_xname); 123 124 /* Hook info system monitor. */ 125 sc->sc_sysmon.sme_ranges = lmtemp_ranges; 126 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 127 sc->sc_sysmon.sme_sensor_data = sc->sc_sensor; 128 sc->sc_sysmon.sme_cookie = sc; 129 130 sc->sc_sysmon.sme_gtredata = lmtemp_gtredata; 131 sc->sc_sysmon.sme_streinfo = lmtemp_streinfo; 132 133 sc->sc_sysmon.sme_nsensors = 1; 134 sc->sc_sysmon.sme_envsys_version = 1000; 135 136 if (sysmon_envsys_register(&sc->sc_sysmon)) 137 aprint_error("%s: unable to register with sysmon\n", 138 sc->sc_dev.dv_xname); 139 } 140 141 static int 142 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 143 { 144 uint8_t cmdbuf[2]; 145 146 cmdbuf[0] = LM75_REG_CONFIG; 147 cmdbuf[1] = val; 148 149 return (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 150 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)); 151 } 152 153 static int 154 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp) 155 { 156 int error, neg, temp; 157 uint32_t val; 158 uint8_t cmdbuf[1]; 159 uint8_t buf[LM75_TEMP_LEN]; 160 161 cmdbuf[0] = which; 162 163 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 164 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 165 if (error) 166 return (error); 167 168 if (buf[0] & 1) { 169 /* Below 0C */ 170 temp = ~buf[1] + 1; 171 neg = 1; 172 } else { 173 temp = buf[1]; 174 neg = 0; 175 } 176 177 /* Temp is given in 1/2 deg. C, we convert to uK. */ 178 val = ((neg ? -temp : temp) / 2) * 1000000 + 273150000; 179 if (temp & 1) { 180 if (neg) 181 val -= 500000; 182 else 183 val += 500000; 184 } 185 *valp = val; 186 187 return (0); 188 } 189 190 static void 191 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 192 { 193 uint32_t val; 194 int error; 195 196 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val); 197 if (error) { 198 #if 0 199 printf("%s: unable to read temperature, error = %d\n", 200 sc->sc_dev.dv_xname, error); 201 #endif 202 sc->sc_sensor[0].validflags &= ~ENVSYS_FCURVALID; 203 return; 204 } 205 206 sc->sc_sensor[0].cur.data_us = val; 207 sc->sc_sensor[0].validflags |= ENVSYS_FCURVALID; 208 } 209 210 static int 211 lmtemp_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 212 { 213 struct lmtemp_softc *sc = sme->sme_cookie; 214 215 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 216 217 lmtemp_refresh_sensor_data(sc); 218 *tred = sc->sc_sensor[tred->sensor]; 219 220 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 221 222 return (0); 223 } 224 225 static int 226 lmtemp_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 227 { 228 struct lmtemp_softc *sc = sme->sme_cookie; 229 230 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */ 231 232 memcpy(sc->sc_info[binfo->sensor].desc, binfo->desc, 233 sizeof(sc->sc_info[binfo->sensor].desc)); 234 sc->sc_info[binfo->sensor].desc[ 235 sizeof(sc->sc_info[binfo->sensor].desc) - 1] = '\0'; 236 237 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 238 239 binfo->validflags = ENVSYS_FVALID; 240 241 return (0); 242 } 243