1 /* $NetBSD: smsc.c,v 1.8 2008/04/28 20:23:52 martin Exp $ */
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * This is a driver for the Standard Microsystems Corp (SMSC)
34 * LPC47B397 "super i/o" chip. This driver only handles the environment
35 * monitoring capabilities of the chip, the other functions will be
36 * probed/matched as "normal" PC hardware devices (serial ports, fdc, so on).
37 * SMSC has not deigned to release a datasheet for this particular chip
38 * (though they do for others they make) so this driver was written from
39 * information contained in the comment block for the Linux driver.
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: smsc.c,v 1.8 2008/04/28 20:23:52 martin Exp $");
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/device.h>
50 #include <dev/isa/isareg.h>
51 #include <dev/isa/isavar.h>
53 #include <dev/sysmon/sysmonvar.h>
54 #include <dev/isa/smscvar.h>
57 #define DPRINTF(x) do { printf x; } while (0)
62 static int smsc_match(device_t
, cfdata_t
, void *);
63 static void smsc_attach(device_t
, device_t
, void *);
64 static int smsc_detach(device_t
, int);
66 static uint8_t smsc_readreg(bus_space_tag_t
, bus_space_handle_t
, int);
67 static void smsc_writereg(bus_space_tag_t
, bus_space_handle_t
, int, int);
69 static void smsc_refresh(struct sysmon_envsys
*, envsys_data_t
*);
71 CFATTACH_DECL_NEW(smsc
, sizeof(struct smsc_softc
),
72 smsc_match
, smsc_attach
, smsc_detach
, NULL
);
75 * Probe for the SMSC Super I/O chip
78 smsc_match(device_t parent
, cfdata_t match
, void *aux
)
80 bus_space_handle_t ioh
;
81 struct isa_attach_args
*ia
= aux
;
85 /* Must supply an address */
89 if (ISA_DIRECT_CONFIG(ia
))
92 if (ia
->ia_io
[0].ir_addr
== ISA_UNKNOWN_PORT
)
95 if (bus_space_map(ia
->ia_iot
, ia
->ia_io
[0].ir_addr
, 2, 0, &ioh
))
98 /* To get the device ID we must enter config mode... */
99 bus_space_write_1(ia
->ia_iot
, ioh
, SMSC_ADDR
, SMSC_CONFIG_START
);
101 /* Then select the device id register */
102 cr
= smsc_readreg(ia
->ia_iot
, ioh
, SMSC_DEVICE_ID
);
104 /* Exit config mode, apparently this is important to do */
105 bus_space_write_1(ia
->ia_iot
, ioh
, SMSC_ADDR
, SMSC_CONFIG_END
);
109 case SMSC_ID_SCH5307NS
:
110 case SMSC_ID_SCH5317
:
118 DPRINTF(("smsc: rv = %d, cr = %x\n", rv
, cr
));
120 bus_space_unmap(ia
->ia_iot
, ioh
, 2);
124 ia
->ia_io
[0].ir_size
= 2;
135 * Get the base address for the monitoring registers and set up the
136 * sysmon_envsys(9) framework.
139 smsc_attach(device_t parent
, device_t self
, void *aux
)
141 struct smsc_softc
*sc
= device_private(self
);
142 struct isa_attach_args
*ia
= aux
;
143 bus_space_handle_t ioh
;
144 uint8_t rev
, msb
, lsb
, chipid
;
148 sc
->sc_iot
= ia
->ia_iot
;
153 * To attach we need to find the actual Hardware Monitor
156 if (bus_space_map(ia
->ia_iot
, ia
->ia_io
[0].ir_addr
, 2, 0,
158 aprint_error(": can't map base i/o space\n");
162 /* Enter config mode */
163 bus_space_write_1(ia
->ia_iot
, ioh
, SMSC_ADDR
, SMSC_CONFIG_START
);
166 * While we have the base registers mapped, grab the chip
167 * revision and device ID.
169 rev
= smsc_readreg(ia
->ia_iot
, ioh
, SMSC_DEVICE_REVISION
);
170 chipid
= smsc_readreg(ia
->ia_iot
, ioh
, SMSC_DEVICE_ID
);
172 /* Select the Hardware Monitor LDN */
173 smsc_writereg(ia
->ia_iot
, ioh
, SMSC_LOGICAL_DEV_SEL
,
174 SMSC_LOGICAL_DEVICE
);
176 /* Read the base address for the registers. */
177 msb
= smsc_readreg(ia
->ia_iot
, ioh
, SMSC_IO_BASE_MSB
);
178 lsb
= smsc_readreg(ia
->ia_iot
, ioh
, SMSC_IO_BASE_LSB
);
179 address
= (msb
<< 8) | lsb
;
181 /* Exit config mode */
182 bus_space_write_1(ia
->ia_iot
, ioh
, SMSC_ADDR
, SMSC_CONFIG_END
);
183 bus_space_unmap(ia
->ia_iot
, ioh
, 2);
185 /* Map the Hardware Monitor I/O space. */
186 if (bus_space_map(ia
->ia_iot
, address
, 2, 0, &sc
->sc_ioh
)) {
187 aprint_error(": can't map register i/o space\n");
191 sc
->sc_sme
= sysmon_envsys_create();
193 #define INITSENSOR(index, string, reg, type) \
195 strlcpy(sc->sc_sensor[index].desc, string, \
196 sizeof(sc->sc_sensor[index].desc)); \
197 sc->sc_sensor[index].units = type; \
198 sc->sc_regs[index] = reg; \
199 sc->sc_sensor[index].state = ENVSYS_SVALID; \
200 } while (/* CONSTCOND */ 0)
202 /* Temperature sensors */
203 INITSENSOR(0, "Temp0", SMSC_TEMP1
, ENVSYS_STEMP
);
204 INITSENSOR(1, "Temp1", SMSC_TEMP2
, ENVSYS_STEMP
);
205 INITSENSOR(2, "Temp2", SMSC_TEMP3
, ENVSYS_STEMP
);
206 INITSENSOR(3, "Temp3", SMSC_TEMP4
, ENVSYS_STEMP
);
209 INITSENSOR(4, "Fan0", SMSC_FAN1_LSB
, ENVSYS_SFANRPM
);
210 INITSENSOR(5, "Fan1", SMSC_FAN2_LSB
, ENVSYS_SFANRPM
);
211 INITSENSOR(6, "Fan2", SMSC_FAN3_LSB
, ENVSYS_SFANRPM
);
212 INITSENSOR(7, "Fan3", SMSC_FAN4_LSB
, ENVSYS_SFANRPM
);
214 for (i
= 0; i
< SMSC_MAX_SENSORS
; i
++) {
215 if (sysmon_envsys_sensor_attach(sc
->sc_sme
,
216 &sc
->sc_sensor
[i
])) {
217 sysmon_envsys_destroy(sc
->sc_sme
);
218 bus_space_unmap(sc
->sc_iot
, sc
->sc_ioh
, 2);
223 sc
->sc_sme
->sme_name
= device_xname(self
);
224 sc
->sc_sme
->sme_cookie
= sc
;
225 sc
->sc_sme
->sme_refresh
= smsc_refresh
;
227 if ((i
= sysmon_envsys_register(sc
->sc_sme
)) != 0) {
228 aprint_error(": unable to register with sysmon (%d)\n", i
);
229 sysmon_envsys_destroy(sc
->sc_sme
);
230 bus_space_unmap(sc
->sc_iot
, sc
->sc_ioh
, 2);
236 aprint_normal(": SMSC LPC47B397 Super I/O");
238 case SMSC_ID_SCH5307NS
:
239 aprint_normal(": SMSC SCH5307-NS Super I/O");
241 case SMSC_ID_SCH5317
:
242 aprint_normal(": SMSC SCH5317 Super I/O");
246 aprint_normal(" (rev %u)\n", rev
);
247 aprint_normal_dev(self
, "Hardware Monitor registers at 0x%04x\n",
252 smsc_detach(device_t self
, int flags
)
254 struct smsc_softc
*sc
= device_private(self
);
256 sysmon_envsys_unregister(sc
->sc_sme
);
257 bus_space_unmap(sc
->sc_iot
, sc
->sc_ioh
, 2);
262 * Read the value of the given register
265 smsc_readreg(bus_space_tag_t iot
, bus_space_handle_t ioh
, int reg
)
267 bus_space_write_1(iot
, ioh
, SMSC_ADDR
, reg
);
268 return bus_space_read_1(iot
, ioh
, SMSC_DATA
);
272 * Write the given value to the given register.
275 smsc_writereg(bus_space_tag_t iot
, bus_space_handle_t ioh
, int reg
, int val
)
277 bus_space_write_1(iot
, ioh
, SMSC_ADDR
, reg
);
278 bus_space_write_1(iot
, ioh
, SMSC_DATA
, val
);
281 /* convert temperature read from the chip to micro kelvin */
283 smsc_temp2muk(uint8_t t
)
287 return temp
* 1000000 + 273150000U;
291 * convert register value read from chip into rpm using:
293 * RPM = 60/(Count * 11.111us)
299 smsc_reg2rpm(unsigned int r
)
306 rpm
= (90000 * 60) / ((unsigned long) r
);
310 /* min and max temperatures in uK */
311 #define SMSC_MIN_TEMP_UK ((-127 * 1000000) + 273150000)
312 #define SMSC_MAX_TEMP_UK ((127 * 1000000) + 273150000)
315 * Get the data for the requested sensor, update the sysmon structure
316 * with the retrieved value.
319 smsc_refresh(struct sysmon_envsys
*sme
, envsys_data_t
*edata
)
321 struct smsc_softc
*sc
= sme
->sme_cookie
;
326 reg
= sc
->sc_regs
[edata
->sensor
];
328 switch (edata
->units
) {
331 smsc_temp2muk(smsc_readreg(sc
->sc_iot
, sc
->sc_ioh
, reg
));
335 /* reading lsb first locks msb... */
336 lsb
= smsc_readreg(sc
->sc_iot
, sc
->sc_ioh
, reg
);
337 msb
= smsc_readreg(sc
->sc_iot
, sc
->sc_ioh
, reg
+ 1);
338 rpm
= (msb
<< 8) | lsb
;
339 edata
->value_cur
= smsc_reg2rpm(rpm
);