1 /* $OpenBSD: asc7611.c,v 1.3 2022/04/06 18:59:28 naddy Exp $ */
2
3 /*
4 * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
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 /*
27 * Andigilog aSC7611
28 * Hardware Monitor with Integrated Fan Control
29 * http://www.andigilog.com/downloads/aSC7611_70A05007.pdf
30 * October 2006
31 */
32
33 /* Temperature */
34 #define ANDL_NUM_TEMPS 3
35 static const struct {
36 const char *name;
37 const uint8_t mreg;
38 const uint8_t lreg;
39 } andl_temp[ANDL_NUM_TEMPS] = {
40 { "External", 0x25, 0x10 },
41 { "Internal", 0x26, 0x15 },
42 { "External", 0x27, 0x0e }
43 };
44
45 /* Voltage */
46 #define ANDL_NUM_VOLTS 5
47 static const struct {
48 const char *name;
49 const short nominal;
50 const uint8_t mreg;
51 } andl_volt[ANDL_NUM_VOLTS] = {
52 { "+2.5V", 2500, 0x20 },
53 { "Vccp", 2250, 0x21 },
54 { "+3.3V", 3300, 0x22 },
55 { "+5V", 5000, 0x23 },
56 { "+12V", 12000, 0x24 }
57 };
58
59 /* Fan */
60 #define ANDL_NUM_TACHS 4
61 #define ANDL_TACH_START 0x28
62
63 #define ANDL_NUM_TOTAL (ANDL_NUM_TEMPS + ANDL_NUM_VOLTS + ANDL_NUM_TACHS)
64
65 struct andl_softc {
66 struct device sc_dev;
67 i2c_tag_t sc_tag;
68 i2c_addr_t sc_addr;
69
70 struct ksensor sc_sensors[ANDL_NUM_TOTAL];
71 struct ksensordev sc_sensordev;
72 };
73
74
75 int andl_match(struct device *, void *, void *);
76 void andl_attach(struct device *, struct device *, void *);
77 void andl_refresh(void *);
78
79 int andl_refresh_temps(struct andl_softc *, struct ksensor *);
80 int andl_refresh_volts(struct andl_softc *, struct ksensor *);
81 int andl_refresh_tachs(struct andl_softc *, struct ksensor *);
82
83 uint8_t andl_readreg(struct andl_softc *, uint8_t);
84 void andl_writereg(struct andl_softc *, uint8_t, uint8_t);
85
86
87 const struct cfattach andl_ca = {
88 sizeof(struct andl_softc), andl_match, andl_attach
89 };
90
91 struct cfdriver andl_cd = {
92 NULL, "andl", DV_DULL
93 };
94
95
96 int
andl_match(struct device * parent,void * match,void * aux)97 andl_match(struct device *parent, void *match, void *aux)
98 {
99 struct i2c_attach_args *ia = aux;
100
101 if (strcmp(ia->ia_name, "asc7611") == 0)
102 return 1;
103 return 0;
104 }
105
106 void
andl_attach(struct device * parent,struct device * self,void * aux)107 andl_attach(struct device *parent, struct device *self, void *aux)
108 {
109 struct andl_softc *sc = (struct andl_softc *)self;
110 struct i2c_attach_args *ia = aux;
111 int i, j;
112
113 sc->sc_tag = ia->ia_tag;
114 sc->sc_addr = ia->ia_addr;
115
116 printf(": %s", ia->ia_name);
117
118 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
119 sizeof(sc->sc_sensordev.xname));
120
121 for (i = 0; i < ANDL_NUM_TEMPS; i++) {
122 strlcpy(sc->sc_sensors[i].desc, andl_temp[i].name,
123 sizeof(sc->sc_sensors[i].desc));
124 sc->sc_sensors[i].type = SENSOR_TEMP;
125 }
126
127 for (j = i; i < j + ANDL_NUM_VOLTS; i++) {
128 strlcpy(sc->sc_sensors[i].desc, andl_volt[i - j].name,
129 sizeof(sc->sc_sensors[i].desc));
130 sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
131 }
132
133 for (j = i + ANDL_NUM_TACHS; i < j; i++)
134 sc->sc_sensors[i].type = SENSOR_FANRPM;
135
136 for (i = 0; i < j; i++)
137 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
138
139 if (sensor_task_register(sc, andl_refresh, 5) == NULL) {
140 printf(", unable to register update task\n");
141 return;
142 }
143
144 sensordev_install(&sc->sc_sensordev);
145 printf("\n");
146 }
147
148 void
andl_refresh(void * arg)149 andl_refresh(void *arg)
150 {
151 struct andl_softc *sc = arg;
152 struct ksensor *s = sc->sc_sensors;
153
154 iic_acquire_bus(sc->sc_tag, 0);
155
156 s += andl_refresh_temps(sc, s);
157 s += andl_refresh_volts(sc, s);
158 s += andl_refresh_tachs(sc, s);
159
160 iic_release_bus(sc->sc_tag, 0);
161 }
162
163 int
andl_refresh_temps(struct andl_softc * sc,struct ksensor * s)164 andl_refresh_temps(struct andl_softc *sc, struct ksensor *s)
165 {
166 int i;
167
168 for (i = 0; i < ANDL_NUM_TEMPS; i++) {
169 uint8_t m = andl_readreg(sc, andl_temp[i].mreg);
170 uint8_t l = andl_readreg(sc, andl_temp[i].lreg);
171 int32_t t = (m << 8 | l) >> (16 - 10);
172
173 if (t & 0x200)
174 t -= 0x400;
175 t *= 250;
176 if (t < -55000 || t > 125000) {
177 s[i].flags |= SENSOR_FINVALID;
178 s[i].value = 0;
179 } else {
180 s[i].value = t * 1000 + 273150000;
181 s[i].flags &= ~SENSOR_FINVALID;
182 }
183 }
184 return i;
185 }
186
187 int
andl_refresh_volts(struct andl_softc * sc,struct ksensor * s)188 andl_refresh_volts(struct andl_softc *sc, struct ksensor *s)
189 {
190 int i;
191
192 for (i = 0; i < ANDL_NUM_VOLTS; i++)
193 s[i].value = 1000ll * andl_readreg(sc, andl_volt[i].mreg) *
194 andl_volt[i].nominal / 0xc0;
195 return i;
196 }
197
198 int
andl_refresh_tachs(struct andl_softc * sc,struct ksensor * s)199 andl_refresh_tachs(struct andl_softc *sc, struct ksensor *s)
200 {
201 int i;
202
203 for (i = 0; i < ANDL_NUM_TACHS; i++) {
204 uint8_t l = andl_readreg(sc, ANDL_TACH_START + i * 2);
205 uint8_t m = andl_readreg(sc, ANDL_TACH_START + i * 2 + 1);
206 uint16_t b = m << 8 | l;
207
208 if (b >= 0xfffc || b == 0) {
209 s[i].flags |= SENSOR_FINVALID;
210 s[i].value = 0;
211 } else {
212 s[i].value = (90000 * 60) / b;
213 s[i].flags &= ~SENSOR_FINVALID;
214 }
215 }
216 return i;
217 }
218
219 uint8_t
andl_readreg(struct andl_softc * sc,uint8_t reg)220 andl_readreg(struct andl_softc *sc, uint8_t reg)
221 {
222 uint8_t data;
223
224 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
225 sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
226
227 return data;
228 }
229
230 void
andl_writereg(struct andl_softc * sc,uint8_t reg,uint8_t data)231 andl_writereg(struct andl_softc *sc, uint8_t reg, uint8_t data)
232 {
233 iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
234 sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
235 }
236