xref: /openbsd/sys/dev/i2c/adm1021.c (revision 471aeecf)
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