1 /* $NetBSD: smscmon.c,v 1.2 2011/06/20 20:16:19 pgoyette Exp $ */
2
3 /*
4 * Copyright (c) 2009 Takahiro Hayashi
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: smscmon.c,v 1.2 2011/06/20 20:16:19 pgoyette Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35
36 #include <dev/i2c/i2cvar.h>
37 #include <dev/sysmon/sysmonvar.h>
38 #include <dev/i2c/smscmonvar.h>
39
40 /*
41 * A driver for SMSC LPC47M192 hardware monitor at SMBus.
42 * This driver supports 8 Voltage and 3 Temperature sensors.
43 * Fan RPM monitoring is not supported in this driver because
44 * they are seen on ISA bus.
45 *
46 * Datasheet available (as of Feb. 20, 2010) at
47 * http://pdf1.alldatasheet.com/datasheet-pdf/view/109752/SMSC/LPC47M192-NC.html
48 */
49
50 static int smscmon_match(device_t, cfdata_t, void *);
51 static void smscmon_attach(device_t, device_t, void *);
52 static uint8_t smscmon_readreg(struct smscmon_sc *, int);
53 static void smscmon_writereg(struct smscmon_sc *, int, int);
54 static void smscmon_sensors_setup(struct smscmon_sc *, struct smscmon_sensor *);
55 static void smscmon_refresh_volt(struct smscmon_sc *, envsys_data_t *);
56 static void smscmon_refresh_temp(struct smscmon_sc *, envsys_data_t *);
57 static void smscmon_refresh(struct sysmon_envsys *, envsys_data_t *);
58
59 CFATTACH_DECL_NEW(smscmon, sizeof(struct smscmon_sc),
60 smscmon_match, smscmon_attach, NULL, NULL);
61
62 static struct smscmon_sensor smscmon_lpc47m192[] = {
63 {
64 .desc = "+2.5V",
65 .type = ENVSYS_SVOLTS_DC,
66 .reg = 0x20,
67 .refresh = smscmon_refresh_volt,
68 .vmin = 13000,
69 .vmax = 3320000
70 },
71 {
72 .desc = "Vccp",
73 .type = ENVSYS_SVOLTS_DC,
74 .reg = 0x21,
75 .refresh = smscmon_refresh_volt,
76 .vmin = 12000,
77 .vmax = 2988000
78 },
79 {
80 .desc = "+3.3V",
81 .type = ENVSYS_SVOLTS_DC,
82 .reg = 0x22,
83 .refresh = smscmon_refresh_volt,
84 .vmin = 17000,
85 .vmax = 4383000
86 },
87 {
88 .desc = "+5V",
89 .type = ENVSYS_SVOLTS_DC,
90 .reg = 0x23,
91 .refresh = smscmon_refresh_volt,
92 .vmin = 26000,
93 .vmax = 6640000
94 },
95 {
96 .desc = "+12V",
97 .type = ENVSYS_SVOLTS_DC,
98 .reg = 0x24,
99 .refresh = smscmon_refresh_volt,
100 .vmin = 62000,
101 .vmax = 15938000
102 },
103 {
104 .desc = "Vcc",
105 .type = ENVSYS_SVOLTS_DC,
106 .reg = 0x25,
107 .refresh = smscmon_refresh_volt,
108 .vmin = 17000,
109 .vmax = 4383000
110 },
111 {
112 .desc = "+1.5V",
113 .type = ENVSYS_SVOLTS_DC,
114 .reg = 0x50,
115 .refresh = smscmon_refresh_volt,
116 .vmin = 8000,
117 .vmax = 1992000
118 },
119 {
120 .desc = "+1.8V",
121 .type = ENVSYS_SVOLTS_DC,
122 .reg = 0x51,
123 .refresh = smscmon_refresh_volt,
124 .vmin = 9000,
125 .vmax = 2391000
126 },
127 {
128 .desc = "Remote Temp1",
129 .type = ENVSYS_STEMP,
130 .reg = 0x26,
131 .refresh = smscmon_refresh_temp,
132 .vmin = 0,
133 .vmax = 0
134 },
135 {
136 .desc = "Ambient Temp",
137 .type = ENVSYS_STEMP,
138 .reg = 0x27,
139 .refresh = smscmon_refresh_temp,
140 .vmin = 0,
141 .vmax = 0
142 },
143 {
144 .desc = "Remote Temp2",
145 .type = ENVSYS_STEMP,
146 .reg = 0x52,
147 .refresh = smscmon_refresh_temp,
148 .vmax = 0,
149 .vmin = 0
150 },
151
152 { .desc = NULL }
153 };
154
155 static int
smscmon_match(device_t parent,cfdata_t match,void * aux)156 smscmon_match(device_t parent, cfdata_t match, void *aux)
157 {
158 struct i2c_attach_args *ia = aux;
159 uint8_t cmd, cid, rev;
160
161 /* Address is hardwired to 010_110x */
162 if ((ia->ia_addr & SMSCMON_ADDR_MASK) != SMSCMON_ADDR)
163 return 0;
164
165 iic_acquire_bus(ia->ia_tag, 0);
166
167 cmd = SMSCMON_REG_COMPANY;
168 if (iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP,
169 ia->ia_addr, &cmd, sizeof cmd, &cid, sizeof cid, 0)) {
170 iic_release_bus(ia->ia_tag, 0);
171 return 0;
172 }
173 cmd = SMSCMON_REG_STEPPING;
174 if (iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP,
175 ia->ia_addr, &cmd, sizeof cmd, &rev, sizeof rev, 0)) {
176 iic_release_bus(ia->ia_tag, 0);
177 return 0;
178 }
179
180 if ( cid != SMSC_CID_47M192 || rev != SMSC_REV_47M192) {
181 iic_release_bus(ia->ia_tag, 0);
182 return 0;
183 }
184
185 iic_release_bus(ia->ia_tag, 0);
186 return 1;
187 }
188
189 static void
smscmon_attach(device_t parent,device_t self,void * aux)190 smscmon_attach(device_t parent, device_t self, void *aux)
191 {
192 struct smscmon_sc *sc = device_private(self);
193 struct i2c_attach_args *ia = aux;
194 uint8_t cid, rev;
195 int i;
196
197 sc->sc_dev = self;
198 sc->sc_tag = ia->ia_tag;
199 sc->sc_addr = ia->ia_addr;
200 sc->smscmon_readreg = smscmon_readreg;
201 sc->smscmon_writereg = smscmon_writereg;
202 sc->smscmon_sensors = NULL;
203
204 cid = sc->smscmon_readreg(sc, SMSCMON_REG_COMPANY);
205 rev = sc->smscmon_readreg(sc, SMSCMON_REG_STEPPING);
206 switch (cid) {
207 case SMSC_CID_47M192:
208 if (rev == SMSC_REV_47M192) {
209 smscmon_sensors_setup(sc, smscmon_lpc47m192);
210 aprint_normal(": LPC47M192 hardware monitor\n");
211 }
212 break;
213 default:
214 /* unknown chip */
215 break;
216 }
217
218 if (sc->smscmon_sensors == NULL) {
219 aprint_normal(": unknown chip: cid 0x%02x rev 0x%02x\n",
220 cid, rev);
221 return;
222 }
223
224 if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
225 aprint_error_dev(sc->sc_dev,
226 "unable to create sysmon structure\n");
227 return;
228 }
229
230 for (i = 0; i < sc->numsensors; i++) {
231 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sensors[i])) {
232 aprint_error_dev(sc->sc_dev,
233 "unable to attach sensor\n");
234 sysmon_envsys_destroy(sc->sc_sme);
235 return;
236 }
237 }
238
239 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
240 sc->sc_sme->sme_cookie = sc;
241 sc->sc_sme->sme_refresh = smscmon_refresh;
242 if (sysmon_envsys_register(sc->sc_sme)) {
243 aprint_error_dev(sc->sc_dev,
244 "unable to register with sysmon\n");
245 sysmon_envsys_destroy(sc->sc_sme);
246 return;
247 }
248 }
249
250 static uint8_t
smscmon_readreg(struct smscmon_sc * sc,int reg)251 smscmon_readreg(struct smscmon_sc *sc, int reg)
252 {
253 uint8_t cmd, data;
254
255 iic_acquire_bus(sc->sc_tag, 0);
256
257 cmd = reg;
258 iic_smbus_read_byte(sc->sc_tag, sc->sc_addr, cmd, &data, 0);
259
260 iic_release_bus(sc->sc_tag, 0);
261
262 return data;
263 }
264
265 static void
smscmon_writereg(struct smscmon_sc * sc,int reg,int val)266 smscmon_writereg(struct smscmon_sc *sc, int reg, int val)
267 {
268 uint8_t cmd, data;
269
270 iic_acquire_bus(sc->sc_tag, 0);
271
272 cmd = reg;
273 data = val;
274 iic_smbus_write_byte(sc->sc_tag, sc->sc_addr, cmd, data, 0);
275
276 iic_release_bus(sc->sc_tag, 0);
277 }
278
279 static void
smscmon_sensors_setup(struct smscmon_sc * sc,struct smscmon_sensor * sens)280 smscmon_sensors_setup(struct smscmon_sc *sc, struct smscmon_sensor *sens)
281 {
282 int i;
283
284 for (i = 0; sens[i].desc; i++) {
285 strlcpy(sc->sensors[i].desc, sens[i].desc,
286 sizeof(sc->sensors[i].desc));
287 sc->sensors[i].units = sens[i].type;
288 sc->sensors[i].state = ENVSYS_SINVALID;
289 sc->numsensors++;
290 }
291 sc->smscmon_sensors = sens;
292 }
293
294 static void
smscmon_refresh_volt(struct smscmon_sc * sc,envsys_data_t * edata)295 smscmon_refresh_volt(struct smscmon_sc *sc, envsys_data_t *edata)
296 {
297 struct smscmon_sensor *sens = &sc->smscmon_sensors[edata->sensor];
298 int data;
299
300 data = (*sc->smscmon_readreg)(sc, sens->reg);
301 if (data == 0xff) {
302 edata->state = ENVSYS_SINVALID;
303 } else {
304 edata->value_cur =
305 (sens->vmax - sens->vmin)/255 * data + sens->vmin;
306 edata->state = ENVSYS_SVALID;
307 }
308 }
309
310 static void
smscmon_refresh_temp(struct smscmon_sc * sc,envsys_data_t * edata)311 smscmon_refresh_temp(struct smscmon_sc *sc, envsys_data_t *edata)
312 {
313 struct smscmon_sensor *sens = &sc->smscmon_sensors[edata->sensor];
314 int data;
315
316 data = (*sc->smscmon_readreg)(sc, sens->reg);
317 if (data == 0xff) {
318 edata->state = ENVSYS_SINVALID;
319 } else {
320 /* convert data(degC) to uK */
321 edata->value_cur = 273150000 + 1000000 * data;
322 edata->state = ENVSYS_SVALID;
323 }
324 }
325
326 static void
smscmon_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)327 smscmon_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
328 {
329 struct smscmon_sc *sc = sme->sme_cookie;
330
331 sc->smscmon_sensors[edata->sensor].refresh(sc, edata);
332 }
333