1 /* $OpenBSD: adm1021.c,v 1.27 2007/06/24 05:34:35 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Theo de Raadt 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 24 #include <dev/i2c/i2cvar.h> 25 26 /* ADM 1021 registers */ 27 #define ADM1021_INT_TEMP 0x00 28 #define ADM1021_EXT_TEMP 0x01 29 #define ADM1021_STATUS 0x02 30 #define ADM1021_STATUS_INVAL 0x7f 31 #define ADM1021_STATUS_NOEXT 0x40 32 #define ADM1021_CONFIG_READ 0x03 33 #define ADM1021_CONFIG_WRITE 0x09 34 #define ADM1021_CONFIG_RUN 0x40 35 #define ADM1021_COMPANY 0xfe /* contains 0x41 */ 36 #define ADM1021_STEPPING 0xff /* contains 0x3? */ 37 38 /* Sensors */ 39 #define ADMTEMP_EXT 0 40 #define ADMTEMP_INT 1 41 #define ADMTEMP_NUM_SENSORS 2 42 43 struct admtemp_softc { 44 struct device sc_dev; 45 i2c_tag_t sc_tag; 46 i2c_addr_t sc_addr; 47 48 struct ksensor sc_sensor[ADMTEMP_NUM_SENSORS]; 49 struct ksensordev sc_sensordev; 50 int sc_noexternal; 51 }; 52 53 int admtemp_match(struct device *, void *, void *); 54 void admtemp_attach(struct device *, struct device *, void *); 55 void admtemp_refresh(void *); 56 57 struct cfattach admtemp_ca = { 58 sizeof(struct admtemp_softc), admtemp_match, admtemp_attach 59 }; 60 61 struct cfdriver admtemp_cd = { 62 NULL, "admtemp", DV_DULL 63 }; 64 65 int 66 admtemp_match(struct device *parent, void *match, void *aux) 67 { 68 struct i2c_attach_args *ia = aux; 69 70 if (strcmp(ia->ia_name, "adm1021") == 0 || 71 strcmp(ia->ia_name, "adm1023") == 0 || 72 strcmp(ia->ia_name, "adm1032") == 0 || 73 strcmp(ia->ia_name, "g781") == 0 || 74 strcmp(ia->ia_name, "g781-1") == 0 || 75 strcmp(ia->ia_name, "gl523sm") == 0 || 76 strcmp(ia->ia_name, "max1617") == 0 || 77 strcmp(ia->ia_name, "xeontemp") == 0) 78 return (1); 79 return (0); 80 } 81 82 void 83 admtemp_attach(struct device *parent, struct device *self, void *aux) 84 { 85 struct admtemp_softc *sc = (struct admtemp_softc *)self; 86 struct i2c_attach_args *ia = aux; 87 u_int8_t cmd, data, stat; 88 int xeon = 0, i; 89 90 sc->sc_tag = ia->ia_tag; 91 sc->sc_addr = ia->ia_addr; 92 93 if (strcmp(ia->ia_name, "xeontemp") == 0) { 94 printf(": Xeon"); 95 xeon = 1; 96 } else 97 printf(": %s", ia->ia_name); 98 99 iic_acquire_bus(sc->sc_tag, 0); 100 cmd = ADM1021_CONFIG_READ; 101 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 102 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 103 iic_release_bus(sc->sc_tag, 0); 104 printf(", cannot get control register\n"); 105 return; 106 } 107 if (data & ADM1021_CONFIG_RUN) { 108 cmd = ADM1021_STATUS; 109 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 110 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) { 111 iic_release_bus(sc->sc_tag, 0); 112 printf(", cannot read status register\n"); 113 return; 114 } 115 if ((stat & ADM1021_STATUS_INVAL) == ADM1021_STATUS_INVAL) { 116 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 117 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) { 118 iic_release_bus(sc->sc_tag, 0); 119 printf(", cannot read status register\n"); 120 return; 121 } 122 } 123 124 /* means external is dead */ 125 if ((stat & ADM1021_STATUS_INVAL) != ADM1021_STATUS_INVAL && 126 (stat & ADM1021_STATUS_NOEXT)) 127 sc->sc_noexternal = 1; 128 129 data &= ~ADM1021_CONFIG_RUN; 130 cmd = ADM1021_CONFIG_WRITE; 131 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 132 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 133 iic_release_bus(sc->sc_tag, 0); 134 printf(", cannot set control register\n"); 135 return; 136 } 137 } 138 iic_release_bus(sc->sc_tag, 0); 139 140 /* Initialize sensor data. */ 141 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 142 sizeof(sc->sc_sensordev.xname)); 143 144 sc->sc_sensor[ADMTEMP_EXT].type = SENSOR_TEMP; 145 strlcpy(sc->sc_sensor[ADMTEMP_EXT].desc, 146 xeon ? "Xeon" : "External", 147 sizeof(sc->sc_sensor[ADMTEMP_EXT].desc)); 148 149 sc->sc_sensor[ADMTEMP_INT].type = SENSOR_TEMP; 150 strlcpy(sc->sc_sensor[ADMTEMP_INT].desc, 151 xeon ? "Xeon" : "Internal", 152 sizeof(sc->sc_sensor[ADMTEMP_INT].desc)); 153 154 if (sensor_task_register(sc, admtemp_refresh, 5) == NULL) { 155 printf(", unable to register update task\n"); 156 return; 157 } 158 159 for (i = 0; i < (sc->sc_noexternal ? 1 : ADMTEMP_NUM_SENSORS); i++) 160 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 161 sensordev_install(&sc->sc_sensordev); 162 163 printf("\n"); 164 } 165 166 void 167 admtemp_refresh(void *arg) 168 { 169 struct admtemp_softc *sc = arg; 170 u_int8_t cmd; 171 int8_t sdata; 172 173 iic_acquire_bus(sc->sc_tag, 0); 174 175 if (sc->sc_noexternal == 0) { 176 cmd = ADM1021_EXT_TEMP; 177 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 178 &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) { 179 if (sdata == 0x7f) { 180 sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID; 181 } else { 182 sc->sc_sensor[ADMTEMP_EXT].value = 183 273150000 + 1000000 * sdata; 184 sc->sc_sensor[ADMTEMP_EXT].flags &= ~SENSOR_FINVALID; 185 } 186 } 187 } else 188 sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID; 189 190 191 cmd = ADM1021_INT_TEMP; 192 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 193 &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) { 194 if (sdata == 0x7f) { 195 sc->sc_sensor[ADMTEMP_INT].flags |= SENSOR_FINVALID; 196 } else { 197 sc->sc_sensor[ADMTEMP_INT].value = 198 273150000 + 1000000 * sdata; 199 sc->sc_sensor[ADMTEMP_INT].flags &= ~SENSOR_FINVALID; 200 } 201 } 202 203 iic_release_bus(sc->sc_tag, 0); 204 } 205