1/* $OpenBSD: w83l784r.c,v 1.14 2022/04/08 15:02:28 naddy Exp $ */ 2 3/* 4 * Copyright (c) 2006 Mark Kettenis 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/* W83L784R registers */ 27#define W83L784R_VCORE 0x20 28#define W83L784R_VBAT 0x21 29#define W83L784R_3_3V 0x22 30#define W83L784R_VCC 0x23 31#define W83L784R_TEMP1 0x27 32#define W83L784R_FAN1 0x28 33#define W83L784R_FAN2 0x29 34#define W83L784R_CONFIG 0x40 35#define W83L784R_FANDIV 0x49 36#define W83L784R_T23ADDR 0x4b 37#define W83L784R_CHIPID 0x4e 38 39#define W83L784R_TEMP23 0x00 40 41/* W83L785R registers */ 42#define W83L785R_2_5V 0x21 43#define W83L785R_1_5V 0x22 44#define W83L785R_VCC 0x23 45#define W83L785R_TEMP2 0x26 46#define W83L785R_FANDIV 0x47 47 48/* Chip IDs */ 49#define WBENV_CHIPID_W83L784R 0x50 50#define WBENV_CHIPID_W83L785R 0x60 51#define WBENV_CHIPID_W83L785TS_L 0x70 52 53#define WBENV_MAX_SENSORS 9 54 55/* 56 * The W83L784R/W83L785R can measure voltages up to 4.096/2.048 V. 57 * To measure higher voltages the input is attenuated with (external) 58 * resistors. So we have to convert the sensor values back to real 59 * voltages by applying the appropriate resistor factor. 60 */ 61#define RFACT_NONE 10000 62#define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y)) 63 64struct wbenv_softc; 65 66struct wbenv_sensor { 67 char *desc; 68 enum sensor_type type; 69 u_int8_t reg; 70 void (*refresh)(struct wbenv_softc *, int); 71 int rfact; 72}; 73 74struct wbenv_softc { 75 struct device sc_dev; 76 77 i2c_tag_t sc_tag; 78 i2c_addr_t sc_addr[3]; 79 u_int8_t sc_chip_id; 80 81 struct ksensor sc_sensors[WBENV_MAX_SENSORS]; 82 struct ksensordev sc_sensordev; 83 const struct wbenv_sensor *sc_wbenv_sensors; 84 int sc_numsensors; 85}; 86 87int wbenv_match(struct device *, void *, void *); 88void wbenv_attach(struct device *, struct device *, void *); 89 90void wbenv_setup_sensors(struct wbenv_softc *, const struct wbenv_sensor *); 91void wbenv_refresh(void *); 92 93void w83l784r_refresh_volt(struct wbenv_softc *, int); 94void w83l785r_refresh_volt(struct wbenv_softc *, int); 95void wbenv_refresh_temp(struct wbenv_softc *, int); 96void w83l784r_refresh_temp(struct wbenv_softc *, int); 97void w83l784r_refresh_fanrpm(struct wbenv_softc *, int); 98void w83l785r_refresh_fanrpm(struct wbenv_softc *, int); 99 100u_int8_t wbenv_readreg(struct wbenv_softc *, u_int8_t); 101void wbenv_writereg(struct wbenv_softc *, u_int8_t, u_int8_t); 102 103const struct cfattach wbenv_ca = { 104 sizeof(struct wbenv_softc), wbenv_match, wbenv_attach 105}; 106 107struct cfdriver wbenv_cd = { 108 NULL, "wbenv", DV_DULL 109}; 110 111const struct wbenv_sensor w83l784r_sensors[] = 112{ 113 { "VCore", SENSOR_VOLTS_DC, W83L784R_VCORE, w83l784r_refresh_volt, RFACT_NONE }, 114 { "VBAT", SENSOR_VOLTS_DC, W83L784R_VBAT, w83l784r_refresh_volt, RFACT(232, 99) }, 115 { "+3.3V", SENSOR_VOLTS_DC, W83L784R_3_3V, w83l784r_refresh_volt, RFACT_NONE }, 116 { "+5V", SENSOR_VOLTS_DC, W83L784R_VCC, w83l784r_refresh_volt, RFACT(50, 34) }, 117 { "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp }, 118 { "", SENSOR_TEMP, 1, w83l784r_refresh_temp }, 119 { "", SENSOR_TEMP, 2, w83l784r_refresh_temp }, 120 { "", SENSOR_FANRPM, W83L784R_FAN1, w83l784r_refresh_fanrpm }, 121 { "", SENSOR_FANRPM, W83L784R_FAN2, w83l784r_refresh_fanrpm }, 122 123 { NULL } 124}; 125 126const struct wbenv_sensor w83l785r_sensors[] = 127{ 128 { "VCore", SENSOR_VOLTS_DC, W83L784R_VCORE, w83l785r_refresh_volt, RFACT_NONE }, 129 { "+2.5V", SENSOR_VOLTS_DC, W83L785R_2_5V, w83l785r_refresh_volt, RFACT(100, 100) }, 130 { "+1.5V", SENSOR_VOLTS_DC, W83L785R_1_5V, w83l785r_refresh_volt, RFACT_NONE }, 131 { "+3.3V", SENSOR_VOLTS_DC, W83L785R_VCC, w83l785r_refresh_volt, RFACT(20, 40) }, 132 { "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp }, 133 { "", SENSOR_TEMP, W83L785R_TEMP2, wbenv_refresh_temp }, 134 { "", SENSOR_FANRPM, W83L784R_FAN1, w83l785r_refresh_fanrpm }, 135 { "", SENSOR_FANRPM, W83L784R_FAN2, w83l785r_refresh_fanrpm }, 136 137 { NULL } 138}; 139 140const struct wbenv_sensor w83l785ts_l_sensors[] = 141{ 142 { "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp }, 143 144 { NULL } 145}; 146 147int 148wbenv_match(struct device *parent, void *match, void *aux) 149{ 150 struct i2c_attach_args *ia = aux; 151 152 if (strcmp(ia->ia_name, "w83l784r") == 0 || 153 strcmp(ia->ia_name, "w83l785r") == 0 || 154 strcmp(ia->ia_name, "w83l785ts-l") == 0) 155 return (1); 156 return (0); 157} 158 159void 160wbenv_attach(struct device *parent, struct device *self, void *aux) 161{ 162 struct wbenv_softc *sc = (struct wbenv_softc *)self; 163 struct i2c_attach_args *ia = aux; 164 u_int8_t cmd, data, config; 165 int i; 166 167 sc->sc_tag = ia->ia_tag; 168 sc->sc_addr[0] = ia->ia_addr; 169 170 iic_acquire_bus(sc->sc_tag, 0); 171 172 cmd = W83L784R_CHIPID; 173 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 174 sc->sc_addr[0], &cmd, sizeof cmd, &data, sizeof data, 0)) { 175 iic_release_bus(sc->sc_tag, 0); 176 printf(": cannot read chip ID register\n"); 177 return; 178 } 179 180 iic_release_bus(sc->sc_tag, 0); 181 182 sc->sc_chip_id = data; 183 184 switch (sc->sc_chip_id) { 185 case WBENV_CHIPID_W83L784R: 186 printf(": W83L784R\n"); 187 wbenv_setup_sensors(sc, w83l784r_sensors); 188 break; 189 case WBENV_CHIPID_W83L785R: 190 printf(": W83L785R\n"); 191 wbenv_setup_sensors(sc, w83l785r_sensors); 192 goto start; 193 case WBENV_CHIPID_W83L785TS_L: 194 printf(": W83L785TS-L\n"); 195 wbenv_setup_sensors(sc, w83l785ts_l_sensors); 196 goto start; 197 default: 198 printf(": unknown Winbond chip (ID 0x%x)\n", sc->sc_chip_id); 199 return; 200 } 201 202 iic_acquire_bus(sc->sc_tag, 0); 203 204 cmd = W83L784R_T23ADDR; 205 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 206 sc->sc_addr[0], &cmd, sizeof cmd, &data, sizeof data, 0)) { 207 iic_release_bus(sc->sc_tag, 0); 208 printf(": cannot read address register\n"); 209 return; 210 } 211 212 iic_release_bus(sc->sc_tag, 0); 213 214 sc->sc_addr[1] = 0x48 + (data & 0x7); 215 sc->sc_addr[2] = 0x48 + ((data >> 4) & 0x7); 216 217 /* Make the bus scan ignore the satellites. */ 218 iic_ignore_addr(sc->sc_addr[1]); 219 iic_ignore_addr(sc->sc_addr[2]); 220 221 start: 222 if (sensor_task_register(sc, wbenv_refresh, 5) == NULL) { 223 printf("%s: unable to register update task\n", 224 sc->sc_dev.dv_xname); 225 return; 226 } 227 228 /* Start the monitoring loop */ 229 config = wbenv_readreg(sc, W83L784R_CONFIG); 230 wbenv_writereg(sc, W83L784R_CONFIG, config | 0x01); 231 232 /* Add sensors */ 233 for (i = 0; i < sc->sc_numsensors; ++i) 234 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]); 235 sensordev_install(&sc->sc_sensordev); 236} 237 238void 239wbenv_setup_sensors(struct wbenv_softc *sc, const struct wbenv_sensor *sensors) 240{ 241 int i; 242 243 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 244 sizeof(sc->sc_sensordev.xname)); 245 246 for (i = 0; sensors[i].desc; i++) { 247 sc->sc_sensors[i].type = sensors[i].type; 248 strlcpy(sc->sc_sensors[i].desc, sensors[i].desc, 249 sizeof(sc->sc_sensors[i].desc)); 250 sc->sc_numsensors++; 251 } 252 sc->sc_wbenv_sensors = sensors; 253} 254 255void 256wbenv_refresh(void *arg) 257{ 258 struct wbenv_softc *sc = arg; 259 int i; 260 261 iic_acquire_bus(sc->sc_tag, 0); 262 263 for (i = 0; i < sc->sc_numsensors; i++) 264 sc->sc_wbenv_sensors[i].refresh(sc, i); 265 266 iic_release_bus(sc->sc_tag, 0); 267} 268 269void 270w83l784r_refresh_volt(struct wbenv_softc *sc, int n) 271{ 272 struct ksensor *sensor = &sc->sc_sensors[n]; 273 int data, reg = sc->sc_wbenv_sensors[n].reg; 274 275 data = wbenv_readreg(sc, reg); 276 sensor->value = (data << 4); /* 16 mV LSB */ 277 sensor->value *= sc->sc_wbenv_sensors[n].rfact; 278 sensor->value /= 10; 279} 280 281void 282w83l785r_refresh_volt(struct wbenv_softc *sc, int n) 283{ 284 struct ksensor *sensor = &sc->sc_sensors[n]; 285 int data, reg = sc->sc_wbenv_sensors[n].reg; 286 287 data = wbenv_readreg(sc, reg); 288 sensor->value = (data << 3); /* 8 mV LSB */ 289 sensor->value *= sc->sc_wbenv_sensors[n].rfact; 290 sensor->value /= 10; 291} 292 293void 294wbenv_refresh_temp(struct wbenv_softc *sc, int n) 295{ 296 struct ksensor *sensor = &sc->sc_sensors[n]; 297 int sdata; 298 299 sdata = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg); 300 if (sdata & 0x80) 301 sdata -= 0x100; 302 sensor->value = sdata * 1000000 + 273150000; 303} 304 305void 306w83l784r_refresh_temp(struct wbenv_softc *sc, int n) 307{ 308 struct ksensor *sensor = &sc->sc_sensors[n]; 309 int16_t sdata; 310 u_int8_t cmd = 0; 311 312 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 313 sc->sc_addr[sc->sc_wbenv_sensors[n].reg], 314 &cmd, sizeof cmd, &sdata, sizeof sdata, 0); 315 sensor->value = (sdata >> 7) * 500000 + 273150000; 316} 317 318void 319w83l784r_refresh_fanrpm(struct wbenv_softc *sc, int n) 320{ 321 struct ksensor *sensor = &sc->sc_sensors[n]; 322 int data, divisor; 323 324 data = wbenv_readreg(sc, W83L784R_FANDIV); 325 if (sc->sc_wbenv_sensors[n].reg == W83L784R_FAN1) 326 divisor = data & 0x07; 327 else 328 divisor = (data >> 4) & 0x07; 329 330 data = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg); 331 if (data == 0xff || data == 0x00) { 332 sensor->flags |= SENSOR_FINVALID; 333 sensor->value = 0; 334 } else { 335 sensor->flags &= ~SENSOR_FINVALID; 336 sensor->value = 1350000 / (data << divisor); 337 } 338} 339 340void 341w83l785r_refresh_fanrpm(struct wbenv_softc *sc, int n) 342{ 343 struct ksensor *sensor = &sc->sc_sensors[n]; 344 int data, divisor; 345 346 data = wbenv_readreg(sc, W83L785R_FANDIV); 347 if (sc->sc_wbenv_sensors[n].reg == W83L784R_FAN1) 348 divisor = data & 0x07; 349 else 350 divisor = (data >> 4) & 0x07; 351 352 data = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg); 353 if (data == 0xff || data == 0x00) { 354 sensor->flags |= SENSOR_FINVALID; 355 sensor->value = 0; 356 } else { 357 sensor->flags &= ~SENSOR_FINVALID; 358 sensor->value = 1350000 / (data << divisor); 359 } 360} 361 362u_int8_t 363wbenv_readreg(struct wbenv_softc *sc, u_int8_t reg) 364{ 365 u_int8_t data; 366 367 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 368 sc->sc_addr[0], ®, sizeof reg, &data, sizeof data, 0); 369 370 return data; 371} 372 373void 374wbenv_writereg(struct wbenv_softc *sc, u_int8_t reg, u_int8_t data) 375{ 376 iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 377 sc->sc_addr[0], ®, sizeof reg, &data, sizeof data, 0); 378} 379