1*471aeecfSnaddy /* $OpenBSD: adm1021.c,v 1.29 2022/04/06 18:59:28 naddy Exp $ */
24e3ece99Sderaadt
34e3ece99Sderaadt /*
44e3ece99Sderaadt * Copyright (c) 2005 Theo de Raadt
54e3ece99Sderaadt *
64e3ece99Sderaadt * Permission to use, copy, modify, and distribute this software for any
74e3ece99Sderaadt * purpose with or without fee is hereby granted, provided that the above
84e3ece99Sderaadt * copyright notice and this permission notice appear in all copies.
94e3ece99Sderaadt *
104e3ece99Sderaadt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
114e3ece99Sderaadt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
124e3ece99Sderaadt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
134e3ece99Sderaadt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144e3ece99Sderaadt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154e3ece99Sderaadt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164e3ece99Sderaadt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174e3ece99Sderaadt */
184e3ece99Sderaadt
194e3ece99Sderaadt #include <sys/param.h>
204e3ece99Sderaadt #include <sys/systm.h>
214e3ece99Sderaadt #include <sys/device.h>
224e3ece99Sderaadt #include <sys/sensors.h>
234e3ece99Sderaadt
244e3ece99Sderaadt #include <dev/i2c/i2cvar.h>
254e3ece99Sderaadt
264e3ece99Sderaadt /* ADM 1021 registers */
274e3ece99Sderaadt #define ADM1021_INT_TEMP 0x00
284e3ece99Sderaadt #define ADM1021_EXT_TEMP 0x01
294e3ece99Sderaadt #define ADM1021_STATUS 0x02
302ccdc525Sderaadt #define ADM1021_STATUS_INVAL 0x7f
312ccdc525Sderaadt #define ADM1021_STATUS_NOEXT 0x40
32dd58f502Sderaadt #define ADM1021_CONFIG_READ 0x03
33dd58f502Sderaadt #define ADM1021_CONFIG_WRITE 0x09
34992a98bbSderaadt #define ADM1021_CONFIG_RUN 0x40
354e3ece99Sderaadt #define ADM1021_COMPANY 0xfe /* contains 0x41 */
364e3ece99Sderaadt #define ADM1021_STEPPING 0xff /* contains 0x3? */
374e3ece99Sderaadt
384e3ece99Sderaadt /* Sensors */
394a9fb704Sderaadt #define ADMTEMP_EXT 0
404a9fb704Sderaadt #define ADMTEMP_INT 1
414e3ece99Sderaadt #define ADMTEMP_NUM_SENSORS 2
424e3ece99Sderaadt
434e3ece99Sderaadt struct admtemp_softc {
444e3ece99Sderaadt struct device sc_dev;
454e3ece99Sderaadt i2c_tag_t sc_tag;
464e3ece99Sderaadt i2c_addr_t sc_addr;
474e3ece99Sderaadt
48275cbf62Sderaadt struct ksensor sc_sensor[ADMTEMP_NUM_SENSORS];
49275cbf62Sderaadt struct ksensordev sc_sensordev;
502ccdc525Sderaadt int sc_noexternal;
514e3ece99Sderaadt };
524e3ece99Sderaadt
534e3ece99Sderaadt int admtemp_match(struct device *, void *, void *);
544e3ece99Sderaadt void admtemp_attach(struct device *, struct device *, void *);
554e3ece99Sderaadt void admtemp_refresh(void *);
564e3ece99Sderaadt
57*471aeecfSnaddy const struct cfattach admtemp_ca = {
584e3ece99Sderaadt sizeof(struct admtemp_softc), admtemp_match, admtemp_attach
594e3ece99Sderaadt };
604e3ece99Sderaadt
614e3ece99Sderaadt struct cfdriver admtemp_cd = {
624e3ece99Sderaadt NULL, "admtemp", DV_DULL
634e3ece99Sderaadt };
644e3ece99Sderaadt
654e3ece99Sderaadt int
admtemp_match(struct device * parent,void * match,void * aux)664e3ece99Sderaadt admtemp_match(struct device *parent, void *match, void *aux)
674e3ece99Sderaadt {
684e3ece99Sderaadt struct i2c_attach_args *ia = aux;
694e3ece99Sderaadt
70e109779fSderaadt if (strcmp(ia->ia_name, "adm1021") == 0 ||
71bd3616cdSkettenis strcmp(ia->ia_name, "adm1023") == 0 ||
7281fc4c8eSderaadt strcmp(ia->ia_name, "adm1032") == 0 ||
737a7081baSkettenis strcmp(ia->ia_name, "g781") == 0 ||
747a7081baSkettenis strcmp(ia->ia_name, "g781-1") == 0 ||
757a7081baSkettenis strcmp(ia->ia_name, "gl523sm") == 0 ||
767a7081baSkettenis strcmp(ia->ia_name, "max1617") == 0 ||
77ab19912bSderaadt strcmp(ia->ia_name, "sa56004x") == 0 ||
787a7081baSkettenis strcmp(ia->ia_name, "xeontemp") == 0)
794e3ece99Sderaadt return (1);
80f571b3bfSderaadt return (0);
814e3ece99Sderaadt }
824e3ece99Sderaadt
834e3ece99Sderaadt void
admtemp_attach(struct device * parent,struct device * self,void * aux)844e3ece99Sderaadt admtemp_attach(struct device *parent, struct device *self, void *aux)
854e3ece99Sderaadt {
864e3ece99Sderaadt struct admtemp_softc *sc = (struct admtemp_softc *)self;
874e3ece99Sderaadt struct i2c_attach_args *ia = aux;
882ccdc525Sderaadt u_int8_t cmd, data, stat;
892ccdc525Sderaadt int xeon = 0, i;
904e3ece99Sderaadt
914e3ece99Sderaadt sc->sc_tag = ia->ia_tag;
924e3ece99Sderaadt sc->sc_addr = ia->ia_addr;
934e3ece99Sderaadt
949b06f7aeSderaadt if (strcmp(ia->ia_name, "xeontemp") == 0) {
95c179919eSderaadt printf(": Xeon");
962ccdc525Sderaadt xeon = 1;
979b06f7aeSderaadt } else
989b06f7aeSderaadt printf(": %s", ia->ia_name);
99af73b13aSderaadt
100992a98bbSderaadt iic_acquire_bus(sc->sc_tag, 0);
101992a98bbSderaadt cmd = ADM1021_CONFIG_READ;
102992a98bbSderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
103b8631cf2Sderaadt sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
104992a98bbSderaadt iic_release_bus(sc->sc_tag, 0);
105e3a4b174Sderaadt printf(", cannot get control register\n");
106992a98bbSderaadt return;
107992a98bbSderaadt }
1081bb70835Sderaadt if (data & ADM1021_CONFIG_RUN) {
1092ccdc525Sderaadt cmd = ADM1021_STATUS;
1102ccdc525Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
111b8631cf2Sderaadt sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) {
1122ccdc525Sderaadt iic_release_bus(sc->sc_tag, 0);
1132ccdc525Sderaadt printf(", cannot read status register\n");
1142ccdc525Sderaadt return;
1152ccdc525Sderaadt }
1162ccdc525Sderaadt if ((stat & ADM1021_STATUS_INVAL) == ADM1021_STATUS_INVAL) {
1172ccdc525Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
1182ccdc525Sderaadt sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) {
1192ccdc525Sderaadt iic_release_bus(sc->sc_tag, 0);
1202ccdc525Sderaadt printf(", cannot read status register\n");
1212ccdc525Sderaadt return;
1222ccdc525Sderaadt }
1232ccdc525Sderaadt }
1242ccdc525Sderaadt
1252ccdc525Sderaadt /* means external is dead */
1262ccdc525Sderaadt if ((stat & ADM1021_STATUS_INVAL) != ADM1021_STATUS_INVAL &&
1272ccdc525Sderaadt (stat & ADM1021_STATUS_NOEXT))
1282ccdc525Sderaadt sc->sc_noexternal = 1;
1292ccdc525Sderaadt
130992a98bbSderaadt data &= ~ADM1021_CONFIG_RUN;
131992a98bbSderaadt cmd = ADM1021_CONFIG_WRITE;
132992a98bbSderaadt if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
133b8631cf2Sderaadt sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
134992a98bbSderaadt iic_release_bus(sc->sc_tag, 0);
135e3a4b174Sderaadt printf(", cannot set control register\n");
136992a98bbSderaadt return;
137992a98bbSderaadt }
138e3a4b174Sderaadt }
139992a98bbSderaadt iic_release_bus(sc->sc_tag, 0);
140dd58f502Sderaadt
1414e3ece99Sderaadt /* Initialize sensor data. */
14227515a6bSderaadt strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
14327515a6bSderaadt sizeof(sc->sc_sensordev.xname));
1444e3ece99Sderaadt
1454a9fb704Sderaadt sc->sc_sensor[ADMTEMP_EXT].type = SENSOR_TEMP;
1464a9fb704Sderaadt strlcpy(sc->sc_sensor[ADMTEMP_EXT].desc,
14727515a6bSderaadt xeon ? "Xeon" : "External",
1484a9fb704Sderaadt sizeof(sc->sc_sensor[ADMTEMP_EXT].desc));
1494a9fb704Sderaadt
1504e3ece99Sderaadt sc->sc_sensor[ADMTEMP_INT].type = SENSOR_TEMP;
1512ccdc525Sderaadt strlcpy(sc->sc_sensor[ADMTEMP_INT].desc,
15227515a6bSderaadt xeon ? "Xeon" : "Internal",
1534e3ece99Sderaadt sizeof(sc->sc_sensor[ADMTEMP_INT].desc));
1544e3ece99Sderaadt
155abd9fc28Sdlg if (sensor_task_register(sc, admtemp_refresh, 5) == NULL) {
1564e3ece99Sderaadt printf(", unable to register update task\n");
1574e3ece99Sderaadt return;
1584e3ece99Sderaadt }
1594e3ece99Sderaadt
1602ccdc525Sderaadt for (i = 0; i < (sc->sc_noexternal ? 1 : ADMTEMP_NUM_SENSORS); i++)
16127515a6bSderaadt sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
16227515a6bSderaadt sensordev_install(&sc->sc_sensordev);
1634e3ece99Sderaadt
1644e3ece99Sderaadt printf("\n");
1654e3ece99Sderaadt }
1664e3ece99Sderaadt
1674e3ece99Sderaadt void
admtemp_refresh(void * arg)1684e3ece99Sderaadt admtemp_refresh(void *arg)
1694e3ece99Sderaadt {
1704e3ece99Sderaadt struct admtemp_softc *sc = arg;
171dd58f502Sderaadt u_int8_t cmd;
172dd58f502Sderaadt int8_t sdata;
1734e3ece99Sderaadt
1744e3ece99Sderaadt iic_acquire_bus(sc->sc_tag, 0);
1754e3ece99Sderaadt
1762ccdc525Sderaadt if (sc->sc_noexternal == 0) {
1774a9fb704Sderaadt cmd = ADM1021_EXT_TEMP;
1784a9fb704Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
179b8631cf2Sderaadt &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) {
1809cc05711Sderaadt if (sdata == 0x7f) {
181861c5a6bSderaadt sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID;
1829cc05711Sderaadt } else {
1834a9fb704Sderaadt sc->sc_sensor[ADMTEMP_EXT].value =
1844a9fb704Sderaadt 273150000 + 1000000 * sdata;
1859cc05711Sderaadt sc->sc_sensor[ADMTEMP_EXT].flags &= ~SENSOR_FINVALID;
1869cc05711Sderaadt }
1879cc05711Sderaadt }
1882ccdc525Sderaadt } else
1892ccdc525Sderaadt sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID;
1904a9fb704Sderaadt
1912ccdc525Sderaadt
1924e3ece99Sderaadt cmd = ADM1021_INT_TEMP;
1934a9fb704Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
194b8631cf2Sderaadt &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) {
1959cc05711Sderaadt if (sdata == 0x7f) {
196861c5a6bSderaadt sc->sc_sensor[ADMTEMP_INT].flags |= SENSOR_FINVALID;
1979cc05711Sderaadt } else {
198af73b13aSderaadt sc->sc_sensor[ADMTEMP_INT].value =
199af73b13aSderaadt 273150000 + 1000000 * sdata;
2009cc05711Sderaadt sc->sc_sensor[ADMTEMP_INT].flags &= ~SENSOR_FINVALID;
2019cc05711Sderaadt }
2029cc05711Sderaadt }
2034e3ece99Sderaadt
2044e3ece99Sderaadt iic_release_bus(sc->sc_tag, 0);
2054e3ece99Sderaadt }
206