1/*	$OpenBSD: adm1021.c,v 1.29 2022/04/06 18:59:28 naddy Exp $	*/
2
3/*
4 * Copyright (c) 2005 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/* ADM 1021 registers */
27#define ADM1021_INT_TEMP	0x00
28#define ADM1021_EXT_TEMP	0x01
29#define ADM1021_STATUS		0x02
30#define  ADM1021_STATUS_INVAL	0x7f
31#define  ADM1021_STATUS_NOEXT	0x40
32#define ADM1021_CONFIG_READ	0x03
33#define ADM1021_CONFIG_WRITE	0x09
34#define  ADM1021_CONFIG_RUN	0x40
35#define ADM1021_COMPANY		0xfe	/* contains 0x41 */
36#define ADM1021_STEPPING	0xff	/* contains 0x3? */
37
38/* Sensors */
39#define ADMTEMP_EXT		0
40#define ADMTEMP_INT		1
41#define ADMTEMP_NUM_SENSORS	2
42
43struct admtemp_softc {
44	struct device	sc_dev;
45	i2c_tag_t	sc_tag;
46	i2c_addr_t	sc_addr;
47
48	struct ksensor	sc_sensor[ADMTEMP_NUM_SENSORS];
49	struct ksensordev sc_sensordev;
50	int		sc_noexternal;
51};
52
53int	admtemp_match(struct device *, void *, void *);
54void	admtemp_attach(struct device *, struct device *, void *);
55void	admtemp_refresh(void *);
56
57const struct cfattach admtemp_ca = {
58	sizeof(struct admtemp_softc), admtemp_match, admtemp_attach
59};
60
61struct cfdriver admtemp_cd = {
62	NULL, "admtemp", DV_DULL
63};
64
65int
66admtemp_match(struct device *parent, void *match, void *aux)
67{
68	struct i2c_attach_args *ia = aux;
69
70	if (strcmp(ia->ia_name, "adm1021") == 0 ||
71	    strcmp(ia->ia_name, "adm1023") == 0 ||
72	    strcmp(ia->ia_name, "adm1032") == 0 ||
73	    strcmp(ia->ia_name, "g781") == 0 ||
74	    strcmp(ia->ia_name, "g781-1") == 0 ||
75	    strcmp(ia->ia_name, "gl523sm") == 0 ||
76	    strcmp(ia->ia_name, "max1617") == 0 ||
77	    strcmp(ia->ia_name, "sa56004x") == 0 ||
78	    strcmp(ia->ia_name, "xeontemp") == 0)
79		return (1);
80	return (0);
81}
82
83void
84admtemp_attach(struct device *parent, struct device *self, void *aux)
85{
86	struct admtemp_softc *sc = (struct admtemp_softc *)self;
87	struct i2c_attach_args *ia = aux;
88	u_int8_t cmd, data, stat;
89	int xeon = 0, i;
90
91	sc->sc_tag = ia->ia_tag;
92	sc->sc_addr = ia->ia_addr;
93
94	if (strcmp(ia->ia_name, "xeontemp") == 0) {
95		printf(": Xeon");
96		xeon = 1;
97	} else
98		printf(": %s", ia->ia_name);
99
100	iic_acquire_bus(sc->sc_tag, 0);
101	cmd = ADM1021_CONFIG_READ;
102	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
103	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
104		iic_release_bus(sc->sc_tag, 0);
105		printf(", cannot get control register\n");
106		return;
107	}
108	if (data & ADM1021_CONFIG_RUN) {
109		cmd = ADM1021_STATUS;
110		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
111		    sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) {
112			iic_release_bus(sc->sc_tag, 0);
113			printf(", cannot read status register\n");
114			return;
115		}
116		if ((stat & ADM1021_STATUS_INVAL) == ADM1021_STATUS_INVAL) {
117			if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
118			    sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) {
119				iic_release_bus(sc->sc_tag, 0);
120				printf(", cannot read status register\n");
121				return;
122			}
123		}
124
125		/* means external is dead */
126		if ((stat & ADM1021_STATUS_INVAL) != ADM1021_STATUS_INVAL &&
127		    (stat & ADM1021_STATUS_NOEXT))
128			sc->sc_noexternal = 1;
129
130		data &= ~ADM1021_CONFIG_RUN;
131		cmd = ADM1021_CONFIG_WRITE;
132		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
133		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
134			iic_release_bus(sc->sc_tag, 0);
135			printf(", cannot set control register\n");
136			return;
137		}
138	}
139	iic_release_bus(sc->sc_tag, 0);
140
141	/* Initialize sensor data. */
142	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
143	    sizeof(sc->sc_sensordev.xname));
144
145	sc->sc_sensor[ADMTEMP_EXT].type = SENSOR_TEMP;
146	strlcpy(sc->sc_sensor[ADMTEMP_EXT].desc,
147	    xeon ? "Xeon" : "External",
148	    sizeof(sc->sc_sensor[ADMTEMP_EXT].desc));
149
150	sc->sc_sensor[ADMTEMP_INT].type = SENSOR_TEMP;
151	strlcpy(sc->sc_sensor[ADMTEMP_INT].desc,
152	    xeon ? "Xeon" : "Internal",
153	    sizeof(sc->sc_sensor[ADMTEMP_INT].desc));
154
155	if (sensor_task_register(sc, admtemp_refresh, 5) == NULL) {
156		printf(", unable to register update task\n");
157		return;
158	}
159
160	for (i = 0; i < (sc->sc_noexternal ? 1 : ADMTEMP_NUM_SENSORS); i++)
161		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
162	sensordev_install(&sc->sc_sensordev);
163
164	printf("\n");
165}
166
167void
168admtemp_refresh(void *arg)
169{
170	struct admtemp_softc *sc = arg;
171	u_int8_t cmd;
172	int8_t sdata;
173
174	iic_acquire_bus(sc->sc_tag, 0);
175
176	if (sc->sc_noexternal == 0) {
177		cmd = ADM1021_EXT_TEMP;
178		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
179		    &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) {
180			if (sdata == 0x7f) {
181				sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID;
182			} else {
183				sc->sc_sensor[ADMTEMP_EXT].value =
184				    273150000 + 1000000 * sdata;
185				sc->sc_sensor[ADMTEMP_EXT].flags &= ~SENSOR_FINVALID;
186			}
187		}
188	} else
189		sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID;
190
191
192	cmd = ADM1021_INT_TEMP;
193	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
194	    &cmd, sizeof cmd, &sdata,  sizeof sdata, 0) == 0) {
195		if (sdata == 0x7f) {
196			sc->sc_sensor[ADMTEMP_INT].flags |= SENSOR_FINVALID;
197		} else {
198			sc->sc_sensor[ADMTEMP_INT].value =
199			    273150000 + 1000000 * sdata;
200			sc->sc_sensor[ADMTEMP_INT].flags &= ~SENSOR_FINVALID;
201		}
202	}
203
204	iic_release_bus(sc->sc_tag, 0);
205}
206