1 /* $NetBSD: titemp.c,v 1.11 2021/01/27 02:29:48 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: titemp.c,v 1.11 2021/01/27 02:29:48 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/conf.h> 36 #include <sys/bus.h> 37 #include <sys/kmem.h> 38 39 #include <dev/i2c/i2cvar.h> 40 41 #include <dev/sysmon/sysmonvar.h> 42 43 #define TITEMP_LTEMP_HI_REG 0x00 44 #define TITEMP_RTEMP_HI_REG 0x01 45 #define TITEMP_STATUS_REG 0x02 46 #define TITEMP_CONFIG_REG 0x03 47 #define TITEMP_CONVRATE_REG 0x04 48 #define TITEMP_LTEMP_HLIMIT_HI_REG 0x05 49 #define TITEMP_LTEMP_LLIMIT_HI_REG 0x06 50 #define TITEMP_RTEMP_HLIMIT_HI_REG 0x07 51 #define TITEMP_RTEMP_LLIMIT_HI_REG 0x08 52 #define TITEMP_RTEMP_LO_REG 0x10 53 #define TITEMP_RTEMP_OFF_HI_REG 0x11 54 #define TITEMP_RTEMP_OFF_LO_REG 0x12 55 #define TITEMP_RTEMP_HLIMIT_LO_REG 0x13 56 #define TITEMP_RTEMP_LLIMIT_LO_REG 0x14 57 #define TITEMP_LTEMP_LO_REG 0x15 58 #define TITEMP_RTEMP_THERM_LIMIT_REG 0x19 59 #define TITEMP_LTEMP_THERM_LIMIT_REG 0x20 60 #define TITEMP_THERM_HYST_REG 0x21 61 #define TITEMP_CONAL_REG 0x22 62 #define TITEMP_NC_REG 0x23 63 #define TITEMP_DF_REG 0x24 64 #define TITEMP_MFID_REG 0xfe 65 66 #define TITEMP_MFID_TMP451 0x55 67 68 struct titemp_softc { 69 device_t sc_dev; 70 i2c_tag_t sc_i2c; 71 i2c_addr_t sc_addr; 72 73 struct sysmon_envsys *sc_sme; 74 envsys_data_t sc_sensor_ltemp; 75 envsys_data_t sc_sensor_rtemp; 76 }; 77 78 static int titemp_match(device_t, cfdata_t, void *); 79 static void titemp_attach(device_t, device_t, void *); 80 81 static void titemp_sensors_refresh(struct sysmon_envsys *, envsys_data_t *); 82 static int titemp_read(struct titemp_softc *, uint8_t, uint8_t *); 83 84 CFATTACH_DECL_NEW(titemp, sizeof(struct titemp_softc), 85 titemp_match, titemp_attach, NULL, NULL); 86 87 static const struct device_compatible_entry compat_data[] = { 88 { .compat = "ti,tmp451" }, 89 DEVICE_COMPAT_EOL 90 }; 91 92 static int 93 titemp_match(device_t parent, cfdata_t match, void *aux) 94 { 95 struct i2c_attach_args *ia = aux; 96 uint8_t mfid; 97 int error, match_result; 98 99 if (iic_use_direct_match(ia, match, compat_data, &match_result)) 100 return match_result; 101 102 if (ia->ia_addr != 0x4c) 103 return 0; 104 105 if (iic_acquire_bus(ia->ia_tag, 0) != 0) 106 return 0; 107 error = iic_smbus_read_byte(ia->ia_tag, ia->ia_addr, 108 TITEMP_MFID_REG, &mfid, 0); 109 iic_release_bus(ia->ia_tag, 0); 110 111 if (error || mfid != TITEMP_MFID_TMP451) 112 return 0; 113 114 return I2C_MATCH_ADDRESS_AND_PROBE; 115 } 116 117 static void 118 titemp_attach(device_t parent, device_t self, void *aux) 119 { 120 struct titemp_softc *sc = device_private(self); 121 struct i2c_attach_args *ia = aux; 122 123 sc->sc_dev = self; 124 sc->sc_i2c = ia->ia_tag; 125 sc->sc_addr = ia->ia_addr; 126 127 aprint_naive("\n"); 128 aprint_normal(": TMP451\n"); 129 130 sc->sc_sme = sysmon_envsys_create(); 131 sc->sc_sme->sme_name = device_xname(self); 132 sc->sc_sme->sme_cookie = sc; 133 sc->sc_sme->sme_refresh = titemp_sensors_refresh; 134 135 sc->sc_sensor_ltemp.units = ENVSYS_STEMP; 136 sc->sc_sensor_ltemp.state = ENVSYS_SINVALID; 137 snprintf(sc->sc_sensor_ltemp.desc, sizeof(sc->sc_sensor_ltemp.desc), 138 "local temperature"); 139 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_ltemp); 140 141 sc->sc_sensor_rtemp.units = ENVSYS_STEMP; 142 sc->sc_sensor_rtemp.state = ENVSYS_SINVALID; 143 snprintf(sc->sc_sensor_rtemp.desc, sizeof(sc->sc_sensor_rtemp.desc), 144 "remote temperature"); 145 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_rtemp); 146 147 sysmon_envsys_register(sc->sc_sme); 148 } 149 150 static void 151 titemp_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 152 { 153 struct titemp_softc *sc = sme->sme_cookie; 154 uint8_t reg_hi, reg_lo, temp[2]; 155 int error; 156 157 if (edata == &sc->sc_sensor_ltemp) { 158 reg_hi = TITEMP_LTEMP_HI_REG; 159 reg_lo = TITEMP_LTEMP_LO_REG; 160 } else if (edata == &sc->sc_sensor_rtemp) { 161 reg_hi = TITEMP_RTEMP_HI_REG; 162 reg_lo = TITEMP_RTEMP_LO_REG; 163 } else { 164 edata->state = ENVSYS_SINVALID; 165 return; 166 } 167 168 iic_acquire_bus(sc->sc_i2c, 0); 169 if ((error = titemp_read(sc, reg_hi, &temp[0])) != 0) 170 goto done; 171 if ((error = titemp_read(sc, reg_lo, &temp[1])) != 0) 172 goto done; 173 done: 174 iic_release_bus(sc->sc_i2c, 0); 175 176 if (error) { 177 edata->state = ENVSYS_SINVALID; 178 } else { 179 edata->value_cur = 180 ((uint64_t)temp[0] * 1000000) + 181 ((uint64_t)(temp[1]>>4) * 62500) + 182 + 273150000; 183 edata->state = ENVSYS_SVALID; 184 } 185 } 186 187 static int 188 titemp_read(struct titemp_softc *sc, uint8_t reg, uint8_t *val) 189 { 190 return iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0); 191 } 192