Line data Source code
1 : /* $OpenBSD: asc7611.c,v 1.2 2009/01/26 15:07:49 kettenis 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 : 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
97 0 : andl_match(struct device *parent, void *match, void *aux)
98 : {
99 0 : struct i2c_attach_args *ia = aux;
100 :
101 0 : if (strcmp(ia->ia_name, "asc7611") == 0)
102 0 : return 1;
103 0 : return 0;
104 0 : }
105 :
106 : void
107 0 : andl_attach(struct device *parent, struct device *self, void *aux)
108 : {
109 0 : struct andl_softc *sc = (struct andl_softc *)self;
110 0 : struct i2c_attach_args *ia = aux;
111 : int i, j;
112 :
113 0 : sc->sc_tag = ia->ia_tag;
114 0 : sc->sc_addr = ia->ia_addr;
115 :
116 0 : printf(": %s", ia->ia_name);
117 :
118 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
119 : sizeof(sc->sc_sensordev.xname));
120 :
121 0 : for (i = 0; i < ANDL_NUM_TEMPS; i++) {
122 0 : strlcpy(sc->sc_sensors[i].desc, andl_temp[i].name,
123 : sizeof(sc->sc_sensors[i].desc));
124 0 : sc->sc_sensors[i].type = SENSOR_TEMP;
125 : }
126 :
127 0 : for (j = i; i < j + ANDL_NUM_VOLTS; i++) {
128 0 : strlcpy(sc->sc_sensors[i].desc, andl_volt[i - j].name,
129 : sizeof(sc->sc_sensors[i].desc));
130 0 : sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
131 : }
132 :
133 0 : for (j = i + ANDL_NUM_TACHS; i < j; i++)
134 0 : sc->sc_sensors[i].type = SENSOR_FANRPM;
135 :
136 0 : for (i = 0; i < j; i++)
137 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
138 :
139 0 : if (sensor_task_register(sc, andl_refresh, 5) == NULL) {
140 0 : printf(", unable to register update task\n");
141 0 : return;
142 : }
143 :
144 0 : sensordev_install(&sc->sc_sensordev);
145 0 : printf("\n");
146 0 : }
147 :
148 : void
149 0 : andl_refresh(void *arg)
150 : {
151 0 : struct andl_softc *sc = arg;
152 0 : struct ksensor *s = sc->sc_sensors;
153 :
154 0 : iic_acquire_bus(sc->sc_tag, 0);
155 :
156 0 : s += andl_refresh_temps(sc, s);
157 0 : s += andl_refresh_volts(sc, s);
158 0 : s += andl_refresh_tachs(sc, s);
159 :
160 0 : iic_release_bus(sc->sc_tag, 0);
161 0 : }
162 :
163 : int
164 0 : andl_refresh_temps(struct andl_softc *sc, struct ksensor *s)
165 : {
166 : int i;
167 :
168 0 : for (i = 0; i < ANDL_NUM_TEMPS; i++) {
169 0 : uint8_t m = andl_readreg(sc, andl_temp[i].mreg);
170 0 : uint8_t l = andl_readreg(sc, andl_temp[i].lreg);
171 0 : int32_t t = (m << 8 | l) >> (16 - 10);
172 :
173 0 : if (t & 0x200)
174 0 : t -= 0x400;
175 0 : t *= 250;
176 0 : if (t < -55000 || t > 125000) {
177 0 : s[i].flags |= SENSOR_FINVALID;
178 0 : s[i].value = 0;
179 0 : } else {
180 0 : s[i].value = t * 1000 + 273150000;
181 0 : s[i].flags &= ~SENSOR_FINVALID;
182 : }
183 : }
184 0 : return i;
185 : }
186 :
187 : int
188 0 : andl_refresh_volts(struct andl_softc *sc, struct ksensor *s)
189 : {
190 : int i;
191 :
192 0 : for (i = 0; i < ANDL_NUM_VOLTS; i++)
193 0 : s[i].value = 1000ll * andl_readreg(sc, andl_volt[i].mreg) *
194 0 : andl_volt[i].nominal / 0xc0;
195 0 : return i;
196 : }
197 :
198 : int
199 0 : andl_refresh_tachs(struct andl_softc *sc, struct ksensor *s)
200 : {
201 : int i;
202 :
203 0 : for (i = 0; i < ANDL_NUM_TACHS; i++) {
204 0 : uint8_t l = andl_readreg(sc, ANDL_TACH_START + i * 2);
205 0 : uint8_t m = andl_readreg(sc, ANDL_TACH_START + i * 2 + 1);
206 0 : uint16_t b = m << 8 | l;
207 :
208 0 : if (b >= 0xfffc || b == 0) {
209 0 : s[i].flags |= SENSOR_FINVALID;
210 0 : s[i].value = 0;
211 0 : } else {
212 0 : s[i].value = (90000 * 60) / b;
213 0 : s[i].flags &= ~SENSOR_FINVALID;
214 : }
215 : }
216 0 : return i;
217 : }
218 :
219 : uint8_t
220 0 : andl_readreg(struct andl_softc *sc, uint8_t reg)
221 : {
222 0 : uint8_t data;
223 :
224 0 : iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
225 0 : sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
226 :
227 0 : return data;
228 0 : }
229 :
230 : void
231 0 : andl_writereg(struct andl_softc *sc, uint8_t reg, uint8_t data)
232 : {
233 0 : iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
234 0 : sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
235 0 : }
|