xref: /openbsd/sys/dev/i2c/asc7621.c (revision 4bdff4be)
1 /*	$OpenBSD: asc7621.c,v 1.5 2022/04/06 18:59:28 naddy Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 Mike Belopuhov
5  * Copyright (c) 2007 Theo de Raadt
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/sensors.h>
24 
25 #include <dev/i2c/i2cvar.h>
26 
27 /* ASC7621 registers */
28 
29 #define ASC7621_PECI		0x40	/* Check for PECI monitoring */
30 #define ASC7621_PECI_MASK	0x10	/* 00010000 */
31 
32 #define ASC7621_LEGACY		0x36	/* Check for legacy mode */
33 #define ASC7621_LEGACY_MASK	0x10	/* 00010000 */
34 
35 #define ASC7621_TEMP1H		0x25	/* Zone 1 Temperature (MS Byte) */
36 #define ASC7621_TEMP1L		0x10	/* Zone 1 Temperature (LS Byte) */
37 #define ASC7621_TEMP2H		0x26	/* Zone 2 Temperature (MS Byte) */
38 #define ASC7621_TEMP2L		0x15	/* Zone 2 Temperature (LS Byte) */
39 #define ASC7621_TEMP3H		0x27	/* Zone 3 Temperature (MS Byte) */
40 #define ASC7621_TEMP3L		0x16	/* Zone 3 Temperature (LS Byte) */
41 #define ASC7621_TEMP4H		0x33	/* Zone 4 Temperature (MS Byte) */
42 #define ASC7621_TEMP4L		0x17	/* Zone 4 Temperature (LS Byte) */
43 #define ASC7621_TEMP_NA		0x80	/* Not plugged */
44 
45 #define ASC7621_IN1_VH		0x20	/* 2.5V (MS Byte) */
46 #define ASC7621_IN1_VL		0x13	/* 2.5V (LS Byte) */
47 #define ASC7621_IN2_VH		0x21	/* VCCP (MS Byte) */
48 #define ASC7621_IN2_VL		0x18	/* VCCP (LS Byte) */
49 #define ASC7621_IN3_VH		0x22	/* 3.3V (MS Byte) */
50 #define ASC7621_IN3_VL		0x11	/* 2.3V (LS Byte) */
51 #define ASC7621_IN4_VH		0x23	/* 5V   (MS Byte) */
52 #define ASC7621_IN4_VL		0x12	/* 5V   (LS Byte) */
53 #define ASC7621_IN5_VH		0x24	/* 12V  (MS Byte) */
54 #define ASC7621_IN5_VL		0x14	/* 12V  (LS Byte) */
55 
56 #define ASC7621_TACH1H		0x29	/* Tachometer 1 (MS Byte) */
57 #define ASC7621_TACH1L		0x28	/* Tachometer 1 (LS Byte) */
58 #define ASC7621_TACH2H		0x2b	/* Tachometer 2 (MS Byte) */
59 #define ASC7621_TACH2L		0x2a	/* Tachometer 2 (LS Byte) */
60 #define ASC7621_TACH3H		0x2d	/* Tachometer 3 (MS Byte) */
61 #define ASC7621_TACH3L		0x2c	/* Tachometer 3 (LS Byte) */
62 #define ASC7621_TACH4H		0x2f	/* Tachometer 4 (MS Byte) */
63 #define ASC7621_TACH4L		0x2e	/* Tachometer 4 (LS Byte) */
64 
65 /* Sensors */
66 #define ADL_TEMP1		0
67 #define ADL_TEMP2		1
68 #define ADL_TEMP3		2
69 #define ADL_TEMP4		3
70 #define ADL_IN1_V		4
71 #define ADL_IN2_V		5
72 #define ADL_IN3_V		6
73 #define ADL_IN4_V		7
74 #define ADL_IN5_V		8
75 #define ADL_TACH1		9
76 #define ADL_TACH2		10
77 #define ADL_TACH3		11
78 #define ADL_TACH4		12
79 #define ADL_NUM_SENSORS		13
80 
81 struct {
82 	char		sensor;
83 	u_int8_t	hreg;			/* MS-byte register */
84 	u_int8_t	lreg;			/* LS-byte register */
85 	char		*name;
86 	u_short		mVscale;
87 	u_short		tempscale;		/* else a fan */
88 } adl_worklist[] = {
89 	{ ADL_TEMP1, ASC7621_TEMP1H, ASC7621_TEMP1L, "CPU", 0, 1 },
90 	{ ADL_TEMP2, ASC7621_TEMP2H, ASC7621_TEMP2L, "CPU", 0, 1 },
91 	{ ADL_TEMP3, ASC7621_TEMP3H, ASC7621_TEMP3L, "Internal", 0, 1 },
92 	{ ADL_TEMP4, ASC7621_TEMP4H, ASC7621_TEMP4L, "External", 0, 1 },
93 
94 	{ ADL_IN1_V, ASC7621_IN1_VH, ASC7621_IN1_VL, "+1.5V", 2500, 0 },
95 	{ ADL_IN2_V, ASC7621_IN2_VH, ASC7621_IN2_VL, "Vccp",  2250, 0 },
96 	{ ADL_IN3_V, ASC7621_IN3_VH, ASC7621_IN3_VL, "+3.3V", 3300, 0 },
97 	{ ADL_IN4_V, ASC7621_IN4_VH, ASC7621_IN4_VL, "+5V",   5000, 0 },
98 	{ ADL_IN5_V, ASC7621_IN5_VH, ASC7621_IN5_VL, "+12V", 12000, 0 },
99 
100 	{ ADL_TACH1, ASC7621_TACH1L, ASC7621_TACH1H, "", 0, 0 },
101 	{ ADL_TACH2, ASC7621_TACH2L, ASC7621_TACH2H, "", 0, 0 },
102 	{ ADL_TACH3, ASC7621_TACH3L, ASC7621_TACH3H, "", 0, 0 },
103 	{ ADL_TACH4, ASC7621_TACH4L, ASC7621_TACH4H, "", 0, 0 }
104 };
105 
106 struct adl_softc {
107 	struct device sc_dev;
108 	i2c_tag_t sc_tag;
109 	i2c_addr_t sc_addr;
110 	u_int8_t sc_conf;
111 
112 	struct ksensor sc_sensor[ADL_NUM_SENSORS];
113 	struct ksensordev sc_sensordev;
114 };
115 
116 #if 0
117 static int peci_enabled;
118 static int legacy_mode;
119 #endif
120 
121 int	adl_match(struct device *, void *, void *);
122 void	adl_attach(struct device *, struct device *, void *);
123 
124 void	adl_refresh(void *);
125 
126 const struct cfattach adl_ca = {
127 	sizeof(struct adl_softc), adl_match, adl_attach
128 };
129 
130 struct cfdriver adl_cd = {
131 	NULL, "adl", DV_DULL
132 };
133 
134 int
135 adl_match(struct device *parent, void *match, void *aux)
136 {
137 	struct i2c_attach_args *ia = aux;
138 
139 	if (strcmp(ia->ia_name, "asc7621") == 0)
140 		return (1);
141 	return (0);
142 }
143 
144 void
145 adl_attach(struct device *parent, struct device *self, void *aux)
146 {
147 	struct adl_softc *sc = (struct adl_softc *)self;
148 	struct i2c_attach_args *ia = aux;
149 	u_int8_t cmd, data;
150 	int i;
151 
152 	sc->sc_tag = ia->ia_tag;
153 	sc->sc_addr = ia->ia_addr;
154 
155 	printf(": %s", ia->ia_name);
156 
157 	/* Initialize sensor data. */
158 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
159 	    sizeof(sc->sc_sensordev.xname));
160 
161 	/* Check for PECI mode */
162 	cmd = ASC7621_PECI;
163 	(void)iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
164 	    &cmd, sizeof(cmd), &data, sizeof(data), 0);
165 	if (data & ASC7621_PECI_MASK)
166 		printf(", PECI enabled\n");
167 
168 #if 0
169 	/* Check for legacy mode */
170 	cmd = ASC7621_LEGACY;
171 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
172 	    &cmd, sizeof(cmd), &data, sizeof(data), 0)) {
173 		printf(", unable to read PECI configuration register");
174 	}
175 	if (data & ASC7621_LEGACY_MASK)
176 		legacy_mode = 1;
177 #endif
178 
179 	if (sensor_task_register(sc, adl_refresh, 5) == NULL) {
180 		printf(", unable to register update task\n");
181 		return;
182 	}
183 
184 	for (i = 0; i < ADL_NUM_SENSORS; i++) {
185 		if (adl_worklist[i].tempscale)
186 			sc->sc_sensor[i].type = SENSOR_TEMP;
187 		else if (adl_worklist[i].mVscale)
188 			sc->sc_sensor[i].type = SENSOR_VOLTS_DC;
189 		else
190 			sc->sc_sensor[i].type = SENSOR_FANRPM;
191 		strlcpy(sc->sc_sensor[i].desc, adl_worklist[i].name,
192 		    sizeof(sc->sc_sensor[i].desc));
193 
194 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
195 	}
196 	sensordev_install(&sc->sc_sensordev);
197 
198 	printf("\n");
199 }
200 
201 void
202 adl_refresh(void *arg)
203 {
204 	struct adl_softc *sc = arg;
205 	int64_t temp, volt;
206 	u_int8_t hdata, ldata, hreg, lreg;
207 	u_int16_t fan;
208 	int i;
209 
210 	iic_acquire_bus(sc->sc_tag, 0);
211 
212 	for (i = 0; i < sizeof adl_worklist / sizeof(adl_worklist[0]); i++) {
213 		hreg = adl_worklist[i].hreg;
214 		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
215 		    sc->sc_addr, &hreg, sizeof hreg, &hdata, sizeof hdata, 0)) {
216 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
217 			continue;
218 		}
219 		lreg = adl_worklist[i].lreg;
220 		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
221 		    sc->sc_addr, &lreg, sizeof lreg, &ldata, sizeof ldata, 0)) {
222 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
223 			continue;
224 		}
225 
226 		sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
227 		if (adl_worklist[i].tempscale) {
228 			if (hdata == ASC7621_TEMP_NA)
229 				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
230 			else {
231 				/*
232 				 * 10-bit two's complement integer in
233 				 * steps of 0.25
234 				 */
235 				temp = ((hdata << 8 | ldata)) >> (16 - 10);
236 				temp = temp * 250000 + 273150000;
237 				sc->sc_sensor[i].value = temp;
238 			}
239 		} else if (adl_worklist[i].mVscale) {
240 			volt = ((hdata << 8 | ldata)) >> (16 - 10);
241 			volt = volt * adl_worklist[i].mVscale / (192 << 2);
242 			sc->sc_sensor[i].value = volt * 1000;
243 		} else {
244 			/*
245 			 * Inversed to ensure that the LS byte will be read
246 			 * before MS byte.
247 			 */
248 			fan = hdata + (ldata << 8);
249 			if (fan == 0 || fan == 0xffff)
250 				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
251 			else
252 				sc->sc_sensor[i].value = (90000 * 60) / fan;
253 		}
254 	}
255 
256 	iic_release_bus(sc->sc_tag, 0);
257 }
258