1 /* $OpenBSD: asc7621.c,v 1.5 2022/04/06 18:59:28 naddy Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Mike Belopuhov 5 * Copyright (c) 2007 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/param.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/sensors.h> 24 25 #include <dev/i2c/i2cvar.h> 26 27 /* ASC7621 registers */ 28 29 #define ASC7621_PECI 0x40 /* Check for PECI monitoring */ 30 #define ASC7621_PECI_MASK 0x10 /* 00010000 */ 31 32 #define ASC7621_LEGACY 0x36 /* Check for legacy mode */ 33 #define ASC7621_LEGACY_MASK 0x10 /* 00010000 */ 34 35 #define ASC7621_TEMP1H 0x25 /* Zone 1 Temperature (MS Byte) */ 36 #define ASC7621_TEMP1L 0x10 /* Zone 1 Temperature (LS Byte) */ 37 #define ASC7621_TEMP2H 0x26 /* Zone 2 Temperature (MS Byte) */ 38 #define ASC7621_TEMP2L 0x15 /* Zone 2 Temperature (LS Byte) */ 39 #define ASC7621_TEMP3H 0x27 /* Zone 3 Temperature (MS Byte) */ 40 #define ASC7621_TEMP3L 0x16 /* Zone 3 Temperature (LS Byte) */ 41 #define ASC7621_TEMP4H 0x33 /* Zone 4 Temperature (MS Byte) */ 42 #define ASC7621_TEMP4L 0x17 /* Zone 4 Temperature (LS Byte) */ 43 #define ASC7621_TEMP_NA 0x80 /* Not plugged */ 44 45 #define ASC7621_IN1_VH 0x20 /* 2.5V (MS Byte) */ 46 #define ASC7621_IN1_VL 0x13 /* 2.5V (LS Byte) */ 47 #define ASC7621_IN2_VH 0x21 /* VCCP (MS Byte) */ 48 #define ASC7621_IN2_VL 0x18 /* VCCP (LS Byte) */ 49 #define ASC7621_IN3_VH 0x22 /* 3.3V (MS Byte) */ 50 #define ASC7621_IN3_VL 0x11 /* 2.3V (LS Byte) */ 51 #define ASC7621_IN4_VH 0x23 /* 5V (MS Byte) */ 52 #define ASC7621_IN4_VL 0x12 /* 5V (LS Byte) */ 53 #define ASC7621_IN5_VH 0x24 /* 12V (MS Byte) */ 54 #define ASC7621_IN5_VL 0x14 /* 12V (LS Byte) */ 55 56 #define ASC7621_TACH1H 0x29 /* Tachometer 1 (MS Byte) */ 57 #define ASC7621_TACH1L 0x28 /* Tachometer 1 (LS Byte) */ 58 #define ASC7621_TACH2H 0x2b /* Tachometer 2 (MS Byte) */ 59 #define ASC7621_TACH2L 0x2a /* Tachometer 2 (LS Byte) */ 60 #define ASC7621_TACH3H 0x2d /* Tachometer 3 (MS Byte) */ 61 #define ASC7621_TACH3L 0x2c /* Tachometer 3 (LS Byte) */ 62 #define ASC7621_TACH4H 0x2f /* Tachometer 4 (MS Byte) */ 63 #define ASC7621_TACH4L 0x2e /* Tachometer 4 (LS Byte) */ 64 65 /* Sensors */ 66 #define ADL_TEMP1 0 67 #define ADL_TEMP2 1 68 #define ADL_TEMP3 2 69 #define ADL_TEMP4 3 70 #define ADL_IN1_V 4 71 #define ADL_IN2_V 5 72 #define ADL_IN3_V 6 73 #define ADL_IN4_V 7 74 #define ADL_IN5_V 8 75 #define ADL_TACH1 9 76 #define ADL_TACH2 10 77 #define ADL_TACH3 11 78 #define ADL_TACH4 12 79 #define ADL_NUM_SENSORS 13 80 81 struct { 82 char sensor; 83 u_int8_t hreg; /* MS-byte register */ 84 u_int8_t lreg; /* LS-byte register */ 85 char *name; 86 u_short mVscale; 87 u_short tempscale; /* else a fan */ 88 } adl_worklist[] = { 89 { ADL_TEMP1, ASC7621_TEMP1H, ASC7621_TEMP1L, "CPU", 0, 1 }, 90 { ADL_TEMP2, ASC7621_TEMP2H, ASC7621_TEMP2L, "CPU", 0, 1 }, 91 { ADL_TEMP3, ASC7621_TEMP3H, ASC7621_TEMP3L, "Internal", 0, 1 }, 92 { ADL_TEMP4, ASC7621_TEMP4H, ASC7621_TEMP4L, "External", 0, 1 }, 93 94 { ADL_IN1_V, ASC7621_IN1_VH, ASC7621_IN1_VL, "+1.5V", 2500, 0 }, 95 { ADL_IN2_V, ASC7621_IN2_VH, ASC7621_IN2_VL, "Vccp", 2250, 0 }, 96 { ADL_IN3_V, ASC7621_IN3_VH, ASC7621_IN3_VL, "+3.3V", 3300, 0 }, 97 { ADL_IN4_V, ASC7621_IN4_VH, ASC7621_IN4_VL, "+5V", 5000, 0 }, 98 { ADL_IN5_V, ASC7621_IN5_VH, ASC7621_IN5_VL, "+12V", 12000, 0 }, 99 100 { ADL_TACH1, ASC7621_TACH1L, ASC7621_TACH1H, "", 0, 0 }, 101 { ADL_TACH2, ASC7621_TACH2L, ASC7621_TACH2H, "", 0, 0 }, 102 { ADL_TACH3, ASC7621_TACH3L, ASC7621_TACH3H, "", 0, 0 }, 103 { ADL_TACH4, ASC7621_TACH4L, ASC7621_TACH4H, "", 0, 0 } 104 }; 105 106 struct adl_softc { 107 struct device sc_dev; 108 i2c_tag_t sc_tag; 109 i2c_addr_t sc_addr; 110 u_int8_t sc_conf; 111 112 struct ksensor sc_sensor[ADL_NUM_SENSORS]; 113 struct ksensordev sc_sensordev; 114 }; 115 116 #if 0 117 static int peci_enabled; 118 static int legacy_mode; 119 #endif 120 121 int adl_match(struct device *, void *, void *); 122 void adl_attach(struct device *, struct device *, void *); 123 124 void adl_refresh(void *); 125 126 const struct cfattach adl_ca = { 127 sizeof(struct adl_softc), adl_match, adl_attach 128 }; 129 130 struct cfdriver adl_cd = { 131 NULL, "adl", DV_DULL 132 }; 133 134 int 135 adl_match(struct device *parent, void *match, void *aux) 136 { 137 struct i2c_attach_args *ia = aux; 138 139 if (strcmp(ia->ia_name, "asc7621") == 0) 140 return (1); 141 return (0); 142 } 143 144 void 145 adl_attach(struct device *parent, struct device *self, void *aux) 146 { 147 struct adl_softc *sc = (struct adl_softc *)self; 148 struct i2c_attach_args *ia = aux; 149 u_int8_t cmd, data; 150 int i; 151 152 sc->sc_tag = ia->ia_tag; 153 sc->sc_addr = ia->ia_addr; 154 155 printf(": %s", ia->ia_name); 156 157 /* Initialize sensor data. */ 158 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 159 sizeof(sc->sc_sensordev.xname)); 160 161 /* Check for PECI mode */ 162 cmd = ASC7621_PECI; 163 (void)iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 164 &cmd, sizeof(cmd), &data, sizeof(data), 0); 165 if (data & ASC7621_PECI_MASK) 166 printf(", PECI enabled\n"); 167 168 #if 0 169 /* Check for legacy mode */ 170 cmd = ASC7621_LEGACY; 171 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 172 &cmd, sizeof(cmd), &data, sizeof(data), 0)) { 173 printf(", unable to read PECI configuration register"); 174 } 175 if (data & ASC7621_LEGACY_MASK) 176 legacy_mode = 1; 177 #endif 178 179 if (sensor_task_register(sc, adl_refresh, 5) == NULL) { 180 printf(", unable to register update task\n"); 181 return; 182 } 183 184 for (i = 0; i < ADL_NUM_SENSORS; i++) { 185 if (adl_worklist[i].tempscale) 186 sc->sc_sensor[i].type = SENSOR_TEMP; 187 else if (adl_worklist[i].mVscale) 188 sc->sc_sensor[i].type = SENSOR_VOLTS_DC; 189 else 190 sc->sc_sensor[i].type = SENSOR_FANRPM; 191 strlcpy(sc->sc_sensor[i].desc, adl_worklist[i].name, 192 sizeof(sc->sc_sensor[i].desc)); 193 194 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 195 } 196 sensordev_install(&sc->sc_sensordev); 197 198 printf("\n"); 199 } 200 201 void 202 adl_refresh(void *arg) 203 { 204 struct adl_softc *sc = arg; 205 int64_t temp, volt; 206 u_int8_t hdata, ldata, hreg, lreg; 207 u_int16_t fan; 208 int i; 209 210 iic_acquire_bus(sc->sc_tag, 0); 211 212 for (i = 0; i < sizeof adl_worklist / sizeof(adl_worklist[0]); i++) { 213 hreg = adl_worklist[i].hreg; 214 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 215 sc->sc_addr, &hreg, sizeof hreg, &hdata, sizeof hdata, 0)) { 216 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 217 continue; 218 } 219 lreg = adl_worklist[i].lreg; 220 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 221 sc->sc_addr, &lreg, sizeof lreg, &ldata, sizeof ldata, 0)) { 222 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 223 continue; 224 } 225 226 sc->sc_sensor[i].flags &= ~SENSOR_FINVALID; 227 if (adl_worklist[i].tempscale) { 228 if (hdata == ASC7621_TEMP_NA) 229 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 230 else { 231 /* 232 * 10-bit two's complement integer in 233 * steps of 0.25 234 */ 235 temp = ((hdata << 8 | ldata)) >> (16 - 10); 236 temp = temp * 250000 + 273150000; 237 sc->sc_sensor[i].value = temp; 238 } 239 } else if (adl_worklist[i].mVscale) { 240 volt = ((hdata << 8 | ldata)) >> (16 - 10); 241 volt = volt * adl_worklist[i].mVscale / (192 << 2); 242 sc->sc_sensor[i].value = volt * 1000; 243 } else { 244 /* 245 * Inversed to ensure that the LS byte will be read 246 * before MS byte. 247 */ 248 fan = hdata + (ldata << 8); 249 if (fan == 0 || fan == 0xffff) 250 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 251 else 252 sc->sc_sensor[i].value = (90000 * 60) / fan; 253 } 254 } 255 256 iic_release_bus(sc->sc_tag, 0); 257 } 258