1219131Srwatson/* $OpenBSD: lm75.c,v 1.21 2022/04/06 18:59:28 naddy Exp $ */ 2219131Srwatson/* $NetBSD: lm75.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $ */ 3219131Srwatson/* 4219131Srwatson * Copyright (c) 2006 Theo de Raadt <deraadt@openbsd.org> 5219131Srwatson * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> 6219131Srwatson * 7219131Srwatson * Permission to use, copy, modify, and distribute this software for any 8219131Srwatson * purpose with or without fee is hereby granted, provided that the above 9219131Srwatson * copyright notice and this permission notice appear in all copies. 10219131Srwatson * 11219131Srwatson * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12219131Srwatson * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13219131Srwatson * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14219131Srwatson * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15219131Srwatson * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16219131Srwatson * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17219131Srwatson * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18219131Srwatson */ 19219131Srwatson 20219131Srwatson/* 21219131Srwatson * National Semiconductor LM75/LM76/LM77 temperature sensor. 22219131Srwatson */ 23219131Srwatson 24219131Srwatson#include <sys/param.h> 25219131Srwatson#include <sys/systm.h> 26219131Srwatson#include <sys/device.h> 27219131Srwatson#include <sys/sensors.h> 28219131Srwatson 29219131Srwatson#include <dev/i2c/i2cvar.h> 30219131Srwatson 31219131Srwatson#define LM_MODEL_LM75 1 32219131Srwatson#define LM_MODEL_LM77 2 33219131Srwatson#define LM_MODEL_DS1775 3 34219131Srwatson#define LM_MODEL_LM75A 4 35219131Srwatson#define LM_MODEL_LM76 5 36219131Srwatson 37219131Srwatson#define LM_POLLTIME 3 /* 3s */ 38219131Srwatson 39219131Srwatson#define LM75_REG_TEMP 0x00 40219131Srwatson#define LM75_REG_CONFIG 0x01 41219131Srwatson#define LM75_CONFIG_SHUTDOWN 0x01 42219131Srwatson#define LM75_CONFIG_CMPINT 0x02 43219131Srwatson#define LM75_CONFIG_OSPOLARITY 0x04 44219131Srwatson#define LM75_CONFIG_FAULT_QUEUE_MASK 0x18 45219131Srwatson#define LM75_CONFIG_FAULT_QUEUE_1 (0 << 3) 46219131Srwatson#define LM75_CONFIG_FAULT_QUEUE_2 (1 << 3) 47219131Srwatson#define LM75_CONFIG_FAULT_QUEUE_4 (2 << 3) 48219131Srwatson#define LM75_CONFIG_FAULT_QUEUE_6 (3 << 3) 49219131Srwatson#define LM77_CONFIG_INTPOLARITY 0x08 50219131Srwatson#define LM77_CONFIG_FAULT_QUEUE_4 0x10 51219131Srwatson#define DS1755_CONFIG_RESOLUTION(i) (9 + (((i) >> 5) & 3)) 52219131Srwatson#define LM75_REG_THYST_SET_POINT 0x02 53219131Srwatson#define LM75_REG_TOS_SET_POINT 0x03 54219131Srwatson#define LM77_REG_TLOW 0x04 55219131Srwatson#define LM77_REG_THIGH 0x05 56219131Srwatson 57219131Srwatsonstruct lmtemp_softc { 58219131Srwatson struct device sc_dev; 59219131Srwatson i2c_tag_t sc_tag; 60219131Srwatson int sc_addr; 61219131Srwatson int sc_model; 62219131Srwatson int sc_bits; 63219131Srwatson int sc_ratio; 64219131Srwatson 65219131Srwatson struct ksensor sc_sensor; 66219131Srwatson struct ksensordev sc_sensordev; 67219131Srwatson}; 68219131Srwatson 69219131Srwatsonint lmtemp_match(struct device *, void *, void *); 70219131Srwatsonvoid lmtemp_attach(struct device *, struct device *, void *); 71219131Srwatson 72219131Srwatsonconst struct cfattach lmtemp_ca = { 73219131Srwatson sizeof(struct lmtemp_softc), 74219131Srwatson lmtemp_match, 75219131Srwatson lmtemp_attach 76219131Srwatson}; 77219131Srwatson 78219131Srwatsonstruct cfdriver lmtemp_cd = { 79219131Srwatson NULL, "lmtemp", DV_DULL 80219131Srwatson}; 81219131Srwatson 82219131Srwatson/* 83219131Srwatson * Temperature on the LM75 is represented by a 9-bit two's complement 84219131Srwatson * integer in steps of 0.5C. The following examples are taken from 85219131Srwatson * the LM75 data sheet: 86219131Srwatson * 87219131Srwatson * +125C 0 1111 1010 0x0fa 88219131Srwatson * +25C 0 0011 0010 0x032 89219131Srwatson * +0.5C 0 0000 0001 0x001 90219131Srwatson * 0C 0 0000 0000 0x000 91219131Srwatson * -0.5C 1 1111 1111 0x1ff 92219131Srwatson * -25C 1 1100 1110 0x1ce 93219131Srwatson * -55C 1 1001 0010 0x192 94219131Srwatson * 95219131Srwatson * Temperature on the LM75A is represented by an 11-bit two's complement 96219131Srwatson * integer in steps of 0.125C. The LM75A can be treated like an LM75 if 97219131Srwatson * the extra precision is not required. The following examples are 98219131Srwatson * taken from the LM75A data sheet: 99219131Srwatson * 100219131Srwatson * +127.000C 011 1111 1000 0x3f8 101219131Srwatson * +126.875C 011 1111 0111 0x3f7 102219131Srwatson * +126.125C 011 1111 0001 0x3f1 103219131Srwatson * +125.000C 011 1110 1000 0x3e8 104219131Srwatson * +25.000C 000 1100 1000 0x0c8 105219131Srwatson * +0.125C 000 0000 0001 0x001 106219131Srwatson * 0C 000 0000 0000 0x000 107219131Srwatson * -0.125C 111 1111 1111 0x7ff 108219131Srwatson * -25.000C 111 0011 1000 0x738 109219131Srwatson * -54.875C 110 0100 1001 0x649 110219131Srwatson * -55.000C 110 0100 1000 0x648 111219131Srwatson * 112219131Srwatson * Temperature on the LM77 is represented by a 13-bit two's complement 113219131Srwatson * integer in steps of 0.5C. The LM76 is similar, but the integer is 114219131Srwatson * in steps of 0.065C 115219131Srwatson * 116219131Srwatson * LM75 temperature word: 117219131Srwatson * 118219131Srwatson * MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X X X 119219131Srwatson * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 120219131Srwatson * 121219131Srwatson * 122219131Srwatson * LM75A temperature word: 123219131Srwatson * 124219131Srwatson * MSB Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X 125219131Srwatson * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 126219131Srwatson * 127219131Srwatson * 128219131Srwatson * LM77 temperature word: 129219131Srwatson * 130219131Srwatson * Sign Sign Sign Sign MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 Status bits 131219131Srwatson * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 132219131Srwatson */ 133219131Srwatson 134219131Srwatsonint lmtemp_temp_read(struct lmtemp_softc *, uint8_t, int *); 135219131Srwatsonvoid lmtemp_refresh_sensor_data(void *); 136219131Srwatson 137219131Srwatsonint 138219131Srwatsonlmtemp_match(struct device *parent, void *match, void *aux) 139219131Srwatson{ 140219131Srwatson struct i2c_attach_args *ia = aux; 141219131Srwatson 142219131Srwatson if (strcmp(ia->ia_name, "lm75") == 0 || 143219131Srwatson strcmp(ia->ia_name, "lm76") == 0 || 144219131Srwatson strcmp(ia->ia_name, "lm77") == 0 || 145219131Srwatson strcmp(ia->ia_name, "ds1775") == 0 || 146219131Srwatson strcmp(ia->ia_name, "lm75a") == 0) 147219131Srwatson return (1); 148219131Srwatson return (0); 149219131Srwatson} 150219131Srwatson 151219131Srwatsonvoid 152219131Srwatsonlmtemp_attach(struct device *parent, struct device *self, void *aux) 153219131Srwatson{ 154219131Srwatson struct lmtemp_softc *sc = (struct lmtemp_softc *)self; 155219131Srwatson struct i2c_attach_args *ia = aux; 156219131Srwatson u_int8_t cmd, data; 157219131Srwatson 158219131Srwatson sc->sc_tag = ia->ia_tag; 159219131Srwatson sc->sc_addr = ia->ia_addr; 160219131Srwatson 161219131Srwatson printf(": %s", ia->ia_name); 162219131Srwatson 163219131Srwatson /* If in SHUTDOWN mode, wake it up */ 164219131Srwatson iic_acquire_bus(sc->sc_tag, 0); 165219131Srwatson cmd = LM75_REG_CONFIG; 166219131Srwatson if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 167219131Srwatson sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 168219131Srwatson iic_release_bus(sc->sc_tag, 0); 169219131Srwatson printf(", fails to respond\n"); 170219131Srwatson return; 171219131Srwatson } 172219131Srwatson if (data & LM75_CONFIG_SHUTDOWN) { 173219131Srwatson data &= ~LM75_CONFIG_SHUTDOWN; 174219131Srwatson if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 175219131Srwatson sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 176219131Srwatson printf(", cannot wake up\n"); 177219131Srwatson iic_release_bus(sc->sc_tag, 0); 178219131Srwatson return; 179219131Srwatson } 180219131Srwatson printf(", woken up"); 181219131Srwatson } 182219131Srwatson iic_release_bus(sc->sc_tag, 0); 183219131Srwatson 184219131Srwatson sc->sc_model = LM_MODEL_LM75; 185219131Srwatson sc->sc_bits = 9; 186219131Srwatson sc->sc_ratio = 500000; /* 0.5 degC for LSB */ 187219131Srwatson if (strcmp(ia->ia_name, "lm77") == 0) { 188219131Srwatson sc->sc_model = LM_MODEL_LM77; 189219131Srwatson sc->sc_bits = 13; 190219131Srwatson } else if (strcmp(ia->ia_name, "lm76") == 0) { 191219131Srwatson sc->sc_model = LM_MODEL_LM76; 192219131Srwatson sc->sc_bits = 13; 193219131Srwatson sc->sc_ratio = 62500; /* 0.0625 degC for LSB */ 194219131Srwatson } else if (strcmp(ia->ia_name, "ds1775") == 0) { 195219131Srwatson sc->sc_model = LM_MODEL_DS1775; 196219131Srwatson //sc->sc_bits = DS1755_CONFIG_RESOLUTION(data); 197219131Srwatson } else if (strcmp(ia->ia_name, "lm75a") == 0) { 198219131Srwatson /* For simplicity's sake, treat the LM75A as an LM75 */ 199219131Srwatson sc->sc_model = LM_MODEL_LM75A; 200219131Srwatson } 201219131Srwatson 202219131Srwatson printf("\n"); 203219131Srwatson 204219131Srwatson /* Initialize sensor data */ 205219131Srwatson strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 206219131Srwatson sizeof(sc->sc_sensordev.xname)); 207219131Srwatson sc->sc_sensor.type = SENSOR_TEMP; 208219131Srwatson 209219131Srwatson /* Hook into the hw.sensors sysctl */ 210219131Srwatson sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 211219131Srwatson sensordev_install(&sc->sc_sensordev); 212219131Srwatson 213219131Srwatson sensor_task_register(sc, lmtemp_refresh_sensor_data, LM_POLLTIME); 214219131Srwatson} 215219131Srwatson 216219131Srwatsonint 217219131Srwatsonlmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, int *valp) 218219131Srwatson{ 219219131Srwatson u_int8_t cmd = which; 220219131Srwatson u_int16_t data = 0x0000; 221219131Srwatson int error; 222219131Srwatson 223219131Srwatson iic_acquire_bus(sc->sc_tag, 0); 224219131Srwatson error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 225219131Srwatson sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0); 226219131Srwatson iic_release_bus(sc->sc_tag, 0); 227219131Srwatson if (error) 228219131Srwatson return (error); 229219131Srwatson 230219131Srwatson /* Some chips return transient 0's.. we try next time */ 231219131Srwatson if (data == 0x0000) 232219131Srwatson return (1); 233219131Srwatson 234219131Srwatson /* convert to half-degrees C */ 235219131Srwatson *valp = betoh16(data) / (1 << (16 - sc->sc_bits)); 236219131Srwatson return (0); 237219131Srwatson} 238219131Srwatson 239219131Srwatsonvoid 240219131Srwatsonlmtemp_refresh_sensor_data(void *aux) 241219131Srwatson{ 242224812Sjonathan struct lmtemp_softc *sc = aux; 243219131Srwatson int val; 244219131Srwatson int error; 245219131Srwatson 246219131Srwatson error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val); 247219131Srwatson if (error) { 248219131Srwatson#if 0 249219131Srwatson printf("%s: unable to read temperature, error = %d\n", 250219131Srwatson sc->sc_dev.dv_xname, error); 251219131Srwatson#endif 252219131Srwatson sc->sc_sensor.flags |= SENSOR_FINVALID; 253219131Srwatson return; 254219131Srwatson } 255219131Srwatson 256219131Srwatson sc->sc_sensor.value = val * sc->sc_ratio + 273150000; 257219131Srwatson sc->sc_sensor.flags &= ~SENSOR_FINVALID; 258219131Srwatson} 259219131Srwatson