1 1.49 thorpej /* $NetBSD: lm75.c,v 1.49 2025/09/21 13:54:56 thorpej Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 2003 Wasabi Systems, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 1.1 thorpej * 9 1.1 thorpej * Redistribution and use in source and binary forms, with or without 10 1.1 thorpej * modification, are permitted provided that the following conditions 11 1.1 thorpej * are met: 12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer. 14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 16 1.1 thorpej * documentation and/or other materials provided with the distribution. 17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 18 1.1 thorpej * must display the following acknowledgement: 19 1.1 thorpej * This product includes software developed for the NetBSD Project by 20 1.1 thorpej * Wasabi Systems, Inc. 21 1.1 thorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 1.1 thorpej * or promote products derived from this software without specific prior 23 1.1 thorpej * written permission. 24 1.1 thorpej * 25 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 36 1.1 thorpej */ 37 1.1 thorpej 38 1.17 lukem #include <sys/cdefs.h> 39 1.49 thorpej __KERNEL_RCSID(0, "$NetBSD: lm75.c,v 1.49 2025/09/21 13:54:56 thorpej Exp $"); 40 1.17 lukem 41 1.1 thorpej #include <sys/param.h> 42 1.1 thorpej #include <sys/systm.h> 43 1.1 thorpej #include <sys/device.h> 44 1.1 thorpej #include <sys/kernel.h> 45 1.23 macallan #include <sys/sysctl.h> 46 1.1 thorpej 47 1.1 thorpej #include <dev/sysmon/sysmonvar.h> 48 1.1 thorpej 49 1.1 thorpej #include <dev/i2c/i2cvar.h> 50 1.1 thorpej #include <dev/i2c/lm75reg.h> 51 1.1 thorpej 52 1.1 thorpej struct lmtemp_softc { 53 1.23 macallan device_t sc_dev; 54 1.1 thorpej i2c_tag_t sc_tag; 55 1.1 thorpej int sc_address; 56 1.1 thorpej 57 1.18 xtraeme struct sysmon_envsys *sc_sme; 58 1.16 xtraeme envsys_data_t sc_sensor; 59 1.23 macallan int sc_tmax; 60 1.28 jdc uint32_t sc_smax, sc_smin, sc_scrit; 61 1.9 kiyohara 62 1.27 jdc uint32_t (*sc_lmtemp_decode)(const uint8_t *, int); 63 1.28 jdc void (*sc_lmtemp_encode)(const uint32_t, uint8_t *, int); 64 1.1 thorpej }; 65 1.1 thorpej 66 1.18 xtraeme static int lmtemp_match(device_t, cfdata_t, void *); 67 1.18 xtraeme static void lmtemp_attach(device_t, device_t, void *); 68 1.1 thorpej 69 1.18 xtraeme CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc), 70 1.1 thorpej lmtemp_match, lmtemp_attach, NULL, NULL); 71 1.1 thorpej 72 1.16 xtraeme static void lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 73 1.18 xtraeme static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 74 1.28 jdc static int lmtemp_temp_write(struct lmtemp_softc *, uint8_t, uint32_t, 75 1.28 jdc int); 76 1.27 jdc static int lmtemp_temp_read(struct lmtemp_softc *, uint8_t, uint32_t *, 77 1.27 jdc int); 78 1.27 jdc static uint32_t lmtemp_decode_lm75(const uint8_t *, int); 79 1.27 jdc static uint32_t lmtemp_decode_ds75(const uint8_t *, int); 80 1.27 jdc static uint32_t lmtemp_decode_lm77(const uint8_t *, int); 81 1.28 jdc static void lmtemp_encode_lm75(const uint32_t, uint8_t *, int); 82 1.28 jdc static void lmtemp_encode_ds75(const uint32_t, uint8_t *, int); 83 1.28 jdc static void lmtemp_encode_lm77(const uint32_t, uint8_t *, int); 84 1.28 jdc static void lmtemp_getlim_lm75(struct sysmon_envsys *, envsys_data_t *, 85 1.28 jdc sysmon_envsys_lim_t *, uint32_t *); 86 1.28 jdc static void lmtemp_getlim_lm77(struct sysmon_envsys *, envsys_data_t *, 87 1.28 jdc sysmon_envsys_lim_t *, uint32_t *); 88 1.28 jdc static void lmtemp_setlim_lm75(struct sysmon_envsys *, envsys_data_t *, 89 1.28 jdc sysmon_envsys_lim_t *, uint32_t *); 90 1.28 jdc static void lmtemp_setlim_lm77(struct sysmon_envsys *, envsys_data_t *, 91 1.28 jdc sysmon_envsys_lim_t *, uint32_t *); 92 1.9 kiyohara 93 1.23 macallan static void lmtemp_setup_sysctl(struct lmtemp_softc *); 94 1.23 macallan static int sysctl_lm75_temp(SYSCTLFN_ARGS); 95 1.21 martin 96 1.41 thorpej enum { 97 1.41 thorpej lmtemp_lm75 = 0, 98 1.41 thorpej lmtemp_ds75 = 1, 99 1.41 thorpej lmtemp_lm77 = 2, 100 1.41 thorpej }; 101 1.41 thorpej 102 1.33 thorpej static const struct device_compatible_entry compat_data[] = { 103 1.41 thorpej { .compat = "national,lm75", .value = lmtemp_lm75 }, 104 1.41 thorpej { .compat = "i2c-lm75", .value = lmtemp_lm75 }, 105 1.41 thorpej { .compat = "lm75", .value = lmtemp_lm75 }, 106 1.41 thorpej 107 1.41 thorpej /* XXX Linux treats ds1775 and ds75 differently. */ 108 1.41 thorpej { .compat = "dallas,ds1775", .value = lmtemp_ds75 }, 109 1.41 thorpej { .compat = "ds1775", .value = lmtemp_ds75 }, 110 1.41 thorpej 111 1.41 thorpej { .compat = "national,lm77", .value = lmtemp_lm77 }, 112 1.41 thorpej 113 1.21 martin /* 114 1.21 martin * see XXX in _attach() below: add code once non-lm75 matches are 115 1.21 martin * added here! 116 1.21 martin */ 117 1.39 thorpej DEVICE_COMPAT_EOL 118 1.32 thorpej }; 119 1.32 thorpej 120 1.9 kiyohara static const struct { 121 1.9 kiyohara const char *lmtemp_name; 122 1.9 kiyohara int lmtemp_addrmask; 123 1.9 kiyohara int lmtemp_addr; 124 1.27 jdc uint32_t (*lmtemp_decode)(const uint8_t *, int); 125 1.28 jdc void (*lmtemp_encode)(const uint32_t, uint8_t *, int); 126 1.28 jdc void (*lmtemp_getlim)(struct sysmon_envsys *, envsys_data_t *, 127 1.28 jdc sysmon_envsys_lim_t *, uint32_t *); 128 1.28 jdc void (*lmtemp_setlim)(struct sysmon_envsys *, envsys_data_t *, 129 1.28 jdc sysmon_envsys_lim_t *, uint32_t *); 130 1.9 kiyohara } lmtemptbl[] = { 131 1.41 thorpej [lmtemp_lm75] = 132 1.41 thorpej { 133 1.41 thorpej .lmtemp_name = "LM75", 134 1.41 thorpej .lmtemp_addrmask = LM75_ADDRMASK, 135 1.41 thorpej .lmtemp_addr = LM75_ADDR, 136 1.41 thorpej .lmtemp_decode = lmtemp_decode_lm75, 137 1.41 thorpej .lmtemp_encode = lmtemp_encode_lm75, 138 1.41 thorpej .lmtemp_getlim = lmtemp_getlim_lm75, 139 1.41 thorpej .lmtemp_setlim = lmtemp_setlim_lm75, 140 1.41 thorpej }, 141 1.41 thorpej [lmtemp_ds75] = 142 1.41 thorpej { 143 1.41 thorpej .lmtemp_name = "DS75", 144 1.41 thorpej .lmtemp_addrmask = LM75_ADDRMASK, 145 1.41 thorpej .lmtemp_addr = LM75_ADDR, 146 1.41 thorpej .lmtemp_decode = lmtemp_decode_ds75, 147 1.41 thorpej .lmtemp_encode = lmtemp_encode_ds75, 148 1.41 thorpej .lmtemp_getlim = lmtemp_getlim_lm75, 149 1.41 thorpej .lmtemp_setlim = lmtemp_setlim_lm75, 150 1.41 thorpej }, 151 1.41 thorpej [lmtemp_lm77] = 152 1.41 thorpej { 153 1.41 thorpej .lmtemp_name = "LM77", 154 1.41 thorpej .lmtemp_addrmask = LM77_ADDRMASK, 155 1.41 thorpej .lmtemp_addr = LM77_ADDR, 156 1.41 thorpej .lmtemp_decode = lmtemp_decode_lm77, 157 1.41 thorpej .lmtemp_encode = lmtemp_encode_lm77, 158 1.41 thorpej .lmtemp_getlim = lmtemp_getlim_lm77, 159 1.41 thorpej .lmtemp_setlim = lmtemp_setlim_lm77, 160 1.41 thorpej }, 161 1.9 kiyohara }; 162 1.1 thorpej 163 1.1 thorpej static int 164 1.18 xtraeme lmtemp_match(device_t parent, cfdata_t cf, void *aux) 165 1.1 thorpej { 166 1.1 thorpej struct i2c_attach_args *ia = aux; 167 1.31 thorpej int i, match_result; 168 1.9 kiyohara 169 1.33 thorpej if (iic_use_direct_match(ia, cf, compat_data, &match_result)) 170 1.31 thorpej return match_result; 171 1.21 martin 172 1.31 thorpej /* 173 1.31 thorpej * Indirect config - not much we can do! 174 1.31 thorpej */ 175 1.41 thorpej for (i = 0; i < __arraycount(lmtemptbl); i++) { 176 1.41 thorpej if (i == cf->cf_flags) { 177 1.31 thorpej break; 178 1.41 thorpej } 179 1.41 thorpej } 180 1.41 thorpej if (i == __arraycount(lmtemptbl)) { 181 1.31 thorpej return 0; 182 1.41 thorpej } 183 1.31 thorpej 184 1.31 thorpej if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == 185 1.31 thorpej lmtemptbl[i].lmtemp_addr) 186 1.31 thorpej return I2C_MATCH_ADDRESS_ONLY; 187 1.1 thorpej 188 1.18 xtraeme return 0; 189 1.1 thorpej } 190 1.1 thorpej 191 1.1 thorpej static void 192 1.18 xtraeme lmtemp_attach(device_t parent, device_t self, void *aux) 193 1.1 thorpej { 194 1.6 thorpej struct lmtemp_softc *sc = device_private(self); 195 1.1 thorpej struct i2c_attach_args *ia = aux; 196 1.41 thorpej const struct device_compatible_entry *dce; 197 1.30 macallan char name[64]; 198 1.36 macallan const char *desc; 199 1.9 kiyohara int i; 200 1.48 macallan uint8_t config = LM75_CONFIG_FAULT_QUEUE_4; 201 1.9 kiyohara 202 1.23 macallan sc->sc_dev = self; 203 1.41 thorpej dce = iic_compatible_lookup(ia, compat_data); 204 1.41 thorpej if (dce != NULL) { 205 1.41 thorpej i = (int)dce->value; 206 1.41 thorpej } else { 207 1.41 thorpej for (i = 0; i < __arraycount(lmtemptbl); i++) { 208 1.41 thorpej if (i == device_cfdata(self)->cf_flags) { 209 1.21 martin break; 210 1.41 thorpej } 211 1.30 macallan } 212 1.41 thorpej KASSERT(i < __arraycount(lmtemptbl)); 213 1.21 martin } 214 1.1 thorpej 215 1.1 thorpej sc->sc_tag = ia->ia_tag; 216 1.1 thorpej sc->sc_address = ia->ia_addr; 217 1.1 thorpej 218 1.1 thorpej aprint_naive(": Temperature Sensor\n"); 219 1.21 martin if (ia->ia_name) { 220 1.21 martin aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name, 221 1.21 martin lmtemptbl[i].lmtemp_name); 222 1.21 martin } else { 223 1.21 martin aprint_normal(": %s Temperature Sensor\n", 224 1.21 martin lmtemptbl[i].lmtemp_name); 225 1.21 martin } 226 1.1 thorpej 227 1.27 jdc sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode; 228 1.28 jdc sc->sc_lmtemp_encode = lmtemptbl[i].lmtemp_encode; 229 1.27 jdc 230 1.44 mlelstv if (iic_acquire_bus(sc->sc_tag, 0)) { 231 1.44 mlelstv aprint_error_dev(self, 232 1.44 mlelstv "unable to acquire I2C bus\n"); 233 1.44 mlelstv return; 234 1.44 mlelstv } 235 1.27 jdc 236 1.28 jdc /* Read temperature limit(s) and remember initial value(s). */ 237 1.28 jdc if (i == lmtemp_lm77) { 238 1.29 jdc if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, 239 1.29 jdc &sc->sc_scrit, 1) != 0) { 240 1.29 jdc aprint_error_dev(self, 241 1.29 jdc "unable to read low register\n"); 242 1.35 thorpej iic_release_bus(sc->sc_tag, 0); 243 1.29 jdc return; 244 1.29 jdc } 245 1.28 jdc if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT, 246 1.29 jdc &sc->sc_smin, 1) != 0) { 247 1.28 jdc aprint_error_dev(self, 248 1.28 jdc "unable to read low register\n"); 249 1.35 thorpej iic_release_bus(sc->sc_tag, 0); 250 1.28 jdc return; 251 1.28 jdc } 252 1.28 jdc if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT, 253 1.28 jdc &sc->sc_smax, 1) != 0) { 254 1.28 jdc aprint_error_dev(self, 255 1.28 jdc "unable to read high register\n"); 256 1.35 thorpej iic_release_bus(sc->sc_tag, 0); 257 1.28 jdc return; 258 1.28 jdc } 259 1.29 jdc } else { /* LM75 or compatible */ 260 1.29 jdc if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, 261 1.29 jdc &sc->sc_smax, 1) != 0) { 262 1.29 jdc aprint_error_dev(self, "unable to read Tos register\n"); 263 1.35 thorpej iic_release_bus(sc->sc_tag, 0); 264 1.29 jdc return; 265 1.29 jdc } 266 1.28 jdc } 267 1.29 jdc sc->sc_tmax = sc->sc_smax; 268 1.27 jdc 269 1.23 macallan if (i == lmtemp_lm75) 270 1.23 macallan lmtemp_setup_sysctl(sc); 271 1.23 macallan 272 1.48 macallan /* DS75 has better resolution */ 273 1.48 macallan if (i == lmtemp_ds75) 274 1.48 macallan config = LM75_CONFIG_FAULT_QUEUE_4 | DS75_CONFIG_RES_11BIT; 275 1.48 macallan 276 1.1 thorpej /* Set the configuration of the LM75 to defaults. */ 277 1.48 macallan if (lmtemp_config_write(sc, config) != 0) { 278 1.18 xtraeme aprint_error_dev(self, "unable to write config register\n"); 279 1.35 thorpej iic_release_bus(sc->sc_tag, 0); 280 1.1 thorpej return; 281 1.1 thorpej } 282 1.35 thorpej iic_release_bus(sc->sc_tag, 0); 283 1.1 thorpej 284 1.16 xtraeme sc->sc_sme = sysmon_envsys_create(); 285 1.1 thorpej /* Initialize sensor data. */ 286 1.47 skrll sc->sc_sensor.units = ENVSYS_STEMP; 287 1.47 skrll sc->sc_sensor.state = ENVSYS_SINVALID; 288 1.47 skrll sc->sc_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 289 1.30 macallan 290 1.30 macallan (void)strlcpy(name, 291 1.21 martin ia->ia_name? ia->ia_name : device_xname(self), 292 1.18 xtraeme sizeof(sc->sc_sensor.desc)); 293 1.36 macallan 294 1.49 thorpej if (prop_dictionary_get_string(device_properties(self), "s00", &desc)) { 295 1.36 macallan strncpy(name, desc, 64); 296 1.30 macallan } 297 1.36 macallan 298 1.30 macallan (void)strlcpy(sc->sc_sensor.desc, name, 299 1.30 macallan sizeof(sc->sc_sensor.desc)); 300 1.16 xtraeme if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 301 1.16 xtraeme sysmon_envsys_destroy(sc->sc_sme); 302 1.16 xtraeme return; 303 1.16 xtraeme } 304 1.1 thorpej 305 1.7 riz /* Hook into system monitor. */ 306 1.18 xtraeme sc->sc_sme->sme_name = device_xname(self); 307 1.16 xtraeme sc->sc_sme->sme_cookie = sc; 308 1.16 xtraeme sc->sc_sme->sme_refresh = lmtemp_refresh; 309 1.28 jdc sc->sc_sme->sme_get_limits = lmtemptbl[i].lmtemp_getlim; 310 1.28 jdc sc->sc_sme->sme_set_limits = lmtemptbl[i].lmtemp_setlim; 311 1.1 thorpej 312 1.16 xtraeme if (sysmon_envsys_register(sc->sc_sme)) { 313 1.18 xtraeme aprint_error_dev(self, "unable to register with sysmon\n"); 314 1.16 xtraeme sysmon_envsys_destroy(sc->sc_sme); 315 1.16 xtraeme } 316 1.1 thorpej } 317 1.1 thorpej 318 1.1 thorpej static int 319 1.1 thorpej lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 320 1.1 thorpej { 321 1.1 thorpej uint8_t cmdbuf[2]; 322 1.1 thorpej 323 1.1 thorpej cmdbuf[0] = LM75_REG_CONFIG; 324 1.1 thorpej cmdbuf[1] = val; 325 1.1 thorpej 326 1.18 xtraeme return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 327 1.35 thorpej sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0); 328 1.1 thorpej } 329 1.1 thorpej 330 1.1 thorpej static int 331 1.28 jdc lmtemp_temp_write(struct lmtemp_softc *sc, uint8_t reg, uint32_t val, int degc) 332 1.23 macallan { 333 1.23 macallan uint8_t cmdbuf[3]; 334 1.23 macallan 335 1.23 macallan cmdbuf[0] = reg; 336 1.28 jdc sc->sc_lmtemp_encode(val, &cmdbuf[1], degc); 337 1.23 macallan 338 1.23 macallan return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 339 1.35 thorpej sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, 0); 340 1.23 macallan } 341 1.23 macallan 342 1.23 macallan static int 343 1.27 jdc lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp, 344 1.27 jdc int degc) 345 1.1 thorpej { 346 1.2 scw int error; 347 1.1 thorpej uint8_t cmdbuf[1]; 348 1.1 thorpej uint8_t buf[LM75_TEMP_LEN]; 349 1.1 thorpej 350 1.1 thorpej cmdbuf[0] = which; 351 1.1 thorpej 352 1.1 thorpej error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 353 1.1 thorpej sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 354 1.1 thorpej if (error) 355 1.18 xtraeme return error; 356 1.1 thorpej 357 1.27 jdc *valp = sc->sc_lmtemp_decode(buf, degc); 358 1.18 xtraeme return 0; 359 1.1 thorpej } 360 1.1 thorpej 361 1.1 thorpej static void 362 1.1 thorpej lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 363 1.1 thorpej { 364 1.1 thorpej uint32_t val; 365 1.1 thorpej int error; 366 1.1 thorpej 367 1.27 jdc error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val, 0); 368 1.1 thorpej if (error) { 369 1.1 thorpej #if 0 370 1.25 chs aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n", 371 1.19 cegger error); 372 1.1 thorpej #endif 373 1.16 xtraeme sc->sc_sensor.state = ENVSYS_SINVALID; 374 1.1 thorpej return; 375 1.1 thorpej } 376 1.1 thorpej 377 1.16 xtraeme sc->sc_sensor.value_cur = val; 378 1.16 xtraeme sc->sc_sensor.state = ENVSYS_SVALID; 379 1.1 thorpej } 380 1.1 thorpej 381 1.16 xtraeme static void 382 1.16 xtraeme lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 383 1.1 thorpej { 384 1.1 thorpej struct lmtemp_softc *sc = sme->sme_cookie; 385 1.1 thorpej 386 1.44 mlelstv if (iic_acquire_bus(sc->sc_tag, 0)) /* also locks our instance */ 387 1.44 mlelstv return; 388 1.1 thorpej lmtemp_refresh_sensor_data(sc); 389 1.1 thorpej iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 390 1.1 thorpej } 391 1.2 scw 392 1.28 jdc static void 393 1.28 jdc lmtemp_getlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata, 394 1.28 jdc sysmon_envsys_lim_t *limits, uint32_t *props) 395 1.28 jdc { 396 1.28 jdc struct lmtemp_softc *sc = sme->sme_cookie; 397 1.28 jdc uint32_t val; 398 1.28 jdc 399 1.28 jdc *props &= ~(PROP_CRITMAX); 400 1.28 jdc 401 1.44 mlelstv if (iic_acquire_bus(sc->sc_tag, 0)) 402 1.44 mlelstv return; 403 1.28 jdc if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, &val, 0) == 0) { 404 1.28 jdc limits->sel_critmax = val; 405 1.28 jdc *props |= PROP_CRITMAX; 406 1.28 jdc } 407 1.29 jdc iic_release_bus(sc->sc_tag, 0); 408 1.28 jdc } 409 1.28 jdc 410 1.28 jdc static void 411 1.28 jdc lmtemp_getlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata, 412 1.28 jdc sysmon_envsys_lim_t *limits, uint32_t *props) 413 1.28 jdc { 414 1.28 jdc struct lmtemp_softc *sc = sme->sme_cookie; 415 1.28 jdc uint32_t val; 416 1.28 jdc 417 1.28 jdc *props &= ~(PROP_CRITMAX | PROP_WARNMAX | PROP_WARNMIN); 418 1.28 jdc 419 1.44 mlelstv if (iic_acquire_bus(sc->sc_tag, 0)) 420 1.44 mlelstv return; 421 1.28 jdc if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, &val, 0) == 0) { 422 1.28 jdc limits->sel_critmax = val; 423 1.28 jdc *props |= PROP_CRITMAX; 424 1.28 jdc } 425 1.28 jdc if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT, &val, 0) == 0) { 426 1.28 jdc limits->sel_warnmax = val; 427 1.28 jdc *props |= PROP_WARNMAX; 428 1.28 jdc } 429 1.28 jdc if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT, &val, 0) == 0) { 430 1.28 jdc limits->sel_warnmin = val; 431 1.28 jdc *props |= PROP_WARNMIN; 432 1.28 jdc } 433 1.29 jdc iic_release_bus(sc->sc_tag, 0); 434 1.28 jdc } 435 1.28 jdc 436 1.28 jdc static void 437 1.46 skrll lmtemp_setlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata, 438 1.28 jdc sysmon_envsys_lim_t *limits, uint32_t *props) 439 1.28 jdc { 440 1.28 jdc struct lmtemp_softc *sc = sme->sme_cookie; 441 1.28 jdc int32_t limit; 442 1.28 jdc 443 1.28 jdc if (*props & PROP_CRITMAX) { 444 1.28 jdc if (limits == NULL) /* Restore defaults */ 445 1.28 jdc limit = sc->sc_smax; 446 1.28 jdc else 447 1.28 jdc limit = limits->sel_critmax; 448 1.44 mlelstv if (iic_acquire_bus(sc->sc_tag, 0)) 449 1.44 mlelstv return; 450 1.28 jdc lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 451 1.28 jdc limit - 5000000, 0); 452 1.28 jdc lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, limit, 0); 453 1.29 jdc iic_release_bus(sc->sc_tag, 0); 454 1.28 jdc 455 1.28 jdc /* Synchronise sysctl */ 456 1.28 jdc sc->sc_tmax = (limit - 273150000) / 1000000; 457 1.28 jdc } 458 1.28 jdc } 459 1.28 jdc 460 1.28 jdc static void 461 1.46 skrll lmtemp_setlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata, 462 1.28 jdc sysmon_envsys_lim_t *limits, uint32_t *props) 463 1.28 jdc { 464 1.28 jdc struct lmtemp_softc *sc = sme->sme_cookie; 465 1.28 jdc int32_t limit; 466 1.28 jdc 467 1.29 jdc iic_acquire_bus(sc->sc_tag, 0); 468 1.28 jdc if (*props & PROP_CRITMAX) { 469 1.28 jdc if (limits == NULL) /* Restore defaults */ 470 1.29 jdc limit = sc->sc_scrit; 471 1.28 jdc else 472 1.28 jdc limit = limits->sel_critmax; 473 1.28 jdc lmtemp_temp_write(sc, LM77_REG_TCRIT_SET_POINT, limit, 0); 474 1.28 jdc } 475 1.28 jdc if (*props & PROP_WARNMAX) { 476 1.28 jdc if (limits == NULL) /* Restore defaults */ 477 1.28 jdc limit = sc->sc_smax; 478 1.28 jdc else 479 1.28 jdc limit = limits->sel_warnmax; 480 1.28 jdc lmtemp_temp_write(sc, LM77_REG_THIGH_SET_POINT, limit, 0); 481 1.28 jdc } 482 1.28 jdc if (*props & PROP_WARNMIN) { 483 1.28 jdc if (limits == NULL) /* Restore defaults */ 484 1.29 jdc limit = sc->sc_smin; 485 1.28 jdc else 486 1.28 jdc limit = limits->sel_warnmin; 487 1.28 jdc lmtemp_temp_write(sc, LM77_REG_TLOW_SET_POINT, limit, 0); 488 1.28 jdc } 489 1.29 jdc iic_release_bus(sc->sc_tag, 0); 490 1.28 jdc } 491 1.28 jdc 492 1.2 scw static uint32_t 493 1.27 jdc lmtemp_decode_lm75(const uint8_t *buf, int degc) 494 1.2 scw { 495 1.20 briggs int temp; 496 1.2 scw uint32_t val; 497 1.2 scw 498 1.20 briggs /* 499 1.20 briggs * LM75 temps are the most-significant 9 bits of a 16-bit reg. 500 1.20 briggs * sign-extend the MSB and add in the 0.5 from the LSB 501 1.20 briggs */ 502 1.20 briggs temp = (int8_t) buf[0]; 503 1.20 briggs temp = (temp << 1) + ((buf[1] >> 7) & 0x1); 504 1.2 scw 505 1.27 jdc /* Temp is given in 1/2 deg. C, we convert to C or uK. */ 506 1.27 jdc if (degc) 507 1.27 jdc val = temp / 2; 508 1.27 jdc else 509 1.27 jdc val = temp * 500000 + 273150000; 510 1.2 scw 511 1.18 xtraeme return val; 512 1.2 scw } 513 1.2 scw 514 1.2 scw static uint32_t 515 1.27 jdc lmtemp_decode_ds75(const uint8_t *buf, int degc) 516 1.2 scw { 517 1.2 scw int temp; 518 1.2 scw 519 1.2 scw /* 520 1.2 scw * Sign-extend the MSB byte, and add in the fractions of a 521 1.7 riz * degree contained in the LSB (precision 1/16th DegC). 522 1.2 scw */ 523 1.2 scw temp = (int8_t)buf[0]; 524 1.2 scw temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 525 1.2 scw 526 1.2 scw /* 527 1.27 jdc * Conversion to C or uK is simple. 528 1.2 scw */ 529 1.27 jdc if (degc) 530 1.27 jdc return temp / 16; 531 1.27 jdc else 532 1.27 jdc return (temp * 62500 + 273150000); 533 1.2 scw } 534 1.2 scw 535 1.9 kiyohara static uint32_t 536 1.27 jdc lmtemp_decode_lm77(const uint8_t *buf, int degc) 537 1.9 kiyohara { 538 1.9 kiyohara int temp; 539 1.9 kiyohara uint32_t val; 540 1.9 kiyohara 541 1.9 kiyohara /* 542 1.9 kiyohara * Describe each bits of temperature registers on LM77. 543 1.9 kiyohara * D15 - D12: Sign 544 1.9 kiyohara * D11 - D3 : Bit8(MSB) - Bit0 545 1.9 kiyohara */ 546 1.9 kiyohara temp = (int8_t)buf[0]; 547 1.10 kiyohara temp = (temp << 5) | ((buf[1] >> 3) & 0x1f); 548 1.9 kiyohara 549 1.27 jdc /* Temp is given in 1/2 deg. C, we convert to C or uK. */ 550 1.27 jdc if (degc) 551 1.27 jdc val = temp / 2; 552 1.27 jdc else 553 1.27 jdc val = temp * 500000 + 273150000; 554 1.9 kiyohara 555 1.18 xtraeme return val; 556 1.9 kiyohara } 557 1.9 kiyohara 558 1.28 jdc static void lmtemp_encode_lm75(const uint32_t val, uint8_t *buf, int degc) 559 1.28 jdc { 560 1.28 jdc int temp; 561 1.28 jdc 562 1.28 jdc /* Convert from C or uK to register format */ 563 1.28 jdc if (degc) 564 1.28 jdc temp = val * 2; 565 1.28 jdc else 566 1.28 jdc temp = (val - 273150000) / 500000; 567 1.28 jdc buf[0] = (temp >> 1) & 0xff; 568 1.28 jdc buf[1] = (temp & 1) << 7; 569 1.28 jdc } 570 1.28 jdc 571 1.28 jdc static void lmtemp_encode_ds75(const uint32_t val, uint8_t *buf, int degc) 572 1.28 jdc { 573 1.28 jdc int temp; 574 1.28 jdc 575 1.28 jdc /* Convert from C or uK to register format */ 576 1.28 jdc if (degc) 577 1.28 jdc temp = val * 16; 578 1.28 jdc else 579 1.28 jdc temp = (val - 273150000) / 62500; 580 1.28 jdc buf[0] = (temp >> 4) & 0xff; 581 1.28 jdc buf[1] = (temp & 0xf) << 4; 582 1.28 jdc } 583 1.28 jdc 584 1.28 jdc static void lmtemp_encode_lm77(const uint32_t val, uint8_t *buf, int degc) 585 1.28 jdc { 586 1.28 jdc int temp; 587 1.28 jdc 588 1.28 jdc /* Convert from C or uK to register format */ 589 1.28 jdc if (degc) 590 1.28 jdc temp = val * 2; 591 1.28 jdc else 592 1.28 jdc temp = (val - 273150000) / 500000; 593 1.28 jdc buf[0] = (temp >> 5) & 0xff; 594 1.28 jdc buf[1] = (temp & 0x1f) << 3; 595 1.28 jdc } 596 1.28 jdc 597 1.23 macallan static void 598 1.23 macallan lmtemp_setup_sysctl(struct lmtemp_softc *sc) 599 1.23 macallan { 600 1.23 macallan const struct sysctlnode *me = NULL, *node = NULL; 601 1.23 macallan 602 1.23 macallan sysctl_createv(NULL, 0, NULL, &me, 603 1.23 macallan CTLFLAG_READWRITE, 604 1.23 macallan CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 605 1.23 macallan NULL, 0, NULL, 0, 606 1.23 macallan CTL_MACHDEP, CTL_CREATE, CTL_EOL); 607 1.23 macallan 608 1.23 macallan sysctl_createv(NULL, 0, NULL, &node, 609 1.23 macallan CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 610 1.23 macallan CTLTYPE_INT, "temp", "Threshold temperature", 611 1.24 dsl sysctl_lm75_temp, 1, (void *)sc, 0, 612 1.23 macallan CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 613 1.23 macallan } 614 1.23 macallan 615 1.23 macallan static int 616 1.23 macallan sysctl_lm75_temp(SYSCTLFN_ARGS) 617 1.23 macallan { 618 1.23 macallan struct sysctlnode node = *rnode; 619 1.23 macallan struct lmtemp_softc *sc = node.sysctl_data; 620 1.44 mlelstv int temp, error; 621 1.23 macallan 622 1.23 macallan if (newp) { 623 1.23 macallan 624 1.46 skrll /* we're asked to write */ 625 1.23 macallan node.sysctl_data = &sc->sc_tmax; 626 1.23 macallan if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 627 1.23 macallan 628 1.23 macallan temp = *(int *)node.sysctl_data; 629 1.23 macallan sc->sc_tmax = temp; 630 1.44 mlelstv error = iic_acquire_bus(sc->sc_tag, 0); 631 1.44 mlelstv if (error) 632 1.44 mlelstv return error; 633 1.23 macallan lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 634 1.28 jdc sc->sc_tmax - 5, 1); 635 1.23 macallan lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, 636 1.28 jdc sc->sc_tmax, 1); 637 1.35 thorpej iic_release_bus(sc->sc_tag, 0); 638 1.28 jdc 639 1.28 jdc /* Synchronise envsys - calls lmtemp_getlim_lm75() */ 640 1.28 jdc sysmon_envsys_update_limits(sc->sc_sme, &sc->sc_sensor); 641 1.23 macallan return 0; 642 1.23 macallan } 643 1.23 macallan return EINVAL; 644 1.23 macallan } else { 645 1.23 macallan 646 1.23 macallan node.sysctl_data = &sc->sc_tmax; 647 1.23 macallan node.sysctl_size = 4; 648 1.23 macallan return (sysctl_lookup(SYSCTLFN_CALL(&node))); 649 1.23 macallan } 650 1.23 macallan 651 1.23 macallan return 0; 652 1.23 macallan } 653 1.23 macallan 654 1.23 macallan SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup") 655 1.23 macallan { 656 1.23 macallan 657 1.23 macallan sysctl_createv(NULL, 0, NULL, NULL, 658 1.23 macallan CTLFLAG_PERMANENT, 659 1.23 macallan CTLTYPE_NODE, "machdep", NULL, 660 1.23 macallan NULL, 0, NULL, 0, 661 1.23 macallan CTL_MACHDEP, CTL_EOL); 662 1.23 macallan } 663 1.23 macallan 664 1.23 macallan 665