xref: /openbsd/sys/dev/i2c/lm93.c (revision d89ec533)
1 /*	$OpenBSD: lm93.c,v 1.8 2007/10/31 20:46:17 cnst Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 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 /* LM93 registers */
27 #define LM93_CPU1_TEMP		0x50
28 #define LM93_CPU2_TEMP		0x51
29 #define LM93_INT_TEMP		0x52
30 #define LM93_EXT_TEMP		0x53
31 #define LM93_IN1_V		0x56
32 #define LM93_IN2_V		0x57
33 #define LM93_IN3_V		0x58
34 #define LM93_IN4_V		0x59
35 #define LM93_IN5_V		0x5a
36 #define LM93_IN6_V		0x5b
37 #define LM93_IN7_V		0x5c
38 #define LM93_IN8_V		0x5d
39 #define LM93_IN9_V		0x5e
40 #define LM93_IN10_V		0x5f
41 #define LM93_IN11_V		0x60
42 #define LM93_IN12_V		0x61
43 #define LM93_IN13_V		0x62
44 #define LM93_IN14_V		0x63
45 #define LM93_IN15_V		0x64
46 #define LM93_IN16_V		0x65
47 #define LM93_TACH1L		0x6e
48 #define LM93_TACH1H		0x6f
49 #define LM93_TACH2L		0x70
50 #define LM93_TACH2H		0x71
51 #define LM93_TACH3L		0x72
52 #define LM93_TACH3H		0x73
53 #define LM93_TACH4L		0x74
54 #define LM93_TACH4H		0x75
55 #define LM93_REVISION		0x3f
56 
57 /* Sensors */
58 #define LMN_CPU1_TEMP		0
59 #define LMN_CPU2_TEMP		1
60 #define LMN_INT_TEMP		2
61 #define LMN_EXT_TEMP		3
62 #define LMN_IN1_V		4
63 #define LMN_IN2_V		5
64 #define LMN_IN3_V		6
65 #define LMN_IN4_V		7
66 #define LMN_IN5_V		8
67 #define LMN_IN6_V		9
68 #define LMN_IN7_V		10
69 #define LMN_IN8_V		11
70 #define LMN_IN9_V		12
71 #define LMN_IN10_V		13
72 #define LMN_IN11_V		14
73 #define LMN_IN12_V		15
74 #define LMN_IN13_V		16
75 #define LMN_IN14_V		17
76 #define LMN_IN15_V		18
77 #define LMN_IN16_V		19
78 #define LMN_TACH1		20
79 #define LMN_TACH2		21
80 #define LMN_TACH3		22
81 #define LMN_TACH4		23
82 #define LMN_NUM_SENSORS		24
83 
84 struct {
85 	char		sensor;
86 	u_int8_t	cmd;
87 	char		*name;
88 	u_short		mVscale;
89 	u_short		tempscale;		/* else a fan */
90 } lmn_worklist[] = {
91 	{ LMN_CPU1_TEMP, LM93_CPU1_TEMP, "CPU", 0, 1 },
92 	{ LMN_CPU2_TEMP, LM93_CPU2_TEMP, "CPU", 0, 1 },
93 	{ LMN_INT_TEMP, LM93_INT_TEMP, "Internal", 0, 1 },
94 	{ LMN_EXT_TEMP, LM93_EXT_TEMP, "External", 0, 1 },
95 
96 	{ LMN_IN1_V, LM93_IN1_V, "+12V", 1236*10, 0 },
97 	{ LMN_IN2_V, LM93_IN2_V, "+12V", 1236*10, 0 },
98 	{ LMN_IN3_V, LM93_IN3_V, "+12V", 1236*10, 0 },
99 	{ LMN_IN4_V, LM93_IN4_V, "FSB_Vtt 1.6V", 1600, 0 },
100 	{ LMN_IN5_V, LM93_IN5_V, "3GIO 2V ", 2000, 0 },
101 	{ LMN_IN6_V, LM93_IN6_V, "ICH_Core 2V", 2000, 0 },
102 	{ LMN_IN7_V, LM93_IN7_V, "Vccp 1.6V", 1600, 0 },
103 	{ LMN_IN8_V, LM93_IN8_V, "Vccp 1.6V", 1600, 0 },
104 	{ LMN_IN9_V, LM93_IN9_V, "+3.3V", 4400, 0 },
105 	{ LMN_IN10_V, LM93_IN10_V, "+5V", 6667, 0 },
106 	{ LMN_IN11_V, LM93_IN11_V, "SCSI_Core 3.3V", 3333, 0 },
107 	{ LMN_IN12_V, LM93_IN12_V, "Mem_Core 2.6V", 2625, 0 },
108 	{ LMN_IN13_V, LM93_IN13_V, "Mem_Vtt 1.3V", 1312, 0 },
109 	{ LMN_IN14_V, LM93_IN14_V, "Gbit_Core 1.3V", 1312, 0 },
110 	{ LMN_IN15_V, LM93_IN15_V, "-12V", -1236*10, 0 },
111 	{ LMN_IN16_V, LM93_IN16_V, "+3.3V S/B", 3600, 0 },
112 
113 	{ LMN_TACH1, LM93_TACH1L, "", 0, 0 },
114 	{ LMN_TACH2, LM93_TACH2L, "", 0, 0 },
115 	{ LMN_TACH3, LM93_TACH3L, "", 0, 0 },
116 	{ LMN_TACH4, LM93_TACH4L, "", 0, 0 }
117 };
118 
119 struct lmn_softc {
120 	struct device sc_dev;
121 	i2c_tag_t sc_tag;
122 	i2c_addr_t sc_addr;
123 	u_int8_t sc_conf;
124 
125 	struct ksensor sc_sensor[LMN_NUM_SENSORS];
126 	struct ksensordev sc_sensordev;
127 };
128 
129 int	lmn_match(struct device *, void *, void *);
130 void	lmn_attach(struct device *, struct device *, void *);
131 
132 void	lmn_refresh(void *);
133 
134 struct cfattach lmn_ca = {
135 	sizeof(struct lmn_softc), lmn_match, lmn_attach
136 };
137 
138 struct cfdriver lmn_cd = {
139 	NULL, "lmn", DV_DULL
140 };
141 
142 int
143 lmn_match(struct device *parent, void *match, void *aux)
144 {
145 	struct i2c_attach_args *ia = aux;
146 
147 	if (strcmp(ia->ia_name, "lm93") == 0)
148 		return (1);
149 	return (0);
150 }
151 
152 void
153 lmn_attach(struct device *parent, struct device *self, void *aux)
154 {
155 	struct lmn_softc *sc = (struct lmn_softc *)self;
156 	struct i2c_attach_args *ia = aux;
157 	int i;
158 
159 	sc->sc_tag = ia->ia_tag;
160 	sc->sc_addr = ia->ia_addr;
161 
162 	printf(": %s", ia->ia_name);
163 
164 	/* Initialize sensor data. */
165 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
166 	    sizeof(sc->sc_sensordev.xname));
167 
168 	if (sensor_task_register(sc, lmn_refresh, 5) == NULL) {
169 		printf(", unable to register update task\n");
170 		return;
171 	}
172 
173 	for (i = 0; i < LMN_NUM_SENSORS; i++) {
174 		if (lmn_worklist[i].tempscale)
175 			sc->sc_sensor[i].type = SENSOR_TEMP;
176 		else if (lmn_worklist[i].mVscale)
177 			sc->sc_sensor[i].type = SENSOR_VOLTS_DC;
178 		else
179 			sc->sc_sensor[i].type = SENSOR_FANRPM;
180 		strlcpy(sc->sc_sensor[i].desc, lmn_worklist[i].name,
181 		    sizeof(sc->sc_sensor[i].desc));
182 
183 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
184 	}
185 	sensordev_install(&sc->sc_sensordev);
186 
187 	printf("\n");
188 }
189 
190 void
191 lmn_refresh(void *arg)
192 {
193 	struct lmn_softc *sc = arg;
194 	u_int8_t cmd, data, data2;
195 	u_int16_t fan;
196 	int i;
197 
198 	iic_acquire_bus(sc->sc_tag, 0);
199 
200 	for (i = 0; i < sizeof lmn_worklist / sizeof(lmn_worklist[0]); i++) {
201 
202 		cmd = lmn_worklist[i].cmd;
203 		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
204 		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
205 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
206 			continue;
207 		}
208 
209 		sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
210 		if (lmn_worklist[i].tempscale) {
211 			if (data == 0x80)
212 				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
213 			else
214 				sc->sc_sensor[i].value =
215 				    (int8_t)data * 1000000 + 273150000;
216 		} else if (lmn_worklist[i].mVscale) {
217 			sc->sc_sensor[i].value = lmn_worklist[i].mVscale *
218 			    1000 * (u_int)data / 192;
219 		} else {
220 			cmd = lmn_worklist[i].cmd + 1; /* TACHnH follows TACHnL */
221 			if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
222 			    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
223 				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
224 				continue;
225 			}
226 
227 			fan = data + (data2 << 8);
228 			if (fan == 0 || fan == 0xffff)
229 				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
230 			else
231 				sc->sc_sensor[i].value = (90000 * 60) / fan;
232 		}
233 	}
234 
235 	iic_release_bus(sc->sc_tag, 0);
236 }
237