smsc.c revision 1.1.2.2 1 /* $NetBSD: smsc.c,v 1.1.2.2 2007/06/09 21:37:19 ad Exp $ */
2
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Brett Lymn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * This is a driver for the Standard Microsystems Corp (SMSC)
41 * LPC47B397 "super i/o" chip. This driver only handles the environment
42 * monitoring capabilities of the chip, the other functions will be
43 * probed/matched as "normal" PC hardware devices (serial ports, fdc, so on).
44 * SMSC has not deigned to release a datasheet for this particular chip
45 * (though they do for others they make) so this driver was written from
46 * information contained in the comment block for the Linux driver.
47 */
48
49 #include <sys/cdefs.h>
50 __KERNEL_RCSID(0, "$NetBSD: smsc.c,v 1.1.2.2 2007/06/09 21:37:19 ad Exp $");
51
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/kernel.h>
55 #include <sys/malloc.h>
56 #include <sys/proc.h>
57 #include <sys/device.h>
58 #include <sys/conf.h>
59 #include <sys/time.h>
60
61 #include <machine/bus.h>
62
63 #include <dev/isa/isareg.h>
64 #include <dev/isa/isavar.h>
65
66 #include <dev/sysmon/sysmonvar.h>
67
68 #include <dev/isa/smscvar.h>
69
70 #include <machine/intr.h>
71
72 #if defined(LMDEBUG)
73 #define DPRINTF(x) do { printf x; } while (0)
74 #else
75 #define DPRINTF(x)
76 #endif
77
78 int smsc_probe(struct device *, struct cfdata *, void *);
79 void smsc_attach(struct device *, struct device *, void *);
80 static uint8_t smsc_readreg(struct smsc_softc *, int);
81 /*static void smsc_writereg(struct smsc_softc *, int, int);*/
82 void smsc_setup(struct smsc_softc *);
83
84 static int smsc_gtredata(struct sysmon_envsys *, struct envsys_tre_data *);
85 static int smsc_streinfo(struct sysmon_envsys *, struct envsys_basic_info *);
86
87
88 CFATTACH_DECL(smsc, sizeof(struct smsc_softc),
89 smsc_probe, smsc_attach, NULL, NULL);
90
91 struct smsc_sysmon {
92 struct sysmon_envsys sme;
93 struct smsc_softc *sc;
94 struct envsys_tre_data smsc_info[];
95 };
96
97 /*
98 * Probe for the SMSC Super I/O chip
99 */
100 int
101 smsc_probe(struct device *parent, struct cfdata *match, void *aux)
102 {
103 bus_space_handle_t ioh;
104 struct isa_attach_args *ia = aux;
105 int rv;
106 uint8_t cr;
107
108 /* Must supply an address */
109 if (ia->ia_nio < 1)
110 return 0;
111
112 if (ISA_DIRECT_CONFIG(ia))
113 return 0;
114
115 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT)
116 return 0;
117
118 if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr, 2, 0, &ioh))
119 return 0;
120
121 /* To get the device ID we must enter config mode... */
122 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_START);
123
124 /* Then select the device id register */
125 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_DEVICE_ID);
126
127 /* Finally, read the id from the chip */
128 cr = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
129
130 /* Exit config mode, apparently this is important to do */
131 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_END);
132
133 if (cr == SMSC_ID)
134 rv = 1;
135 else
136 rv = 0;
137
138 DPRINTF(("smsc: rv = %d, cr = %x\n", rv, cr));
139
140 bus_space_unmap(ia->ia_iot, ioh, 2);
141
142 if (rv) {
143 ia->ia_nio = 1;
144 ia->ia_io[0].ir_size = 2;
145
146 ia->ia_niomem = 0;
147 ia->ia_nirq = 0;
148 ia->ia_ndrq = 0;
149 }
150
151 return rv;
152 }
153
154 /*
155 * Get the base address for the monitoring registers and set up the
156 * env sysmon framework.
157 */
158 void
159 smsc_attach(struct device *parent, struct device *self, void *aux)
160 {
161 struct smsc_softc *smsc_sc = (void *)self;
162 struct isa_attach_args *ia = aux;
163 bus_space_handle_t ioh;
164 uint8_t rev, msb, lsb;
165 unsigned address;
166
167 smsc_sc->smsc_iot = ia->ia_iot;
168
169 /* To attach we need to find the actual i/o register space,
170 map the base registers in first. */
171 if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr, 2, 0,
172 &ioh)) {
173 aprint_error(": can't map base i/o space\n");
174 return;
175 }
176
177 /* Enter config mode. */
178 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_START);
179
180 /* While we have the base registers mapped, grab the chip revision */
181 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_DEVICE_REVISION);
182 rev = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
183
184 /* Select the correct logical device */
185 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_LOGICAL_DEV_SEL);
186 bus_space_write_1(ia->ia_iot, ioh, SMSC_DATA, SMSC_LOGICAL_DEVICE);
187
188 /* Read the base address for the registers. */
189 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_IO_BASE_MSB);
190 msb = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
191 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_IO_BASE_LSB);
192 lsb = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
193 address = (msb << 8) | lsb;
194
195 /* Exit config mode */
196 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_END);
197
198 bus_space_unmap(ia->ia_iot, ioh, 2);
199
200 /* Map the i/o space for the registers. */
201 if (bus_space_map(ia->ia_iot, address, 2, 0, &smsc_sc->smsc_ioh)) {
202 aprint_error(": can't map register i/o space\n");
203 return;
204 }
205
206 aprint_normal(": monitor registers at 0x%04x (rev. %u)\n",
207 address, rev);
208
209 smsc_setup(smsc_sc);
210 }
211
212
213 /*
214 * Read the value of the given register
215 */
216 static uint8_t
217 smsc_readreg(struct smsc_softc *sc, int reg)
218 {
219 bus_space_write_1(sc->smsc_iot, sc->smsc_ioh, SMSC_ADDR, reg);
220 return bus_space_read_1(sc->smsc_iot, sc->smsc_ioh, SMSC_DATA);
221 }
222
223
224 /*
225 * Write the given value to the given register - here just for completeness
226 * it is unused in the current code.
227
228 static void
229 smsc_writereg (struct smsc_softc *sc, int reg, int val)
230 {
231 bus_space_write_1(sc->smsc_iot, sc->smsc_ioh, SMSC_ADDR, reg);
232 bus_space_write_1(sc->smsc_iot, sc->smsc_ioh, SMSC_DATA, val);
233 } */
234
235 /* convert temperature read from the chip to micro kelvin */
236 static inline int
237 smsc_temp2muk(uint8_t t)
238 {
239 int temp=t;
240
241 return temp * 1000000 + 273150000U;
242 }
243
244 /*
245 * convert register value read from chip into rpm using:
246 *
247 * RPM = 60/(Count * 11.111us)
248 *
249 * 1/1.1111us = 90kHz
250 *
251 */
252 static inline int
253 smsc_reg2rpm(unsigned int r)
254 {
255 unsigned long rpm;
256
257 if (r == 0x0)
258 return 0;
259
260 rpm = (90000 * 60) / ((unsigned long) r);
261 return (int) rpm;
262 }
263
264 /* min and max temperatures in uK */
265 #define SMSC_MIN_TEMP_UK ((-127 * 1000000) + 273150000)
266 #define SMSC_MAX_TEMP_UK ((127 * 1000000) + 273150000)
267
268 struct envsys_range *smsc_ranges;
269 struct envsys_basic_info *smsc_info;
270
271 /*
272 * Set up the environment monitoring framework for all the devices
273 * that we monitor.
274 */
275 void
276 smsc_setup(struct smsc_softc *sc)
277 {
278 struct smsc_sysmon *datap;
279 int error, i;
280 char label[8];
281 struct envsys_range *cur_r;
282 struct envsys_basic_info *cur_i;
283 struct envsys_tre_data *cur_t;
284
285 datap = malloc(sizeof(struct sysmon_envsys) + SMSC_MAX_SENSORS *
286 sizeof(struct envsys_tre_data) + sizeof(void *),
287 M_DEVBUF, M_WAITOK | M_ZERO);
288
289 smsc_ranges = malloc (sizeof(struct envsys_range) * SMSC_MAX_SENSORS,
290 M_DEVBUF, M_WAITOK | M_ZERO);
291
292 smsc_info =
293 malloc (sizeof(struct envsys_basic_info) * SMSC_MAX_SENSORS,
294 M_DEVBUF, M_WAITOK | M_ZERO);
295
296 for (i = 0; i < 4; i++) {
297 cur_r = &smsc_ranges[i];
298 cur_i = &smsc_info[i];
299 cur_t = &datap->smsc_info[i];
300 sprintf(label, "Temp-%d", i + 1);
301 strcpy(cur_i->desc, label);
302 cur_i->units = ENVSYS_STEMP;
303 cur_i->sensor = i;
304 cur_r->low = SMSC_MIN_TEMP_UK;
305 cur_r->high = SMSC_MAX_TEMP_UK;
306 cur_r->units = ENVSYS_STEMP;
307 cur_i->validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
308 cur_t->sensor = i;
309 cur_t->warnflags = ENVSYS_WARN_OK;
310 cur_t->validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
311 cur_t->units = cur_i->units;
312 switch (i) {
313 case 0:
314 sc->regs[i] = SMSC_TEMP1;
315 break;
316
317 case 1:
318 sc->regs[i] = SMSC_TEMP2;
319 break;
320
321 case 2:
322 sc->regs[i] = SMSC_TEMP3;
323 break;
324
325 case 3:
326 sc->regs[i] = SMSC_TEMP4;
327 break;
328 }
329
330 }
331
332 for (i = 4; i < SMSC_MAX_SENSORS; i++) {
333 cur_r = &smsc_ranges[i];
334 cur_i = &smsc_info[i];
335 cur_t = &datap->smsc_info[i];
336 sprintf(label, "Fan-%d", i - 3);
337 strcpy(cur_i->desc, label);
338 cur_i->units = ENVSYS_SFANRPM;
339 cur_i->sensor = i;
340 cur_r->low = 90;
341 cur_r->high = 8000;
342 cur_r->units = ENVSYS_SFANRPM;
343 cur_i->validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
344 cur_t->sensor = i;
345 cur_t->warnflags = ENVSYS_WARN_OK;
346 cur_t->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
347 cur_t->units = cur_i->units;
348
349 switch (i) {
350 case 4:
351 sc->regs[i] = SMSC_FAN1_LSB;
352 break;
353
354 case 5:
355 sc->regs[i] = SMSC_FAN2_LSB;
356 break;
357
358 case 6:
359 sc->regs[i] = SMSC_FAN3_LSB;
360 break;
361
362 case 7:
363 sc->regs[i] = SMSC_FAN4_LSB;
364 break;
365
366 default:
367 aprint_error(": more fans than expected");
368 break;
369 }
370 }
371
372
373
374 sc->smsc_sysmon = &datap->sme;
375 datap->sme.sme_nsensors = SMSC_MAX_SENSORS;
376 datap->sme.sme_envsys_version = 1000;
377 datap->sme.sme_ranges = smsc_ranges;
378 datap->sme.sme_sensor_info = smsc_info;
379 datap->sme.sme_sensor_data = datap->smsc_info;
380
381 datap->sme.sme_cookie = sc;
382 datap->sme.sme_gtredata = smsc_gtredata;
383 datap->sme.sme_streinfo = smsc_streinfo;
384 datap->sme.sme_flags = 0;
385
386 if ((error = sysmon_envsys_register(&datap->sme)) != 0) {
387 aprint_error("%s: unable to register with sysmon (%d)\n",
388 sc->sc_dev.dv_xname, error);
389 return;
390 }
391
392 }
393
394 /*
395 * Get the data for the requested sensor, update the sysmon structure
396 * with the retrieved value.
397 */
398 static int
399 smsc_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
400 {
401 struct smsc_softc *sc = sme->sme_cookie;
402 struct envsys_tre_data *cur_tre;
403 int i, reg;
404 unsigned int rpm;
405 uint8_t msb, lsb;
406
407 i = tred->sensor;
408 cur_tre = &sme->sme_sensor_data[i];
409 reg = sc->regs[i];
410
411 switch (cur_tre->units) {
412 case ENVSYS_STEMP:
413 cur_tre->cur.data_s = smsc_temp2muk(smsc_readreg(sc, reg));
414 break;
415
416 case ENVSYS_SFANRPM:
417 /* reading lsb first locks msb... */
418 lsb = smsc_readreg(sc, reg);
419 msb = smsc_readreg(sc, reg + 1); /* XXX note explicit
420 assumption that msb is
421 the next register along */
422 rpm = (msb << 8) | lsb;
423 cur_tre->cur.data_us = smsc_reg2rpm(rpm);
424 break;
425 }
426
427 cur_tre->validflags |= ENVSYS_FCURVALID | ENVSYS_FVALID;
428 *tred = sme->sme_sensor_data[i];
429 return 0;
430 }
431
432 /*
433 * Set parameters for the monitoring - nothing to do here.
434 */
435 static int
436 smsc_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
437 {
438
439 /* There is nothing to set here. */
440 return (EINVAL);
441 }
442