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
43 struct 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
53 int admtemp_match(struct device *, void *, void *);
54 void admtemp_attach(struct device *, struct device *, void *);
55 void admtemp_refresh(void *);
56
57 const struct cfattach admtemp_ca = {
58 sizeof(struct admtemp_softc), admtemp_match, admtemp_attach
59 };
60
61 struct cfdriver admtemp_cd = {
62 NULL, "admtemp", DV_DULL
63 };
64
65 int
admtemp_match(struct device * parent,void * match,void * aux)66 admtemp_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
83 void
admtemp_attach(struct device * parent,struct device * self,void * aux)84 admtemp_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
167 void
admtemp_refresh(void * arg)168 admtemp_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