1 /* $NetBSD: adm1021.c,v 1.10 2015/12/07 20:59:44 jdc Exp $ */ 2 /* $OpenBSD: adm1021.c,v 1.27 2007/06/24 05:34:35 dlg Exp $ */ 3 4 /* 5 * Copyright (c) 2005 Theo de Raadt 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 __KERNEL_RCSID(0, "$NetBSD: adm1021.c,v 1.10 2015/12/07 20:59:44 jdc Exp $"); 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <dev/sysmon/sysmonvar.h> 27 28 #include <dev/i2c/i2cvar.h> 29 30 31 /* ADM 1021 registers */ 32 #define ADM1021_INT_TEMP 0x00 33 #define ADM1021_EXT_TEMP 0x01 34 #define ADM1021_STATUS 0x02 35 #define ADM1021_STATUS_INVAL 0x7f 36 #define ADM1021_STATUS_NOEXT 0x40 37 #define ADM1021_CONFIG_READ 0x03 38 #define ADM1021_CONFIG_WRITE 0x09 39 #define ADM1021_CONFIG_RUN 0x40 40 #define ADM1021_COMPANY 0xfe /* contains 0x41 */ 41 #define ADM1021_DIE_REVISION 0xff 42 43 /* Sensors */ 44 #define ADMTEMP_INT 0 45 #define ADMTEMP_EXT 1 46 #define ADMTEMP_NUM_SENSORS 2 47 48 struct admtemp_softc { 49 i2c_tag_t sc_tag; 50 i2c_addr_t sc_addr; 51 52 int sc_noexternal; 53 struct sysmon_envsys *sc_sme; 54 envsys_data_t sc_sensor[ADMTEMP_NUM_SENSORS]; 55 }; 56 57 int admtemp_match(device_t, cfdata_t, void *); 58 void admtemp_attach(device_t, device_t, void *); 59 void admtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 60 61 CFATTACH_DECL_NEW(admtemp, sizeof(struct admtemp_softc), 62 admtemp_match, admtemp_attach, NULL, NULL); 63 64 static const char * admtemp_compats[] = { 65 "i2c-max1617", 66 NULL 67 }; 68 69 int 70 admtemp_match(device_t parent, cfdata_t match, void *aux) 71 { 72 struct i2c_attach_args *ia = aux; 73 74 if (ia->ia_name == NULL) { 75 /* 76 * Indirect config - not much we can do! 77 * Check typical addresses. 78 */ 79 if (((ia->ia_addr >= 0x18) && (ia->ia_addr <= 0x1a)) || 80 ((ia->ia_addr >= 0x29) && (ia->ia_addr <= 0x2b)) || 81 ((ia->ia_addr >= 0x4c) && (ia->ia_addr <= 0x4e))) 82 return (1); 83 } else { 84 /* 85 * Direct config - match via the list of compatible 86 * hardware or simply match the device name. 87 */ 88 if (ia->ia_ncompat > 0) { 89 if (iic_compat_match(ia, admtemp_compats)) 90 return 1; 91 } else { 92 if (strcmp(ia->ia_name, "admtemp") == 0) 93 return 1; 94 } 95 } 96 97 return 0; 98 } 99 100 101 void 102 admtemp_attach(device_t parent, device_t self, void *aux) 103 { 104 struct admtemp_softc *sc = device_private(self); 105 struct i2c_attach_args *ia = aux; 106 u_int8_t cmd, data, stat; 107 108 sc->sc_tag = ia->ia_tag; 109 sc->sc_addr = ia->ia_addr; 110 111 aprint_normal(": ADM1021 or compatible environmental sensor\n"); 112 aprint_naive(": Environmental sensor\n"); 113 114 iic_acquire_bus(sc->sc_tag, 0); 115 cmd = ADM1021_CONFIG_READ; 116 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 117 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 118 iic_release_bus(sc->sc_tag, 0); 119 aprint_error_dev(self, "cannot get control register\n"); 120 return; 121 } 122 if (data & ADM1021_CONFIG_RUN) { 123 cmd = ADM1021_STATUS; 124 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 125 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) { 126 iic_release_bus(sc->sc_tag, 0); 127 aprint_error_dev(self, 128 "cannot read status register\n"); 129 return; 130 } 131 if ((stat & ADM1021_STATUS_INVAL) == ADM1021_STATUS_INVAL) { 132 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 133 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 134 0)) { 135 iic_release_bus(sc->sc_tag, 0); 136 aprint_error_dev(self, 137 "cannot read status register\n"); 138 return; 139 } 140 } 141 142 /* means external is dead */ 143 if ((stat & ADM1021_STATUS_INVAL) != ADM1021_STATUS_INVAL && 144 (stat & ADM1021_STATUS_NOEXT)) 145 sc->sc_noexternal = 1; 146 147 data &= ~ADM1021_CONFIG_RUN; 148 cmd = ADM1021_CONFIG_WRITE; 149 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 150 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 151 iic_release_bus(sc->sc_tag, 0); 152 aprint_error_dev(self, 153 "cannot set control register\n"); 154 return; 155 } 156 } 157 iic_release_bus(sc->sc_tag, 0); 158 159 /* Initialize sensor data. */ 160 sc->sc_sensor[ADMTEMP_INT].state = ENVSYS_SINVALID; 161 sc->sc_sensor[ADMTEMP_INT].units = ENVSYS_STEMP; 162 sc->sc_sensor[ADMTEMP_EXT].state = ENVSYS_SINVALID; 163 sc->sc_sensor[ADMTEMP_EXT].units = ENVSYS_STEMP; 164 sc->sc_sensor[ADMTEMP_INT].state = ENVSYS_SINVALID; 165 sc->sc_sensor[ADMTEMP_EXT].state = ENVSYS_SINVALID; 166 strlcpy(sc->sc_sensor[ADMTEMP_INT].desc, "internal", 167 sizeof(sc->sc_sensor[ADMTEMP_INT].desc)); 168 strlcpy(sc->sc_sensor[ADMTEMP_EXT].desc, "external", 169 sizeof(sc->sc_sensor[ADMTEMP_EXT].desc)); 170 sc->sc_sme = sysmon_envsys_create(); 171 if (sysmon_envsys_sensor_attach( 172 sc->sc_sme, &sc->sc_sensor[ADMTEMP_INT])) { 173 sysmon_envsys_destroy(sc->sc_sme); 174 aprint_error_dev(self, 175 "unable to attach internal at sysmon\n"); 176 return; 177 } 178 if (sc->sc_noexternal == 0 && 179 sysmon_envsys_sensor_attach( 180 sc->sc_sme, &sc->sc_sensor[ADMTEMP_EXT])) { 181 sysmon_envsys_destroy(sc->sc_sme); 182 aprint_error_dev(self, 183 "unable to attach external at sysmon\n"); 184 return; 185 } 186 sc->sc_sme->sme_name = device_xname(self); 187 sc->sc_sme->sme_cookie = sc; 188 sc->sc_sme->sme_refresh = admtemp_refresh; 189 if (sysmon_envsys_register(sc->sc_sme)) { 190 aprint_error_dev(self, 191 "unable to register with sysmon\n"); 192 sysmon_envsys_destroy(sc->sc_sme); 193 return; 194 } 195 } 196 197 198 void 199 admtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 200 { 201 struct admtemp_softc *sc = sme->sme_cookie; 202 u_int8_t cmd; 203 int8_t sdata; 204 205 iic_acquire_bus(sc->sc_tag, 0); 206 207 if (edata->sensor == ADMTEMP_INT) 208 cmd = ADM1021_INT_TEMP; 209 else 210 cmd = ADM1021_EXT_TEMP; 211 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 212 &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) { 213 if (sdata == ADM1021_STATUS_INVAL) { 214 edata->state = ENVSYS_SINVALID; 215 } else { 216 edata->value_cur = 273150000 + 1000000 * sdata; 217 edata->state = ENVSYS_SVALID; 218 } 219 } 220 221 iic_release_bus(sc->sc_tag, 0); 222 } 223