1 /* $NetBSD: titemp.c,v 1.4 2018/04/30 20:37:01 jmcneill 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.4 2018/04/30 20:37:01 jmcneill 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 char * titemp_compats[] = { 88 "ti,tmp451", 89 NULL 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; 98 99 if (ia->ia_name == NULL) { 100 if (ia->ia_addr != 0x4c) 101 return 0; 102 103 if (iic_acquire_bus(ia->ia_tag, I2C_F_POLL) != 0) 104 return 0; 105 error = iic_smbus_read_byte(ia->ia_tag, ia->ia_addr, 106 TITEMP_MFID_REG, &mfid, I2C_F_POLL); 107 iic_release_bus(ia->ia_tag, I2C_F_POLL); 108 109 if (error || mfid != TITEMP_MFID_TMP451) 110 return 0; 111 112 return 1; 113 } else { 114 return iic_compat_match(ia, titemp_compats); 115 } 116 } 117 118 static void 119 titemp_attach(device_t parent, device_t self, void *aux) 120 { 121 struct titemp_softc *sc = device_private(self); 122 struct i2c_attach_args *ia = aux; 123 124 sc->sc_dev = self; 125 sc->sc_i2c = ia->ia_tag; 126 sc->sc_addr = ia->ia_addr; 127 128 aprint_naive("\n"); 129 aprint_normal(": TMP451\n"); 130 131 sc->sc_sme = sysmon_envsys_create(); 132 sc->sc_sme->sme_name = device_xname(self); 133 sc->sc_sme->sme_cookie = sc; 134 sc->sc_sme->sme_refresh = titemp_sensors_refresh; 135 136 sc->sc_sensor_ltemp.units = ENVSYS_STEMP; 137 sc->sc_sensor_ltemp.state = ENVSYS_SINVALID; 138 snprintf(sc->sc_sensor_ltemp.desc, sizeof(sc->sc_sensor_ltemp.desc), 139 "local temperature"); 140 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_ltemp); 141 142 sc->sc_sensor_rtemp.units = ENVSYS_STEMP; 143 sc->sc_sensor_rtemp.state = ENVSYS_SINVALID; 144 snprintf(sc->sc_sensor_rtemp.desc, sizeof(sc->sc_sensor_rtemp.desc), 145 "remote temperature"); 146 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_rtemp); 147 148 sysmon_envsys_register(sc->sc_sme); 149 } 150 151 static void 152 titemp_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 153 { 154 struct titemp_softc *sc = sme->sme_cookie; 155 uint8_t reg_hi, reg_lo, temp[2]; 156 int error; 157 158 if (edata == &sc->sc_sensor_ltemp) { 159 reg_hi = TITEMP_LTEMP_HI_REG; 160 reg_lo = TITEMP_LTEMP_LO_REG; 161 } else if (edata == &sc->sc_sensor_rtemp) { 162 reg_hi = TITEMP_RTEMP_HI_REG; 163 reg_lo = TITEMP_RTEMP_LO_REG; 164 } else { 165 edata->state = ENVSYS_SINVALID; 166 return; 167 } 168 169 iic_acquire_bus(sc->sc_i2c, 0); 170 if ((error = titemp_read(sc, reg_hi, &temp[0])) != 0) 171 goto done; 172 if ((error = titemp_read(sc, reg_lo, &temp[1])) != 0) 173 goto done; 174 done: 175 iic_release_bus(sc->sc_i2c, 0); 176 177 if (error) { 178 edata->state = ENVSYS_SINVALID; 179 } else { 180 edata->value_cur = 181 ((uint64_t)temp[0] * 1000000) + 182 ((uint64_t)(temp[1]>>4) * 62500) + 183 + 273150000; 184 edata->state = ENVSYS_SVALID; 185 } 186 } 187 188 static int 189 titemp_read(struct titemp_softc *sc, uint8_t reg, uint8_t *val) 190 { 191 return iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0); 192 } 193