xref: /openbsd/sys/dev/i2c/asc7611.c (revision 471aeecf)
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, &reg, 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, &reg, sizeof reg, &data, sizeof data, 0);
235 }
236