1 /* $OpenBSD: ad741x.c,v 1.15 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 /* AD741x registers */
27 #define AD741X_TEMP 0x00
28 #define AD741X_CONFIG 0x01
29 #define AD741X_THYST 0x02
30 #define AD741X_TOTI 0x03
31 #define AD741X_ADC 0x04
32 #define AD741X_CONFIG2 0x05
33
34 #define AD741X_CONFMASK 0xe0
35
36 /* Sensors */
37 #define ADC_TEMP 0
38 #define ADC_ADC0 1
39 #define ADC_ADC1 2
40 #define ADC_ADC2 3
41 #define ADC_ADC3 4
42 #define ADC_MAX_SENSORS 5
43
44 struct adc_softc {
45 struct device sc_dev;
46 i2c_tag_t sc_tag;
47 i2c_addr_t sc_addr;
48 int sc_chip;
49 u_int8_t sc_config;
50
51 struct ksensor sc_sensor[ADC_MAX_SENSORS];
52 struct ksensordev sc_sensordev;
53 };
54
55 int adc_match(struct device *, void *, void *);
56 void adc_attach(struct device *, struct device *, void *);
57 void adc_refresh(void *);
58
59 const struct cfattach adc_ca = {
60 sizeof(struct adc_softc), adc_match, adc_attach
61 };
62
63 struct cfdriver adc_cd = {
64 NULL, "adc", DV_DULL
65 };
66
67 int
adc_match(struct device * parent,void * match,void * aux)68 adc_match(struct device *parent, void *match, void *aux)
69 {
70 struct i2c_attach_args *ia = aux;
71
72 if (strcmp(ia->ia_name, "ad7417") == 0 ||
73 strcmp(ia->ia_name, "ad7418") == 0)
74 return (1);
75 return (0);
76 }
77
78 void
adc_attach(struct device * parent,struct device * self,void * aux)79 adc_attach(struct device *parent, struct device *self, void *aux)
80 {
81 struct adc_softc *sc = (struct adc_softc *)self;
82 struct i2c_attach_args *ia = aux;
83 u_int8_t cmd, data;
84 int nsens = 0, i;
85
86 sc->sc_tag = ia->ia_tag;
87 sc->sc_addr = ia->ia_addr;
88
89 printf(": %s", ia->ia_name);
90
91 sc->sc_chip = 0;
92 if (strcmp(ia->ia_name, "ad7417") == 0)
93 sc->sc_chip = 7417;
94 if (strcmp(ia->ia_name, "ad7418") == 0)
95 sc->sc_chip = 7418;
96
97 if (sc->sc_chip != 0) {
98 cmd = AD741X_CONFIG2;
99 data = 0;
100 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
101 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
102 printf(", config2 reset failed\n");
103 return;
104 }
105 }
106
107 cmd = AD741X_CONFIG;
108 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
109 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
110 printf(", config reset failed\n");
111 return;
112 }
113 data &= 0xfe;
114 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
115 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
116 printf(", config reset failed\n");
117 return;
118 }
119 sc->sc_config = data;
120
121 /* Initialize sensor data. */
122 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
123 sizeof(sc->sc_sensordev.xname));
124
125 sc->sc_sensor[ADC_TEMP].type = SENSOR_TEMP;
126 strlcpy(sc->sc_sensor[ADC_TEMP].desc, "Internal",
127 sizeof(sc->sc_sensor[ADC_TEMP].desc));
128 nsens = 1;
129
130 if (sc->sc_chip == 7417 || sc->sc_chip == 7418) {
131 sc->sc_sensor[ADC_ADC0].type = SENSOR_INTEGER;
132 nsens++;
133 }
134 if (sc->sc_chip == 7417 || sc->sc_chip == 7418) {
135 sc->sc_sensor[ADC_ADC1].type = SENSOR_INTEGER;
136 sc->sc_sensor[ADC_ADC2].type = SENSOR_INTEGER;
137 sc->sc_sensor[ADC_ADC3].type = SENSOR_INTEGER;
138 nsens += 3;
139 }
140
141 if (sensor_task_register(sc, adc_refresh, 5) == NULL) {
142 printf(", unable to register update task\n");
143 return;
144 }
145
146 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[0]);
147 if (sc->sc_chip == 7417 || sc->sc_chip == 7418)
148 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[1]);
149 if (sc->sc_chip == 7417)
150 for (i = 2; i < nsens; i++)
151 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
152 sensordev_install(&sc->sc_sensordev);
153
154 printf("\n");
155 }
156
157 void
adc_refresh(void * arg)158 adc_refresh(void *arg)
159 {
160 struct adc_softc *sc = arg;
161 u_int8_t cmd, reg;
162 u_int16_t data;
163 int i;
164
165 iic_acquire_bus(sc->sc_tag, 0);
166
167 reg = (sc->sc_config & AD741X_CONFMASK) | (0 << 5);
168 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
169 sc->sc_addr, &cmd, sizeof cmd, ®, sizeof reg, 0))
170 goto done;
171 delay(1000);
172 cmd = AD741X_TEMP;
173 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
174 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
175 goto done;
176 sc->sc_sensor[ADC_TEMP].value = 273150000 +
177 (betoh16(data) >> 6) * 250000;
178
179 if (sc->sc_chip == 0)
180 goto done;
181
182 if (sc->sc_chip == 7418) {
183 reg = (reg & AD741X_CONFMASK) | (4 << 5);
184 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
185 sc->sc_addr, &cmd, sizeof cmd, ®, sizeof reg, 0))
186 goto done;
187 delay(1000);
188 cmd = AD741X_ADC;
189 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
190 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
191 goto done;
192 sc->sc_sensor[ADC_ADC0].value = betoh16(data) >> 6;
193 goto done;
194 }
195
196 for (i = 0; i < 4; i++) {
197 reg = (reg & AD741X_CONFMASK) | (i << 5);
198 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
199 sc->sc_addr, &cmd, sizeof cmd, ®, sizeof reg, 0))
200 goto done;
201 delay(1000);
202 cmd = AD741X_ADC;
203 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
204 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
205 goto done;
206 sc->sc_sensor[ADC_ADC0 + i].value = betoh16(data) >> 6;
207 }
208
209 done:
210 iic_release_bus(sc->sc_tag, 0);
211 }
212