1 /* $NetBSD: adm1021.c,v 1.5 2010/03/11 04:19:55 mrg 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.5 2010/03/11 04:19:55 mrg 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 struct device sc_dev; 50 i2c_tag_t sc_tag; 51 i2c_addr_t sc_addr; 52 53 int sc_noexternal; 54 struct sysmon_envsys *sc_sme; 55 envsys_data_t sc_sensor[ADMTEMP_NUM_SENSORS]; 56 }; 57 58 int admtemp_match(device_t, cfdata_t, void *); 59 void admtemp_attach(device_t, device_t, void *); 60 void admtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 61 62 CFATTACH_DECL_NEW(admtemp, sizeof(struct admtemp_softc), 63 admtemp_match, admtemp_attach, NULL, NULL); 64 65 static const char * admtemp_compats[] = { 66 "i2c-max1617", 67 NULL 68 }; 69 70 int 71 admtemp_match(device_t parent, cfdata_t match, void *aux) 72 { 73 struct i2c_attach_args *ia = aux; 74 75 if (ia->ia_name == NULL) { 76 /* 77 * Indirect config - not much we can do! 78 * Check typical addresses. 79 */ 80 if (((ia->ia_addr >= 0x18) && (ia->ia_addr <= 0x1a)) || 81 ((ia->ia_addr >= 0x29) && (ia->ia_addr <= 0x2b)) || 82 ((ia->ia_addr >= 0x4c) && (ia->ia_addr <= 0x4e))) 83 return (1); 84 } else { 85 /* 86 * Direct config - match via the list of compatible 87 * hardware. 88 */ 89 if (iic_compat_match(ia, admtemp_compats)) 90 return 1; 91 } 92 93 return 0; 94 } 95 96 97 void 98 admtemp_attach(device_t parent, device_t self, void *aux) 99 { 100 struct admtemp_softc *sc = device_private(self); 101 struct i2c_attach_args *ia = aux; 102 u_int8_t cmd, data, stat; 103 104 sc->sc_tag = ia->ia_tag; 105 sc->sc_addr = ia->ia_addr; 106 107 aprint_normal(": ADM1021 or compatible environmental sensor\n"); 108 aprint_naive(": Environmental sensor\n"); 109 110 iic_acquire_bus(sc->sc_tag, 0); 111 cmd = ADM1021_CONFIG_READ; 112 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 113 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 114 iic_release_bus(sc->sc_tag, 0); 115 aprint_error_dev(&sc->sc_dev, "cannot get control register\n"); 116 return; 117 } 118 if (data & ADM1021_CONFIG_RUN) { 119 cmd = ADM1021_STATUS; 120 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 121 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) { 122 iic_release_bus(sc->sc_tag, 0); 123 aprint_error_dev(&sc->sc_dev, 124 "cannot read status register\n"); 125 return; 126 } 127 if ((stat & ADM1021_STATUS_INVAL) == ADM1021_STATUS_INVAL) { 128 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 129 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 130 0)) { 131 iic_release_bus(sc->sc_tag, 0); 132 aprint_error_dev(&sc->sc_dev, 133 "cannot read status register\n"); 134 return; 135 } 136 } 137 138 /* means external is dead */ 139 if ((stat & ADM1021_STATUS_INVAL) != ADM1021_STATUS_INVAL && 140 (stat & ADM1021_STATUS_NOEXT)) 141 sc->sc_noexternal = 1; 142 143 data &= ~ADM1021_CONFIG_RUN; 144 cmd = ADM1021_CONFIG_WRITE; 145 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 146 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 147 iic_release_bus(sc->sc_tag, 0); 148 aprint_error_dev(&sc->sc_dev, 149 "cannot set control register\n"); 150 return; 151 } 152 } 153 iic_release_bus(sc->sc_tag, 0); 154 155 /* Initialize sensor data. */ 156 sc->sc_sensor[ADMTEMP_INT].units = ENVSYS_STEMP; 157 sc->sc_sensor[ADMTEMP_EXT].units = ENVSYS_STEMP; 158 strlcpy(sc->sc_sensor[ADMTEMP_INT].desc, "internal",sizeof("internal")); 159 strlcpy(sc->sc_sensor[ADMTEMP_EXT].desc, "external",sizeof("external")); 160 sc->sc_sme = sysmon_envsys_create(); 161 if (sysmon_envsys_sensor_attach( 162 sc->sc_sme, &sc->sc_sensor[ADMTEMP_INT])) { 163 sysmon_envsys_destroy(sc->sc_sme); 164 aprint_error_dev(&sc->sc_dev, 165 "unable to attach internal at sysmon\n"); 166 return; 167 } 168 if (sc->sc_noexternal == 0 && 169 sysmon_envsys_sensor_attach( 170 sc->sc_sme, &sc->sc_sensor[ADMTEMP_EXT])) { 171 sysmon_envsys_destroy(sc->sc_sme); 172 aprint_error_dev(&sc->sc_dev, 173 "unable to attach external at sysmon\n"); 174 return; 175 } 176 sc->sc_sme->sme_name = device_xname(self); 177 sc->sc_sme->sme_cookie = sc; 178 sc->sc_sme->sme_refresh = admtemp_refresh; 179 if (sysmon_envsys_register(sc->sc_sme)) { 180 aprint_error_dev(&sc->sc_dev, 181 "unable to register with sysmon\n"); 182 sysmon_envsys_destroy(sc->sc_sme); 183 return; 184 } 185 } 186 187 188 void 189 admtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 190 { 191 struct admtemp_softc *sc = sme->sme_cookie; 192 u_int8_t cmd; 193 int8_t sdata; 194 195 iic_acquire_bus(sc->sc_tag, 0); 196 197 if (edata->sensor == ADMTEMP_INT) 198 cmd = ADM1021_INT_TEMP; 199 else 200 cmd = ADM1021_EXT_TEMP; 201 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 202 &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) { 203 if (sdata == ADM1021_STATUS_INVAL) { 204 edata->state = ENVSYS_SINVALID; 205 } else { 206 edata->value_cur = 273150000 + 1000000 * sdata; 207 edata->state = ENVSYS_SVALID; 208 } 209 } 210 211 iic_release_bus(sc->sc_tag, 0); 212 } 213