1 /* $OpenBSD: lm93.c,v 1.9 2022/04/06 18:59:28 naddy Exp $ */ 2 3 /* 4 * Copyright (c) 2007 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 /* LM93 registers */ 27 #define LM93_CPU1_TEMP 0x50 28 #define LM93_CPU2_TEMP 0x51 29 #define LM93_INT_TEMP 0x52 30 #define LM93_EXT_TEMP 0x53 31 #define LM93_IN1_V 0x56 32 #define LM93_IN2_V 0x57 33 #define LM93_IN3_V 0x58 34 #define LM93_IN4_V 0x59 35 #define LM93_IN5_V 0x5a 36 #define LM93_IN6_V 0x5b 37 #define LM93_IN7_V 0x5c 38 #define LM93_IN8_V 0x5d 39 #define LM93_IN9_V 0x5e 40 #define LM93_IN10_V 0x5f 41 #define LM93_IN11_V 0x60 42 #define LM93_IN12_V 0x61 43 #define LM93_IN13_V 0x62 44 #define LM93_IN14_V 0x63 45 #define LM93_IN15_V 0x64 46 #define LM93_IN16_V 0x65 47 #define LM93_TACH1L 0x6e 48 #define LM93_TACH1H 0x6f 49 #define LM93_TACH2L 0x70 50 #define LM93_TACH2H 0x71 51 #define LM93_TACH3L 0x72 52 #define LM93_TACH3H 0x73 53 #define LM93_TACH4L 0x74 54 #define LM93_TACH4H 0x75 55 #define LM93_REVISION 0x3f 56 57 /* Sensors */ 58 #define LMN_CPU1_TEMP 0 59 #define LMN_CPU2_TEMP 1 60 #define LMN_INT_TEMP 2 61 #define LMN_EXT_TEMP 3 62 #define LMN_IN1_V 4 63 #define LMN_IN2_V 5 64 #define LMN_IN3_V 6 65 #define LMN_IN4_V 7 66 #define LMN_IN5_V 8 67 #define LMN_IN6_V 9 68 #define LMN_IN7_V 10 69 #define LMN_IN8_V 11 70 #define LMN_IN9_V 12 71 #define LMN_IN10_V 13 72 #define LMN_IN11_V 14 73 #define LMN_IN12_V 15 74 #define LMN_IN13_V 16 75 #define LMN_IN14_V 17 76 #define LMN_IN15_V 18 77 #define LMN_IN16_V 19 78 #define LMN_TACH1 20 79 #define LMN_TACH2 21 80 #define LMN_TACH3 22 81 #define LMN_TACH4 23 82 #define LMN_NUM_SENSORS 24 83 84 struct { 85 char sensor; 86 u_int8_t cmd; 87 char *name; 88 u_short mVscale; 89 u_short tempscale; /* else a fan */ 90 } lmn_worklist[] = { 91 { LMN_CPU1_TEMP, LM93_CPU1_TEMP, "CPU", 0, 1 }, 92 { LMN_CPU2_TEMP, LM93_CPU2_TEMP, "CPU", 0, 1 }, 93 { LMN_INT_TEMP, LM93_INT_TEMP, "Internal", 0, 1 }, 94 { LMN_EXT_TEMP, LM93_EXT_TEMP, "External", 0, 1 }, 95 96 { LMN_IN1_V, LM93_IN1_V, "+12V", 1236*10, 0 }, 97 { LMN_IN2_V, LM93_IN2_V, "+12V", 1236*10, 0 }, 98 { LMN_IN3_V, LM93_IN3_V, "+12V", 1236*10, 0 }, 99 { LMN_IN4_V, LM93_IN4_V, "FSB_Vtt 1.6V", 1600, 0 }, 100 { LMN_IN5_V, LM93_IN5_V, "3GIO 2V ", 2000, 0 }, 101 { LMN_IN6_V, LM93_IN6_V, "ICH_Core 2V", 2000, 0 }, 102 { LMN_IN7_V, LM93_IN7_V, "Vccp 1.6V", 1600, 0 }, 103 { LMN_IN8_V, LM93_IN8_V, "Vccp 1.6V", 1600, 0 }, 104 { LMN_IN9_V, LM93_IN9_V, "+3.3V", 4400, 0 }, 105 { LMN_IN10_V, LM93_IN10_V, "+5V", 6667, 0 }, 106 { LMN_IN11_V, LM93_IN11_V, "SCSI_Core 3.3V", 3333, 0 }, 107 { LMN_IN12_V, LM93_IN12_V, "Mem_Core 2.6V", 2625, 0 }, 108 { LMN_IN13_V, LM93_IN13_V, "Mem_Vtt 1.3V", 1312, 0 }, 109 { LMN_IN14_V, LM93_IN14_V, "Gbit_Core 1.3V", 1312, 0 }, 110 { LMN_IN15_V, LM93_IN15_V, "-12V", -1236*10, 0 }, 111 { LMN_IN16_V, LM93_IN16_V, "+3.3V S/B", 3600, 0 }, 112 113 { LMN_TACH1, LM93_TACH1L, "", 0, 0 }, 114 { LMN_TACH2, LM93_TACH2L, "", 0, 0 }, 115 { LMN_TACH3, LM93_TACH3L, "", 0, 0 }, 116 { LMN_TACH4, LM93_TACH4L, "", 0, 0 } 117 }; 118 119 struct lmn_softc { 120 struct device sc_dev; 121 i2c_tag_t sc_tag; 122 i2c_addr_t sc_addr; 123 u_int8_t sc_conf; 124 125 struct ksensor sc_sensor[LMN_NUM_SENSORS]; 126 struct ksensordev sc_sensordev; 127 }; 128 129 int lmn_match(struct device *, void *, void *); 130 void lmn_attach(struct device *, struct device *, void *); 131 132 void lmn_refresh(void *); 133 134 const struct cfattach lmn_ca = { 135 sizeof(struct lmn_softc), lmn_match, lmn_attach 136 }; 137 138 struct cfdriver lmn_cd = { 139 NULL, "lmn", DV_DULL 140 }; 141 142 int 143 lmn_match(struct device *parent, void *match, void *aux) 144 { 145 struct i2c_attach_args *ia = aux; 146 147 if (strcmp(ia->ia_name, "lm93") == 0) 148 return (1); 149 return (0); 150 } 151 152 void 153 lmn_attach(struct device *parent, struct device *self, void *aux) 154 { 155 struct lmn_softc *sc = (struct lmn_softc *)self; 156 struct i2c_attach_args *ia = aux; 157 int i; 158 159 sc->sc_tag = ia->ia_tag; 160 sc->sc_addr = ia->ia_addr; 161 162 printf(": %s", ia->ia_name); 163 164 /* Initialize sensor data. */ 165 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 166 sizeof(sc->sc_sensordev.xname)); 167 168 if (sensor_task_register(sc, lmn_refresh, 5) == NULL) { 169 printf(", unable to register update task\n"); 170 return; 171 } 172 173 for (i = 0; i < LMN_NUM_SENSORS; i++) { 174 if (lmn_worklist[i].tempscale) 175 sc->sc_sensor[i].type = SENSOR_TEMP; 176 else if (lmn_worklist[i].mVscale) 177 sc->sc_sensor[i].type = SENSOR_VOLTS_DC; 178 else 179 sc->sc_sensor[i].type = SENSOR_FANRPM; 180 strlcpy(sc->sc_sensor[i].desc, lmn_worklist[i].name, 181 sizeof(sc->sc_sensor[i].desc)); 182 183 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 184 } 185 sensordev_install(&sc->sc_sensordev); 186 187 printf("\n"); 188 } 189 190 void 191 lmn_refresh(void *arg) 192 { 193 struct lmn_softc *sc = arg; 194 u_int8_t cmd, data, data2; 195 u_int16_t fan; 196 int i; 197 198 iic_acquire_bus(sc->sc_tag, 0); 199 200 for (i = 0; i < sizeof lmn_worklist / sizeof(lmn_worklist[0]); i++) { 201 202 cmd = lmn_worklist[i].cmd; 203 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 204 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 205 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 206 continue; 207 } 208 209 sc->sc_sensor[i].flags &= ~SENSOR_FINVALID; 210 if (lmn_worklist[i].tempscale) { 211 if (data == 0x80) 212 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 213 else 214 sc->sc_sensor[i].value = 215 (int8_t)data * 1000000 + 273150000; 216 } else if (lmn_worklist[i].mVscale) { 217 sc->sc_sensor[i].value = lmn_worklist[i].mVscale * 218 1000 * (u_int)data / 192; 219 } else { 220 cmd = lmn_worklist[i].cmd + 1; /* TACHnH follows TACHnL */ 221 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 222 sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) { 223 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 224 continue; 225 } 226 227 fan = data + (data2 << 8); 228 if (fan == 0 || fan == 0xffff) 229 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 230 else 231 sc->sc_sensor[i].value = (90000 * 60) / fan; 232 } 233 } 234 235 iic_release_bus(sc->sc_tag, 0); 236 } 237