smsc.c revision 1.3 1 /* $NetBSD: smsc.c,v 1.3 2007/09/09 05:17:17 xtraeme 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.3 2007/09/09 05:17:17 xtraeme 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 int smsc_detach(struct device *, int);
81 static uint8_t smsc_readreg(struct smsc_softc *, int);
82 /*static void smsc_writereg(struct smsc_softc *, int, int);*/
83 void smsc_setup(struct smsc_softc *);
84
85 static int smsc_gtredata(struct sysmon_envsys *, envsys_data_t *);
86
87 CFATTACH_DECL(smsc, sizeof(struct smsc_softc),
88 smsc_probe, smsc_attach, smsc_detach, NULL);
89
90 struct smsc_sysmon {
91 struct sysmon_envsys sme;
92 struct smsc_softc *sc;
93 envsys_data_t smsc_sensor[];
94 };
95
96 /*
97 * Probe for the SMSC Super I/O chip
98 */
99 int
100 smsc_probe(struct device *parent, struct cfdata *match, void *aux)
101 {
102 bus_space_handle_t ioh;
103 struct isa_attach_args *ia = aux;
104 int rv;
105 uint8_t cr;
106
107 /* Must supply an address */
108 if (ia->ia_nio < 1)
109 return 0;
110
111 if (ISA_DIRECT_CONFIG(ia))
112 return 0;
113
114 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT)
115 return 0;
116
117 if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr, 2, 0, &ioh))
118 return 0;
119
120 /* To get the device ID we must enter config mode... */
121 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_START);
122
123 /* Then select the device id register */
124 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_DEVICE_ID);
125
126 /* Finally, read the id from the chip */
127 cr = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
128
129 /* Exit config mode, apparently this is important to do */
130 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_END);
131
132 if (cr == SMSC_ID)
133 rv = 1;
134 else
135 rv = 0;
136
137 DPRINTF(("smsc: rv = %d, cr = %x\n", rv, cr));
138
139 bus_space_unmap(ia->ia_iot, ioh, 2);
140
141 if (rv) {
142 ia->ia_nio = 1;
143 ia->ia_io[0].ir_size = 2;
144
145 ia->ia_niomem = 0;
146 ia->ia_nirq = 0;
147 ia->ia_ndrq = 0;
148 }
149
150 return rv;
151 }
152
153 /*
154 * Get the base address for the monitoring registers and set up the
155 * env sysmon framework.
156 */
157 void
158 smsc_attach(struct device *parent, struct device *self, void *aux)
159 {
160 struct smsc_softc *smsc_sc = (void *)self;
161 struct isa_attach_args *ia = aux;
162 bus_space_handle_t ioh;
163 uint8_t rev, msb, lsb;
164 unsigned address;
165
166 smsc_sc->smsc_iot = ia->ia_iot;
167
168 /* To attach we need to find the actual i/o register space,
169 map the base registers in first. */
170 if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr, 2, 0,
171 &ioh)) {
172 aprint_error(": can't map base i/o space\n");
173 return;
174 }
175
176 /* Enter config mode. */
177 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_START);
178
179 /* While we have the base registers mapped, grab the chip revision */
180 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_DEVICE_REVISION);
181 rev = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
182
183 /* Select the correct logical device */
184 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_LOGICAL_DEV_SEL);
185 bus_space_write_1(ia->ia_iot, ioh, SMSC_DATA, SMSC_LOGICAL_DEVICE);
186
187 /* Read the base address for the registers. */
188 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_IO_BASE_MSB);
189 msb = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
190 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_IO_BASE_LSB);
191 lsb = bus_space_read_1(ia->ia_iot, ioh, SMSC_DATA);
192 address = (msb << 8) | lsb;
193
194 /* Exit config mode */
195 bus_space_write_1(ia->ia_iot, ioh, SMSC_ADDR, SMSC_CONFIG_END);
196
197 bus_space_unmap(ia->ia_iot, ioh, 2);
198
199 /* Map the i/o space for the registers. */
200 if (bus_space_map(ia->ia_iot, address, 2, 0, &smsc_sc->smsc_ioh)) {
201 aprint_error(": can't map register i/o space\n");
202 return;
203 }
204
205 aprint_normal(": monitor registers at 0x%04x (rev. %u)\n",
206 address, rev);
207
208 smsc_setup(smsc_sc);
209 }
210
211 int
212 smsc_detach(struct device *self, int flags)
213 {
214 struct smsc_softc *sc = device_private(self);
215
216 sysmon_envsys_unregister(sc->smsc_sysmon);
217 bus_space_unmap(sc->smsc_iot, sc->smsc_ioh, 2);
218 return 0;
219 }
220
221 /*
222 * Read the value of the given register
223 */
224 static uint8_t
225 smsc_readreg(struct smsc_softc *sc, int reg)
226 {
227 bus_space_write_1(sc->smsc_iot, sc->smsc_ioh, SMSC_ADDR, reg);
228 return bus_space_read_1(sc->smsc_iot, sc->smsc_ioh, SMSC_DATA);
229 }
230
231
232 /*
233 * Write the given value to the given register - here just for completeness
234 * it is unused in the current code.
235
236 static void
237 smsc_writereg (struct smsc_softc *sc, int reg, int val)
238 {
239 bus_space_write_1(sc->smsc_iot, sc->smsc_ioh, SMSC_ADDR, reg);
240 bus_space_write_1(sc->smsc_iot, sc->smsc_ioh, SMSC_DATA, val);
241 } */
242
243 /* convert temperature read from the chip to micro kelvin */
244 static inline int
245 smsc_temp2muk(uint8_t t)
246 {
247 int temp=t;
248
249 return temp * 1000000 + 273150000U;
250 }
251
252 /*
253 * convert register value read from chip into rpm using:
254 *
255 * RPM = 60/(Count * 11.111us)
256 *
257 * 1/1.1111us = 90kHz
258 *
259 */
260 static inline int
261 smsc_reg2rpm(unsigned int r)
262 {
263 unsigned long rpm;
264
265 if (r == 0x0)
266 return 0;
267
268 rpm = (90000 * 60) / ((unsigned long) r);
269 return (int) rpm;
270 }
271
272 /* min and max temperatures in uK */
273 #define SMSC_MIN_TEMP_UK ((-127 * 1000000) + 273150000)
274 #define SMSC_MAX_TEMP_UK ((127 * 1000000) + 273150000)
275
276 /*
277 * Set up the environment monitoring framework for all the devices
278 * that we monitor.
279 */
280 void
281 smsc_setup(struct smsc_softc *sc)
282 {
283 struct smsc_sysmon *datap;
284 int error, i;
285 char label[8];
286 envsys_data_t *edata;
287
288 datap = malloc(sizeof(struct sysmon_envsys) + SMSC_MAX_SENSORS *
289 sizeof(envsys_data_t) + sizeof(void *),
290 M_DEVBUF, M_WAITOK | M_ZERO);
291
292 for (i = 0; i < 4; i++) {
293 edata = &datap->smsc_sensor[i];
294 sprintf(label, "Temp-%d", i + 1);
295 strlcpy(edata->desc, label, sizeof(edata->desc));
296 edata->units = ENVSYS_STEMP;
297 edata->sensor = i;
298 edata->state = ENVSYS_SVALID;
299 switch (i) {
300 case 0:
301 sc->regs[i] = SMSC_TEMP1;
302 break;
303
304 case 1:
305 sc->regs[i] = SMSC_TEMP2;
306 break;
307
308 case 2:
309 sc->regs[i] = SMSC_TEMP3;
310 break;
311
312 case 3:
313 sc->regs[i] = SMSC_TEMP4;
314 break;
315 }
316
317 }
318
319 for (i = 4; i < SMSC_MAX_SENSORS; i++) {
320 edata = &datap->smsc_sensor[i];
321 sprintf(label, "Fan-%d", i - 3);
322 strlcpy(edata->desc, label, sizeof(edata->desc));
323 edata->units = ENVSYS_SFANRPM;
324 edata->sensor = i;
325 edata->units = ENVSYS_SFANRPM;
326 edata->state = ENVSYS_SVALID;
327
328 switch (i) {
329 case 4:
330 sc->regs[i] = SMSC_FAN1_LSB;
331 break;
332
333 case 5:
334 sc->regs[i] = SMSC_FAN2_LSB;
335 break;
336
337 case 6:
338 sc->regs[i] = SMSC_FAN3_LSB;
339 break;
340
341 case 7:
342 sc->regs[i] = SMSC_FAN4_LSB;
343 break;
344
345 default:
346 aprint_error(": more fans than expected");
347 break;
348 }
349 }
350
351 sc->smsc_sysmon = &datap->sme;
352 datap->sme.sme_nsensors = SMSC_MAX_SENSORS;
353 datap->sme.sme_sensor_data = datap->smsc_sensor;
354 datap->sme.sme_name = sc->sc_dev.dv_xname;
355 datap->sme.sme_cookie = sc;
356 datap->sme.sme_gtredata = smsc_gtredata;
357
358 if ((error = sysmon_envsys_register(&datap->sme)) != 0) {
359 aprint_error("%s: unable to register with sysmon (%d)\n",
360 sc->sc_dev.dv_xname, error);
361 return;
362 }
363
364 }
365
366 /*
367 * Get the data for the requested sensor, update the sysmon structure
368 * with the retrieved value.
369 */
370 static int
371 smsc_gtredata(struct sysmon_envsys *sme, envsys_data_t *edata)
372 {
373 struct smsc_softc *sc = sme->sme_cookie;
374 int i, reg;
375 unsigned int rpm;
376 uint8_t msb, lsb;
377
378 i = edata->sensor;
379 reg = sc->regs[i];
380
381 switch (edata->units) {
382 case ENVSYS_STEMP:
383 edata->value_cur = smsc_temp2muk(smsc_readreg(sc, reg));
384 break;
385
386 case ENVSYS_SFANRPM:
387 /* reading lsb first locks msb... */
388 lsb = smsc_readreg(sc, reg);
389 msb = smsc_readreg(sc, reg + 1); /* XXX note explicit
390 assumption that msb is
391 the next register along */
392 rpm = (msb << 8) | lsb;
393 edata->value_cur = smsc_reg2rpm(rpm);
394 break;
395 }
396
397 edata->state = ENVSYS_SVALID;
398 return 0;
399 }
400