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