summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornetchild <netchild@FreeBSD.org>2007-10-14 10:55:50 +0000
committernetchild <netchild@FreeBSD.org>2007-10-14 10:55:50 +0000
commit8423df3d9411c6e4e79b3a3b06ea1c3c6aaaa81f (patch)
treee4b345443d79b791d5fbc3088afe36b5cdb70868
parent4af9918bc0e8f388ffda416ed716c9b17ca6c0fd (diff)
downloadFreeBSD-src-8423df3d9411c6e4e79b3a3b06ea1c3c6aaaa81f.zip
FreeBSD-src-8423df3d9411c6e4e79b3a3b06ea1c3c6aaaa81f.tar.gz
Import it(4) and lm(4), supporting most popular Super I/O Hardware Monitors.
Submitted by: Constantine A. Murenin <cnst@FreeBSD.org> Sponsored by: Google Summer of Code 2007 (GSoC2007/cnst-sensors) Mentored by: syrinx Tested by: many OKed by: kensmith Obtained from: OpenBSD (parts)
-rw-r--r--share/man/man4/Makefile3
-rw-r--r--share/man/man4/it.4104
-rw-r--r--share/man/man4/lm.4138
-rw-r--r--sys/amd64/conf/GENERIC.hints8
-rw-r--r--sys/conf/files2
-rw-r--r--sys/conf/files.amd641
-rw-r--r--sys/conf/files.i3861
-rw-r--r--sys/dev/it/it.c346
-rw-r--r--sys/dev/it/itvar.h95
-rw-r--r--sys/dev/lm/lm78.c909
-rw-r--r--sys/dev/lm/lm78_isa.c251
-rw-r--r--sys/dev/lm/lm78var.h158
-rw-r--r--sys/i386/conf/GENERIC.hints8
-rw-r--r--sys/modules/Makefile6
-rw-r--r--sys/modules/it/Makefile9
-rw-r--r--sys/modules/lm/Makefile9
16 files changed, 2048 insertions, 0 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 2777514..1a8001c 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -131,6 +131,7 @@ MAN= aac.4 \
iscsi_initiator.4 \
isp.4 \
ispfw.4 \
+ ${_it.4} \
iwi.4 \
ixgb.4 \
joy.4 \
@@ -143,6 +144,7 @@ MAN= aac.4 \
le.4 \
led.4 \
lge.4 \
+ lm.4 \
lmc.4 \
lo.4 \
lp.4 \
@@ -547,6 +549,7 @@ _ichwd.4= ichwd.4
_if_nfe.4= if_nfe.4
_if_nve.4= if_nve.4
_if_nxge.4= if_nxge.4
+_it.4= it.4
_ipmi.4= ipmi.4
_nfsmb.4= nfsmb.4
_nfe.4= nfe.4
diff --git a/share/man/man4/it.4 b/share/man/man4/it.4
new file mode 100644
index 0000000..6b65628
--- /dev/null
+++ b/share/man/man4/it.4
@@ -0,0 +1,104 @@
+.\" $FreeBSD$
+.\" $OpenBSD: it.4,v 1.8 2006/09/08 15:09:14 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Julien Bordet <zejames@greygats.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd 13 September 2007
+.Dt IT 4
+.Os
+.Sh NAME
+.Nm it
+.Nd ITE IT8705F/12F/16F and SiS SiS950 Super I/O Hardware Monitor
+.Sh SYNOPSIS
+.Cd "device isa"
+.Cd "device it"
+.Pp
+In
+.Pa /boot/device.hints :
+.Cd hint.it.0.at="isa"
+.Cd hint.it.0.port="0x290"
+.Cd hint.it.1.at="isa"
+.Cd hint.it.1.port="0xc00"
+.Cd hint.it.2.at="isa"
+.Cd hint.it.2.port="0xd00"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the
+.Tn IT8705F , IT8712F , IT8716F
+and
+.Tn SiS950
+hardware monitors.
+The values are exposed through the
+.Va HW_SENSORS
+.Xr sysctl 3
+interface.
+.Pp
+Most supported devices possess 15 sensors:
+.Bl -column "Sensor" "Units" "Typical" -offset indent
+.It Sy "Sensor" Ta Sy "Units" Ta Sy "Typical Use"
+.It Li "Fan0" Ta "RPM" Ta "CPU Fan"
+.It Li "Fan1" Ta "RPM" Ta "Fan"
+.It Li "Fan2" Ta "RPM" Ta "Fan"
+.It Li "IN0" Ta "uV DC" Ta "Core voltage"
+.It Li "IN1" Ta "uV DC" Ta "Core voltage"
+.It Li "IN2" Ta "uV DC" Ta "+3.3V"
+.It Li "IN3" Ta "uV DC" Ta "+5V"
+.It Li "IN4" Ta "uV DC" Ta "+12V"
+.It Li "IN5" Ta "uV DC" Ta "Unknown"
+.It Li "IN6" Ta "uV DC" Ta "-12V"
+.It Li "IN7" Ta "uV DC" Ta "-5V"
+.It Li "IN8" Ta "uV DC" Ta "VBAT"
+.It Li "Temp" Ta "uK" Ta "Motherboard Temperature"
+.It Li "Temp" Ta "uK" Ta "Motherboard Temperature"
+.It Li "Temp" Ta "uK" Ta "CPU Temperature"
+.El
+.Pp
+For some devices, sensors' names and numbers will be different.
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr sysctl 3 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 3.4 .
+.Fx
+support was added in
+.Fx 7.XXX .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Julien Bordet Aq zejames@greyhats.org .
+It was ported to
+.Fx
+by
+.An Constantine A. Murenin Aq cnst@FreeBSD.org
+as a part of a Google Summer of Code 2007 project.
+.Sh BUGS
+Interrupt support is unimplemented.
diff --git a/share/man/man4/lm.4 b/share/man/man4/lm.4
new file mode 100644
index 0000000..1c731b4
--- /dev/null
+++ b/share/man/man4/lm.4
@@ -0,0 +1,138 @@
+.\" $FreeBSD$
+.\" $OpenBSD: lm.4,v 1.16 2007/05/26 22:38:55 cnst Exp $
+.\" $NetBSD: lm.4,v 1.11 2001/09/22 01:22:49 wiz Exp $
+.\"
+.\" Copyright (c) 2000 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Bill Squier.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd 19 August 2007
+.Dt LM 4
+.Os
+.Sh NAME
+.Nm lm
+.Nd NatSemi LM78/79/81 and Winbond Super I/O Hardware Monitor
+.Sh SYNOPSIS
+.Cd "device isa"
+.Cd "device lm"
+.Pp
+In
+.Pa /boot/device.hints :
+.Cd hint.lm.0.at="isa"
+.Cd hint.lm.0.port="0x290"
+.Cd hint.lm.1.at="isa"
+.Cd hint.lm.1.port="0x280"
+.Cd hint.lm.2.at="isa"
+.Cd hint.lm.2.port="0x310"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the
+.Tn National Semiconductor
+LM 78/79/81 and
+.Tn Winbond
+Super I/O
+hardware monitors,
+and registers compatible chips under the
+.Va HW_SENSORS
+.Xr sysctl 3
+tree.
+.Sh HARDWARE
+Chips supported by the
+.Nm
+driver include:
+.Pp
+.Bl -dash -offset indent -compact
+.It
+National Semiconductor LM78 and LM78-J
+.It
+National Semiconductor LM79
+.It
+National Semiconductor LM81
+.It
+Winbond W83627HF, W83627THF, W83637HF and W83697HF
+.It
+Winbond W83627DHG and W83627EHF
+.It
+Winbond W83781D, W83782D and W83783S
+.It
+Winbond W83791D, W83791SD and W83792D
+.It
+ASUS AS99127F
+.El
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr sysctl 3 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Nx 1.5 ;
+.Ox
+support was added in
+.Ox 3.4 ;
+.Fx
+support was added in
+.Fx 7.XXX .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Bill Squier
+and ported to
+.Ox 3.4
+by
+.An Alexander Yurchenko Aq grange@openbsd.org .
+The driver was largely rewritten for
+.Ox 3.9
+by
+.An Mark Kettenis Aq kettenis@openbsd.org .
+The driver was then ported to
+.Fx
+by
+.An Constantine A. Murenin Aq cnst@FreeBSD.org
+as a part of a Google Summer of Code 2007 project.
+.Sh CAVEATS
+Some vendors connect these chips to non-standard thermal diodes and
+resistors.
+This will result in bogus sensor values.
+.Sh BUGS
+Interrupt support is unimplemented.
+.Pp
+There are currently no known pnpbios IDs assigned to LM chips.
+.Pp
+This driver attaches to the Winbond W83791SD chip even though that
+chip does not have any sensors.
diff --git a/sys/amd64/conf/GENERIC.hints b/sys/amd64/conf/GENERIC.hints
index 9400343..594d9ca 100644
--- a/sys/amd64/conf/GENERIC.hints
+++ b/sys/amd64/conf/GENERIC.hints
@@ -33,3 +33,11 @@ hint.sio.3.port="0x2E8"
hint.sio.3.irq="9"
hint.ppc.0.at="isa"
hint.ppc.0.irq="7"
+hint.lm.0.at="isa"
+hint.lm.0.port="0x290"
+hint.it.0.at="isa"
+hint.it.0.port="0x290"
+hint.it.1.at="isa"
+hint.it.1.port="0xc00"
+hint.it.2.at="isa"
+hint.it.2.port="0xd00"
diff --git a/sys/conf/files b/sys/conf/files
index 5089ef3..af6b58d 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -758,6 +758,8 @@ dev/le/if_le_pci.c optional le pci
dev/le/lance.c optional le
dev/led/led.c standard
dev/lge/if_lge.c optional lge
+dev/lm/lm78.c optional lm
+dev/lm/lm78_isa.c optional lm isa
dev/lmc/if_lmc.c optional lmc
dev/mc146818/mc146818.c optional mc146818
dev/mca/mca_bus.c optional mca
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index 2a9a60a..d4ff130 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -169,6 +169,7 @@ dev/ipmi/ipmi_smbus.c optional ipmi smbus
dev/ipmi/ipmi_smbios.c optional ipmi
dev/ipmi/ipmi_ssif.c optional ipmi smbus
dev/ipmi/ipmi_pci.c optional ipmi pci
+dev/it/it.c optional it isa
dev/fdc/fdc.c optional fdc
dev/fdc/fdc_acpi.c optional fdc
dev/fdc/fdc_isa.c optional fdc isa
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index fc47798..9122c4a 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -204,6 +204,7 @@ dev/ipmi/ipmi_smbus.c optional ipmi smbus
dev/ipmi/ipmi_smbios.c optional ipmi
dev/ipmi/ipmi_ssif.c optional ipmi smbus
dev/ipmi/ipmi_pci.c optional ipmi pci
+dev/it/it.c optional it isa
dev/kbd/kbd.c optional atkbd | sc | ukbd | vt
dev/le/if_le_isa.c optional le isa
dev/mem/memutil.c optional mem
diff --git a/sys/dev/it/it.c b/sys/dev/it/it.c
new file mode 100644
index 0000000..a5f0a8e
--- /dev/null
+++ b/sys/dev/it/it.c
@@ -0,0 +1,346 @@
+/* $FreeBSD$ */
+/* $OpenBSD: it.c,v 1.22 2007/03/22 16:55:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 2003 Julien Bordet <zejames@greyhats.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <sys/rman.h>
+
+#include <isa/isavar.h>
+#include <sys/systm.h>
+
+#include <sys/sensors.h>
+
+#include <dev/it/itvar.h>
+
+#if defined(ITDEBUG)
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+/*
+ * IT87-compatible chips can typically measure voltages up to 4.096 V.
+ * To measure higher voltages the input is attenuated with (external)
+ * resistors. Negative voltages are measured using a reference
+ * voltage. So we have to convert the sensor values back to real
+ * voltages by applying the appropriate resistor factor.
+ */
+#define RFACT_NONE 10000
+#define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y))
+
+int it_probe(struct device *);
+int it_attach(struct device *);
+int it_detach(struct device *);
+u_int8_t it_readreg(struct it_softc *, int);
+void it_writereg(struct it_softc *, int, int);
+void it_setup_volt(struct it_softc *, int, int);
+void it_setup_temp(struct it_softc *, int, int);
+void it_setup_fan(struct it_softc *, int, int);
+
+void it_generic_stemp(struct it_softc *, struct ksensor *);
+void it_generic_svolt(struct it_softc *, struct ksensor *);
+void it_generic_fanrpm(struct it_softc *, struct ksensor *);
+
+void it_refresh_sensor_data(struct it_softc *);
+void it_refresh(void *);
+
+extern struct cfdriver it_cd;
+
+static device_method_t it_methods[] = {
+ /* Methods from the device interface */
+ DEVMETHOD(device_probe, it_probe),
+ DEVMETHOD(device_attach, it_attach),
+ DEVMETHOD(device_detach, it_detach),
+
+ /* Terminate method list */
+ { 0, 0 }
+};
+
+static driver_t it_driver = {
+ "it",
+ it_methods,
+ sizeof (struct it_softc)
+};
+
+static devclass_t it_devclass;
+
+DRIVER_MODULE(it, isa, it_driver, it_devclass, NULL, NULL);
+
+
+const int it_vrfact[] = {
+ RFACT_NONE,
+ RFACT_NONE,
+ RFACT_NONE,
+ RFACT(68, 100),
+ RFACT(30, 10),
+ RFACT(21, 10),
+ RFACT(83, 20),
+ RFACT(68, 100),
+ RFACT_NONE
+};
+
+int
+it_probe(struct device *dev)
+{
+ struct resource *iores;
+ int iorid = 0;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ u_int8_t cr;
+
+ iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (iores == NULL) {
+ DPRINTF(("%s: can't map i/o space\n", __func__));
+ return 1;
+ }
+ iot = rman_get_bustag(iores);
+ ioh = rman_get_bushandle(iores);
+
+ /* Check Vendor ID */
+ bus_space_write_1(iot, ioh, ITC_ADDR, ITD_CHIPID);
+ cr = bus_space_read_1(iot, ioh, ITC_DATA);
+ bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
+ DPRINTF(("it: vendor id 0x%x\n", cr));
+ if (cr != IT_ID_IT87)
+ return 1;
+
+ return 0;
+}
+
+int
+it_attach(struct device *dev)
+{
+ struct it_softc *sc = device_get_softc(dev);
+ int i;
+ u_int8_t cr;
+
+ sc->sc_dev = dev;
+ sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (sc->sc_iores == NULL) {
+ device_printf(dev, "can't map i/o space\n");
+ return 1;
+ }
+ sc->sc_iot = rman_get_bustag(sc->sc_iores);
+ sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
+
+ sc->numsensors = IT_NUM_SENSORS;
+
+ it_setup_fan(sc, 0, 3);
+ it_setup_volt(sc, 3, 9);
+ it_setup_temp(sc, 12, 3);
+
+ if (sensor_task_register(sc, it_refresh, 5)) {
+ device_printf(sc->sc_dev, "unable to register update task\n");
+ return 1;
+ }
+
+ /* Activate monitoring */
+ cr = it_readreg(sc, ITD_CONFIG);
+ cr |= 0x01 | 0x08;
+ it_writereg(sc, ITD_CONFIG, cr);
+
+ /* Initialize sensors */
+ strlcpy(sc->sensordev.xname, device_get_nameunit(sc->sc_dev),
+ sizeof(sc->sensordev.xname));
+ for (i = 0; i < sc->numsensors; ++i)
+ sensor_attach(&sc->sensordev, &sc->sensors[i]);
+ sensordev_install(&sc->sensordev);
+
+ return 0;
+}
+
+int
+it_detach(struct device *dev)
+{
+ struct it_softc *sc = device_get_softc(dev);
+ int error;
+
+ sensordev_deinstall(&sc->sensordev);
+ sensor_task_unregister(sc);
+
+ error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid,
+ sc->sc_iores);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+u_int8_t
+it_readreg(struct it_softc *sc, int reg)
+{
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg);
+ return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, ITC_DATA));
+}
+
+void
+it_writereg(struct it_softc *sc, int reg, int val)
+{
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_DATA, val);
+}
+
+void
+it_setup_volt(struct it_softc *sc, int start, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ sc->sensors[start + i].type = SENSOR_VOLTS_DC;
+ }
+
+ snprintf(sc->sensors[start + 0].desc, sizeof(sc->sensors[0].desc),
+ "VCORE_A");
+ snprintf(sc->sensors[start + 1].desc, sizeof(sc->sensors[1].desc),
+ "VCORE_B");
+ snprintf(sc->sensors[start + 2].desc, sizeof(sc->sensors[2].desc),
+ "+3.3V");
+ snprintf(sc->sensors[start + 3].desc, sizeof(sc->sensors[3].desc),
+ "+5V");
+ snprintf(sc->sensors[start + 4].desc, sizeof(sc->sensors[4].desc),
+ "+12V");
+ snprintf(sc->sensors[start + 5].desc, sizeof(sc->sensors[5].desc),
+ "Unused");
+ snprintf(sc->sensors[start + 6].desc, sizeof(sc->sensors[6].desc),
+ "-12V");
+ snprintf(sc->sensors[start + 7].desc, sizeof(sc->sensors[7].desc),
+ "+5VSB");
+ snprintf(sc->sensors[start + 8].desc, sizeof(sc->sensors[8].desc),
+ "VBAT");
+}
+
+void
+it_setup_temp(struct it_softc *sc, int start, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ sc->sensors[start + i].type = SENSOR_TEMP;
+}
+
+void
+it_setup_fan(struct it_softc *sc, int start, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ sc->sensors[start + i].type = SENSOR_FANRPM;
+}
+
+void
+it_generic_stemp(struct it_softc *sc, struct ksensor *sensors)
+{
+ int i, sdata;
+
+ for (i = 0; i < 3; i++) {
+ sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i);
+ /* Convert temperature to Fahrenheit degres */
+ sensors[i].value = sdata * 1000000 + 273150000;
+ }
+}
+
+void
+it_generic_svolt(struct it_softc *sc, struct ksensor *sensors)
+{
+ int i, sdata;
+
+ for (i = 0; i < 9; i++) {
+ sdata = it_readreg(sc, ITD_SENSORVOLTBASE + i);
+ DPRINTF(("sdata[volt%d] 0x%x\n", i, sdata));
+ /* voltage returned as (mV >> 4) */
+ sensors[i].value = (sdata << 4);
+ /* these two values are negative and formula is different */
+ if (i == 5 || i == 6)
+ sensors[i].value = ((sdata << 4) - IT_VREF);
+ /* rfact is (factor * 10^4) */
+ sensors[i].value *= it_vrfact[i];
+ /* division by 10 gets us back to uVDC */
+ sensors[i].value /= 10;
+ if (i == 5 || i == 6)
+ sensors[i].value += IT_VREF * 1000;
+ }
+}
+
+void
+it_generic_fanrpm(struct it_softc *sc, struct ksensor *sensors)
+{
+ int i, sdata, divisor, odivisor, ndivisor;
+
+ odivisor = ndivisor = divisor = it_readreg(sc, ITD_FAN);
+ for (i = 0; i < 3; i++, divisor >>= 3) {
+ sensors[i].flags &= ~SENSOR_FINVALID;
+ if ((sdata = it_readreg(sc, ITD_SENSORFANBASE + i)) == 0xff) {
+ sensors[i].flags |= SENSOR_FINVALID;
+ if (i == 2)
+ ndivisor ^= 0x40;
+ else {
+ ndivisor &= ~(7 << (i * 3));
+ ndivisor |= ((divisor + 1) & 7) << (i * 3);
+ }
+ } else if (sdata == 0) {
+ sensors[i].value = 0;
+ } else {
+ if (i == 2)
+ divisor = divisor & 1 ? 3 : 1;
+ sensors[i].value = 1350000 / (sdata << (divisor & 7));
+ }
+ }
+ if (ndivisor != odivisor)
+ it_writereg(sc, ITD_FAN, ndivisor);
+}
+
+/*
+ * pre: last read occurred >= 1.5 seconds ago
+ * post: sensors[] current data are the latest from the chip
+ */
+void
+it_refresh_sensor_data(struct it_softc *sc)
+{
+ /* Refresh our stored data for every sensor */
+ it_generic_stemp(sc, &sc->sensors[12]);
+ it_generic_svolt(sc, &sc->sensors[3]);
+ it_generic_fanrpm(sc, &sc->sensors[0]);
+}
+
+void
+it_refresh(void *arg)
+{
+ struct it_softc *sc = (struct it_softc *)arg;
+
+ it_refresh_sensor_data(sc);
+}
diff --git a/sys/dev/it/itvar.h b/sys/dev/it/itvar.h
new file mode 100644
index 0000000..2395db3
--- /dev/null
+++ b/sys/dev/it/itvar.h
@@ -0,0 +1,95 @@
+/* $FreeBSD$ */
+/* $OpenBSD: itvar.h,v 1.4 2007/03/22 16:55:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 2003 Julien Bordet <zejames@greyhats.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DEV_ISA_ITVAR_H
+#define _DEV_ISA_ITVAR_H
+
+#define IT_NUM_SENSORS 15
+
+/* chip ids */
+#define IT_ID_IT87 0x90
+
+/* ctl registers */
+
+#define ITC_ADDR 0x05
+#define ITC_DATA 0x06
+
+/* data registers */
+
+#define ITD_CONFIG 0x00
+#define ITD_ISR1 0x01
+#define ITD_ISR2 0x02
+#define ITD_ISR3 0x03
+#define ITD_SMI1 0x04
+#define ITD_SMI2 0x05
+#define ITD_SMI3 0x06
+#define ITD_IMR1 0x07
+#define ITD_IMR2 0x08
+#define ITD_IMR3 0x09
+#define ITD_VID 0x0a
+#define ITD_FAN 0x0b
+
+#define ITD_FANMINBASE 0x10
+#define ITD_FANENABLE 0x13
+
+#define ITD_SENSORFANBASE 0x0d /* Fan from 0x0d to 0x0f */
+#define ITD_SENSORVOLTBASE 0x20 /* Fan from 0x20 to 0x28 */
+#define ITD_SENSORTEMPBASE 0x29 /* Fan from 0x29 to 0x2b */
+
+#define ITD_VOLTMAXBASE 0x30
+#define ITD_VOLTMINBASE 0x31
+
+#define ITD_TEMPMAXBASE 0x40
+#define ITD_TEMPMINBASE 0x41
+
+#define ITD_SBUSADDR 0x48
+#define ITD_VOLTENABLE 0x50
+#define ITD_TEMPENABLE 0x51
+
+#define ITD_CHIPID 0x58
+
+#define IT_VREF (4096) /* Vref = 4.096 V */
+
+struct it_softc {
+ struct device *sc_dev;
+
+ struct resource *sc_iores;
+ int sc_iorid;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ struct ksensor sensors[IT_NUM_SENSORS];
+ struct ksensordev sensordev;
+ u_int numsensors;
+ void (*refresh_sensor_data)(struct it_softc *);
+
+ u_int8_t (*it_readreg)(struct it_softc *, int);
+ void (*it_writereg)(struct it_softc *, int, int);
+};
+
+#endif
diff --git a/sys/dev/lm/lm78.c b/sys/dev/lm/lm78.c
new file mode 100644
index 0000000..311a789
--- /dev/null
+++ b/sys/dev/lm/lm78.c
@@ -0,0 +1,909 @@
+/* $FreeBSD$ */
+/* $OpenBSD: lm78.c,v 1.18 2007/05/26 22:47:39 cnst Exp $ */
+
+/*-
+ * Copyright (c) 2005, 2006 Mark Kettenis
+ * Copyright (c) 2006, 2007 Constantine A. Murenin
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/sensors.h>
+#include <machine/bus.h>
+
+#include <dev/lm/lm78var.h>
+
+#if defined(LMDEBUG)
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+/*
+ * LM78-compatible chips can typically measure voltages up to 4.096 V.
+ * To measure higher voltages the input is attenuated with (external)
+ * resistors. Negative voltages are measured using inverting op amps
+ * and resistors. So we have to convert the sensor values back to
+ * real voltages by applying the appropriate resistor factor.
+ */
+#define RFACT_NONE 10000
+#define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y))
+#define NRFACT(x, y) (-RFACT_NONE * (x) / (y))
+
+int lm_match(struct lm_softc *);
+int wb_match(struct lm_softc *);
+int def_match(struct lm_softc *);
+
+void lm_setup_sensors(struct lm_softc *, struct lm_sensor *);
+void lm_refresh(void *);
+
+void lm_refresh_sensor_data(struct lm_softc *);
+void lm_refresh_volt(struct lm_softc *, int);
+void lm_refresh_temp(struct lm_softc *, int);
+void lm_refresh_fanrpm(struct lm_softc *, int);
+
+void wb_refresh_sensor_data(struct lm_softc *);
+void wb_w83637hf_refresh_vcore(struct lm_softc *, int);
+void wb_refresh_nvolt(struct lm_softc *, int);
+void wb_w83627ehf_refresh_nvolt(struct lm_softc *, int);
+void wb_refresh_temp(struct lm_softc *, int);
+void wb_refresh_fanrpm(struct lm_softc *, int);
+void wb_w83792d_refresh_fanrpm(struct lm_softc *, int);
+
+void as_refresh_temp(struct lm_softc *, int);
+
+struct lm_chip {
+ int (*chip_match)(struct lm_softc *);
+};
+
+struct lm_chip lm_chips[] = {
+ { wb_match },
+ { lm_match },
+ { def_match } /* Must be last */
+};
+
+struct lm_sensor lm78_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(68, 100) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(30, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, NRFACT(240, 60) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, NRFACT(100, 60) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, lm_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor w83627hf_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+/*
+ * The W83627EHF can measure voltages up to 2.048 V instead of the
+ * traditional 4.096 V. For measuring positive voltages, this can be
+ * accounted for by halving the resistor factor. Negative voltages
+ * need special treatment, also because the reference voltage is 2.048 V
+ * instead of the traditional 3.6 V.
+ */
+struct lm_sensor w83627ehf_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE / 2},
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(56, 10) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_w83627ehf_refresh_nvolt },
+ { "", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, RFACT_NONE / 2 },
+ { "", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT_NONE / 2 },
+ { "3.3VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE / 2 },
+ { "", SENSOR_VOLTS_DC, 5, 0x52, lm_refresh_volt, RFACT_NONE / 2 },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+/*
+ * w83627dhg is almost identical to w83627ehf, except that
+ * it has 9 instead of 10 voltage sensors
+ */
+struct lm_sensor w83627dhg_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE / 2},
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(56, 10) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_w83627ehf_refresh_nvolt },
+ { "", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, RFACT_NONE / 2 },
+ { "", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT_NONE / 2 },
+ { "3.3VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE / 2 },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor w83637hf_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, wb_w83637hf_refresh_vcore },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(28, 10) },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 51) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_refresh_nvolt, RFACT(232, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 51) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor w83697hf_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+/*
+ * The datasheet doesn't mention the (internal) resistors used for the
+ * +5V, but using the values from the W83782D datasheets seems to
+ * provide sensible results.
+ */
+struct lm_sensor w83781d_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, NRFACT(2100, 604) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, NRFACT(909, 604) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, lm_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor w83782d_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VINR0", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor w83783s_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor w83791d_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VINR0", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 0, 0xb0, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 0, 0xb1, lm_refresh_volt, RFACT_NONE },
+ { "VINR1", SENSOR_VOLTS_DC, 0, 0xb2, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc0, wb_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc8, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xba, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xbb, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor w83792d_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x23, wb_refresh_nvolt, RFACT(120, 56) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT(34, 50) },
+ { "5VSB", SENSOR_VOLTS_DC, 0, 0xb0, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 0, 0xb1, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc0, wb_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc8, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xb8, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xb9, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xba, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xbe, wb_w83792d_refresh_fanrpm },
+
+ { NULL }
+};
+
+struct lm_sensor as99127f_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, as_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, as_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, lm_refresh_fanrpm },
+
+ { NULL }
+};
+
+void
+lm_probe(struct lm_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sizeof(lm_chips) / sizeof(lm_chips[0]); i++)
+ if (lm_chips[i].chip_match(sc))
+ break;
+}
+
+void
+lm_attach(struct lm_softc *sc)
+{
+ u_int i, config;
+
+ /* No point in doing anything if we don't have any sensors. */
+ if (sc->numsensors == 0)
+ return;
+
+ if (sensor_task_register(sc, lm_refresh, 5)) {
+ device_printf(sc->sc_dev, "unable to register update task\n");
+ return;
+ }
+
+ /* Start the monitoring loop */
+ config = sc->lm_readreg(sc, LM_CONFIG);
+ sc->lm_writereg(sc, LM_CONFIG, config | 0x01);
+
+ /* Add sensors */
+ strlcpy(sc->sensordev.xname, device_get_nameunit(sc->sc_dev),
+ sizeof(sc->sensordev.xname));
+ for (i = 0; i < sc->numsensors; ++i)
+ sensor_attach(&sc->sensordev, &sc->sensors[i]);
+ sensordev_install(&sc->sensordev);
+}
+
+int
+lm_detach(struct lm_softc *sc)
+{
+ int i;
+
+ /* Remove sensors */
+ sensordev_deinstall(&sc->sensordev);
+ for (i = 0; i < sc->numsensors; i++)
+ sensor_detach(&sc->sensordev, &sc->sensors[i]);
+
+ sensor_task_unregister(sc);
+
+ return 0;
+}
+
+int
+lm_match(struct lm_softc *sc)
+{
+ int chipid;
+ const char *cdesc;
+ char fulldesc[64];
+
+ /* See if we have an LM78 or LM79. */
+ chipid = sc->lm_readreg(sc, LM_CHIPID) & LM_CHIPID_MASK;
+ switch(chipid) {
+ case LM_CHIPID_LM78:
+ cdesc = "LM78";
+ break;
+ case LM_CHIPID_LM78J:
+ cdesc = "LM78J";
+ break;
+ case LM_CHIPID_LM79:
+ cdesc = "LM79";
+ break;
+ case LM_CHIPID_LM81:
+ cdesc = "LM81";
+ break;
+ default:
+ return 0;
+ }
+ snprintf(fulldesc, sizeof(fulldesc),
+ "National Semiconductor %s Hardware Monitor", cdesc);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+
+ lm_setup_sensors(sc, lm78_sensors);
+ sc->refresh_sensor_data = lm_refresh_sensor_data;
+ return 1;
+}
+
+int
+def_match(struct lm_softc *sc)
+{
+ int chipid;
+ char fulldesc[64];
+
+ chipid = sc->lm_readreg(sc, LM_CHIPID) & LM_CHIPID_MASK;
+ snprintf(fulldesc, sizeof(fulldesc),
+ "unknown Hardware Monitor (ID 0x%x)", chipid);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+
+ lm_setup_sensors(sc, lm78_sensors);
+ sc->refresh_sensor_data = lm_refresh_sensor_data;
+ return 1;
+}
+
+int
+wb_match(struct lm_softc *sc)
+{
+ int banksel, vendid, devid;
+ const char *cdesc;
+ char desc[64];
+ char fulldesc[64];
+
+ /* Read vendor ID */
+ banksel = sc->lm_readreg(sc, WB_BANKSEL);
+ sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_HBAC);
+ vendid = sc->lm_readreg(sc, WB_VENDID) << 8;
+ sc->lm_writereg(sc, WB_BANKSEL, 0);
+ vendid |= sc->lm_readreg(sc, WB_VENDID);
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+ DPRINTF((" winbond vend id 0x%x\n", vendid));
+ if (vendid != WB_VENDID_WINBOND && vendid != WB_VENDID_ASUS)
+ return 0;
+
+ /* Read device/chip ID */
+ sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+ devid = sc->lm_readreg(sc, LM_CHIPID);
+ sc->chipid = sc->lm_readreg(sc, WB_BANK0_CHIPID);
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+ DPRINTF((" winbond chip id 0x%x\n", sc->chipid));
+ switch(sc->chipid) {
+ case WB_CHIPID_W83627HF:
+ cdesc = "W83627HF";
+ lm_setup_sensors(sc, w83627hf_sensors);
+ break;
+ case WB_CHIPID_W83627THF:
+ cdesc = "W83627THF";
+ lm_setup_sensors(sc, w83637hf_sensors);
+ break;
+ case WB_CHIPID_W83627EHF:
+ cdesc = "W83627EHF";
+ lm_setup_sensors(sc, w83627ehf_sensors);
+ break;
+ case WB_CHIPID_W83627DHG:
+ cdesc = "W83627DHG";
+ lm_setup_sensors(sc, w83627dhg_sensors);
+ break;
+ case WB_CHIPID_W83637HF:
+ cdesc = "W83637HF";
+ sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+ if (sc->lm_readreg(sc, WB_BANK0_CONFIG) & WB_CONFIG_VMR9)
+ sc->vrm9 = 1;
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+ lm_setup_sensors(sc, w83637hf_sensors);
+ break;
+ case WB_CHIPID_W83697HF:
+ cdesc = "W83697HF";
+ lm_setup_sensors(sc, w83697hf_sensors);
+ break;
+ case WB_CHIPID_W83781D:
+ case WB_CHIPID_W83781D_2:
+ cdesc = "W83781D";
+ lm_setup_sensors(sc, w83781d_sensors);
+ break;
+ case WB_CHIPID_W83782D:
+ cdesc = "W83782D";
+ lm_setup_sensors(sc, w83782d_sensors);
+ break;
+ case WB_CHIPID_W83783S:
+ cdesc = "W83783S";
+ lm_setup_sensors(sc, w83783s_sensors);
+ break;
+ case WB_CHIPID_W83791D:
+ cdesc = "W83791D";
+ lm_setup_sensors(sc, w83791d_sensors);
+ break;
+ case WB_CHIPID_W83791SD:
+ cdesc = "W83791SD";
+ break;
+ case WB_CHIPID_W83792D:
+ if (devid >= 0x10 && devid <= 0x29)
+ snprintf(desc, sizeof(desc),
+ "W83792D rev %c", 'A' + devid - 0x10);
+ else
+ snprintf(desc, sizeof(desc),
+ "W83792D rev 0x%x", devid);
+ cdesc = desc;
+ lm_setup_sensors(sc, w83792d_sensors);
+ break;
+ case WB_CHIPID_AS99127F:
+ if (vendid == WB_VENDID_ASUS) {
+ cdesc = "AS99127F";
+ lm_setup_sensors(sc, w83781d_sensors);
+ } else {
+ cdesc = "AS99127F rev 2";
+ lm_setup_sensors(sc, as99127f_sensors);
+ }
+ break;
+ default:
+ snprintf(fulldesc, sizeof(fulldesc),
+ "unknown Winbond Hardware Monitor (Chip ID 0x%x)",
+ sc->chipid);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+ /* Handle as a standard LM78. */
+ lm_setup_sensors(sc, lm78_sensors);
+ sc->refresh_sensor_data = lm_refresh_sensor_data;
+ return 1;
+ }
+
+ if (cdesc[0] == 'W')
+ snprintf(fulldesc, sizeof(fulldesc),
+ "Winbond %s Hardware Monitor", cdesc);
+ else
+ snprintf(fulldesc, sizeof(fulldesc),
+ "ASUS %s Hardware Monitor", cdesc);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+
+ sc->refresh_sensor_data = wb_refresh_sensor_data;
+ return 1;
+}
+
+void
+lm_setup_sensors(struct lm_softc *sc, struct lm_sensor *sensors)
+{
+ int i;
+
+ for (i = 0; sensors[i].desc; i++) {
+ sc->sensors[i].type = sensors[i].type;
+ strlcpy(sc->sensors[i].desc, sensors[i].desc,
+ sizeof(sc->sensors[i].desc));
+ sc->numsensors++;
+ }
+ sc->lm_sensors = sensors;
+}
+
+void
+lm_refresh(void *arg)
+{
+ struct lm_softc *sc = arg;
+
+ sc->refresh_sensor_data(sc);
+}
+
+void
+lm_refresh_sensor_data(struct lm_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->numsensors; i++)
+ sc->lm_sensors[i].refresh(sc, i);
+}
+
+void
+lm_refresh_volt(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ sensor->value = (data << 4);
+ sensor->value *= sc->lm_sensors[n].rfact;
+ sensor->value /= 10;
+}
+
+void
+lm_refresh_temp(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int sdata;
+
+ /*
+ * The data sheet suggests that the range of the temperature
+ * sensor is between -55 degC and +125 degC.
+ */
+ sdata = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (sdata > 0x7d && sdata < 0xc9) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (sdata & 0x80)
+ sdata -= 0x100;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = sdata * 1000000 + 273150000;
+ }
+}
+
+void
+lm_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data, divisor = 1;
+
+ /*
+ * We might get more accurate fan readings by adjusting the
+ * divisor, but that might interfere with APM or other SMM
+ * BIOS code reading the fan speeds.
+ */
+
+ /* FAN3 has a fixed fan divisor. */
+ if (sc->lm_sensors[n].reg == LM_FAN1 ||
+ sc->lm_sensors[n].reg == LM_FAN2) {
+ data = sc->lm_readreg(sc, LM_VIDFAN);
+ if (sc->lm_sensors[n].reg == LM_FAN1)
+ divisor = (data >> 4) & 0x03;
+ else
+ divisor = (data >> 6) & 0x03;
+ }
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (data == 0xff || data == 0x00) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = 1350000 / (data << divisor);
+ }
+}
+
+void
+wb_refresh_sensor_data(struct lm_softc *sc)
+{
+ int banksel, bank, i;
+
+ /*
+ * Properly save and restore bank selection register.
+ */
+
+ banksel = bank = sc->lm_readreg(sc, WB_BANKSEL);
+ for (i = 0; i < sc->numsensors; i++) {
+ if (bank != sc->lm_sensors[i].bank) {
+ bank = sc->lm_sensors[i].bank;
+ sc->lm_writereg(sc, WB_BANKSEL, bank);
+ }
+ sc->lm_sensors[i].refresh(sc, i);
+ }
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+}
+
+void
+wb_w83637hf_refresh_vcore(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+
+ /*
+ * Depending on the voltage detection method,
+ * one of the following formulas is used:
+ * VRM8 method: value = raw * 0.016V
+ * VRM9 method: value = raw * 0.00488V + 0.70V
+ */
+ if (sc->vrm9)
+ sensor->value = (data * 4880) + 700000;
+ else
+ sensor->value = (data * 16000);
+}
+
+void
+wb_refresh_nvolt(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ sensor->value = ((data << 4) - WB_VREF);
+ sensor->value *= sc->lm_sensors[n].rfact;
+ sensor->value /= 10;
+ sensor->value += WB_VREF * 1000;
+}
+
+void
+wb_w83627ehf_refresh_nvolt(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ sensor->value = ((data << 3) - WB_W83627EHF_VREF);
+ sensor->value *= RFACT(232, 10);
+ sensor->value /= 10;
+ sensor->value += WB_W83627EHF_VREF * 1000;
+}
+
+void
+wb_refresh_temp(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int sdata;
+
+ /*
+ * The data sheet suggests that the range of the temperature
+ * sensor is between -55 degC and +125 degC. However, values
+ * around -48 degC seem to be a very common bogus values.
+ * Since such values are unreasonably low, we use -45 degC for
+ * the lower limit instead.
+ */
+ sdata = sc->lm_readreg(sc, sc->lm_sensors[n].reg) << 1;
+ sdata += sc->lm_readreg(sc, sc->lm_sensors[n].reg + 1) >> 7;
+ if (sdata > 0x0fa && sdata < 0x1a6) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (sdata & 0x100)
+ sdata -= 0x200;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = sdata * 500000 + 273150000;
+ }
+}
+
+void
+wb_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int fan, data, divisor = 0;
+
+ /*
+ * This is madness; the fan divisor bits are scattered all
+ * over the place.
+ */
+
+ if (sc->lm_sensors[n].reg == LM_FAN1 ||
+ sc->lm_sensors[n].reg == LM_FAN2 ||
+ sc->lm_sensors[n].reg == LM_FAN3) {
+ data = sc->lm_readreg(sc, WB_BANK0_VBAT);
+ fan = (sc->lm_sensors[n].reg - LM_FAN1);
+ if ((data >> 5) & (1 << fan))
+ divisor |= 0x04;
+ }
+
+ if (sc->lm_sensors[n].reg == LM_FAN1 ||
+ sc->lm_sensors[n].reg == LM_FAN2) {
+ data = sc->lm_readreg(sc, LM_VIDFAN);
+ if (sc->lm_sensors[n].reg == LM_FAN1)
+ divisor |= (data >> 4) & 0x03;
+ else
+ divisor |= (data >> 6) & 0x03;
+ } else if (sc->lm_sensors[n].reg == LM_FAN3) {
+ data = sc->lm_readreg(sc, WB_PIN);
+ divisor |= (data >> 6) & 0x03;
+ } else if (sc->lm_sensors[n].reg == WB_BANK0_FAN4 ||
+ sc->lm_sensors[n].reg == WB_BANK0_FAN5) {
+ data = sc->lm_readreg(sc, WB_BANK0_FAN45);
+ if (sc->lm_sensors[n].reg == WB_BANK0_FAN4)
+ divisor |= (data >> 0) & 0x07;
+ else
+ divisor |= (data >> 4) & 0x07;
+ }
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (data == 0xff || data == 0x00) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = 1350000 / (data << divisor);
+ }
+}
+
+void
+wb_w83792d_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int reg, shift, data, divisor = 1;
+
+ switch (sc->lm_sensors[n].reg) {
+ case 0x28:
+ reg = 0x47; shift = 0;
+ break;
+ case 0x29:
+ reg = 0x47; shift = 4;
+ break;
+ case 0x2a:
+ reg = 0x5b; shift = 0;
+ break;
+ case 0xb8:
+ reg = 0x5b; shift = 4;
+ break;
+ case 0xb9:
+ reg = 0x5c; shift = 0;
+ break;
+ case 0xba:
+ reg = 0x5c; shift = 4;
+ break;
+ case 0xbe:
+ reg = 0x9e; shift = 0;
+ break;
+ default:
+ reg = 0; shift = 0;
+ break;
+ }
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (data == 0xff || data == 0x00) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (reg != 0)
+ divisor = (sc->lm_readreg(sc, reg) >> shift) & 0x7;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = 1350000 / (data << divisor);
+ }
+}
+
+void
+as_refresh_temp(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int sdata;
+
+ /*
+ * It seems a shorted temperature diode produces an all-ones
+ * bit pattern.
+ */
+ sdata = sc->lm_readreg(sc, sc->lm_sensors[n].reg) << 1;
+ sdata += sc->lm_readreg(sc, sc->lm_sensors[n].reg + 1) >> 7;
+ if (sdata == 0x1ff) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (sdata & 0x100)
+ sdata -= 0x200;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = sdata * 500000 + 273150000;
+ }
+}
diff --git a/sys/dev/lm/lm78_isa.c b/sys/dev/lm/lm78_isa.c
new file mode 100644
index 0000000..7449d00
--- /dev/null
+++ b/sys/dev/lm/lm78_isa.c
@@ -0,0 +1,251 @@
+/* $FreeBSD$ */
+/* $OpenBSD: lm78_isa.c,v 1.2 2007/07/01 21:48:57 cnst Exp $ */
+
+/*-
+ * Copyright (c) 2005, 2006 Mark Kettenis
+ * Copyright (c) 2007 Constantine A. Murenin, Google Summer of Code
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <isa/isavar.h>
+
+#include <sys/systm.h>
+
+#include <sys/sensors.h>
+
+#include <dev/lm/lm78var.h>
+
+/* ISA registers */
+#define LMC_ADDR 0x05
+#define LMC_DATA 0x06
+
+extern struct cfdriver lm_cd;
+
+#if defined(LMDEBUG)
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+struct lm_isa_softc {
+ struct lm_softc sc_lmsc;
+
+ struct resource *sc_iores;
+ int sc_iorid;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+};
+
+static int lm_isa_probe(struct device *);
+static int lm_isa_attach(struct device *);
+static int lm_isa_detach(struct device *);
+u_int8_t lm_isa_readreg(struct lm_softc *, int);
+void lm_isa_writereg(struct lm_softc *, int, int);
+
+static device_method_t lm_isa_methods[] = {
+ /* Methods from the device interface */
+ DEVMETHOD(device_probe, lm_isa_probe),
+ DEVMETHOD(device_attach, lm_isa_attach),
+ DEVMETHOD(device_detach, lm_isa_detach),
+
+ /* Terminate method list */
+ { 0, 0 }
+};
+
+static driver_t lm_isa_driver = {
+ "lm",
+ lm_isa_methods,
+ sizeof (struct lm_isa_softc)
+};
+
+static devclass_t lm_devclass;
+
+DRIVER_MODULE(lm, isa, lm_isa_driver, lm_devclass, NULL, NULL);
+
+int
+lm_isa_probe(struct device *dev)
+{
+ struct lm_isa_softc *sc = device_get_softc(dev);
+ struct resource *iores;
+ int iorid = 0;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ int banksel, vendid, chipid, addr;
+
+ iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (iores == NULL) {
+ DPRINTF(("%s: can't map i/o space\n", __func__));
+ return (1);
+ }
+ iot = rman_get_bustag(iores);
+ ioh = rman_get_bushandle(iores);
+
+ /* Probe for Winbond chips. */
+ bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL);
+ banksel = bus_space_read_1(iot, ioh, LMC_DATA);
+ bus_space_write_1(iot, ioh, LMC_ADDR, WB_VENDID);
+ vendid = bus_space_read_1(iot, ioh, LMC_DATA);
+ if (((banksel & 0x80) && vendid == (WB_VENDID_WINBOND >> 8)) ||
+ (!(banksel & 0x80) && vendid == (WB_VENDID_WINBOND & 0xff)))
+ goto found;
+
+ /* Probe for ITE chips (and don't attach if we find one). */
+ bus_space_write_1(iot, ioh, LMC_ADDR, 0x58 /*ITD_CHIPID*/);
+ vendid = bus_space_read_1(iot, ioh, LMC_DATA);
+ if (vendid == 0x90 /*IT_ID_IT87*/)
+ goto notfound;
+
+ /*
+ * Probe for National Semiconductor LM78/79/81.
+ *
+ * XXX This assumes the address has not been changed from the
+ * power up default. This is probably a reasonable
+ * assumption, and if it isn't true, we should be able to
+ * access the chip using the serial bus.
+ */
+ bus_space_write_1(iot, ioh, LMC_ADDR, LM_SBUSADDR);
+ addr = bus_space_read_1(iot, ioh, LMC_DATA);
+ if ((addr & 0xfc) == 0x2c) {
+ bus_space_write_1(iot, ioh, LMC_ADDR, LM_CHIPID);
+ chipid = bus_space_read_1(iot, ioh, LMC_DATA);
+
+ switch (chipid & LM_CHIPID_MASK) {
+ case LM_CHIPID_LM78:
+ case LM_CHIPID_LM78J:
+ case LM_CHIPID_LM79:
+ case LM_CHIPID_LM81:
+ goto found;
+ }
+ }
+
+ notfound:
+ bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
+
+ return (1);
+
+ found:
+ /* Bus-independent probe */
+ sc->sc_lmsc.sc_dev = dev;
+ sc->sc_iot = iot;
+ sc->sc_ioh = ioh;
+ sc->sc_lmsc.lm_writereg = lm_isa_writereg;
+ sc->sc_lmsc.lm_readreg = lm_isa_readreg;
+ lm_probe(&sc->sc_lmsc);
+
+ bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
+ sc->sc_iot = 0;
+ sc->sc_ioh = 0;
+
+ return (0);
+}
+
+int
+lm_isa_attach(struct device *dev)
+{
+ struct lm_isa_softc *sc = device_get_softc(dev);
+#ifdef notyet
+ struct lm_softc *lmsc;
+ int i;
+ u_int8_t sbusaddr;
+#endif
+
+ sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (sc->sc_iores == NULL) {
+ device_printf(dev, "can't map i/o space\n");
+ return (1);
+ }
+ sc->sc_iot = rman_get_bustag(sc->sc_iores);
+ sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
+
+ /* Bus-independent attachment */
+ lm_attach(&sc->sc_lmsc);
+
+#ifdef notyet
+ /*
+ * Most devices supported by this driver can attach to iic(4)
+ * as well. However, we prefer to attach them to isa(4) since
+ * that causes less overhead and is more reliable. We look
+ * through all previously attached devices, and if we find an
+ * identical chip at the same serial bus address, we stop
+ * updating its sensors and mark them as invalid.
+ */
+
+ sbusaddr = lm_isa_readreg(&sc->sc_lmsc, LM_SBUSADDR);
+ if (sbusaddr == 0)
+ return (0);
+
+ for (i = 0; i < lm_cd.cd_ndevs; i++) {
+ lmsc = lm_cd.cd_devs[i];
+ if (lmsc == &sc->sc_lmsc)
+ continue;
+ if (lmsc && lmsc->sbusaddr == sbusaddr &&
+ lmsc->chipid == sc->sc_lmsc.chipid)
+ config_detach(&lmsc->sc_dev, 0);
+ }
+#endif
+ return (0);
+}
+
+int
+lm_isa_detach(struct device *dev)
+{
+ struct lm_isa_softc *sc = device_get_softc(dev);
+ int error;
+
+ /* Bus-independent detachment */
+ error = lm_detach(&sc->sc_lmsc);
+ if (error)
+ return (error);
+
+ error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid,
+ sc->sc_iores);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+u_int8_t
+lm_isa_readreg(struct lm_softc *lmsc, int reg)
+{
+ struct lm_isa_softc *sc = (struct lm_isa_softc *)lmsc;
+
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_ADDR, reg);
+ return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, LMC_DATA));
+}
+
+void
+lm_isa_writereg(struct lm_softc *lmsc, int reg, int val)
+{
+ struct lm_isa_softc *sc = (struct lm_isa_softc *)lmsc;
+
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_ADDR, reg);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_DATA, val);
+}
diff --git a/sys/dev/lm/lm78var.h b/sys/dev/lm/lm78var.h
new file mode 100644
index 0000000..1b0ce32
--- /dev/null
+++ b/sys/dev/lm/lm78var.h
@@ -0,0 +1,158 @@
+/* $FreeBSD$ */
+/* $OpenBSD: lm78var.h,v 1.12 2007/05/25 02:26:43 cnst Exp $ */
+
+/*-
+ * Copyright (c) 2005, 2006 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * National Semiconductor LM78/79/81 registers
+ */
+
+#define LM_POST_RAM 0x00 /* POST RAM occupies 0x00 -- 0x1f */
+#define LM_VALUE_RAM 0x20 /* Value RAM occupies 0x20 -- 0x3f */
+#define LM_FAN1 0x28 /* FAN1 reading */
+#define LM_FAN2 0x29 /* FAN2 reading */
+#define LM_FAN3 0x2a /* FAN3 reading */
+
+#define LM_CONFIG 0x40 /* Configuration */
+#define LM_ISR1 0x41 /* Interrupt Status 1 */
+#define LM_ISR2 0x42 /* Interrupt Status 2 */
+#define LM_SMI1 0x43 /* SMI# Mask 1 */
+#define LM_SMI2 0x44 /* SMI# Mask 2 */
+#define LM_NMI1 0x45 /* NMI Mask 1 */
+#define LM_NMI2 0x46 /* NMI Mask 2 */
+#define LM_VIDFAN 0x47 /* VID/Fan Divisor */
+#define LM_SBUSADDR 0x48 /* Serial Bus Address */
+#define LM_CHIPID 0x49 /* Chip Reset/ID */
+
+/* Chip IDs */
+
+#define LM_CHIPID_LM78 0x00
+#define LM_CHIPID_LM78J 0x40
+#define LM_CHIPID_LM79 0xC0
+#define LM_CHIPID_LM81 0x80
+#define LM_CHIPID_MASK 0xfe
+
+/*
+ * Winbond registers
+ *
+ * Several models exists. The W83781D is mostly compatible with the
+ * LM78, but has two extra temperatures. Later models add extra
+ * voltage sensors, fans and bigger fan divisors to accomodate slow
+ * running fans. To accomodate the extra sensors some models have
+ * different memory banks.
+ */
+
+#define WB_T23ADDR 0x4a /* Temperature 2 and 3 Serial Bus Address */
+#define WB_PIN 0x4b /* Pin Control */
+#define WB_BANKSEL 0x4e /* Bank Select */
+#define WB_VENDID 0x4f /* Vendor ID */
+
+/* Bank 0 regs */
+#define WB_BANK0_CHIPID 0x58 /* Chip ID */
+#define WB_BANK0_FAN45 0x5c /* Fan 4/5 Divisor Control (W83791D only) */
+#define WB_BANK0_VBAT 0x5d /* VBAT Monitor Control */
+#define WB_BANK0_FAN4 0xba /* Fan 4 reading (W83791D only) */
+#define WB_BANK0_FAN5 0xbb /* Fan 5 reading (W83791D only) */
+
+#define WB_BANK0_CONFIG 0x18 /* VRM & OVT Config (W83627THF/W83637HF) */
+
+/* Bank 1 registers */
+#define WB_BANK1_T2H 0x50 /* Temperature 2 High Byte */
+#define WB_BANK1_T2L 0x51 /* Temperature 2 Low Byte */
+
+/* Bank 2 registers */
+#define WB_BANK2_T3H 0x50 /* Temperature 3 High Byte */
+#define WB_BANK2_T3L 0x51 /* Temperature 3 Low Byte */
+
+/* Bank 4 registers (W83782D/W83627HF and later models only) */
+#define WB_BANK4_T1OFF 0x54 /* Temperature 1 Offset */
+#define WB_BANK4_T2OFF 0x55 /* Temperature 2 Offset */
+#define WB_BANK4_T3OFF 0x56 /* Temperature 3 Offset */
+
+/* Bank 5 registers (W83782D/W83627HF and later models only) */
+#define WB_BANK5_5VSB 0x50 /* 5VSB reading */
+#define WB_BANK5_VBAT 0x51 /* VBAT reading */
+
+/* Bank selection */
+#define WB_BANKSEL_B0 0x00 /* Bank 0 */
+#define WB_BANKSEL_B1 0x01 /* Bank 1 */
+#define WB_BANKSEL_B2 0x02 /* Bank 2 */
+#define WB_BANKSEL_B3 0x03 /* Bank 3 */
+#define WB_BANKSEL_B4 0x04 /* Bank 4 */
+#define WB_BANKSEL_B5 0x05 /* Bank 5 */
+#define WB_BANKSEL_HBAC 0x80 /* Register 0x4f High Byte Access */
+
+/* Vendor IDs */
+#define WB_VENDID_WINBOND 0x5ca3 /* Winbond */
+#define WB_VENDID_ASUS 0x12c3 /* ASUS */
+
+/* Chip IDs */
+#define WB_CHIPID_W83781D 0x10
+#define WB_CHIPID_W83781D_2 0x11
+#define WB_CHIPID_W83627HF 0x21
+#define WB_CHIPID_AS99127F 0x31 /* Asus W83781D clone */
+#define WB_CHIPID_W83782D 0x30
+#define WB_CHIPID_W83783S 0x40
+#define WB_CHIPID_W83697HF 0x60
+#define WB_CHIPID_W83791D 0x71
+#define WB_CHIPID_W83791SD 0x72
+#define WB_CHIPID_W83792D 0x7a
+#define WB_CHIPID_W83637HF 0x80
+#define WB_CHIPID_W83627THF 0x90
+#define WB_CHIPID_W83627EHF 0xa1
+#define WB_CHIPID_W83627DHG 0xc1
+
+/* Config bits */
+#define WB_CONFIG_VMR9 0x01
+
+/* Reference voltage (mV) */
+#define WB_VREF 3600
+#define WB_W83627EHF_VREF 2048
+
+#define WB_MAX_SENSORS 19
+
+struct lm_softc;
+
+struct lm_sensor {
+ char *desc;
+ enum sensor_type type;
+ u_int8_t bank;
+ u_int8_t reg;
+ void (*refresh)(struct lm_softc *, int);
+ int rfact;
+};
+
+struct lm_softc {
+ struct device *sc_dev;
+
+ struct ksensor sensors[WB_MAX_SENSORS];
+ struct ksensordev sensordev;
+ struct lm_sensor *lm_sensors;
+ u_int numsensors;
+ void (*refresh_sensor_data) (struct lm_softc *);
+
+ u_int8_t (*lm_readreg)(struct lm_softc *, int);
+ void (*lm_writereg)(struct lm_softc *, int, int);
+
+ u_int8_t sbusaddr;
+ u_int8_t chipid;
+ u_int8_t vrm9;
+};
+
+void lm_probe(struct lm_softc *);
+void lm_attach(struct lm_softc *);
+int lm_detach(struct lm_softc *);
diff --git a/sys/i386/conf/GENERIC.hints b/sys/i386/conf/GENERIC.hints
index ed1faa7..e1567b2 100644
--- a/sys/i386/conf/GENERIC.hints
+++ b/sys/i386/conf/GENERIC.hints
@@ -76,3 +76,11 @@ hint.le.0.disabled="1"
hint.le.0.port="0x280"
hint.le.0.irq="10"
hint.le.0.drq="0"
+hint.lm.0.at="isa"
+hint.lm.0.port="0x290"
+hint.it.0.at="isa"
+hint.it.0.port="0x290"
+hint.it.1.at="isa"
+hint.it.1.port="0xc00"
+hint.it.2.at="isa"
+hint.it.2.port="0xd00"
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 4c47167..2ac0894 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -131,6 +131,7 @@ SUBDIR= ${_3dfx} \
iscsi \
isp \
ispfw \
+ ${_it} \
${_iwi} \
${_iwifw} \
${_ixgb} \
@@ -146,6 +147,7 @@ SUBDIR= ${_3dfx} \
${_linprocfs} \
${_linsysfs} \
${_linux} \
+ ${_lm} \
lmc \
lpt \
mac_biba \
@@ -453,9 +455,11 @@ _ipmi= ipmi
_ips= ips
_ipw= ipw
_ipwfw= ipwfw
+_it= it
_iwi= iwi
_iwifw= iwifw
_ixgb= ixgb
+_lm= lm
_mly= mly
_nfe= nfe
_nve= nve
@@ -511,10 +515,12 @@ _ipmi= ipmi
_ips= ips
_ipw= ipw
_ipwfw= ipwfw
+_it= it
_ixgb= ixgb
_linprocfs= linprocfs
_linsysfs= linsysfs
_linux= linux
+_lm= lm
_mly= mly
_ndis= ndis
_nfe= nfe
diff --git a/sys/modules/it/Makefile b/sys/modules/it/Makefile
new file mode 100644
index 0000000..f2f064b
--- /dev/null
+++ b/sys/modules/it/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/it
+
+KMOD= it
+SRCS= it.c
+SRCS+= device_if.h bus_if.h isa_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/lm/Makefile b/sys/modules/lm/Makefile
new file mode 100644
index 0000000..cb0791a
--- /dev/null
+++ b/sys/modules/lm/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/lm
+
+KMOD= lm
+SRCS= lm78.c lm78_isa.c
+SRCS+= device_if.h bus_if.h isa_if.h
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud