mirror of https://gitee.com/openkylin/linux.git
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/i2c-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/i2c-2.6: (44 commits) [PATCH] I2C: I2C controllers go into right place on sysfs [PATCH] hwmon-vid: Add support for Intel Core and Conroe [PATCH] lm70: New hardware monitoring driver [PATCH] hwmon: Fix the Kconfig header [PATCH] i2c-i801: Merge setup function [PATCH] i2c-i801: Better pci subsystem integration [PATCH] i2c-i801: Cleanups [PATCH] i2c-i801: Remove PCI function check [PATCH] i2c-i801: Remove force_addr parameter [PATCH] i2c-i801: Fix block transaction poll loops [PATCH] scx200_acb: Documentation update [PATCH] scx200_acb: Mark scx200_acb_probe __init [PATCH] scx200_acb: Use PCI I/O resource when appropriate [PATCH] i2c: Mark block write buffers as const [PATCH] i2c-ocores: Minor cleanups [PATCH] abituguru: Fix fan detection [PATCH] abituguru: Review fixes [PATCH] abituguru: New hardware monitoring driver [PATCH] w83792d: Add missing data access locks [PATCH] w83792d: Fix setting the PWM value ...
This commit is contained in:
commit
d588fcbe5a
|
@ -0,0 +1,59 @@
|
|||
Kernel driver abituguru
|
||||
=======================
|
||||
|
||||
Supported chips:
|
||||
* Abit uGuru (Hardware Monitor part only)
|
||||
Prefix: 'abituguru'
|
||||
Addresses scanned: ISA 0x0E0
|
||||
Datasheet: Not available, this driver is based on reverse engineering.
|
||||
A "Datasheet" has been written based on the reverse engineering it
|
||||
should be available in the same dir as this file under the name
|
||||
abituguru-datasheet.
|
||||
|
||||
Authors:
|
||||
Hans de Goede <j.w.r.degoede@hhs.nl>,
|
||||
(Initial reverse engineering done by Olle Sandberg
|
||||
<ollebull@gmail.com>)
|
||||
|
||||
|
||||
Module Parameters
|
||||
-----------------
|
||||
|
||||
* force: bool Force detection. Note this parameter only causes the
|
||||
detection to be skipped, if the uGuru can't be read
|
||||
the module initialization (insmod) will still fail.
|
||||
* fan_sensors: int Tell the driver how many fan speed sensors there are
|
||||
on your motherboard. Default: 0 (autodetect).
|
||||
* pwms: int Tell the driver how many fan speed controls (fan
|
||||
pwms) your motherboard has. Default: 0 (autodetect).
|
||||
* verbose: int How verbose should the driver be? (0-3):
|
||||
0 normal output
|
||||
1 + verbose error reporting
|
||||
2 + sensors type probing info\n"
|
||||
3 + retryable error reporting
|
||||
Default: 2 (the driver is still in the testing phase)
|
||||
|
||||
Notice if you need any of the first three options above please insmod the
|
||||
driver with verbose set to 3 and mail me <j.w.r.degoede@hhs.nl> the output of:
|
||||
dmesg | grep abituguru
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports the hardware monitoring features of the Abit uGuru chip
|
||||
found on Abit uGuru featuring motherboards (most modern Abit motherboards).
|
||||
|
||||
The uGuru chip in reality is a Winbond W83L950D in disguise (despite Abit
|
||||
claiming it is "a new microprocessor designed by the ABIT Engineers").
|
||||
Unfortunatly this doesn't help since the W83L950D is a generic
|
||||
microcontroller with a custom Abit application running on it.
|
||||
|
||||
Despite Abit not releasing any information regarding the uGuru, Olle
|
||||
Sandberg <ollebull@gmail.com> has managed to reverse engineer the sensor part
|
||||
of the uGuru. Without his work this driver would not have been possible.
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
The voltage and frequency control parts of the Abit uGuru are not supported.
|
|
@ -0,0 +1,312 @@
|
|||
uGuru datasheet
|
||||
===============
|
||||
|
||||
First of all, what I know about uGuru is no fact based on any help, hints or
|
||||
datasheet from Abit. The data I have got on uGuru have I assembled through
|
||||
my weak knowledge in "backwards engineering".
|
||||
And just for the record, you may have noticed uGuru isn't a chip developed by
|
||||
Abit, as they claim it to be. It's realy just an microprocessor (uC) created by
|
||||
Winbond (W83L950D). And no, reading the manual for this specific uC or
|
||||
mailing Windbond for help won't give any usefull data about uGuru, as it is
|
||||
the program inside the uC that is responding to calls.
|
||||
|
||||
Olle Sandberg <ollebull@gmail.com>, 2005-05-25
|
||||
|
||||
|
||||
Original version by Olle Sandberg who did the heavy lifting of the initial
|
||||
reverse engineering. This version has been almost fully rewritten for clarity
|
||||
and extended with write support and info on more databanks, the write support
|
||||
is once again reverse engineered by Olle the additional databanks have been
|
||||
reverse engineered by me. I would like to express my thanks to Olle, this
|
||||
document and the Linux driver could not have been written without his efforts.
|
||||
|
||||
Note: because of the lack of specs only the sensors part of the uGuru is
|
||||
described here and not the CPU / RAM / etc voltage & frequency control.
|
||||
|
||||
Hans de Goede <j.w.r.degoede@hhs.nl>, 28-01-2006
|
||||
|
||||
|
||||
Detection
|
||||
=========
|
||||
|
||||
As far as known the uGuru is always placed at and using the (ISA) I/O-ports
|
||||
0xE0 and 0xE4, so we don't have to scan any port-range, just check what the two
|
||||
ports are holding for detection. We will refer to 0xE0 as CMD (command-port)
|
||||
and 0xE4 as DATA because Abit refers to them with these names.
|
||||
|
||||
If DATA holds 0x00 or 0x08 and CMD holds 0x00 or 0xAC an uGuru could be
|
||||
present. We have to check for two different values at data-port, because
|
||||
after a reboot uGuru will hold 0x00 here, but if the driver is removed and
|
||||
later on attached again data-port will hold 0x08, more about this later.
|
||||
|
||||
After wider testing of the Linux kernel driver some variants of the uGuru have
|
||||
turned up which will hold 0x00 instead of 0xAC at the CMD port, thus we also
|
||||
have to test CMD for two different values. On these uGuru's DATA will initally
|
||||
hold 0x09 and will only hold 0x08 after reading CMD first, so CMD must be read
|
||||
first!
|
||||
|
||||
To be really sure an uGuru is present a test read of one or more register
|
||||
sets should be done.
|
||||
|
||||
|
||||
Reading / Writing
|
||||
=================
|
||||
|
||||
Addressing
|
||||
----------
|
||||
|
||||
The uGuru has a number of different addressing levels. The first addressing
|
||||
level we will call banks. A bank holds data for one or more sensors. The data
|
||||
in a bank for a sensor is one or more bytes large.
|
||||
|
||||
The number of bytes is fixed for a given bank, you should always read or write
|
||||
that many bytes, reading / writing more will fail, the results when writing
|
||||
less then the number of bytes for a given bank are undetermined.
|
||||
|
||||
See below for all known bank addresses, numbers of sensors in that bank,
|
||||
number of bytes data per sensor and contents/meaning of those bytes.
|
||||
|
||||
Although both this document and the kernel driver have kept the sensor
|
||||
terminoligy for the addressing within a bank this is not 100% correct, in
|
||||
bank 0x24 for example the addressing within the bank selects a PWM output not
|
||||
a sensor.
|
||||
|
||||
Notice that some banks have both a read and a write address this is how the
|
||||
uGuru determines if a read from or a write to the bank is taking place, thus
|
||||
when reading you should always use the read address and when writing the
|
||||
write address. The write address is always one (1) more then the read address.
|
||||
|
||||
|
||||
uGuru ready
|
||||
-----------
|
||||
|
||||
Before you can read from or write to the uGuru you must first put the uGuru
|
||||
in "ready" mode.
|
||||
|
||||
To put the uGuru in ready mode first write 0x00 to DATA and then wait for DATA
|
||||
to hold 0x09, DATA should read 0x09 within 250 read cycles.
|
||||
|
||||
Next CMD _must_ be read and should hold 0xAC, usually CMD will hold 0xAC the
|
||||
first read but sometimes it takes a while before CMD holds 0xAC and thus it
|
||||
has to be read a number of times (max 50).
|
||||
|
||||
After reading CMD, DATA should hold 0x08 which means that the uGuru is ready
|
||||
for input. As above DATA will usually hold 0x08 the first read but not always.
|
||||
This step can be skipped, but it is undetermined what happens if the uGuru has
|
||||
not yet reported 0x08 at DATA and you proceed with writing a bank address.
|
||||
|
||||
|
||||
Sending bank and sensor addresses to the uGuru
|
||||
----------------------------------------------
|
||||
|
||||
First the uGuru must be in "ready" mode as described above, DATA should hold
|
||||
0x08 indicating that the uGuru wants input, in this case the bank address.
|
||||
|
||||
Next write the bank address to DATA. After the bank address has been written
|
||||
wait for to DATA to hold 0x08 again indicating that it wants / is ready for
|
||||
more input (max 250 reads).
|
||||
|
||||
Once DATA holds 0x08 again write the sensor address to CMD.
|
||||
|
||||
|
||||
Reading
|
||||
-------
|
||||
|
||||
First send the bank and sensor addresses as described above.
|
||||
Then for each byte of data you want to read wait for DATA to hold 0x01
|
||||
which indicates that the uGuru is ready to be read (max 250 reads) and once
|
||||
DATA holds 0x01 read the byte from CMD.
|
||||
|
||||
Once all bytes have been read data will hold 0x09, but there is no reason to
|
||||
test for this. Notice that the number of bytes is bank address dependent see
|
||||
above and below.
|
||||
|
||||
After completing a successfull read it is advised to put the uGuru back in
|
||||
ready mode, so that it is ready for the next read / write cycle. This way
|
||||
if your program / driver is unloaded and later loaded again the detection
|
||||
algorithm described above will still work.
|
||||
|
||||
|
||||
|
||||
Writing
|
||||
-------
|
||||
|
||||
First send the bank and sensor addresses as described above.
|
||||
Then for each byte of data you want to write wait for DATA to hold 0x00
|
||||
which indicates that the uGuru is ready to be written (max 250 reads) and
|
||||
once DATA holds 0x00 write the byte to CMD.
|
||||
|
||||
Once all bytes have been written wait for DATA to hold 0x01 (max 250 reads)
|
||||
don't ask why this is the way it is.
|
||||
|
||||
Once DATA holds 0x01 read CMD it should hold 0xAC now.
|
||||
|
||||
After completing a successfull write it is advised to put the uGuru back in
|
||||
ready mode, so that it is ready for the next read / write cycle. This way
|
||||
if your program / driver is unloaded and later loaded again the detection
|
||||
algorithm described above will still work.
|
||||
|
||||
|
||||
Gotchas
|
||||
-------
|
||||
|
||||
After wider testing of the Linux kernel driver some variants of the uGuru have
|
||||
turned up which do not hold 0x08 at DATA within 250 reads after writing the
|
||||
bank address. With these versions this happens quite frequent, using larger
|
||||
timeouts doesn't help, they just go offline for a second or 2, doing some
|
||||
internal callibration or whatever. Your code should be prepared to handle
|
||||
this and in case of no response in this specific case just goto sleep for a
|
||||
while and then retry.
|
||||
|
||||
|
||||
Address Map
|
||||
===========
|
||||
|
||||
Bank 0x20 Alarms (R)
|
||||
--------------------
|
||||
This bank contains 0 sensors, iow the sensor address is ignored (but must be
|
||||
written) just use 0. Bank 0x20 contains 3 bytes:
|
||||
|
||||
Byte 0:
|
||||
This byte holds the alarm flags for sensor 0-7 of Sensor Bank1, with bit 0
|
||||
corresponding to sensor 0, 1 to 1, etc.
|
||||
|
||||
Byte 1:
|
||||
This byte holds the alarm flags for sensor 8-15 of Sensor Bank1, with bit 0
|
||||
corresponding to sensor 8, 1 to 9, etc.
|
||||
|
||||
Byte 2:
|
||||
This byte holds the alarm flags for sensor 0-5 of Sensor Bank2, with bit 0
|
||||
corresponding to sensor 0, 1 to 1, etc.
|
||||
|
||||
|
||||
Bank 0x21 Sensor Bank1 Values / Readings (R)
|
||||
--------------------------------------------
|
||||
This bank contains 16 sensors, for each sensor it contains 1 byte.
|
||||
So far the following sensors are known to be available on all motherboards:
|
||||
Sensor 0 CPU temp
|
||||
Sensor 1 SYS temp
|
||||
Sensor 3 CPU core volt
|
||||
Sensor 4 DDR volt
|
||||
Sensor 10 DDR Vtt volt
|
||||
Sensor 15 PWM temp
|
||||
|
||||
Byte 0:
|
||||
This byte holds the reading from the sensor. Sensors in Bank1 can be both
|
||||
volt and temp sensors, this is motherboard specific. The uGuru however does
|
||||
seem to know (be programmed with) what kindoff sensor is attached see Sensor
|
||||
Bank1 Settings description.
|
||||
|
||||
Volt sensors use a linear scale, a reading 0 corresponds with 0 volt and a
|
||||
reading of 255 with 3494 mV. The sensors for higher voltages however are
|
||||
connected through a division circuit. The currently known division circuits
|
||||
in use result in ranges of: 0-4361mV, 0-6248mV or 0-14510mV. 3.3 volt sources
|
||||
use the 0-4361mV range, 5 volt the 0-6248mV and 12 volt the 0-14510mV .
|
||||
|
||||
Temp sensors also use a linear scale, a reading of 0 corresponds with 0 degree
|
||||
Celsius and a reading of 255 with a reading of 255 degrees Celsius.
|
||||
|
||||
|
||||
Bank 0x22 Sensor Bank1 Settings (R)
|
||||
Bank 0x23 Sensor Bank1 Settings (W)
|
||||
-----------------------------------
|
||||
|
||||
This bank contains 16 sensors, for each sensor it contains 3 bytes. Each
|
||||
set of 3 bytes contains the settings for the sensor with the same sensor
|
||||
address in Bank 0x21 .
|
||||
|
||||
Byte 0:
|
||||
Alarm behaviour for the selected sensor. A 1 enables the described behaviour.
|
||||
Bit 0: Give an alarm if measured temp is over the warning threshold (RW) *
|
||||
Bit 1: Give an alarm if measured volt is over the max threshold (RW) **
|
||||
Bit 2: Give an alarm if measured volt is under the min threshold (RW) **
|
||||
Bit 3: Beep if alarm (RW)
|
||||
Bit 4: 1 if alarm cause measured temp is over the warning threshold (R)
|
||||
Bit 5: 1 if alarm cause measured volt is over the max threshold (R)
|
||||
Bit 6: 1 if alarm cause measured volt is under the min threshold (R)
|
||||
Bit 7: Volt sensor: Shutdown if alarm persist for more then 4 seconds (RW)
|
||||
Temp sensor: Shutdown if temp is over the shutdown threshold (RW)
|
||||
|
||||
* This bit is only honored/used by the uGuru if a temp sensor is connected
|
||||
** This bit is only honored/used by the uGuru if a volt sensor is connected
|
||||
Note with some trickery this can be used to find out what kinda sensor is
|
||||
detected see the Linux kernel driver for an example with many comments on
|
||||
how todo this.
|
||||
|
||||
Byte 1:
|
||||
Temp sensor: warning threshold (scale as bank 0x21)
|
||||
Volt sensor: min threshold (scale as bank 0x21)
|
||||
|
||||
Byte 2:
|
||||
Temp sensor: shutdown threshold (scale as bank 0x21)
|
||||
Volt sensor: max threshold (scale as bank 0x21)
|
||||
|
||||
|
||||
Bank 0x24 PWM outputs for FAN's (R)
|
||||
Bank 0x25 PWM outputs for FAN's (W)
|
||||
-----------------------------------
|
||||
|
||||
This bank contains 3 "sensors", for each sensor it contains 5 bytes.
|
||||
Sensor 0 usually controls the CPU fan
|
||||
Sensor 1 usually controls the NB (or chipset for single chip) fan
|
||||
Sensor 2 usually controls the System fan
|
||||
|
||||
Byte 0:
|
||||
Flag 0x80 to enable control, Fan runs at 100% when disabled.
|
||||
low nibble (temp)sensor address at bank 0x21 used for control.
|
||||
|
||||
Byte 1:
|
||||
0-255 = 0-12v (linear), specify voltage at which fan will rotate when under
|
||||
low threshold temp (specified in byte 3)
|
||||
|
||||
Byte 2:
|
||||
0-255 = 0-12v (linear), specify voltage at which fan will rotate when above
|
||||
high threshold temp (specified in byte 4)
|
||||
|
||||
Byte 3:
|
||||
Low threshold temp (scale as bank 0x21)
|
||||
|
||||
byte 4:
|
||||
High threshold temp (scale as bank 0x21)
|
||||
|
||||
|
||||
Bank 0x26 Sensors Bank2 Values / Readings (R)
|
||||
---------------------------------------------
|
||||
|
||||
This bank contains 6 sensors (AFAIK), for each sensor it contains 1 byte.
|
||||
So far the following sensors are known to be available on all motherboards:
|
||||
Sensor 0: CPU fan speed
|
||||
Sensor 1: NB (or chipset for single chip) fan speed
|
||||
Sensor 2: SYS fan speed
|
||||
|
||||
Byte 0:
|
||||
This byte holds the reading from the sensor. 0-255 = 0-15300 (linear)
|
||||
|
||||
|
||||
Bank 0x27 Sensors Bank2 Settings (R)
|
||||
Bank 0x28 Sensors Bank2 Settings (W)
|
||||
------------------------------------
|
||||
|
||||
This bank contains 6 sensors (AFAIK), for each sensor it contains 2 bytes.
|
||||
|
||||
Byte 0:
|
||||
Alarm behaviour for the selected sensor. A 1 enables the described behaviour.
|
||||
Bit 0: Give an alarm if measured rpm is under the min threshold (RW)
|
||||
Bit 3: Beep if alarm (RW)
|
||||
Bit 7: Shutdown if alarm persist for more then 4 seconds (RW)
|
||||
|
||||
Byte 1:
|
||||
min threshold (scale as bank 0x26)
|
||||
|
||||
|
||||
Warning for the adventerous
|
||||
===========================
|
||||
|
||||
A word of caution to those who want to experiment and see if they can figure
|
||||
the voltage / clock programming out, I tried reading and only reading banks
|
||||
0-0x30 with the reading code used for the sensor banks (0x20-0x28) and this
|
||||
resulted in a _permanent_ reprogramming of the voltages, luckily I had the
|
||||
sensors part configured so that it would shutdown my system on any out of spec
|
||||
voltages which proprably safed my computer (after a reboot I managed to
|
||||
immediatly enter the bios and reload the defaults). This probably means that
|
||||
the read/write cycle for the non sensor part is different from the sensor part.
|
|
@ -0,0 +1,31 @@
|
|||
Kernel driver lm70
|
||||
==================
|
||||
|
||||
Supported chip:
|
||||
* National Semiconductor LM70
|
||||
Datasheet: http://www.national.com/pf/LM/LM70.html
|
||||
|
||||
Author:
|
||||
Kaiwan N Billimoria <kaiwan@designergraphix.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the National Semiconductor LM70
|
||||
temperature sensor.
|
||||
|
||||
The LM70 temperature sensor chip supports a single temperature sensor.
|
||||
It communicates with a host processor (or microcontroller) via an
|
||||
SPI/Microwire Bus interface.
|
||||
|
||||
Communication with the LM70 is simple: when the temperature is to be sensed,
|
||||
the driver accesses the LM70 using SPI communication: 16 SCLK cycles
|
||||
comprise the MOSI/MISO loop. At the end of the transfer, the 11-bit 2's
|
||||
complement digital temperature (sent via the SIO line), is available in the
|
||||
driver for interpretation. This driver makes use of the kernel's in-core
|
||||
SPI support.
|
||||
|
||||
Thanks to
|
||||
---------
|
||||
Jean Delvare <khali@linux-fr.org> for mentoring the hwmon-side driver
|
||||
development.
|
|
@ -7,6 +7,10 @@ Supported chips:
|
|||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
Datasheet: Publicly available at the National Semiconductor website
|
||||
http://www.national.com/pf/LM/LM83.html
|
||||
* National Semiconductor LM82
|
||||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
Datasheet: Publicly available at the National Semiconductor website
|
||||
http://www.national.com/pf/LM/LM82.html
|
||||
|
||||
|
||||
Author: Jean Delvare <khali@linux-fr.org>
|
||||
|
@ -15,10 +19,11 @@ Description
|
|||
-----------
|
||||
|
||||
The LM83 is a digital temperature sensor. It senses its own temperature as
|
||||
well as the temperature of up to three external diodes. It is compatible
|
||||
with many other devices such as the LM84 and all other ADM1021 clones.
|
||||
The main difference between the LM83 and the LM84 in that the later can
|
||||
only sense the temperature of one external diode.
|
||||
well as the temperature of up to three external diodes. The LM82 is
|
||||
a stripped down version of the LM83 that only supports one external diode.
|
||||
Both are compatible with many other devices such as the LM84 and all
|
||||
other ADM1021 clones. The main difference between the LM83 and the LM84
|
||||
in that the later can only sense the temperature of one external diode.
|
||||
|
||||
Using the adm1021 driver for a LM83 should work, but only two temperatures
|
||||
will be reported instead of four.
|
||||
|
@ -30,12 +35,16 @@ contact us. Note that the LM90 can easily be misdetected as a LM83.
|
|||
|
||||
Confirmed motherboards:
|
||||
SBS P014
|
||||
SBS PSL09
|
||||
|
||||
Unconfirmed motherboards:
|
||||
Gigabyte GA-8IK1100
|
||||
Iwill MPX2
|
||||
Soltek SL-75DRV5
|
||||
|
||||
The LM82 is confirmed to have been found on most AMD Geode reference
|
||||
designs and test platforms.
|
||||
|
||||
The driver has been successfully tested by Magnus Forsström, who I'd
|
||||
like to thank here. More testers will be of course welcome.
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
Kernel driver smsc47m192
|
||||
========================
|
||||
|
||||
Supported chips:
|
||||
* SMSC LPC47M192 and LPC47M997
|
||||
Prefix: 'smsc47m192'
|
||||
Addresses scanned: I2C 0x2c - 0x2d
|
||||
Datasheet: The datasheet for LPC47M192 is publicly available from
|
||||
http://www.smsc.com/
|
||||
The LPC47M997 is compatible for hardware monitoring.
|
||||
|
||||
Author: Hartmut Rick <linux@rick.claranet.de>
|
||||
Special thanks to Jean Delvare for careful checking
|
||||
of the code and many helpful comments and suggestions.
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the hardware sensor capabilities
|
||||
of the SMSC LPC47M192 and LPC47M997 Super-I/O chips.
|
||||
|
||||
These chips support 3 temperature channels and 8 voltage inputs
|
||||
as well as CPU voltage VID input.
|
||||
|
||||
They do also have fan monitoring and control capabilities, but the
|
||||
these features are accessed via ISA bus and are not supported by this
|
||||
driver. Use the 'smsc47m1' driver for fan monitoring and control.
|
||||
|
||||
Voltages and temperatures are measured by an 8-bit ADC, the resolution
|
||||
of the temperatures is 1 bit per degree C.
|
||||
Voltages are scaled such that the nominal voltage corresponds to
|
||||
192 counts, i.e. 3/4 of the full range. Thus the available range for
|
||||
each voltage channel is 0V ... 255/192*(nominal voltage), the resolution
|
||||
is 1 bit per (nominal voltage)/192.
|
||||
Both voltage and temperature values are scaled by 1000, the sys files
|
||||
show voltages in mV and temperatures in units of 0.001 degC.
|
||||
|
||||
The +12V analog voltage input channel (in4_input) is multiplexed with
|
||||
bit 4 of the encoded CPU voltage. This means that you either get
|
||||
a +12V voltage measurement or a 5 bit CPU VID, but not both.
|
||||
The default setting is to use the pin as 12V input, and use only 4 bit VID.
|
||||
This driver assumes that the information in the configuration register
|
||||
is correct, i.e. that the BIOS has updated the configuration if
|
||||
the motherboard has this input wired to VID4.
|
||||
|
||||
The temperature and voltage readings are updated once every 1.5 seconds.
|
||||
Reading them more often repeats the same values.
|
||||
|
||||
|
||||
sysfs interface
|
||||
---------------
|
||||
|
||||
in0_input - +2.5V voltage input
|
||||
in1_input - CPU voltage input (nominal 2.25V)
|
||||
in2_input - +3.3V voltage input
|
||||
in3_input - +5V voltage input
|
||||
in4_input - +12V voltage input (may be missing if used as VID4)
|
||||
in5_input - Vcc voltage input (nominal 3.3V)
|
||||
This is the supply voltage of the sensor chip itself.
|
||||
in6_input - +1.5V voltage input
|
||||
in7_input - +1.8V voltage input
|
||||
|
||||
in[0-7]_min,
|
||||
in[0-7]_max - lower and upper alarm thresholds for in[0-7]_input reading
|
||||
|
||||
All voltages are read and written in mV.
|
||||
|
||||
in[0-7]_alarm - alarm flags for voltage inputs
|
||||
These files read '1' in case of alarm, '0' otherwise.
|
||||
|
||||
temp1_input - chip temperature measured by on-chip diode
|
||||
temp[2-3]_input - temperature measured by external diodes (one of these would
|
||||
typically be wired to the diode inside the CPU)
|
||||
|
||||
temp[1-3]_min,
|
||||
temp[1-3]_max - lower and upper alarm thresholds for temperatures
|
||||
|
||||
temp[1-3]_offset - temperature offset registers
|
||||
The chip adds the offsets stored in these registers to
|
||||
the corresponding temperature readings.
|
||||
Note that temp1 and temp2 offsets share the same register,
|
||||
they cannot both be different from zero at the same time.
|
||||
Writing a non-zero number to one of them will reset the other
|
||||
offset to zero.
|
||||
|
||||
All temperatures and offsets are read and written in
|
||||
units of 0.001 degC.
|
||||
|
||||
temp[1-3]_alarm - alarm flags for temperature inputs, '1' in case of alarm,
|
||||
'0' otherwise.
|
||||
temp[2-3]_input_fault - diode fault flags for temperature inputs 2 and 3.
|
||||
A fault is detected if the two pins for the corresponding
|
||||
sensor are open or shorted, or any of the two is shorted
|
||||
to ground or Vcc. '1' indicates a diode fault.
|
||||
|
||||
cpu0_vid - CPU voltage as received from the CPU
|
||||
|
||||
vrm - CPU VID standard used for decoding CPU voltage
|
||||
|
||||
The *_min, *_max, *_offset and vrm files can be read and
|
||||
written, all others are read-only.
|
|
@ -3,15 +3,15 @@ Naming and data format standards for sysfs files
|
|||
|
||||
The libsensors library offers an interface to the raw sensors data
|
||||
through the sysfs interface. See libsensors documentation and source for
|
||||
more further information. As of writing this document, libsensors
|
||||
(from lm_sensors 2.8.3) is heavily chip-dependant. Adding or updating
|
||||
further information. As of writing this document, libsensors
|
||||
(from lm_sensors 2.8.3) is heavily chip-dependent. Adding or updating
|
||||
support for any given chip requires modifying the library's code.
|
||||
This is because libsensors was written for the procfs interface
|
||||
older kernel modules were using, which wasn't standardized enough.
|
||||
Recent versions of libsensors (from lm_sensors 2.8.2 and later) have
|
||||
support for the sysfs interface, though.
|
||||
|
||||
The new sysfs interface was designed to be as chip-independant as
|
||||
The new sysfs interface was designed to be as chip-independent as
|
||||
possible.
|
||||
|
||||
Note that motherboards vary widely in the connections to sensor chips.
|
||||
|
@ -24,7 +24,7 @@ range using external resistors. Since the values of these resistors
|
|||
can change from motherboard to motherboard, the conversions cannot be
|
||||
hard coded into the driver and have to be done in user space.
|
||||
|
||||
For this reason, even if we aim at a chip-independant libsensors, it will
|
||||
For this reason, even if we aim at a chip-independent libsensors, it will
|
||||
still require a configuration file (e.g. /etc/sensors.conf) for proper
|
||||
values conversion, labeling of inputs and hiding of unused inputs.
|
||||
|
||||
|
@ -39,15 +39,16 @@ If you are developing a userspace application please send us feedback on
|
|||
this standard.
|
||||
|
||||
Note that this standard isn't completely established yet, so it is subject
|
||||
to changes, even important ones. One more reason to use the library instead
|
||||
of accessing sysfs files directly.
|
||||
to changes. If you are writing a new hardware monitoring driver those
|
||||
features can't seem to fit in this interface, please contact us with your
|
||||
extension proposal. Keep in mind that backward compatibility must be
|
||||
preserved.
|
||||
|
||||
Each chip gets its own directory in the sysfs /sys/devices tree. To
|
||||
find all sensor chips, it is easier to follow the symlinks from
|
||||
/sys/i2c/devices/
|
||||
find all sensor chips, it is easier to follow the device symlinks from
|
||||
/sys/class/hwmon/hwmon*.
|
||||
|
||||
All sysfs values are fixed point numbers. To get the true value of some
|
||||
of the values, you should divide by the specified value.
|
||||
All sysfs values are fixed point numbers.
|
||||
|
||||
There is only one value per file, unlike the older /proc specification.
|
||||
The common scheme for files naming is: <type><number>_<item>. Usual
|
||||
|
@ -69,28 +70,40 @@ to cause an alarm) is chip-dependent.
|
|||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
[0-*] denotes any positive number starting from 0
|
||||
[1-*] denotes any positive number starting from 1
|
||||
RO read only value
|
||||
RW read/write value
|
||||
|
||||
Read/write values may be read-only for some chips, depending on the
|
||||
hardware implementation.
|
||||
|
||||
All entries are optional, and should only be created in a given driver
|
||||
if the chip has the feature.
|
||||
|
||||
************
|
||||
* Voltages *
|
||||
************
|
||||
|
||||
in[0-8]_min Voltage min value.
|
||||
in[0-*]_min Voltage min value.
|
||||
Unit: millivolt
|
||||
Read/Write
|
||||
RW
|
||||
|
||||
in[0-8]_max Voltage max value.
|
||||
in[0-*]_max Voltage max value.
|
||||
Unit: millivolt
|
||||
Read/Write
|
||||
RW
|
||||
|
||||
in[0-8]_input Voltage input value.
|
||||
in[0-*]_input Voltage input value.
|
||||
Unit: millivolt
|
||||
Read only
|
||||
RO
|
||||
Voltage measured on the chip pin.
|
||||
Actual voltage depends on the scaling resistors on the
|
||||
motherboard, as recommended in the chip datasheet.
|
||||
This varies by chip and by motherboard.
|
||||
Because of this variation, values are generally NOT scaled
|
||||
by the chip driver, and must be done by the application.
|
||||
However, some drivers (notably lm87 and via686a)
|
||||
do scale, with various degrees of success.
|
||||
do scale, because of internal resistors built into a chip.
|
||||
These drivers will output the actual voltage.
|
||||
|
||||
Typical usage:
|
||||
|
@ -104,58 +117,72 @@ in[0-8]_input Voltage input value.
|
|||
in7_* varies
|
||||
in8_* varies
|
||||
|
||||
cpu[0-1]_vid CPU core reference voltage.
|
||||
cpu[0-*]_vid CPU core reference voltage.
|
||||
Unit: millivolt
|
||||
Read only.
|
||||
RO
|
||||
Not always correct.
|
||||
|
||||
vrm Voltage Regulator Module version number.
|
||||
Read only.
|
||||
Two digit number, first is major version, second is
|
||||
minor version.
|
||||
RW (but changing it should no more be necessary)
|
||||
Originally the VRM standard version multiplied by 10, but now
|
||||
an arbitrary number, as not all standards have a version
|
||||
number.
|
||||
Affects the way the driver calculates the CPU core reference
|
||||
voltage from the vid pins.
|
||||
|
||||
Also see the Alarms section for status flags associated with voltages.
|
||||
|
||||
|
||||
********
|
||||
* Fans *
|
||||
********
|
||||
|
||||
fan[1-3]_min Fan minimum value
|
||||
fan[1-*]_min Fan minimum value
|
||||
Unit: revolution/min (RPM)
|
||||
Read/Write.
|
||||
RW
|
||||
|
||||
fan[1-3]_input Fan input value.
|
||||
fan[1-*]_input Fan input value.
|
||||
Unit: revolution/min (RPM)
|
||||
Read only.
|
||||
RO
|
||||
|
||||
fan[1-3]_div Fan divisor.
|
||||
fan[1-*]_div Fan divisor.
|
||||
Integer value in powers of two (1, 2, 4, 8, 16, 32, 64, 128).
|
||||
RW
|
||||
Some chips only support values 1, 2, 4 and 8.
|
||||
Note that this is actually an internal clock divisor, which
|
||||
affects the measurable speed range, not the read value.
|
||||
|
||||
Also see the Alarms section for status flags associated with fans.
|
||||
|
||||
|
||||
*******
|
||||
* PWM *
|
||||
*******
|
||||
|
||||
pwm[1-3] Pulse width modulation fan control.
|
||||
pwm[1-*] Pulse width modulation fan control.
|
||||
Integer value in the range 0 to 255
|
||||
Read/Write
|
||||
RW
|
||||
255 is max or 100%.
|
||||
|
||||
pwm[1-3]_enable
|
||||
pwm[1-*]_enable
|
||||
Switch PWM on and off.
|
||||
Not always present even if fan*_pwm is.
|
||||
0 to turn off
|
||||
1 to turn on in manual mode
|
||||
2 to turn on in automatic mode
|
||||
Read/Write
|
||||
0: turn off
|
||||
1: turn on in manual mode
|
||||
2+: turn on in automatic mode
|
||||
Check individual chip documentation files for automatic mode details.
|
||||
RW
|
||||
|
||||
pwm[1-*]_mode
|
||||
0: DC mode
|
||||
1: PWM mode
|
||||
RW
|
||||
|
||||
pwm[1-*]_auto_channels_temp
|
||||
Select which temperature channels affect this PWM output in
|
||||
auto mode. Bitfield, 1 is temp1, 2 is temp2, 4 is temp3 etc...
|
||||
Which values are possible depend on the chip used.
|
||||
RW
|
||||
|
||||
pwm[1-*]_auto_point[1-*]_pwm
|
||||
pwm[1-*]_auto_point[1-*]_temp
|
||||
|
@ -163,6 +190,7 @@ pwm[1-*]_auto_point[1-*]_temp_hyst
|
|||
Define the PWM vs temperature curve. Number of trip points is
|
||||
chip-dependent. Use this for chips which associate trip points
|
||||
to PWM output channels.
|
||||
RW
|
||||
|
||||
OR
|
||||
|
||||
|
@ -172,50 +200,57 @@ temp[1-*]_auto_point[1-*]_temp_hyst
|
|||
Define the PWM vs temperature curve. Number of trip points is
|
||||
chip-dependent. Use this for chips which associate trip points
|
||||
to temperature channels.
|
||||
RW
|
||||
|
||||
|
||||
****************
|
||||
* Temperatures *
|
||||
****************
|
||||
|
||||
temp[1-3]_type Sensor type selection.
|
||||
temp[1-*]_type Sensor type selection.
|
||||
Integers 1 to 4 or thermistor Beta value (typically 3435)
|
||||
Read/Write.
|
||||
RW
|
||||
1: PII/Celeron Diode
|
||||
2: 3904 transistor
|
||||
3: thermal diode
|
||||
4: thermistor (default/unknown Beta)
|
||||
Not all types are supported by all chips
|
||||
|
||||
temp[1-4]_max Temperature max value.
|
||||
Unit: millidegree Celcius
|
||||
Read/Write value.
|
||||
temp[1-*]_max Temperature max value.
|
||||
Unit: millidegree Celsius (or millivolt, see below)
|
||||
RW
|
||||
|
||||
temp[1-3]_min Temperature min value.
|
||||
Unit: millidegree Celcius
|
||||
Read/Write value.
|
||||
temp[1-*]_min Temperature min value.
|
||||
Unit: millidegree Celsius
|
||||
RW
|
||||
|
||||
temp[1-3]_max_hyst
|
||||
temp[1-*]_max_hyst
|
||||
Temperature hysteresis value for max limit.
|
||||
Unit: millidegree Celcius
|
||||
Unit: millidegree Celsius
|
||||
Must be reported as an absolute temperature, NOT a delta
|
||||
from the max value.
|
||||
Read/Write value.
|
||||
RW
|
||||
|
||||
temp[1-4]_input Temperature input value.
|
||||
Unit: millidegree Celcius
|
||||
Read only value.
|
||||
temp[1-*]_input Temperature input value.
|
||||
Unit: millidegree Celsius
|
||||
RO
|
||||
|
||||
temp[1-4]_crit Temperature critical value, typically greater than
|
||||
temp[1-*]_crit Temperature critical value, typically greater than
|
||||
corresponding temp_max values.
|
||||
Unit: millidegree Celcius
|
||||
Read/Write value.
|
||||
Unit: millidegree Celsius
|
||||
RW
|
||||
|
||||
temp[1-2]_crit_hyst
|
||||
temp[1-*]_crit_hyst
|
||||
Temperature hysteresis value for critical limit.
|
||||
Unit: millidegree Celcius
|
||||
Unit: millidegree Celsius
|
||||
Must be reported as an absolute temperature, NOT a delta
|
||||
from the critical value.
|
||||
RW
|
||||
|
||||
temp[1-4]_offset
|
||||
Temperature offset which is added to the temperature reading
|
||||
by the chip.
|
||||
Unit: millidegree Celsius
|
||||
Read/Write value.
|
||||
|
||||
If there are multiple temperature sensors, temp1_* is
|
||||
|
@ -225,6 +260,17 @@ temp[1-2]_crit_hyst
|
|||
itself, for example the thermal diode inside the CPU or
|
||||
a thermistor nearby.
|
||||
|
||||
Some chips measure temperature using external thermistors and an ADC, and
|
||||
report the temperature measurement as a voltage. Converting this voltage
|
||||
back to a temperature (or the other way around for limits) requires
|
||||
mathematical functions not available in the kernel, so the conversion
|
||||
must occur in user space. For these chips, all temp* files described
|
||||
above should contain values expressed in millivolt instead of millidegree
|
||||
Celsius. In other words, such temperature channels are handled as voltage
|
||||
channels by the driver.
|
||||
|
||||
Also see the Alarms section for status flags associated with temperatures.
|
||||
|
||||
|
||||
************
|
||||
* Currents *
|
||||
|
@ -233,25 +279,88 @@ temp[1-2]_crit_hyst
|
|||
Note that no known chip provides current measurements as of writing,
|
||||
so this part is theoretical, so to say.
|
||||
|
||||
curr[1-n]_max Current max value
|
||||
curr[1-*]_max Current max value
|
||||
Unit: milliampere
|
||||
Read/Write.
|
||||
RW
|
||||
|
||||
curr[1-n]_min Current min value.
|
||||
curr[1-*]_min Current min value.
|
||||
Unit: milliampere
|
||||
Read/Write.
|
||||
RW
|
||||
|
||||
curr[1-n]_input Current input value
|
||||
curr[1-*]_input Current input value
|
||||
Unit: milliampere
|
||||
Read only.
|
||||
RO
|
||||
|
||||
|
||||
*********
|
||||
* Other *
|
||||
*********
|
||||
**********
|
||||
* Alarms *
|
||||
**********
|
||||
|
||||
Each channel or limit may have an associated alarm file, containing a
|
||||
boolean value. 1 means than an alarm condition exists, 0 means no alarm.
|
||||
|
||||
Usually a given chip will either use channel-related alarms, or
|
||||
limit-related alarms, not both. The driver should just reflect the hardware
|
||||
implementation.
|
||||
|
||||
in[0-*]_alarm
|
||||
fan[1-*]_alarm
|
||||
temp[1-*]_alarm
|
||||
Channel alarm
|
||||
0: no alarm
|
||||
1: alarm
|
||||
RO
|
||||
|
||||
OR
|
||||
|
||||
in[0-*]_min_alarm
|
||||
in[0-*]_max_alarm
|
||||
fan[1-*]_min_alarm
|
||||
temp[1-*]_min_alarm
|
||||
temp[1-*]_max_alarm
|
||||
temp[1-*]_crit_alarm
|
||||
Limit alarm
|
||||
0: no alarm
|
||||
1: alarm
|
||||
RO
|
||||
|
||||
Each input channel may have an associated fault file. This can be used
|
||||
to notify open diodes, unconnected fans etc. where the hardware
|
||||
supports it. When this boolean has value 1, the measurement for that
|
||||
channel should not be trusted.
|
||||
|
||||
in[0-*]_input_fault
|
||||
fan[1-*]_input_fault
|
||||
temp[1-*]_input_fault
|
||||
Input fault condition
|
||||
0: no fault occured
|
||||
1: fault condition
|
||||
RO
|
||||
|
||||
Some chips also offer the possibility to get beeped when an alarm occurs:
|
||||
|
||||
beep_enable Master beep enable
|
||||
0: no beeps
|
||||
1: beeps
|
||||
RW
|
||||
|
||||
in[0-*]_beep
|
||||
fan[1-*]_beep
|
||||
temp[1-*]_beep
|
||||
Channel beep
|
||||
0: disable
|
||||
1: enable
|
||||
RW
|
||||
|
||||
In theory, a chip could provide per-limit beep masking, but no such chip
|
||||
was seen so far.
|
||||
|
||||
Old drivers provided a different, non-standard interface to alarms and
|
||||
beeps. These interface files are deprecated, but will be kept around
|
||||
for compatibility reasons:
|
||||
|
||||
alarms Alarm bitmask.
|
||||
Read only.
|
||||
RO
|
||||
Integer representation of one to four bytes.
|
||||
A '1' bit means an alarm.
|
||||
Chips should be programmed for 'comparator' mode so that
|
||||
|
@ -259,35 +368,26 @@ alarms Alarm bitmask.
|
|||
if it is still valid.
|
||||
Generally a direct representation of a chip's internal
|
||||
alarm registers; there is no standard for the position
|
||||
of individual bits.
|
||||
of individual bits. For this reason, the use of this
|
||||
interface file for new drivers is discouraged. Use
|
||||
individual *_alarm and *_fault files instead.
|
||||
Bits are defined in kernel/include/sensors.h.
|
||||
|
||||
alarms_in Alarm bitmask relative to in (voltage) channels
|
||||
Read only
|
||||
A '1' bit means an alarm, LSB corresponds to in0 and so on
|
||||
Prefered to 'alarms' for newer chips
|
||||
|
||||
alarms_fan Alarm bitmask relative to fan channels
|
||||
Read only
|
||||
A '1' bit means an alarm, LSB corresponds to fan1 and so on
|
||||
Prefered to 'alarms' for newer chips
|
||||
|
||||
alarms_temp Alarm bitmask relative to temp (temperature) channels
|
||||
Read only
|
||||
A '1' bit means an alarm, LSB corresponds to temp1 and so on
|
||||
Prefered to 'alarms' for newer chips
|
||||
|
||||
beep_enable Beep/interrupt enable
|
||||
0 to disable.
|
||||
1 to enable.
|
||||
Read/Write
|
||||
|
||||
beep_mask Bitmask for beep.
|
||||
Same format as 'alarms' with the same bit locations.
|
||||
Read/Write
|
||||
Same format as 'alarms' with the same bit locations,
|
||||
use discouraged for the same reason. Use individual
|
||||
*_beep files instead.
|
||||
RW
|
||||
|
||||
|
||||
*********
|
||||
* Other *
|
||||
*********
|
||||
|
||||
eeprom Raw EEPROM data in binary form.
|
||||
Read only.
|
||||
RO
|
||||
|
||||
pec Enable or disable PEC (SMBus only)
|
||||
Read/Write
|
||||
0: disable
|
||||
1: enable
|
||||
RW
|
||||
|
|
|
@ -6,31 +6,32 @@ voltages, fans speed). They are often connected through an I2C bus, but some
|
|||
are also connected directly through the ISA bus.
|
||||
|
||||
The kernel drivers make the data from the sensor chips available in the /sys
|
||||
virtual filesystem. Userspace tools are then used to display or set or the
|
||||
data in a more friendly manner.
|
||||
virtual filesystem. Userspace tools are then used to display the measured
|
||||
values or configure the chips in a more friendly manner.
|
||||
|
||||
Lm-sensors
|
||||
----------
|
||||
|
||||
Core set of utilites that will allow you to obtain health information,
|
||||
Core set of utilities that will allow you to obtain health information,
|
||||
setup monitoring limits etc. You can get them on their homepage
|
||||
http://www.lm-sensors.nu/ or as a package from your Linux distribution.
|
||||
|
||||
If from website:
|
||||
Get lmsensors from project web site. Please note, you need only userspace
|
||||
part, so compile with "make user_install" target.
|
||||
Get lm-sensors from project web site. Please note, you need only userspace
|
||||
part, so compile with "make user" and install with "make user_install".
|
||||
|
||||
General hints to get things working:
|
||||
|
||||
0) get lm-sensors userspace utils
|
||||
1) compile all drivers in I2C section as modules in your kernel
|
||||
1) compile all drivers in I2C and Hardware Monitoring sections as modules
|
||||
in your kernel
|
||||
2) run sensors-detect script, it will tell you what modules you need to load.
|
||||
3) load them and run "sensors" command, you should see some results.
|
||||
4) fix sensors.conf, labels, limits, fan divisors
|
||||
5) if any more problems consult FAQ, or documentation
|
||||
|
||||
Other utilites
|
||||
--------------
|
||||
Other utilities
|
||||
---------------
|
||||
|
||||
If you want some graphical indicators of system health look for applications
|
||||
like: gkrellm, ksensors, xsensors, wmtemp, wmsensors, wmgtemp, ksysguardd,
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
Kernel driver w83791d
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Winbond W83791D
|
||||
Prefix: 'w83791d'
|
||||
Addresses scanned: I2C 0x2c - 0x2f
|
||||
Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83791Da.pdf
|
||||
|
||||
Author: Charles Spirakis <bezaur@gmail.com>
|
||||
|
||||
This driver was derived from the w83781d.c and w83792d.c source files.
|
||||
|
||||
Credits:
|
||||
w83781d.c:
|
||||
Frodo Looijaard <frodol@dds.nl>,
|
||||
Philip Edelbrock <phil@netroedge.com>,
|
||||
and Mark Studebaker <mdsxyz123@yahoo.com>
|
||||
w83792d.c:
|
||||
Chunhao Huang <DZShen@Winbond.com.tw>,
|
||||
Rudolf Marek <r.marek@sh.cvut.cz>
|
||||
|
||||
Module Parameters
|
||||
-----------------
|
||||
|
||||
* init boolean
|
||||
(default 0)
|
||||
Use 'init=1' to have the driver do extra software initializations.
|
||||
The default behavior is to do the minimum initialization possible
|
||||
and depend on the BIOS to properly setup the chip. If you know you
|
||||
have a w83791d and you're having problems, try init=1 before trying
|
||||
reset=1.
|
||||
|
||||
* reset boolean
|
||||
(default 0)
|
||||
Use 'reset=1' to reset the chip (via index 0x40, bit 7). The default
|
||||
behavior is no chip reset to preserve BIOS settings.
|
||||
|
||||
* force_subclients=bus,caddr,saddr,saddr
|
||||
This is used to force the i2c addresses for subclients of
|
||||
a certain chip. Example usage is `force_subclients=0,0x2f,0x4a,0x4b'
|
||||
to force the subclients of chip 0x2f on bus 0 to i2c addresses
|
||||
0x4a and 0x4b.
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the Winbond W83791D chip.
|
||||
|
||||
Detection of the chip can sometimes be foiled because it can be in an
|
||||
internal state that allows no clean access (Bank with ID register is not
|
||||
currently selected). If you know the address of the chip, use a 'force'
|
||||
parameter; this will put it into a more well-behaved state first.
|
||||
|
||||
The driver implements three temperature sensors, five fan rotation speed
|
||||
sensors, and ten voltage sensors.
|
||||
|
||||
Temperatures are measured in degrees Celsius and measurement resolution is 1
|
||||
degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
|
||||
the temperature gets higher than the Overtemperature Shutdown value; it stays
|
||||
on until the temperature falls below the Hysteresis value.
|
||||
|
||||
Fan rotation speeds are reported in RPM (rotations per minute). An alarm is
|
||||
triggered if the rotation speed has dropped below a programmable limit. Fan
|
||||
readings can be divided by a programmable divider (1, 2, 4, 8 for fan 1/2/3
|
||||
and 1, 2, 4, 8, 16, 32, 64 or 128 for fan 4/5) to give the readings more
|
||||
range or accuracy.
|
||||
|
||||
Voltage sensors (also known as IN sensors) report their values in millivolts.
|
||||
An alarm is triggered if the voltage has crossed a programmable minimum
|
||||
or maximum limit.
|
||||
|
||||
Alarms are provided as output from a "realtime status register". The
|
||||
following bits are defined:
|
||||
|
||||
bit - alarm on:
|
||||
0 - Vcore
|
||||
1 - VINR0
|
||||
2 - +3.3VIN
|
||||
3 - 5VDD
|
||||
4 - temp1
|
||||
5 - temp2
|
||||
6 - fan1
|
||||
7 - fan2
|
||||
8 - +12VIN
|
||||
9 - -12VIN
|
||||
10 - -5VIN
|
||||
11 - fan3
|
||||
12 - chassis
|
||||
13 - temp3
|
||||
14 - VINR1
|
||||
15 - reserved
|
||||
16 - tart1
|
||||
17 - tart2
|
||||
18 - tart3
|
||||
19 - VSB
|
||||
20 - VBAT
|
||||
21 - fan4
|
||||
22 - fan5
|
||||
23 - reserved
|
||||
|
||||
When an alarm goes off, you can be warned by a beeping signal through your
|
||||
computer speaker. It is possible to enable all beeping globally, or only
|
||||
the beeping for some alarms.
|
||||
|
||||
The driver only reads the chip values each 3 seconds; reading them more
|
||||
often will do no harm, but will return 'old' values.
|
||||
|
||||
W83791D TODO:
|
||||
---------------
|
||||
Provide a patch for per-file alarms as discussed on the mailing list
|
||||
Provide a patch for smart-fan control (still need appropriate motherboard/fans)
|
|
@ -21,8 +21,7 @@ Authors:
|
|||
Module Parameters
|
||||
-----------------
|
||||
|
||||
* force_addr: int
|
||||
Forcibly enable the ICH at the given address. EXTREMELY DANGEROUS!
|
||||
None.
|
||||
|
||||
|
||||
Description
|
||||
|
|
|
@ -7,6 +7,8 @@ Supported adapters:
|
|||
* nForce3 250Gb MCP 10de:00E4
|
||||
* nForce4 MCP 10de:0052
|
||||
* nForce4 MCP-04 10de:0034
|
||||
* nForce4 MCP51 10de:0264
|
||||
* nForce4 MCP55 10de:0368
|
||||
|
||||
Datasheet: not publically available, but seems to be similar to the
|
||||
AMD-8111 SMBus 2.0 adapter.
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
Kernel driver i2c-ocores
|
||||
|
||||
Supported adapters:
|
||||
* OpenCores.org I2C controller by Richard Herveille (see datasheet link)
|
||||
Datasheet: http://www.opencores.org/projects.cgi/web/i2c/overview
|
||||
|
||||
Author: Peter Korsgaard <jacmet@sunsite.dk>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
i2c-ocores is an i2c bus driver for the OpenCores.org I2C controller
|
||||
IP core by Richard Herveille.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
i2c-ocores uses the platform bus, so you need to provide a struct
|
||||
platform_device with the base address and interrupt number. The
|
||||
dev.platform_data of the device should also point to a struct
|
||||
ocores_i2c_platform_data (see linux/i2c-ocores.h) describing the
|
||||
distance between registers and the input clock speed.
|
||||
|
||||
E.G. something like:
|
||||
|
||||
static struct resource ocores_resources[] = {
|
||||
[0] = {
|
||||
.start = MYI2C_BASEADDR,
|
||||
.end = MYI2C_BASEADDR + 8,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = MYI2C_IRQ,
|
||||
.end = MYI2C_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct ocores_i2c_platform_data myi2c_data = {
|
||||
.regstep = 2, /* two bytes between registers */
|
||||
.clock_khz = 50000, /* input clock of 50MHz */
|
||||
};
|
||||
|
||||
static struct platform_device myi2c = {
|
||||
.name = "ocores-i2c",
|
||||
.dev = {
|
||||
.platform_data = &myi2c_data,
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(ocores_resources),
|
||||
.resource = ocores_resources,
|
||||
};
|
|
@ -6,6 +6,8 @@ Supported adapters:
|
|||
Datasheet: Publicly available at the Intel website
|
||||
* ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges
|
||||
Datasheet: Only available via NDA from ServerWorks
|
||||
* ATI IXP southbridges IXP200, IXP300, IXP400
|
||||
Datasheet: Not publicly available
|
||||
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
|
||||
Datasheet: Publicly available at the SMSC website http://www.smsc.com
|
||||
|
||||
|
@ -21,8 +23,6 @@ Module Parameters
|
|||
Forcibly enable the PIIX4. DANGEROUS!
|
||||
* force_addr: int
|
||||
Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS!
|
||||
* fix_hstcfg: int
|
||||
Fix config register. Needed on some boards (Force CPCI735).
|
||||
|
||||
|
||||
Description
|
||||
|
@ -63,10 +63,36 @@ The PIIX4E is just an new version of the PIIX4; it is supported as well.
|
|||
The PIIX/PIIX3 does not implement an SMBus or I2C bus, so you can't use
|
||||
this driver on those mainboards.
|
||||
|
||||
The ServerWorks Southbridges, the Intel 440MX, and the Victory766 are
|
||||
The ServerWorks Southbridges, the Intel 440MX, and the Victory66 are
|
||||
identical to the PIIX4 in I2C/SMBus support.
|
||||
|
||||
A few OSB4 southbridges are known to be misconfigured by the BIOS. In this
|
||||
case, you have you use the fix_hstcfg module parameter. Do not use it
|
||||
unless you know you have to, because in some cases it also breaks
|
||||
configuration on southbridges that don't need it.
|
||||
If you own Force CPCI735 motherboard or other OSB4 based systems you may need
|
||||
to change the SMBus Interrupt Select register so the SMBus controller uses
|
||||
the SMI mode.
|
||||
|
||||
1) Use lspci command and locate the PCI device with the SMBus controller:
|
||||
00:0f.0 ISA bridge: ServerWorks OSB4 South Bridge (rev 4f)
|
||||
The line may vary for different chipsets. Please consult the driver source
|
||||
for all possible PCI ids (and lspci -n to match them). Lets assume the
|
||||
device is located at 00:0f.0.
|
||||
2) Now you just need to change the value in 0xD2 register. Get it first with
|
||||
command: lspci -xxx -s 00:0f.0
|
||||
If the value is 0x3 then you need to change it to 0x1
|
||||
setpci -s 00:0f.0 d2.b=1
|
||||
|
||||
Please note that you don't need to do that in all cases, just when the SMBus is
|
||||
not working properly.
|
||||
|
||||
|
||||
Hardware-specific issues
|
||||
------------------------
|
||||
|
||||
This driver will refuse to load on IBM systems with an Intel PIIX4 SMBus.
|
||||
Some of these machines have an RFID EEPROM (24RF08) connected to the SMBus,
|
||||
which can easily get corrupted due to a state machine bug. These are mostly
|
||||
Thinkpad laptops, but desktop systems may also be affected. We have no list
|
||||
of all affected systems, so the only safe solution was to prevent access to
|
||||
the SMBus on all IBM systems (detected using DMI data.)
|
||||
|
||||
For additional information, read:
|
||||
http://www2.lm-sensors.nu/~lm78/cvs/lm_sensors2/README.thinkpad
|
||||
|
|
|
@ -2,14 +2,31 @@ Kernel driver scx200_acb
|
|||
|
||||
Author: Christer Weinigel <wingel@nano-system.com>
|
||||
|
||||
The driver supersedes the older, never merged driver named i2c-nscacb.
|
||||
|
||||
Module Parameters
|
||||
-----------------
|
||||
|
||||
* base: int
|
||||
* base: up to 4 ints
|
||||
Base addresses for the ACCESS.bus controllers on SCx200 and SC1100 devices
|
||||
|
||||
By default the driver uses two base addresses 0x820 and 0x840.
|
||||
If you want only one base address, specify the second as 0 so as to
|
||||
override this default.
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
Enable the use of the ACCESS.bus controller on the Geode SCx200 and
|
||||
SC1100 processors and the CS5535 and CS5536 Geode companion devices.
|
||||
|
||||
Device-specific notes
|
||||
---------------------
|
||||
|
||||
The SC1100 WRAP boards are known to use base addresses 0x810 and 0x820.
|
||||
If the scx200_acb driver is built into the kernel, add the following
|
||||
parameter to your boot command line:
|
||||
scx200_acb.base=0x810,0x820
|
||||
If the scx200_acb driver is built as a module, add the following line to
|
||||
the file /etc/modprobe.conf instead:
|
||||
options scx200_acb base=0x810,0x820
|
||||
|
|
24
MAINTAINERS
24
MAINTAINERS
|
@ -181,6 +181,12 @@ M: bcrl@kvack.org
|
|||
L: linux-aio@kvack.org
|
||||
S: Supported
|
||||
|
||||
ABIT UGURU HARDWARE MONITOR DRIVER
|
||||
P: Hans de Goede
|
||||
M: j.w.r.degoede@hhs.nl
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
|
||||
ACENIC DRIVER
|
||||
P: Jes Sorensen
|
||||
M: jes@trained-monkey.org
|
||||
|
@ -2057,6 +2063,12 @@ M: adaplas@pol.net
|
|||
L: linux-fbdev-devel@lists.sourceforge.net
|
||||
S: Maintained
|
||||
|
||||
OPENCORES I2C BUS DRIVER
|
||||
P: Peter Korsgaard
|
||||
M: jacmet@sunsite.dk
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
|
||||
ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
|
||||
P: Mark Fasheh
|
||||
M: mark.fasheh@oracle.com
|
||||
|
@ -2528,12 +2540,6 @@ M: thomas@winischhofer.net
|
|||
W: http://www.winischhofer.at/linuxsisusbvga.shtml
|
||||
S: Maintained
|
||||
|
||||
SMSC47M1 HARDWARE MONITOR DRIVER
|
||||
P: Jean Delvare
|
||||
M: khali@linux-fr.org
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Odd Fixes
|
||||
|
||||
SMB FILESYSTEM
|
||||
P: Urban Widmark
|
||||
M: urban@teststation.com
|
||||
|
@ -3146,12 +3152,6 @@ L: wbsd-devel@list.drzeus.cx
|
|||
W: http://projects.drzeus.cx/wbsd
|
||||
S: Maintained
|
||||
|
||||
W83L785TS HARDWARE MONITOR DRIVER
|
||||
P: Jean Delvare
|
||||
M: khali@linux-fr.org
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Odd Fixes
|
||||
|
||||
WATCHDOG DEVICE DRIVERS
|
||||
P: Wim Van Sebroeck
|
||||
M: wim@iguana.be
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# I2C Sensor chip drivers configuration
|
||||
# Hardware monitoring chip drivers configuration
|
||||
#
|
||||
|
||||
menu "Hardware Monitoring support"
|
||||
|
@ -16,6 +16,10 @@ config HWMON
|
|||
should say Y here and also to the specific driver(s) for your
|
||||
sensors chip(s) below.
|
||||
|
||||
To find out which specific driver(s) you need, use the
|
||||
sensors-detect script from the lm_sensors package. Read
|
||||
<file:Documentation/hwmon/userspace-tools> for details.
|
||||
|
||||
This support can also be built as a module. If so, the module
|
||||
will be called hwmon.
|
||||
|
||||
|
@ -23,6 +27,18 @@ config HWMON_VID
|
|||
tristate
|
||||
default n
|
||||
|
||||
config SENSORS_ABITUGURU
|
||||
tristate "Abit uGuru"
|
||||
depends on HWMON && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the Abit uGuru chips
|
||||
sensor part. The voltage and frequency control parts of the Abit
|
||||
uGuru are not supported. The Abit uGuru chip can be found on Abit
|
||||
uGuru featuring motherboards (most modern Abit motherboards).
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called abituguru.
|
||||
|
||||
config SENSORS_ADM1021
|
||||
tristate "Analog Devices ADM1021 and compatibles"
|
||||
depends on HWMON && I2C
|
||||
|
@ -188,6 +204,16 @@ config SENSORS_LM63
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called lm63.
|
||||
|
||||
config SENSORS_LM70
|
||||
tristate "National Semiconductor LM70"
|
||||
depends on HWMON && SPI_MASTER && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the National Semiconductor
|
||||
LM70 digital temperature sensor chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm70.
|
||||
|
||||
config SENSORS_LM75
|
||||
tristate "National Semiconductor LM75 and compatibles"
|
||||
depends on HWMON && I2C
|
||||
|
@ -236,11 +262,11 @@ config SENSORS_LM80
|
|||
will be called lm80.
|
||||
|
||||
config SENSORS_LM83
|
||||
tristate "National Semiconductor LM83"
|
||||
tristate "National Semiconductor LM83 and compatibles"
|
||||
depends on HWMON && I2C
|
||||
help
|
||||
If you say yes here you get support for National Semiconductor
|
||||
LM83 sensor chips.
|
||||
LM82 and LM83 sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm83.
|
||||
|
@ -333,11 +359,32 @@ config SENSORS_SMSC47M1
|
|||
help
|
||||
If you say yes here you get support for the integrated fan
|
||||
monitoring and control capabilities of the SMSC LPC47B27x,
|
||||
LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x and LPC47M192 chips.
|
||||
LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x, LPC47M192 and
|
||||
LPC47M997 chips.
|
||||
|
||||
The temperature and voltage sensor features of the LPC47M192
|
||||
and LPC47M997 are supported by another driver, select also
|
||||
"SMSC LPC47M192 and compatibles" below for those.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called smsc47m1.
|
||||
|
||||
config SENSORS_SMSC47M192
|
||||
tristate "SMSC LPC47M192 and compatibles"
|
||||
depends on HWMON && I2C && EXPERIMENTAL
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for the temperature and
|
||||
voltage sensors of the SMSC LPC47M192 and LPC47M997 chips.
|
||||
|
||||
The fan monitoring and control capabilities of these chips
|
||||
are supported by another driver, select
|
||||
"SMSC LPC47M10x and compatibles" above. You need both drivers
|
||||
if you want fan control and voltage/temperature sensor support.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called smsc47m192.
|
||||
|
||||
config SENSORS_SMSC47B397
|
||||
tristate "SMSC LPC47B397-NC"
|
||||
depends on HWMON && I2C && EXPERIMENTAL
|
||||
|
@ -385,6 +432,16 @@ config SENSORS_W83781D
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called w83781d.
|
||||
|
||||
config SENSORS_W83791D
|
||||
tristate "Winbond W83791D"
|
||||
depends on HWMON && I2C && EXPERIMENTAL
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for the Winbond W83791D chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called w83791d.
|
||||
|
||||
config SENSORS_W83792D
|
||||
tristate "Winbond W83792D"
|
||||
depends on HWMON && I2C && EXPERIMENTAL
|
||||
|
|
|
@ -10,7 +10,9 @@ obj-$(CONFIG_SENSORS_ASB100) += asb100.o
|
|||
obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
|
||||
obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
|
||||
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
|
||||
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
||||
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
||||
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
|
||||
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
|
||||
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
|
||||
|
@ -26,6 +28,7 @@ obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
|
|||
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
|
||||
obj-$(CONFIG_SENSORS_IT87) += it87.o
|
||||
obj-$(CONFIG_SENSORS_LM63) += lm63.o
|
||||
obj-$(CONFIG_SENSORS_LM70) += lm70.o
|
||||
obj-$(CONFIG_SENSORS_LM75) += lm75.o
|
||||
obj-$(CONFIG_SENSORS_LM77) += lm77.o
|
||||
obj-$(CONFIG_SENSORS_LM78) += lm78.o
|
||||
|
@ -40,6 +43,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
|
|||
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
|
||||
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
|
||||
obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
|
||||
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -99,10 +99,6 @@ superio_exit(int base)
|
|||
#define ADDR_REG_OFFSET 0
|
||||
#define DATA_REG_OFFSET 1
|
||||
|
||||
static struct resource f71805f_resource __initdata = {
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
|
||||
/*
|
||||
* Registers
|
||||
*/
|
||||
|
@ -782,6 +778,11 @@ static struct platform_driver f71805f_driver = {
|
|||
|
||||
static int __init f71805f_device_add(unsigned short address)
|
||||
{
|
||||
struct resource res = {
|
||||
.start = address,
|
||||
.end = address + REGION_LENGTH - 1,
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
int err;
|
||||
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
|
@ -791,10 +792,8 @@ static int __init f71805f_device_add(unsigned short address)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
f71805f_resource.start = address;
|
||||
f71805f_resource.end = address + REGION_LENGTH - 1;
|
||||
f71805f_resource.name = pdev->name;
|
||||
err = platform_device_add_resources(pdev, &f71805f_resource, 1);
|
||||
res.name = pdev->name;
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed "
|
||||
"(%d)\n", err);
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#define HDAPS_PORT_STATE 0x1611 /* device state */
|
||||
#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */
|
||||
#define HDAPS_PORT_XPOS 0x1614 /* x-axis position */
|
||||
#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in celcius */
|
||||
#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */
|
||||
#define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */
|
||||
#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */
|
||||
#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */
|
||||
|
@ -522,13 +522,15 @@ static int __init hdaps_init(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
/* Note that DMI_MATCH(...,"ThinkPad T42") will match "ThinkPad T42p" */
|
||||
/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
|
||||
"ThinkPad T42p", so the order of the entries matters */
|
||||
struct dmi_system_id hdaps_whitelist[] = {
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"),
|
||||
HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), /* R52 (1846AQG) */
|
||||
HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"),
|
||||
HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"),
|
||||
|
@ -536,9 +538,9 @@ static int __init hdaps_init(void)
|
|||
HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"),
|
||||
HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad X41 Tablet"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"),
|
||||
HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad Z60m"),
|
||||
{ .ident = NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -58,11 +58,20 @@
|
|||
doesn't seem to be any named specification for these. The conversion
|
||||
tables are detailed directly in the various Pentium M datasheets:
|
||||
http://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm
|
||||
|
||||
The 14 specification corresponds to Intel Core series. There
|
||||
doesn't seem to be any named specification for these. The conversion
|
||||
tables are detailed directly in the various Pentium Core datasheets:
|
||||
http://www.intel.com/design/mobile/datashts/309221.htm
|
||||
|
||||
The 110 (VRM 11) specification corresponds to Intel Conroe based series.
|
||||
http://www.intel.com/design/processor/applnots/313214.htm
|
||||
*/
|
||||
|
||||
/* vrm is the VRM/VRD document version multiplied by 10.
|
||||
val is the 4-, 5- or 6-bit VID code.
|
||||
Returned value is in mV to avoid floating point in the kernel. */
|
||||
val is the 4-bit or more VID code.
|
||||
Returned value is in mV to avoid floating point in the kernel.
|
||||
Some VID have some bits in uV scale, this is rounded to mV */
|
||||
int vid_from_reg(int val, u8 vrm)
|
||||
{
|
||||
int vid;
|
||||
|
@ -70,26 +79,36 @@ int vid_from_reg(int val, u8 vrm)
|
|||
switch(vrm) {
|
||||
|
||||
case 100: /* VRD 10.0 */
|
||||
/* compute in uV, round to mV */
|
||||
val &= 0x3f;
|
||||
if((val & 0x1f) == 0x1f)
|
||||
return 0;
|
||||
if((val & 0x1f) <= 0x09 || val == 0x0a)
|
||||
vid = 10875 - (val & 0x1f) * 250;
|
||||
vid = 1087500 - (val & 0x1f) * 25000;
|
||||
else
|
||||
vid = 18625 - (val & 0x1f) * 250;
|
||||
vid = 1862500 - (val & 0x1f) * 25000;
|
||||
if(val & 0x20)
|
||||
vid -= 125;
|
||||
vid /= 10; /* only return 3 dec. places for now */
|
||||
return vid;
|
||||
vid -= 12500;
|
||||
return((vid + 500) / 1000);
|
||||
|
||||
case 110: /* Intel Conroe */
|
||||
/* compute in uV, round to mV */
|
||||
val &= 0xff;
|
||||
if(((val & 0x7e) == 0xfe) || (!(val & 0x7e)))
|
||||
return 0;
|
||||
return((1600000 - (val - 2) * 6250 + 500) / 1000);
|
||||
case 24: /* Opteron processor */
|
||||
val &= 0x1f;
|
||||
return(val == 0x1f ? 0 : 1550 - val * 25);
|
||||
|
||||
case 91: /* VRM 9.1 */
|
||||
case 90: /* VRM 9.0 */
|
||||
val &= 0x1f;
|
||||
return(val == 0x1f ? 0 :
|
||||
1850 - val * 25);
|
||||
|
||||
case 85: /* VRM 8.5 */
|
||||
val &= 0x1f;
|
||||
return((val & 0x10 ? 25 : 0) +
|
||||
((val & 0x0f) > 0x04 ? 2050 : 1250) -
|
||||
((val & 0x0f) * 50));
|
||||
|
@ -98,14 +117,21 @@ int vid_from_reg(int val, u8 vrm)
|
|||
val &= 0x0f;
|
||||
/* fall through */
|
||||
case 82: /* VRM 8.2 */
|
||||
val &= 0x1f;
|
||||
return(val == 0x1f ? 0 :
|
||||
val & 0x10 ? 5100 - (val) * 100 :
|
||||
2050 - (val) * 50);
|
||||
case 17: /* Intel IMVP-II */
|
||||
val &= 0x1f;
|
||||
return(val & 0x10 ? 975 - (val & 0xF) * 25 :
|
||||
1750 - val * 50);
|
||||
case 13:
|
||||
return(1708 - (val & 0x3f) * 16);
|
||||
val &= 0x3f;
|
||||
return(1708 - val * 16);
|
||||
case 14: /* Intel Core */
|
||||
/* compute in uV, round to mV */
|
||||
val &= 0x7f;
|
||||
return(val > 0x77 ? 0 : (1500000 - (val * 12500) + 500) / 1000);
|
||||
default: /* report 0 for unknown */
|
||||
printk(KERN_INFO "hwmon-vid: requested unknown VRM version\n");
|
||||
return 0;
|
||||
|
@ -138,6 +164,8 @@ static struct vrm_model vrm_models[] = {
|
|||
{X86_VENDOR_INTEL, 0x6, 0x9, ANY, 13}, /* Pentium M (130 nm) */
|
||||
{X86_VENDOR_INTEL, 0x6, 0xB, ANY, 85}, /* Tualatin */
|
||||
{X86_VENDOR_INTEL, 0x6, 0xD, ANY, 13}, /* Pentium M (90 nm) */
|
||||
{X86_VENDOR_INTEL, 0x6, 0xE, ANY, 14}, /* Intel Core (65 nm) */
|
||||
{X86_VENDOR_INTEL, 0x6, 0xF, ANY, 110}, /* Intel Conroe */
|
||||
{X86_VENDOR_INTEL, 0x6, ANY, ANY, 82}, /* any P6 */
|
||||
{X86_VENDOR_INTEL, 0x7, ANY, ANY, 0}, /* Itanium */
|
||||
{X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90}, /* P4 */
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* lm70.c
|
||||
*
|
||||
* The LM70 is a temperature sensor chip from National Semiconductor (NS).
|
||||
* Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com>
|
||||
*
|
||||
* The LM70 communicates with a host processor via an SPI/Microwire Bus
|
||||
* interface. The complete datasheet is available at National's website
|
||||
* here:
|
||||
* http://www.national.com/pf/LM/LM70.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
#define DRVNAME "lm70"
|
||||
|
||||
struct lm70 {
|
||||
struct class_device *cdev;
|
||||
struct semaphore sem;
|
||||
};
|
||||
|
||||
/* sysfs hook function */
|
||||
static ssize_t lm70_sense_temp(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
int status, val;
|
||||
u8 rxbuf[2];
|
||||
s16 raw=0;
|
||||
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
|
||||
|
||||
if (down_interruptible(&p_lm70->sem))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/*
|
||||
* spi_read() requires a DMA-safe buffer; so we use
|
||||
* spi_write_then_read(), transmitting 0 bytes.
|
||||
*/
|
||||
status = spi_write_then_read(spi, NULL, 0, &rxbuf[0], 2);
|
||||
if (status < 0) {
|
||||
printk(KERN_WARNING
|
||||
"spi_write_then_read failed with status %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
dev_dbg(dev, "rxbuf[1] : 0x%x rxbuf[0] : 0x%x\n", rxbuf[1], rxbuf[0]);
|
||||
|
||||
raw = (rxbuf[1] << 8) + rxbuf[0];
|
||||
dev_dbg(dev, "raw=0x%x\n", raw);
|
||||
|
||||
/*
|
||||
* The "raw" temperature read into rxbuf[] is a 16-bit signed 2's
|
||||
* complement value. Only the MSB 11 bits (1 sign + 10 temperature
|
||||
* bits) are meaningful; the LSB 5 bits are to be discarded.
|
||||
* See the datasheet.
|
||||
*
|
||||
* Further, each bit represents 0.25 degrees Celsius; so, multiply
|
||||
* by 0.25. Also multiply by 1000 to represent in millidegrees
|
||||
* Celsius.
|
||||
* So it's equivalent to multiplying by 0.25 * 1000 = 250.
|
||||
*/
|
||||
val = ((int)raw/32) * 250;
|
||||
status = sprintf(buf, "%+d\n", val); /* millidegrees Celsius */
|
||||
out:
|
||||
up(&p_lm70->sem);
|
||||
return status;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int __devinit lm70_probe(struct spi_device *spi)
|
||||
{
|
||||
struct lm70 *p_lm70;
|
||||
int status;
|
||||
|
||||
p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL);
|
||||
if (!p_lm70)
|
||||
return -ENOMEM;
|
||||
|
||||
init_MUTEX(&p_lm70->sem);
|
||||
|
||||
/* sysfs hook */
|
||||
p_lm70->cdev = hwmon_device_register(&spi->dev);
|
||||
if (IS_ERR(p_lm70->cdev)) {
|
||||
dev_dbg(&spi->dev, "hwmon_device_register failed.\n");
|
||||
status = PTR_ERR(p_lm70->cdev);
|
||||
goto out_dev_reg_failed;
|
||||
}
|
||||
dev_set_drvdata(&spi->dev, p_lm70);
|
||||
|
||||
if ((status = device_create_file(&spi->dev, &dev_attr_temp1_input))) {
|
||||
dev_dbg(&spi->dev, "device_create_file failure.\n");
|
||||
goto out_dev_create_file_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_dev_create_file_failed:
|
||||
hwmon_device_unregister(p_lm70->cdev);
|
||||
out_dev_reg_failed:
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(p_lm70);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit lm70_remove(struct spi_device *spi)
|
||||
{
|
||||
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
|
||||
|
||||
device_remove_file(&spi->dev, &dev_attr_temp1_input);
|
||||
hwmon_device_unregister(p_lm70->cdev);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(p_lm70);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver lm70_driver = {
|
||||
.driver = {
|
||||
.name = "lm70",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = lm70_probe,
|
||||
.remove = __devexit_p(lm70_remove),
|
||||
};
|
||||
|
||||
static int __init init_lm70(void)
|
||||
{
|
||||
return spi_register_driver(&lm70_driver);
|
||||
}
|
||||
|
||||
static void __exit cleanup_lm70(void)
|
||||
{
|
||||
spi_unregister_driver(&lm70_driver);
|
||||
}
|
||||
|
||||
module_init(init_lm70);
|
||||
module_exit(cleanup_lm70);
|
||||
|
||||
MODULE_AUTHOR("Kaiwan N Billimoria");
|
||||
MODULE_DESCRIPTION("National Semiconductor LM70 Linux driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -12,6 +12,10 @@
|
|||
* Since the datasheet omits to give the chip stepping code, I give it
|
||||
* here: 0x03 (at register 0xff).
|
||||
*
|
||||
* Also supports the LM82 temp sensor, which is basically a stripped down
|
||||
* model of the LM83. Datasheet is here:
|
||||
* http://www.national.com/pf/LM/LM82.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
|
@ -52,7 +56,7 @@ static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
|
|||
* Insmod parameters
|
||||
*/
|
||||
|
||||
I2C_CLIENT_INSMOD_1(lm83);
|
||||
I2C_CLIENT_INSMOD_2(lm83, lm82);
|
||||
|
||||
/*
|
||||
* The LM83 registers
|
||||
|
@ -283,6 +287,9 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
|
|||
if (man_id == 0x01) { /* National Semiconductor */
|
||||
if (chip_id == 0x03) {
|
||||
kind = lm83;
|
||||
} else
|
||||
if (chip_id == 0x01) {
|
||||
kind = lm82;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,6 +303,9 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
|
|||
|
||||
if (kind == lm83) {
|
||||
name = "lm83";
|
||||
} else
|
||||
if (kind == lm82) {
|
||||
name = "lm82";
|
||||
}
|
||||
|
||||
/* We can fill in the remaining client fields */
|
||||
|
@ -319,32 +329,46 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
|
|||
goto exit_detach;
|
||||
}
|
||||
|
||||
/*
|
||||
* The LM82 can only monitor one external diode which is
|
||||
* at the same register as the LM83 temp3 entry - so we
|
||||
* declare 1 and 3 common, and then 2 and 4 only for the LM83.
|
||||
*/
|
||||
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp1_input.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp2_input.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp3_input.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp4_input.dev_attr);
|
||||
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp1_max.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp2_max.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp3_max.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp4_max.dev_attr);
|
||||
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp4_crit.dev_attr);
|
||||
|
||||
device_create_file(&new_client->dev, &dev_attr_alarms);
|
||||
|
||||
if (kind == lm83) {
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp2_input.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp4_input.dev_attr);
|
||||
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp2_max.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp4_max.dev_attr);
|
||||
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr);
|
||||
device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp4_crit.dev_attr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_detach:
|
||||
|
|
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
smsc47m192.c - Support for hardware monitoring block of
|
||||
SMSC LPC47M192 and LPC47M997 Super I/O chips
|
||||
|
||||
Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de>
|
||||
|
||||
Derived from lm78.c and other chip drivers.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
|
||||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(smsc47m192);
|
||||
|
||||
/* SMSC47M192 registers */
|
||||
#define SMSC47M192_REG_IN(nr) ((nr)<6 ? (0x20 + (nr)) : \
|
||||
(0x50 + (nr) - 6))
|
||||
#define SMSC47M192_REG_IN_MAX(nr) ((nr)<6 ? (0x2b + (nr) * 2) : \
|
||||
(0x54 + (((nr) - 6) * 2)))
|
||||
#define SMSC47M192_REG_IN_MIN(nr) ((nr)<6 ? (0x2c + (nr) * 2) : \
|
||||
(0x55 + (((nr) - 6) * 2)))
|
||||
static u8 SMSC47M192_REG_TEMP[3] = { 0x27, 0x26, 0x52 };
|
||||
static u8 SMSC47M192_REG_TEMP_MAX[3] = { 0x39, 0x37, 0x58 };
|
||||
static u8 SMSC47M192_REG_TEMP_MIN[3] = { 0x3A, 0x38, 0x59 };
|
||||
#define SMSC47M192_REG_TEMP_OFFSET(nr) ((nr)==2 ? 0x1e : 0x1f)
|
||||
#define SMSC47M192_REG_ALARM1 0x41
|
||||
#define SMSC47M192_REG_ALARM2 0x42
|
||||
#define SMSC47M192_REG_VID 0x47
|
||||
#define SMSC47M192_REG_VID4 0x49
|
||||
#define SMSC47M192_REG_CONFIG 0x40
|
||||
#define SMSC47M192_REG_SFR 0x4f
|
||||
#define SMSC47M192_REG_COMPANY_ID 0x3e
|
||||
#define SMSC47M192_REG_VERSION 0x3f
|
||||
|
||||
/* generalised scaling with integer rounding */
|
||||
static inline int SCALE(long val, int mul, int div)
|
||||
{
|
||||
if (val < 0)
|
||||
return (val * mul - div / 2) / div;
|
||||
else
|
||||
return (val * mul + div / 2) / div;
|
||||
}
|
||||
|
||||
/* Conversions */
|
||||
|
||||
/* smsc47m192 internally scales voltage measurements */
|
||||
static const u16 nom_mv[] = { 2500, 2250, 3300, 5000, 12000, 3300, 1500, 1800 };
|
||||
|
||||
static inline unsigned int IN_FROM_REG(u8 reg, int n)
|
||||
{
|
||||
return SCALE(reg, nom_mv[n], 192);
|
||||
}
|
||||
|
||||
static inline u8 IN_TO_REG(unsigned long val, int n)
|
||||
{
|
||||
return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255);
|
||||
}
|
||||
|
||||
/* TEMP: 0.001 degC units (-128C to +127C)
|
||||
REG: 1C/bit, two's complement */
|
||||
static inline s8 TEMP_TO_REG(int val)
|
||||
{
|
||||
return SENSORS_LIMIT(SCALE(val, 1, 1000), -128000, 127000);
|
||||
}
|
||||
|
||||
static inline int TEMP_FROM_REG(s8 val)
|
||||
{
|
||||
return val * 1000;
|
||||
}
|
||||
|
||||
struct smsc47m192_data {
|
||||
struct i2c_client client;
|
||||
struct class_device *class_dev;
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 in[8]; /* Register value */
|
||||
u8 in_max[8]; /* Register value */
|
||||
u8 in_min[8]; /* Register value */
|
||||
s8 temp[3]; /* Register value */
|
||||
s8 temp_max[3]; /* Register value */
|
||||
s8 temp_min[3]; /* Register value */
|
||||
s8 temp_offset[3]; /* Register value */
|
||||
u16 alarms; /* Register encoding, combined */
|
||||
u8 vid; /* Register encoding, combined */
|
||||
u8 vrm;
|
||||
};
|
||||
|
||||
static int smsc47m192_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int smsc47m192_detect(struct i2c_adapter *adapter, int address,
|
||||
int kind);
|
||||
static int smsc47m192_detach_client(struct i2c_client *client);
|
||||
static struct smsc47m192_data *smsc47m192_update_device(struct device *dev);
|
||||
|
||||
static struct i2c_driver smsc47m192_driver = {
|
||||
.driver = {
|
||||
.name = "smsc47m192",
|
||||
},
|
||||
.attach_adapter = smsc47m192_attach_adapter,
|
||||
.detach_client = smsc47m192_detach_client,
|
||||
};
|
||||
|
||||
/* Voltages */
|
||||
static ssize_t show_in(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr));
|
||||
}
|
||||
|
||||
static ssize_t show_in_min(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr));
|
||||
}
|
||||
|
||||
static ssize_t show_in_max(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr));
|
||||
}
|
||||
|
||||
static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
data->in_min[nr] = IN_TO_REG(val, nr);
|
||||
i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MIN(nr),
|
||||
data->in_min[nr]);
|
||||
up(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
data->in_max[nr] = IN_TO_REG(val, nr);
|
||||
i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MAX(nr),
|
||||
data->in_max[nr]);
|
||||
up(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
#define show_in_offset(offset) \
|
||||
static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
|
||||
show_in, NULL, offset); \
|
||||
static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_in_min, set_in_min, offset); \
|
||||
static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
|
||||
show_in_max, set_in_max, offset);
|
||||
|
||||
show_in_offset(0)
|
||||
show_in_offset(1)
|
||||
show_in_offset(2)
|
||||
show_in_offset(3)
|
||||
show_in_offset(4)
|
||||
show_in_offset(5)
|
||||
show_in_offset(6)
|
||||
show_in_offset(7)
|
||||
|
||||
/* Temperatures */
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
|
||||
}
|
||||
|
||||
static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
data->temp_min[nr] = TEMP_TO_REG(val);
|
||||
i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MIN[nr],
|
||||
data->temp_min[nr]);
|
||||
up(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
data->temp_max[nr] = TEMP_TO_REG(val);
|
||||
i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MAX[nr],
|
||||
data->temp_max[nr]);
|
||||
up(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_temp_offset(struct device *dev, struct device_attribute
|
||||
*attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_offset[nr]));
|
||||
}
|
||||
|
||||
static ssize_t set_temp_offset(struct device *dev, struct device_attribute
|
||||
*attr, const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
data->temp_offset[nr] = TEMP_TO_REG(val);
|
||||
if (nr>1)
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]);
|
||||
else if (data->temp_offset[nr] != 0) {
|
||||
/* offset[0] and offset[1] share the same register,
|
||||
SFR bit 4 activates offset[0] */
|
||||
i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR,
|
||||
(sfr & 0xef) | (nr==0 ? 0x10 : 0));
|
||||
data->temp_offset[1-nr] = 0;
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]);
|
||||
} else if ((sfr & 0x10) == (nr==0 ? 0x10 : 0))
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_OFFSET(nr), 0);
|
||||
up(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
#define show_temp_index(index) \
|
||||
static SENSOR_DEVICE_ATTR(temp##index##_input, S_IRUGO, \
|
||||
show_temp, NULL, index-1); \
|
||||
static SENSOR_DEVICE_ATTR(temp##index##_min, S_IRUGO | S_IWUSR, \
|
||||
show_temp_min, set_temp_min, index-1); \
|
||||
static SENSOR_DEVICE_ATTR(temp##index##_max, S_IRUGO | S_IWUSR, \
|
||||
show_temp_max, set_temp_max, index-1); \
|
||||
static SENSOR_DEVICE_ATTR(temp##index##_offset, S_IRUGO | S_IWUSR, \
|
||||
show_temp_offset, set_temp_offset, index-1);
|
||||
|
||||
show_temp_index(1)
|
||||
show_temp_index(2)
|
||||
show_temp_index(3)
|
||||
|
||||
/* VID */
|
||||
static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
|
||||
}
|
||||
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
|
||||
|
||||
static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->vrm);
|
||||
}
|
||||
|
||||
static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
data->vrm = simple_strtoul(buf, NULL, 10);
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
|
||||
|
||||
/* Alarms */
|
||||
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct smsc47m192_data *data = smsc47m192_update_device(dev);
|
||||
return sprintf(buf, "%u\n", (data->alarms & nr) ? 1 : 0);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 0x0010);
|
||||
static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 0x0020);
|
||||
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 0x0040);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 0x4000);
|
||||
static SENSOR_DEVICE_ATTR(temp3_input_fault, S_IRUGO, show_alarm, NULL, 0x8000);
|
||||
static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0x0001);
|
||||
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 0x0002);
|
||||
static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 0x0004);
|
||||
static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 0x0008);
|
||||
static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 0x0100);
|
||||
static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 0x0200);
|
||||
static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 0x0400);
|
||||
static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 0x0800);
|
||||
|
||||
/* This function is called when:
|
||||
* smsc47m192_driver is inserted (when this module is loaded), for each
|
||||
available adapter
|
||||
* when a new adapter is inserted (and smsc47m192_driver is still present) */
|
||||
static int smsc47m192_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
if (!(adapter->class & I2C_CLASS_HWMON))
|
||||
return 0;
|
||||
return i2c_probe(adapter, &addr_data, smsc47m192_detect);
|
||||
}
|
||||
|
||||
static void smsc47m192_init_client(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
u8 config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG);
|
||||
u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR);
|
||||
|
||||
/* select cycle mode (pause 1 sec between updates) */
|
||||
i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR,
|
||||
(sfr & 0xfd) | 0x02);
|
||||
if (!(config & 0x01)) {
|
||||
/* initialize alarm limits */
|
||||
for (i=0; i<8; i++) {
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_IN_MIN(i), 0);
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_IN_MAX(i), 0xff);
|
||||
}
|
||||
for (i=0; i<3; i++) {
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_MIN[i], 0x80);
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_MAX[i], 0x7f);
|
||||
}
|
||||
|
||||
/* start monitoring */
|
||||
i2c_smbus_write_byte_data(client, SMSC47M192_REG_CONFIG,
|
||||
(config & 0xf7) | 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called by i2c_probe */
|
||||
static int smsc47m192_detect(struct i2c_adapter *adapter, int address,
|
||||
int kind)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
struct smsc47m192_data *data;
|
||||
int err = 0;
|
||||
int version, config;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
goto exit;
|
||||
|
||||
if (!(data = kzalloc(sizeof(struct smsc47m192_data), GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
client = &data->client;
|
||||
i2c_set_clientdata(client, data);
|
||||
client->addr = address;
|
||||
client->adapter = adapter;
|
||||
client->driver = &smsc47m192_driver;
|
||||
|
||||
if (kind == 0)
|
||||
kind = smsc47m192;
|
||||
|
||||
/* Detection criteria from sensors_detect script */
|
||||
if (kind < 0) {
|
||||
if (i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_COMPANY_ID) == 0x55
|
||||
&& ((version = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_VERSION)) & 0xf0) == 0x20
|
||||
&& (i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_VID) & 0x70) == 0x00
|
||||
&& (i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_VID4) & 0xfe) == 0x80) {
|
||||
dev_info(&adapter->dev,
|
||||
"found SMSC47M192 or SMSC47M997, "
|
||||
"version 2, stepping A%d\n", version & 0x0f);
|
||||
} else {
|
||||
dev_dbg(&adapter->dev,
|
||||
"SMSC47M192 detection failed at 0x%02x\n",
|
||||
address);
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in the remaining client fields and put into the global list */
|
||||
strlcpy(client->name, "smsc47m192", I2C_NAME_SIZE);
|
||||
data->vrm = vid_which_vrm();
|
||||
init_MUTEX(&data->update_lock);
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(client)))
|
||||
goto exit_free;
|
||||
|
||||
/* Initialize the SMSC47M192 chip */
|
||||
smsc47m192_init_client(client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
data->class_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->class_dev)) {
|
||||
err = PTR_ERR(data->class_dev);
|
||||
goto exit_detach;
|
||||
}
|
||||
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in0_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in0_min.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in0_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in0_alarm.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in1_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in1_min.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in1_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in1_alarm.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in2_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in2_min.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in2_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in2_alarm.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in3_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in3_min.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in3_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in3_alarm.dev_attr);
|
||||
|
||||
/* Pin 110 is either in4 (+12V) or VID4 */
|
||||
config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG);
|
||||
if (!(config & 0x20)) {
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_in4_input.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_in4_min.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_in4_max.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_in4_alarm.dev_attr);
|
||||
}
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in5_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in5_min.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in5_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in5_alarm.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in6_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in6_min.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in6_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in6_alarm.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in7_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in7_min.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in7_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_in7_alarm.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp1_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp1_min.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_temp1_offset.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp1_alarm.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp2_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp2_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp2_min.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp2_alarm.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_temp2_input_fault.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp3_input.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp3_max.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp3_min.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_temp3_offset.dev_attr);
|
||||
device_create_file(&client->dev, &sensor_dev_attr_temp3_alarm.dev_attr);
|
||||
device_create_file(&client->dev,
|
||||
&sensor_dev_attr_temp3_input_fault.dev_attr);
|
||||
device_create_file(&client->dev, &dev_attr_cpu0_vid);
|
||||
device_create_file(&client->dev, &dev_attr_vrm);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_detach:
|
||||
i2c_detach_client(client);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smsc47m192_detach_client(struct i2c_client *client)
|
||||
{
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
int err;
|
||||
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
|
||||
if ((err = i2c_detach_client(client)))
|
||||
return err;
|
||||
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct smsc47m192_data *smsc47m192_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
int i, config;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR);
|
||||
|
||||
dev_dbg(&client->dev, "Starting smsc47m192 update\n");
|
||||
|
||||
for (i = 0; i <= 7; i++) {
|
||||
data->in[i] = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_IN(i));
|
||||
data->in_min[i] = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_IN_MIN(i));
|
||||
data->in_max[i] = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_IN_MAX(i));
|
||||
}
|
||||
for (i = 0; i < 3; i++) {
|
||||
data->temp[i] = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_TEMP[i]);
|
||||
data->temp_max[i] = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_MAX[i]);
|
||||
data->temp_min[i] = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_MIN[i]);
|
||||
}
|
||||
for (i = 1; i < 3; i++)
|
||||
data->temp_offset[i] = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_OFFSET(i));
|
||||
/* first offset is temp_offset[0] if SFR bit 4 is set,
|
||||
temp_offset[1] otherwise */
|
||||
if (sfr & 0x10) {
|
||||
data->temp_offset[0] = data->temp_offset[1];
|
||||
data->temp_offset[1] = 0;
|
||||
} else
|
||||
data->temp_offset[0] = 0;
|
||||
|
||||
data->vid = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID)
|
||||
& 0x0f;
|
||||
config = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_CONFIG);
|
||||
if (config & 0x20)
|
||||
data->vid |= (i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_VID4) & 0x01) << 4;
|
||||
data->alarms = i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_ALARM1) |
|
||||
(i2c_smbus_read_byte_data(client,
|
||||
SMSC47M192_REG_ALARM2) << 8);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int __init smsc47m192_init(void)
|
||||
{
|
||||
return i2c_add_driver(&smsc47m192_driver);
|
||||
}
|
||||
|
||||
static void __exit smsc47m192_exit(void)
|
||||
{
|
||||
i2c_del_driver(&smsc47m192_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Hartmut Rick <linux@rick.claranet.de>");
|
||||
MODULE_DESCRIPTION("SMSC47M192 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(smsc47m192_init);
|
||||
module_exit(smsc47m192_exit);
|
|
@ -30,10 +30,7 @@
|
|||
Supports the following chips:
|
||||
|
||||
Chip #vin #fan #pwm #temp chip_id man_id
|
||||
w83627ehf - 5 - 3 0x88 0x5ca3
|
||||
|
||||
This is a preliminary version of the driver, only supporting the
|
||||
fan and temperature inputs. The chip does much more than that.
|
||||
w83627ehf 10 5 - 3 0x88 0x5ca3
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -121,6 +118,14 @@ superio_exit(void)
|
|||
static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
|
||||
static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
|
||||
|
||||
/* The W83627EHF registers for nr=7,8,9 are in bank 5 */
|
||||
#define W83627EHF_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
|
||||
(0x554 + (((nr) - 7) * 2)))
|
||||
#define W83627EHF_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
|
||||
(0x555 + (((nr) - 7) * 2)))
|
||||
#define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
|
||||
(0x550 + (nr) - 7))
|
||||
|
||||
#define W83627EHF_REG_TEMP1 0x27
|
||||
#define W83627EHF_REG_TEMP1_HYST 0x3a
|
||||
#define W83627EHF_REG_TEMP1_OVER 0x39
|
||||
|
@ -136,6 +141,10 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
|
|||
#define W83627EHF_REG_DIODE 0x59
|
||||
#define W83627EHF_REG_SMI_OVT 0x4C
|
||||
|
||||
#define W83627EHF_REG_ALARM1 0x459
|
||||
#define W83627EHF_REG_ALARM2 0x45A
|
||||
#define W83627EHF_REG_ALARM3 0x45B
|
||||
|
||||
/*
|
||||
* Conversions
|
||||
*/
|
||||
|
@ -172,6 +181,20 @@ temp1_to_reg(int temp)
|
|||
return (temp + 500) / 1000;
|
||||
}
|
||||
|
||||
/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */
|
||||
|
||||
static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 };
|
||||
|
||||
static inline long in_from_reg(u8 reg, u8 nr)
|
||||
{
|
||||
return reg * scale_in[nr];
|
||||
}
|
||||
|
||||
static inline u8 in_to_reg(u32 val, u8 nr)
|
||||
{
|
||||
return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, 255);
|
||||
}
|
||||
|
||||
/*
|
||||
* Data structures and manipulation thereof
|
||||
*/
|
||||
|
@ -186,6 +209,9 @@ struct w83627ehf_data {
|
|||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
/* Register values */
|
||||
u8 in[10]; /* Register value */
|
||||
u8 in_max[10]; /* Register value */
|
||||
u8 in_min[10]; /* Register value */
|
||||
u8 fan[5];
|
||||
u8 fan_min[5];
|
||||
u8 fan_div[5];
|
||||
|
@ -196,6 +222,7 @@ struct w83627ehf_data {
|
|||
s16 temp[2];
|
||||
s16 temp_max[2];
|
||||
s16 temp_max_hyst[2];
|
||||
u32 alarms;
|
||||
};
|
||||
|
||||
static inline int is_word_sized(u16 reg)
|
||||
|
@ -349,6 +376,16 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
|
|||
data->fan_div[3] |= (i >> 5) & 0x04;
|
||||
}
|
||||
|
||||
/* Measured voltages and limits */
|
||||
for (i = 0; i < 10; i++) {
|
||||
data->in[i] = w83627ehf_read_value(client,
|
||||
W83627EHF_REG_IN(i));
|
||||
data->in_min[i] = w83627ehf_read_value(client,
|
||||
W83627EHF_REG_IN_MIN(i));
|
||||
data->in_max[i] = w83627ehf_read_value(client,
|
||||
W83627EHF_REG_IN_MAX(i));
|
||||
}
|
||||
|
||||
/* Measured fan speeds and limits */
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (!(data->has_fan & (1 << i)))
|
||||
|
@ -395,6 +432,13 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
|
|||
W83627EHF_REG_TEMP_HYST[i]);
|
||||
}
|
||||
|
||||
data->alarms = w83627ehf_read_value(client,
|
||||
W83627EHF_REG_ALARM1) |
|
||||
(w83627ehf_read_value(client,
|
||||
W83627EHF_REG_ALARM2) << 8) |
|
||||
(w83627ehf_read_value(client,
|
||||
W83627EHF_REG_ALARM3) << 16);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
@ -406,6 +450,109 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
|
|||
/*
|
||||
* Sysfs callback functions
|
||||
*/
|
||||
#define show_in_reg(reg) \
|
||||
static ssize_t \
|
||||
show_##reg(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||
int nr = sensor_attr->index; \
|
||||
return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \
|
||||
}
|
||||
show_in_reg(in)
|
||||
show_in_reg(in_min)
|
||||
show_in_reg(in_max)
|
||||
|
||||
#define store_in_reg(REG, reg) \
|
||||
static ssize_t \
|
||||
store_in_##reg (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct i2c_client *client = to_i2c_client(dev); \
|
||||
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||
int nr = sensor_attr->index; \
|
||||
u32 val = simple_strtoul(buf, NULL, 10); \
|
||||
\
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->in_##reg[nr] = in_to_reg(val, nr); \
|
||||
w83627ehf_write_value(client, W83627EHF_REG_IN_##REG(nr), \
|
||||
data->in_##reg[nr]); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return count; \
|
||||
}
|
||||
|
||||
store_in_reg(MIN, min)
|
||||
store_in_reg(MAX, max)
|
||||
|
||||
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct w83627ehf_data *data = w83627ehf_update_device(dev);
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
return sprintf(buf, "%u\n", (data->alarms >> nr) & 0x01);
|
||||
}
|
||||
|
||||
static struct sensor_device_attribute sda_in_input[] = {
|
||||
SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
|
||||
SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
|
||||
SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
|
||||
SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
|
||||
SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
|
||||
SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
|
||||
SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
|
||||
SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
|
||||
SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
|
||||
SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9),
|
||||
};
|
||||
|
||||
static struct sensor_device_attribute sda_in_alarm[] = {
|
||||
SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0),
|
||||
SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1),
|
||||
SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2),
|
||||
SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3),
|
||||
SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8),
|
||||
SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 21),
|
||||
SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 20),
|
||||
SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16),
|
||||
SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17),
|
||||
SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 19),
|
||||
};
|
||||
|
||||
static struct sensor_device_attribute sda_in_min[] = {
|
||||
SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
|
||||
SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
|
||||
SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
|
||||
SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
|
||||
SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
|
||||
SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
|
||||
SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
|
||||
SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
|
||||
SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
|
||||
SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9),
|
||||
};
|
||||
|
||||
static struct sensor_device_attribute sda_in_max[] = {
|
||||
SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
|
||||
SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
|
||||
SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
|
||||
SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
|
||||
SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
|
||||
SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
|
||||
SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
|
||||
SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
|
||||
SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
|
||||
SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
|
||||
};
|
||||
|
||||
static void device_create_file_in(struct device *dev, int i)
|
||||
{
|
||||
device_create_file(dev, &sda_in_input[i].dev_attr);
|
||||
device_create_file(dev, &sda_in_alarm[i].dev_attr);
|
||||
device_create_file(dev, &sda_in_min[i].dev_attr);
|
||||
device_create_file(dev, &sda_in_max[i].dev_attr);
|
||||
}
|
||||
|
||||
#define show_fan_reg(reg) \
|
||||
static ssize_t \
|
||||
|
@ -505,6 +652,14 @@ static struct sensor_device_attribute sda_fan_input[] = {
|
|||
SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
|
||||
};
|
||||
|
||||
static struct sensor_device_attribute sda_fan_alarm[] = {
|
||||
SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6),
|
||||
SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7),
|
||||
SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11),
|
||||
SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 10),
|
||||
SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 23),
|
||||
};
|
||||
|
||||
static struct sensor_device_attribute sda_fan_min[] = {
|
||||
SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
|
||||
store_fan_min, 0),
|
||||
|
@ -529,6 +684,7 @@ static struct sensor_device_attribute sda_fan_div[] = {
|
|||
static void device_create_file_fan(struct device *dev, int i)
|
||||
{
|
||||
device_create_file(dev, &sda_fan_input[i].dev_attr);
|
||||
device_create_file(dev, &sda_fan_alarm[i].dev_attr);
|
||||
device_create_file(dev, &sda_fan_div[i].dev_attr);
|
||||
device_create_file(dev, &sda_fan_min[i].dev_attr);
|
||||
}
|
||||
|
@ -616,6 +772,9 @@ static struct sensor_device_attribute sda_temp[] = {
|
|||
store_temp_max_hyst, 0),
|
||||
SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
|
||||
store_temp_max_hyst, 1),
|
||||
SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4),
|
||||
SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5),
|
||||
SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -705,6 +864,9 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
|
|||
goto exit_detach;
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
device_create_file_in(dev, i);
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (data->has_fan & (1 << i))
|
||||
device_create_file_fan(dev, i);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -250,8 +250,6 @@ FAN_TO_REG(long rpm, int div)
|
|||
: (val)) / 1000, 0, 0xff))
|
||||
#define TEMP_ADD_TO_REG_LOW(val) ((val%1000) ? 0x80 : 0x00)
|
||||
|
||||
#define PWM_FROM_REG(val) (val)
|
||||
#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
|
||||
#define DIV_FROM_REG(val) (1 << (val))
|
||||
|
||||
static inline u8
|
||||
|
@ -291,7 +289,6 @@ struct w83792d_data {
|
|||
u8 pwm[7]; /* We only consider the first 3 set of pwm,
|
||||
although 792 chip has 7 set of pwm. */
|
||||
u8 pwmenable[3];
|
||||
u8 pwm_mode[7]; /* indicates PWM or DC mode: 1->PWM; 0->DC */
|
||||
u32 alarms; /* realtime status register encoding,combined */
|
||||
u8 chassis; /* Chassis status */
|
||||
u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */
|
||||
|
@ -375,8 +372,10 @@ static ssize_t store_in_##reg (struct device *dev, \
|
|||
u32 val; \
|
||||
\
|
||||
val = simple_strtoul(buf, NULL, 10); \
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->in_##reg[nr] = SENSORS_LIMIT(IN_TO_REG(nr, val)/4, 0, 255); \
|
||||
w83792d_write_value(client, W83792D_REG_IN_##REG[nr], data->in_##reg[nr]); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
\
|
||||
return count; \
|
||||
}
|
||||
|
@ -443,9 +442,11 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
|
|||
u32 val;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
|
||||
w83792d_write_value(client, W83792D_REG_FAN_MIN[nr],
|
||||
data->fan_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -478,6 +479,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
|
|||
u8 tmp_fan_div;
|
||||
|
||||
/* Save fan_min */
|
||||
mutex_lock(&data->update_lock);
|
||||
min = FAN_FROM_REG(data->fan_min[nr],
|
||||
DIV_FROM_REG(data->fan_div[nr]));
|
||||
|
||||
|
@ -493,6 +495,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
|
|||
/* Restore fan_min */
|
||||
data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
|
||||
w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], data->fan_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -547,10 +550,11 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *attr,
|
|||
s32 val;
|
||||
|
||||
val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp1[nr] = TEMP1_TO_REG(val);
|
||||
w83792d_write_value(client, W83792D_REG_TEMP1[nr],
|
||||
data->temp1[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -580,13 +584,14 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr,
|
|||
s32 val;
|
||||
|
||||
val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_add[nr][index] = TEMP_ADD_TO_REG_HIGH(val);
|
||||
data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val);
|
||||
w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index],
|
||||
data->temp_add[nr][index]);
|
||||
w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index+1],
|
||||
data->temp_add[nr][index+1]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -627,7 +632,7 @@ show_pwm(struct device *dev, struct device_attribute *attr,
|
|||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct w83792d_data *data = w83792d_update_device(dev);
|
||||
return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr-1]));
|
||||
return sprintf(buf, "%d\n", (data->pwm[nr] & 0x0f) << 4);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -659,14 +664,16 @@ store_pwm(struct device *dev, struct device_attribute *attr,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index - 1;
|
||||
int nr = sensor_attr->index;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct w83792d_data *data = i2c_get_clientdata(client);
|
||||
u32 val;
|
||||
u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255) >> 4;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
data->pwm[nr] = PWM_TO_REG(val);
|
||||
mutex_lock(&data->update_lock);
|
||||
val |= w83792d_read_value(client, W83792D_REG_PWM[nr]) & 0xf0;
|
||||
data->pwm[nr] = val;
|
||||
w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -683,6 +690,10 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
|
|||
u8 fan_cfg_tmp, cfg1_tmp, cfg2_tmp, cfg3_tmp, cfg4_tmp;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
if (val < 1 || val > 3)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
switch (val) {
|
||||
case 1:
|
||||
data->pwmenable[nr] = 0; /* manual mode */
|
||||
|
@ -693,8 +704,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
|
|||
case 3:
|
||||
data->pwmenable[nr] = 1; /* thermal cruise/Smart Fan I */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
cfg1_tmp = data->pwmenable[0];
|
||||
cfg2_tmp = (data->pwmenable[1]) << 2;
|
||||
|
@ -702,14 +711,15 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
|
|||
cfg4_tmp = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0;
|
||||
fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp;
|
||||
w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct sensor_device_attribute sda_pwm[] = {
|
||||
SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
|
||||
SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
|
||||
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
|
||||
SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
|
||||
SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
|
||||
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
|
||||
};
|
||||
static struct sensor_device_attribute sda_pwm_enable[] = {
|
||||
SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
|
||||
|
@ -728,7 +738,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr,
|
|||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index;
|
||||
struct w83792d_data *data = w83792d_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->pwm_mode[nr-1]);
|
||||
return sprintf(buf, "%d\n", data->pwm[nr] >> 7);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -736,29 +746,35 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
int nr = sensor_attr->index - 1;
|
||||
int nr = sensor_attr->index;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct w83792d_data *data = i2c_get_clientdata(client);
|
||||
u32 val;
|
||||
u8 pwm_mode_mask = 0;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
data->pwm_mode[nr] = SENSORS_LIMIT(val, 0, 1);
|
||||
pwm_mode_mask = w83792d_read_value(client,
|
||||
W83792D_REG_PWM[nr]) & 0x7f;
|
||||
w83792d_write_value(client, W83792D_REG_PWM[nr],
|
||||
((data->pwm_mode[nr]) << 7) | pwm_mode_mask);
|
||||
if (val != 0 && val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm[nr] = w83792d_read_value(client, W83792D_REG_PWM[nr]);
|
||||
if (val) { /* PWM mode */
|
||||
data->pwm[nr] |= 0x80;
|
||||
} else { /* DC mode */
|
||||
data->pwm[nr] &= 0x7f;
|
||||
}
|
||||
w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct sensor_device_attribute sda_pwm_mode[] = {
|
||||
SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
|
||||
show_pwm_mode, store_pwm_mode, 1),
|
||||
show_pwm_mode, store_pwm_mode, 0),
|
||||
SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
|
||||
show_pwm_mode, store_pwm_mode, 2),
|
||||
show_pwm_mode, store_pwm_mode, 1),
|
||||
SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
|
||||
show_pwm_mode, store_pwm_mode, 3),
|
||||
show_pwm_mode, store_pwm_mode, 2),
|
||||
};
|
||||
|
||||
|
||||
|
@ -789,12 +805,13 @@ store_chassis_clear(struct device *dev, struct device_attribute *attr,
|
|||
u8 temp1 = 0, temp2 = 0;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->chassis_clear = SENSORS_LIMIT(val, 0 ,1);
|
||||
temp1 = ((data->chassis_clear) << 7) & 0x80;
|
||||
temp2 = w83792d_read_value(client,
|
||||
W83792D_REG_CHASSIS_CLR) & 0x7f;
|
||||
w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -827,10 +844,12 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr,
|
|||
val = simple_strtoul(buf, NULL, 10);
|
||||
target_tmp = val;
|
||||
target_tmp = target_tmp & 0x7f;
|
||||
mutex_lock(&data->update_lock);
|
||||
target_mask = w83792d_read_value(client, W83792D_REG_THERMAL[nr]) & 0x80;
|
||||
data->thermal_cruise[nr] = SENSORS_LIMIT(target_tmp, 0, 255);
|
||||
w83792d_write_value(client, W83792D_REG_THERMAL[nr],
|
||||
(data->thermal_cruise[nr]) | target_mask);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -867,6 +886,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
|
|||
u8 tol_tmp, tol_mask;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
mutex_lock(&data->update_lock);
|
||||
tol_mask = w83792d_read_value(client,
|
||||
W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0);
|
||||
tol_tmp = SENSORS_LIMIT(val, 0, 15);
|
||||
|
@ -877,6 +897,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
|
|||
}
|
||||
w83792d_write_value(client, W83792D_REG_TOLERANCE[nr],
|
||||
tol_mask | tol_tmp);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -915,11 +936,13 @@ store_sf2_point(struct device *dev, struct device_attribute *attr,
|
|||
u8 mask_tmp = 0;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->sf2_points[index][nr] = SENSORS_LIMIT(val, 0, 127);
|
||||
mask_tmp = w83792d_read_value(client,
|
||||
W83792D_REG_POINTS[index][nr]) & 0x80;
|
||||
w83792d_write_value(client, W83792D_REG_POINTS[index][nr],
|
||||
mask_tmp|data->sf2_points[index][nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -979,6 +1002,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
|
|||
u8 mask_tmp=0, level_tmp=0;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->sf2_levels[index][nr] = SENSORS_LIMIT((val * 15) / 100, 0, 15);
|
||||
mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr])
|
||||
& ((nr==3) ? 0xf0 : 0x0f);
|
||||
|
@ -988,6 +1012,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
|
|||
level_tmp = data->sf2_levels[index][nr] << 4;
|
||||
}
|
||||
w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], level_tmp | mask_tmp);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -1373,7 +1398,7 @@ static struct w83792d_data *w83792d_update_device(struct device *dev)
|
|||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct w83792d_data *data = i2c_get_clientdata(client);
|
||||
int i, j;
|
||||
u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp;
|
||||
u8 reg_array_tmp[4], reg_tmp;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
|
@ -1402,10 +1427,8 @@ static struct w83792d_data *w83792d_update_device(struct device *dev)
|
|||
data->fan_min[i] = w83792d_read_value(client,
|
||||
W83792D_REG_FAN_MIN[i]);
|
||||
/* Update the PWM/DC Value and PWM/DC flag */
|
||||
pwm_array_tmp[i] = w83792d_read_value(client,
|
||||
data->pwm[i] = w83792d_read_value(client,
|
||||
W83792D_REG_PWM[i]);
|
||||
data->pwm[i] = pwm_array_tmp[i] & 0x0f;
|
||||
data->pwm_mode[i] = pwm_array_tmp[i] >> 7;
|
||||
}
|
||||
|
||||
reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG);
|
||||
|
@ -1513,7 +1536,6 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev)
|
|||
dev_dbg(dev, "fan[%d] is: 0x%x\n", i, data->fan[i]);
|
||||
dev_dbg(dev, "fan[%d] min is: 0x%x\n", i, data->fan_min[i]);
|
||||
dev_dbg(dev, "pwm[%d] is: 0x%x\n", i, data->pwm[i]);
|
||||
dev_dbg(dev, "pwm_mode[%d] is: 0x%x\n", i, data->pwm_mode[i]);
|
||||
}
|
||||
dev_dbg(dev, "3 set of Temperatures: =====>\n");
|
||||
for (i=0; i<3; i++) {
|
||||
|
|
|
@ -163,7 +163,7 @@ config I2C_PXA_SLAVE
|
|||
I2C bus.
|
||||
|
||||
config I2C_PIIX4
|
||||
tristate "Intel PIIX4"
|
||||
tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)"
|
||||
depends on I2C && PCI
|
||||
help
|
||||
If you say yes to this option, support will be included for the Intel
|
||||
|
@ -172,6 +172,9 @@ config I2C_PIIX4
|
|||
of Broadcom):
|
||||
Intel PIIX4
|
||||
Intel 440MX
|
||||
ATI IXP200
|
||||
ATI IXP300
|
||||
ATI IXP400
|
||||
Serverworks OSB4
|
||||
Serverworks CSB5
|
||||
Serverworks CSB6
|
||||
|
@ -273,6 +276,17 @@ config I2C_NFORCE2
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-nforce2.
|
||||
|
||||
config I2C_OCORES
|
||||
tristate "OpenCores I2C Controller"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
OpenCores I2C controller. For details see
|
||||
http://www.opencores.org/projects.cgi/web/i2c/overview
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-ocores.
|
||||
|
||||
config I2C_PARPORT
|
||||
tristate "Parallel port adapter"
|
||||
depends on I2C && PARPORT
|
||||
|
@ -500,6 +514,7 @@ config I2C_PCA_ISA
|
|||
tristate "PCA9564 on an ISA bus"
|
||||
depends on I2C
|
||||
select I2C_ALGOPCA
|
||||
default n
|
||||
help
|
||||
This driver supports ISA boards using the Philips PCA 9564
|
||||
Parallel bus to I2C bus controller
|
||||
|
@ -507,6 +522,11 @@ config I2C_PCA_ISA
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-pca-isa.
|
||||
|
||||
This device is almost undetectable and using this driver on a
|
||||
system which doesn't have this device will result in long
|
||||
delays when I2C/SMBus chip drivers are loaded (e.g. at boot
|
||||
time). If unsure, say N.
|
||||
|
||||
config I2C_MV64XXX
|
||||
tristate "Marvell mv64xxx I2C Controller"
|
||||
depends on I2C && MV64X60 && EXPERIMENTAL
|
||||
|
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
|
|||
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
|
||||
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
|
||||
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
|
||||
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
|
||||
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
|
||||
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
|
||||
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
i801.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
i2c-i801.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
|
||||
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
|
||||
|
@ -36,7 +36,7 @@
|
|||
This driver supports several versions of Intel's I/O Controller Hubs (ICH).
|
||||
For SMBus support, they are similar to the PIIX4 and are part
|
||||
of Intel's '810' and other chipsets.
|
||||
See the doc/busses/i2c-i801 file for details.
|
||||
See the file Documentation/i2c/busses/i2c-i801 for details.
|
||||
I2C Block Read and Process Call are not supported.
|
||||
*/
|
||||
|
||||
|
@ -66,9 +66,8 @@
|
|||
#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */
|
||||
|
||||
/* PCI Address Constants */
|
||||
#define SMBBA 0x020
|
||||
#define SMBBAR 4
|
||||
#define SMBHSTCFG 0x040
|
||||
#define SMBREV 0x008
|
||||
|
||||
/* Host configuration bits for SMBHSTCFG */
|
||||
#define SMBHSTCFG_HST_EN 1
|
||||
|
@ -92,92 +91,16 @@
|
|||
#define I801_START 0x40
|
||||
#define I801_PEC_EN 0x80 /* ICH4 only */
|
||||
|
||||
/* insmod parameters */
|
||||
|
||||
/* If force_addr is set to anything different from 0, we forcibly enable
|
||||
the I801 at the given address. VERY DANGEROUS! */
|
||||
static u16 force_addr;
|
||||
module_param(force_addr, ushort, 0);
|
||||
MODULE_PARM_DESC(force_addr,
|
||||
"Forcibly enable the I801 at the given address. "
|
||||
"EXTREMELY DANGEROUS!");
|
||||
|
||||
static int i801_transaction(void);
|
||||
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
int command, int hwpec);
|
||||
|
||||
static unsigned short i801_smba;
|
||||
static unsigned long i801_smba;
|
||||
static struct pci_driver i801_driver;
|
||||
static struct pci_dev *I801_dev;
|
||||
static int isich4;
|
||||
|
||||
static int i801_setup(struct pci_dev *dev)
|
||||
{
|
||||
int error_return = 0;
|
||||
unsigned char temp;
|
||||
|
||||
/* Note: we keep on searching until we have found 'function 3' */
|
||||
if(PCI_FUNC(dev->devfn) != 3)
|
||||
return -ENODEV;
|
||||
|
||||
I801_dev = dev;
|
||||
if ((dev->device == PCI_DEVICE_ID_INTEL_82801DB_3) ||
|
||||
(dev->device == PCI_DEVICE_ID_INTEL_82801EB_3) ||
|
||||
(dev->device == PCI_DEVICE_ID_INTEL_ESB_4))
|
||||
isich4 = 1;
|
||||
else
|
||||
isich4 = 0;
|
||||
|
||||
/* Determine the address of the SMBus areas */
|
||||
if (force_addr) {
|
||||
i801_smba = force_addr & 0xfff0;
|
||||
} else {
|
||||
pci_read_config_word(I801_dev, SMBBA, &i801_smba);
|
||||
i801_smba &= 0xfff0;
|
||||
if(i801_smba == 0) {
|
||||
dev_err(&dev->dev, "SMB base address uninitialized "
|
||||
"- upgrade BIOS or use force_addr=0xaddr\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (!request_region(i801_smba, (isich4 ? 16 : 8), i801_driver.name)) {
|
||||
dev_err(&dev->dev, "I801_smb region 0x%x already in use!\n",
|
||||
i801_smba);
|
||||
error_return = -EBUSY;
|
||||
goto END;
|
||||
}
|
||||
|
||||
pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
|
||||
temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
|
||||
pci_write_config_byte(I801_dev, SMBHSTCFG, temp);
|
||||
|
||||
/* If force_addr is set, we program the new address here. Just to make
|
||||
sure, we disable the device first. */
|
||||
if (force_addr) {
|
||||
pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe);
|
||||
pci_write_config_word(I801_dev, SMBBA, i801_smba);
|
||||
pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01);
|
||||
dev_warn(&dev->dev, "WARNING: I801 SMBus interface set to "
|
||||
"new address %04x!\n", i801_smba);
|
||||
} else if ((temp & 1) == 0) {
|
||||
pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1);
|
||||
dev_warn(&dev->dev, "enabling SMBus device\n");
|
||||
}
|
||||
|
||||
if (temp & 0x02)
|
||||
dev_dbg(&dev->dev, "I801 using Interrupt SMI# for SMBus.\n");
|
||||
else
|
||||
dev_dbg(&dev->dev, "I801 using PCI Interrupt for SMBus.\n");
|
||||
|
||||
pci_read_config_byte(I801_dev, SMBREV, &temp);
|
||||
dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp);
|
||||
dev_dbg(&dev->dev, "I801_smba = 0x%X\n", i801_smba);
|
||||
|
||||
END:
|
||||
return error_return;
|
||||
}
|
||||
|
||||
static int i801_transaction(void)
|
||||
{
|
||||
int temp;
|
||||
|
@ -334,8 +257,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
|||
/* We will always wait for a fraction of a second! */
|
||||
timeout = 0;
|
||||
do {
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
msleep(1);
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
}
|
||||
while ((!(temp & 0x80))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
|
@ -393,8 +316,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
|||
/* wait for INTR bit as advised by Intel */
|
||||
timeout = 0;
|
||||
do {
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
msleep(1);
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
} while ((!(temp & 0x02))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
|
||||
|
@ -541,25 +464,76 @@ MODULE_DEVICE_TABLE (pci, i801_ids);
|
|||
|
||||
static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
unsigned char temp;
|
||||
int err;
|
||||
|
||||
if (i801_setup(dev)) {
|
||||
dev_warn(&dev->dev,
|
||||
"I801 not detected, module not inserted.\n");
|
||||
return -ENODEV;
|
||||
I801_dev = dev;
|
||||
if ((dev->device == PCI_DEVICE_ID_INTEL_82801DB_3) ||
|
||||
(dev->device == PCI_DEVICE_ID_INTEL_82801EB_3) ||
|
||||
(dev->device == PCI_DEVICE_ID_INTEL_ESB_4))
|
||||
isich4 = 1;
|
||||
else
|
||||
isich4 = 0;
|
||||
|
||||
err = pci_enable_device(dev);
|
||||
if (err) {
|
||||
dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n",
|
||||
err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Determine the address of the SMBus area */
|
||||
i801_smba = pci_resource_start(dev, SMBBAR);
|
||||
if (!i801_smba) {
|
||||
dev_err(&dev->dev, "SMBus base address uninitialized, "
|
||||
"upgrade BIOS\n");
|
||||
err = -ENODEV;
|
||||
goto exit_disable;
|
||||
}
|
||||
|
||||
err = pci_request_region(dev, SMBBAR, i801_driver.name);
|
||||
if (err) {
|
||||
dev_err(&dev->dev, "Failed to request SMBus region "
|
||||
"0x%lx-0x%lx\n", i801_smba,
|
||||
pci_resource_end(dev, SMBBAR));
|
||||
goto exit_disable;
|
||||
}
|
||||
|
||||
pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
|
||||
temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
|
||||
if (!(temp & SMBHSTCFG_HST_EN)) {
|
||||
dev_info(&dev->dev, "Enabling SMBus device\n");
|
||||
temp |= SMBHSTCFG_HST_EN;
|
||||
}
|
||||
pci_write_config_byte(I801_dev, SMBHSTCFG, temp);
|
||||
|
||||
if (temp & SMBHSTCFG_SMB_SMI_EN)
|
||||
dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
|
||||
else
|
||||
dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n");
|
||||
|
||||
/* set up the driverfs linkage to our parent device */
|
||||
i801_adapter.dev.parent = &dev->dev;
|
||||
|
||||
snprintf(i801_adapter.name, I2C_NAME_SIZE,
|
||||
"SMBus I801 adapter at %04x", i801_smba);
|
||||
return i2c_add_adapter(&i801_adapter);
|
||||
"SMBus I801 adapter at %04lx", i801_smba);
|
||||
err = i2c_add_adapter(&i801_adapter);
|
||||
if (err) {
|
||||
dev_err(&dev->dev, "Failed to add SMBus adapter\n");
|
||||
goto exit_disable;
|
||||
}
|
||||
|
||||
exit_disable:
|
||||
pci_disable_device(dev);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit i801_remove(struct pci_dev *dev)
|
||||
{
|
||||
i2c_del_adapter(&i801_adapter);
|
||||
release_region(i801_smba, (isich4 ? 16 : 8));
|
||||
pci_release_region(dev, SMBBAR);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static struct pci_driver i801_driver = {
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
nForce3 250Gb MCP 00E4
|
||||
nForce4 MCP 0052
|
||||
nForce4 MCP-04 0034
|
||||
nForce4 MCP51 0264
|
||||
nForce4 MCP55 0368
|
||||
|
||||
This driver supports the 2 SMBuses that are included in the MCP of the
|
||||
nForce2/3/4 chipsets.
|
||||
|
@ -64,6 +66,7 @@ struct nforce2_smbus {
|
|||
|
||||
/*
|
||||
* nVidia nForce2 SMBus control register definitions
|
||||
* (Newer incarnations use standard BARs 4 and 5 instead)
|
||||
*/
|
||||
#define NFORCE_PCI_SMB1 0x50
|
||||
#define NFORCE_PCI_SMB2 0x54
|
||||
|
@ -259,6 +262,8 @@ static struct pci_device_id nforce2_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS) },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
@ -266,19 +271,29 @@ static struct pci_device_id nforce2_ids[] = {
|
|||
MODULE_DEVICE_TABLE (pci, nforce2_ids);
|
||||
|
||||
|
||||
static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg,
|
||||
struct nforce2_smbus *smbus, char *name)
|
||||
static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar,
|
||||
int alt_reg, struct nforce2_smbus *smbus, const char *name)
|
||||
{
|
||||
u16 iobase;
|
||||
int error;
|
||||
|
||||
if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) {
|
||||
dev_err(&smbus->adapter.dev, "Error reading PCI config for %s\n", name);
|
||||
return -1;
|
||||
smbus->base = pci_resource_start(dev, bar);
|
||||
if (smbus->base) {
|
||||
smbus->size = pci_resource_len(dev, bar);
|
||||
} else {
|
||||
/* Older incarnations of the device used non-standard BARs */
|
||||
u16 iobase;
|
||||
|
||||
if (pci_read_config_word(dev, alt_reg, &iobase)
|
||||
!= PCIBIOS_SUCCESSFUL) {
|
||||
dev_err(&dev->dev, "Error reading PCI config for %s\n",
|
||||
name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK;
|
||||
smbus->size = 8;
|
||||
}
|
||||
smbus->dev = dev;
|
||||
smbus->base = iobase & 0xfffc;
|
||||
smbus->size = 8;
|
||||
smbus->dev = dev;
|
||||
|
||||
if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) {
|
||||
dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n",
|
||||
|
@ -313,12 +328,13 @@ static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_
|
|||
pci_set_drvdata(dev, smbuses);
|
||||
|
||||
/* SMBus adapter 1 */
|
||||
res1 = nforce2_probe_smb (dev, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
|
||||
res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
|
||||
if (res1 < 0) {
|
||||
dev_err(&dev->dev, "Error probing SMB1.\n");
|
||||
smbuses[0].base = 0; /* to have a check value */
|
||||
}
|
||||
res2 = nforce2_probe_smb (dev, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
|
||||
/* SMBus adapter 2 */
|
||||
res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
|
||||
if (res2 < 0) {
|
||||
dev_err(&dev->dev, "Error probing SMB2.\n");
|
||||
smbuses[1].base = 0; /* to have a check value */
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* i2c-ocores.c: I2C bus driver for OpenCores I2C controller
|
||||
* (http://www.opencores.org/projects.cgi/web/i2c/overview).
|
||||
*
|
||||
* Peter Korsgaard <jacmet@sunsite.dk>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/i2c-ocores.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct ocores_i2c {
|
||||
void __iomem *base;
|
||||
int regstep;
|
||||
wait_queue_head_t wait;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *msg;
|
||||
int pos;
|
||||
int nmsgs;
|
||||
int state; /* see STATE_ */
|
||||
};
|
||||
|
||||
/* registers */
|
||||
#define OCI2C_PRELOW 0
|
||||
#define OCI2C_PREHIGH 1
|
||||
#define OCI2C_CONTROL 2
|
||||
#define OCI2C_DATA 3
|
||||
#define OCI2C_CMD 4 /* write only */
|
||||
#define OCI2C_STATUS 4 /* read only, same address as OCI2C_CMD */
|
||||
|
||||
#define OCI2C_CTRL_IEN 0x40
|
||||
#define OCI2C_CTRL_EN 0x80
|
||||
|
||||
#define OCI2C_CMD_START 0x91
|
||||
#define OCI2C_CMD_STOP 0x41
|
||||
#define OCI2C_CMD_READ 0x21
|
||||
#define OCI2C_CMD_WRITE 0x11
|
||||
#define OCI2C_CMD_READ_ACK 0x21
|
||||
#define OCI2C_CMD_READ_NACK 0x29
|
||||
#define OCI2C_CMD_IACK 0x01
|
||||
|
||||
#define OCI2C_STAT_IF 0x01
|
||||
#define OCI2C_STAT_TIP 0x02
|
||||
#define OCI2C_STAT_ARBLOST 0x20
|
||||
#define OCI2C_STAT_BUSY 0x40
|
||||
#define OCI2C_STAT_NACK 0x80
|
||||
|
||||
#define STATE_DONE 0
|
||||
#define STATE_START 1
|
||||
#define STATE_WRITE 2
|
||||
#define STATE_READ 3
|
||||
#define STATE_ERROR 4
|
||||
|
||||
static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)
|
||||
{
|
||||
iowrite8(value, i2c->base + reg * i2c->regstep);
|
||||
}
|
||||
|
||||
static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)
|
||||
{
|
||||
return ioread8(i2c->base + reg * i2c->regstep);
|
||||
}
|
||||
|
||||
static void ocores_process(struct ocores_i2c *i2c)
|
||||
{
|
||||
struct i2c_msg *msg = i2c->msg;
|
||||
u8 stat = oc_getreg(i2c, OCI2C_STATUS);
|
||||
|
||||
if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) {
|
||||
/* stop has been sent */
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
|
||||
wake_up(&i2c->wait);
|
||||
return;
|
||||
}
|
||||
|
||||
/* error? */
|
||||
if (stat & OCI2C_STAT_ARBLOST) {
|
||||
i2c->state = STATE_ERROR;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) {
|
||||
i2c->state =
|
||||
(msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
|
||||
|
||||
if (stat & OCI2C_STAT_NACK) {
|
||||
i2c->state = STATE_ERROR;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA);
|
||||
|
||||
/* end of msg? */
|
||||
if (i2c->pos == msg->len) {
|
||||
i2c->nmsgs--;
|
||||
i2c->msg++;
|
||||
i2c->pos = 0;
|
||||
msg = i2c->msg;
|
||||
|
||||
if (i2c->nmsgs) { /* end? */
|
||||
/* send start? */
|
||||
if (!(msg->flags & I2C_M_NOSTART)) {
|
||||
u8 addr = (msg->addr << 1);
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
addr |= 1;
|
||||
|
||||
i2c->state = STATE_START;
|
||||
|
||||
oc_setreg(i2c, OCI2C_DATA, addr);
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
|
||||
return;
|
||||
} else
|
||||
i2c->state = (msg->flags & I2C_M_RD)
|
||||
? STATE_READ : STATE_WRITE;
|
||||
} else {
|
||||
i2c->state = STATE_DONE;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (i2c->state == STATE_READ) {
|
||||
oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ?
|
||||
OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK);
|
||||
} else {
|
||||
oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]);
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ocores_isr(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
struct ocores_i2c *i2c = dev_id;
|
||||
|
||||
ocores_process(i2c);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct ocores_i2c *i2c = i2c_get_adapdata(adap);
|
||||
|
||||
i2c->msg = msgs;
|
||||
i2c->pos = 0;
|
||||
i2c->nmsgs = num;
|
||||
i2c->state = STATE_START;
|
||||
|
||||
oc_setreg(i2c, OCI2C_DATA,
|
||||
(i2c->msg->addr << 1) |
|
||||
((i2c->msg->flags & I2C_M_RD) ? 1:0));
|
||||
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
|
||||
|
||||
if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
|
||||
(i2c->state == STATE_DONE), HZ))
|
||||
return (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
else
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void ocores_init(struct ocores_i2c *i2c,
|
||||
struct ocores_i2c_platform_data *pdata)
|
||||
{
|
||||
int prescale;
|
||||
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
||||
|
||||
/* make sure the device is disabled */
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||
|
||||
prescale = (pdata->clock_khz / (5*100)) - 1;
|
||||
oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff);
|
||||
oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8);
|
||||
|
||||
/* Init the device */
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);
|
||||
}
|
||||
|
||||
|
||||
static u32 ocores_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm ocores_algorithm = {
|
||||
.master_xfer = ocores_xfer,
|
||||
.functionality = ocores_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter ocores_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "i2c-ocores",
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.algo = &ocores_algorithm,
|
||||
};
|
||||
|
||||
|
||||
static int __devinit ocores_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ocores_i2c *i2c;
|
||||
struct ocores_i2c_platform_data *pdata;
|
||||
struct resource *res, *res2;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res2)
|
||||
return -ENODEV;
|
||||
|
||||
pdata = (struct ocores_i2c_platform_data*) pdev->dev.platform_data;
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!request_mem_region(res->start, res->end - res->start + 1,
|
||||
pdev->name)) {
|
||||
dev_err(&pdev->dev, "Memory region busy\n");
|
||||
ret = -EBUSY;
|
||||
goto request_mem_failed;
|
||||
}
|
||||
|
||||
i2c->base = ioremap(res->start, res->end - res->start + 1);
|
||||
if (!i2c->base) {
|
||||
dev_err(&pdev->dev, "Unable to map registers\n");
|
||||
ret = -EIO;
|
||||
goto map_failed;
|
||||
}
|
||||
|
||||
i2c->regstep = pdata->regstep;
|
||||
ocores_init(i2c, pdata);
|
||||
|
||||
init_waitqueue_head(&i2c->wait);
|
||||
ret = request_irq(res2->start, ocores_isr, 0, pdev->name, i2c);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
||||
goto request_irq_failed;
|
||||
}
|
||||
|
||||
/* hook up driver to tree */
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
i2c->adap = ocores_adapter;
|
||||
i2c_set_adapdata(&i2c->adap, i2c);
|
||||
i2c->adap.dev.parent = &pdev->dev;
|
||||
|
||||
/* add i2c adapter to i2c tree */
|
||||
ret = i2c_add_adapter(&i2c->adap);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to add adapter\n");
|
||||
goto add_adapter_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
add_adapter_failed:
|
||||
free_irq(res2->start, i2c);
|
||||
request_irq_failed:
|
||||
iounmap(i2c->base);
|
||||
map_failed:
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
request_mem_failed:
|
||||
kfree(i2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ocores_i2c_remove(struct platform_device* pdev)
|
||||
{
|
||||
struct ocores_i2c *i2c = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
/* disable i2c logic */
|
||||
oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL)
|
||||
& ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||
|
||||
/* remove adapter & data */
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (res)
|
||||
free_irq(res->start, i2c);
|
||||
|
||||
iounmap(i2c->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res)
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
|
||||
kfree(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ocores_i2c_driver = {
|
||||
.probe = ocores_i2c_probe,
|
||||
.remove = __devexit_p(ocores_i2c_remove),
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ocores-i2c",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ocores_i2c_init(void)
|
||||
{
|
||||
return platform_driver_register(&ocores_i2c_driver);
|
||||
}
|
||||
|
||||
static void __exit ocores_i2c_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ocores_i2c_driver);
|
||||
}
|
||||
|
||||
module_init(ocores_i2c_init);
|
||||
module_exit(ocores_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
|
||||
MODULE_DESCRIPTION("OpenCores I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -102,13 +102,6 @@ MODULE_PARM_DESC(force_addr,
|
|||
"Forcibly enable the PIIX4 at the given address. "
|
||||
"EXTREMELY DANGEROUS!");
|
||||
|
||||
/* If fix_hstcfg is set to anything different from 0, we reset one of the
|
||||
registers to be a valid value. */
|
||||
static int fix_hstcfg;
|
||||
module_param (fix_hstcfg, int, 0);
|
||||
MODULE_PARM_DESC(fix_hstcfg,
|
||||
"Fix config register. Needed on some boards (Force CPCI735).");
|
||||
|
||||
static int piix4_transaction(void);
|
||||
|
||||
static unsigned short piix4_smba;
|
||||
|
@ -137,7 +130,7 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
|
|||
/* Don't access SMBus on IBM systems which get corrupted eeproms */
|
||||
if (dmi_check_system(piix4_dmi_table) &&
|
||||
PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) {
|
||||
dev_err(&PIIX4_dev->dev, "IBM Laptop detected; this module "
|
||||
dev_err(&PIIX4_dev->dev, "IBM system detected; this module "
|
||||
"may corrupt your serial eeprom! Refusing to load "
|
||||
"module!\n");
|
||||
return -EPERM;
|
||||
|
@ -166,22 +159,6 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
|
|||
|
||||
pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
|
||||
|
||||
/* Some BIOS will set up the chipset incorrectly and leave a register
|
||||
in an undefined state (causing I2C to act very strangely). */
|
||||
if (temp & 0x02) {
|
||||
if (fix_hstcfg) {
|
||||
dev_info(&PIIX4_dev->dev, "Working around buggy BIOS "
|
||||
"(I2C)\n");
|
||||
temp &= 0xfd;
|
||||
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp);
|
||||
} else {
|
||||
dev_info(&PIIX4_dev->dev, "Unusual config register "
|
||||
"value\n");
|
||||
dev_info(&PIIX4_dev->dev, "Try using fix_hstcfg=1 if "
|
||||
"you experience problems\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* If force_addr is set, we program the new address here. Just to make
|
||||
sure, we disable the PIIX4 first. */
|
||||
if (force_addr) {
|
||||
|
@ -214,7 +191,7 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
|
|||
}
|
||||
}
|
||||
|
||||
if ((temp & 0x0E) == 8)
|
||||
if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2))
|
||||
dev_dbg(&PIIX4_dev->dev, "Using Interrupt 9 for SMBus.\n");
|
||||
else if ((temp & 0x0E) == 0)
|
||||
dev_dbg(&PIIX4_dev->dev, "Using Interrupt SMI# for SMBus.\n");
|
||||
|
@ -413,6 +390,12 @@ static struct i2c_adapter piix4_adapter = {
|
|||
static struct pci_device_id piix4_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3),
|
||||
.driver_data = 3 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5),
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
#include <linux/scx200.h>
|
||||
|
||||
|
@ -85,6 +84,10 @@ struct scx200_acb_iface {
|
|||
u8 *ptr;
|
||||
char needs_reset;
|
||||
unsigned len;
|
||||
|
||||
/* PCI device info */
|
||||
struct pci_dev *pdev;
|
||||
int bar;
|
||||
};
|
||||
|
||||
/* Register Definitions */
|
||||
|
@ -381,7 +384,7 @@ static struct i2c_algorithm scx200_acb_algorithm = {
|
|||
static struct scx200_acb_iface *scx200_acb_list;
|
||||
static DECLARE_MUTEX(scx200_acb_list_mutex);
|
||||
|
||||
static int scx200_acb_probe(struct scx200_acb_iface *iface)
|
||||
static __init int scx200_acb_probe(struct scx200_acb_iface *iface)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
|
@ -417,17 +420,16 @@ static int scx200_acb_probe(struct scx200_acb_iface *iface)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init scx200_acb_create(const char *text, int base, int index)
|
||||
static __init struct scx200_acb_iface *scx200_create_iface(const char *text,
|
||||
int index)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
struct i2c_adapter *adapter;
|
||||
int rc;
|
||||
|
||||
iface = kzalloc(sizeof(*iface), GFP_KERNEL);
|
||||
if (!iface) {
|
||||
printk(KERN_ERR NAME ": can't allocate memory\n");
|
||||
rc = -ENOMEM;
|
||||
goto errout;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
adapter = &iface->adapter;
|
||||
|
@ -440,26 +442,27 @@ static int __init scx200_acb_create(const char *text, int base, int index)
|
|||
|
||||
mutex_init(&iface->mutex);
|
||||
|
||||
if (!request_region(base, 8, adapter->name)) {
|
||||
printk(KERN_ERR NAME ": can't allocate io 0x%x-0x%x\n",
|
||||
base, base + 8-1);
|
||||
rc = -EBUSY;
|
||||
goto errout_free;
|
||||
}
|
||||
iface->base = base;
|
||||
return iface;
|
||||
}
|
||||
|
||||
static int __init scx200_acb_create(struct scx200_acb_iface *iface)
|
||||
{
|
||||
struct i2c_adapter *adapter;
|
||||
int rc;
|
||||
|
||||
adapter = &iface->adapter;
|
||||
|
||||
rc = scx200_acb_probe(iface);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING NAME ": probe failed\n");
|
||||
goto errout_release;
|
||||
return rc;
|
||||
}
|
||||
|
||||
scx200_acb_reset(iface);
|
||||
|
||||
if (i2c_add_adapter(adapter) < 0) {
|
||||
printk(KERN_ERR NAME ": failed to register\n");
|
||||
rc = -ENODEV;
|
||||
goto errout_release;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
down(&scx200_acb_list_mutex);
|
||||
|
@ -468,64 +471,148 @@ static int __init scx200_acb_create(const char *text, int base, int index)
|
|||
up(&scx200_acb_list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errout_release:
|
||||
release_region(iface->base, 8);
|
||||
static __init int scx200_create_pci(const char *text, struct pci_dev *pdev,
|
||||
int bar)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
int rc;
|
||||
|
||||
iface = scx200_create_iface(text, 0);
|
||||
|
||||
if (iface == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
iface->pdev = pdev;
|
||||
iface->bar = bar;
|
||||
|
||||
pci_enable_device_bars(iface->pdev, 1 << iface->bar);
|
||||
|
||||
rc = pci_request_region(iface->pdev, iface->bar, iface->adapter.name);
|
||||
|
||||
if (rc != 0) {
|
||||
printk(KERN_ERR NAME ": can't allocate PCI BAR %d\n",
|
||||
iface->bar);
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
iface->base = pci_resource_start(iface->pdev, iface->bar);
|
||||
rc = scx200_acb_create(iface);
|
||||
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
|
||||
pci_release_region(iface->pdev, iface->bar);
|
||||
pci_dev_put(iface->pdev);
|
||||
errout_free:
|
||||
kfree(iface);
|
||||
errout:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct pci_device_id scx200[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct pci_device_id divil_pci[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
|
||||
{ } /* NULL entry */
|
||||
};
|
||||
|
||||
#define MSR_LBAR_SMB 0x5140000B
|
||||
|
||||
static __init int scx200_add_cs553x(void)
|
||||
static int __init scx200_create_isa(const char *text, unsigned long base,
|
||||
int index)
|
||||
{
|
||||
u32 low, hi;
|
||||
u32 smb_base;
|
||||
struct scx200_acb_iface *iface;
|
||||
int rc;
|
||||
|
||||
/* Grab & reserve the SMB I/O range */
|
||||
rdmsr(MSR_LBAR_SMB, low, hi);
|
||||
iface = scx200_create_iface(text, index);
|
||||
|
||||
/* Check the IO mask and whether SMB is enabled */
|
||||
if (hi != 0x0000F001) {
|
||||
printk(KERN_WARNING NAME ": SMBus not enabled\n");
|
||||
return -ENODEV;
|
||||
if (iface == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (request_region(base, 8, iface->adapter.name) == 0) {
|
||||
printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n",
|
||||
base, base + 8 - 1);
|
||||
rc = -EBUSY;
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
/* SMBus IO size is 8 bytes */
|
||||
smb_base = low & 0x0000FFF8;
|
||||
iface->base = base;
|
||||
rc = scx200_acb_create(iface);
|
||||
|
||||
return scx200_acb_create("CS5535", smb_base, 0);
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
|
||||
release_region(base, 8);
|
||||
errout_free:
|
||||
kfree(iface);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Driver data is an index into the scx200_data array that indicates
|
||||
* the name and the BAR where the I/O address resource is located. ISA
|
||||
* devices are flagged with a bar value of -1 */
|
||||
|
||||
static struct pci_device_id scx200_pci[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA),
|
||||
.driver_data = 1 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA),
|
||||
.driver_data = 2 }
|
||||
};
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int bar;
|
||||
} scx200_data[] = {
|
||||
{ "SCx200", -1 },
|
||||
{ "CS5535", 0 },
|
||||
{ "CS5536", 0 }
|
||||
};
|
||||
|
||||
static __init int scx200_scan_pci(void)
|
||||
{
|
||||
int data, dev;
|
||||
int rc = -ENODEV;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
for(dev = 0; dev < ARRAY_SIZE(scx200_pci); dev++) {
|
||||
pdev = pci_get_device(scx200_pci[dev].vendor,
|
||||
scx200_pci[dev].device, NULL);
|
||||
|
||||
if (pdev == NULL)
|
||||
continue;
|
||||
|
||||
data = scx200_pci[dev].driver_data;
|
||||
|
||||
/* if .bar is greater or equal to zero, this is a
|
||||
* PCI device - otherwise, we assume
|
||||
that the ports are ISA based
|
||||
*/
|
||||
|
||||
if (scx200_data[data].bar >= 0)
|
||||
rc = scx200_create_pci(scx200_data[data].name, pdev,
|
||||
scx200_data[data].bar);
|
||||
else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_DEVICES; ++i) {
|
||||
if (base[i] == 0)
|
||||
continue;
|
||||
|
||||
rc = scx200_create_isa(scx200_data[data].name,
|
||||
base[i],
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init scx200_acb_init(void)
|
||||
{
|
||||
int i;
|
||||
int rc = -ENODEV;
|
||||
int rc;
|
||||
|
||||
pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
|
||||
|
||||
/* Verify that this really is a SCx200 processor */
|
||||
if (pci_dev_present(scx200)) {
|
||||
for (i = 0; i < MAX_DEVICES; ++i) {
|
||||
if (base[i] > 0)
|
||||
rc = scx200_acb_create("SCx200", base[i], i);
|
||||
}
|
||||
} else if (pci_dev_present(divil_pci))
|
||||
rc = scx200_add_cs553x();
|
||||
rc = scx200_scan_pci();
|
||||
|
||||
/* If at least one bus was created, init must succeed */
|
||||
if (scx200_acb_list)
|
||||
|
@ -543,7 +630,14 @@ static void __exit scx200_acb_cleanup(void)
|
|||
up(&scx200_acb_list_mutex);
|
||||
|
||||
i2c_del_adapter(&iface->adapter);
|
||||
release_region(iface->base, 8);
|
||||
|
||||
if (iface->pdev) {
|
||||
pci_release_region(iface->pdev, iface->bar);
|
||||
pci_dev_put(iface->pdev);
|
||||
}
|
||||
else
|
||||
release_region(iface->base, 8);
|
||||
|
||||
kfree(iface);
|
||||
down(&scx200_acb_list_mutex);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ config SENSORS_EEPROM
|
|||
config SENSORS_PCF8574
|
||||
tristate "Philips PCF8574 and PCF8574A"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for Philips PCF8574 and
|
||||
PCF8574A chips.
|
||||
|
@ -46,6 +47,9 @@ config SENSORS_PCF8574
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called pcf8574.
|
||||
|
||||
These devices are hard to detect and rarely found on mainstream
|
||||
hardware. If unsure, say N.
|
||||
|
||||
config SENSORS_PCA9539
|
||||
tristate "Philips PCA9539 16-bit I/O port"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
|
@ -59,12 +63,16 @@ config SENSORS_PCA9539
|
|||
config SENSORS_PCF8591
|
||||
tristate "Philips PCF8591"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for Philips PCF8591 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called pcf8591.
|
||||
|
||||
These devices are hard to detect and rarely found on mainstream
|
||||
hardware. If unsure, say N.
|
||||
|
||||
config ISP1301_OMAP
|
||||
tristate "Philips ISP1301 with OMAP OTG"
|
||||
depends on I2C && ARCH_OMAP_OTG
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/*
|
||||
* drivers/i2c/chips/m41t00.c
|
||||
*
|
||||
* I2C client/driver for the ST M41T00 Real-Time Clock chip.
|
||||
* I2C client/driver for the ST M41T00 family of i2c rtc chips.
|
||||
*
|
||||
* Author: Mark A. Greer <mgreer@mvista.com>
|
||||
*
|
||||
* 2005 (c) MontaVista Software, Inc. This file is licensed under
|
||||
* 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
|
@ -13,9 +11,6 @@
|
|||
/*
|
||||
* This i2c client/driver wedges between the drivers/char/genrtc.c RTC
|
||||
* interface and the SMBus interface of the i2c subsystem.
|
||||
* It would be more efficient to use i2c msgs/i2c_transfer directly but, as
|
||||
* recommened in .../Documentation/i2c/writing-clients section
|
||||
* "Sending and receiving", using SMBus level communication is preferred.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -24,56 +19,110 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/m41t00.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/rtc.h>
|
||||
|
||||
#define M41T00_DRV_NAME "m41t00"
|
||||
|
||||
static DEFINE_MUTEX(m41t00_mutex);
|
||||
|
||||
static struct i2c_driver m41t00_driver;
|
||||
static struct i2c_client *save_client;
|
||||
|
||||
static unsigned short ignore[] = { I2C_CLIENT_END };
|
||||
static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END };
|
||||
static unsigned short normal_addr[] = { I2C_CLIENT_END, I2C_CLIENT_END };
|
||||
|
||||
static struct i2c_client_address_data addr_data = {
|
||||
.normal_i2c = normal_addr,
|
||||
.probe = ignore,
|
||||
.ignore = ignore,
|
||||
.normal_i2c = normal_addr,
|
||||
.probe = ignore,
|
||||
.ignore = ignore,
|
||||
};
|
||||
|
||||
struct m41t00_chip_info {
|
||||
u8 type;
|
||||
char *name;
|
||||
u8 read_limit;
|
||||
u8 sec; /* Offsets for chip regs */
|
||||
u8 min;
|
||||
u8 hour;
|
||||
u8 day;
|
||||
u8 mon;
|
||||
u8 year;
|
||||
u8 alarm_mon;
|
||||
u8 alarm_hour;
|
||||
u8 sqw;
|
||||
u8 sqw_freq;
|
||||
};
|
||||
|
||||
static struct m41t00_chip_info m41t00_chip_info_tbl[] = {
|
||||
{
|
||||
.type = M41T00_TYPE_M41T00,
|
||||
.name = "m41t00",
|
||||
.read_limit = 5,
|
||||
.sec = 0,
|
||||
.min = 1,
|
||||
.hour = 2,
|
||||
.day = 4,
|
||||
.mon = 5,
|
||||
.year = 6,
|
||||
},
|
||||
{
|
||||
.type = M41T00_TYPE_M41T81,
|
||||
.name = "m41t81",
|
||||
.read_limit = 1,
|
||||
.sec = 1,
|
||||
.min = 2,
|
||||
.hour = 3,
|
||||
.day = 5,
|
||||
.mon = 6,
|
||||
.year = 7,
|
||||
.alarm_mon = 0xa,
|
||||
.alarm_hour = 0xc,
|
||||
.sqw = 0x13,
|
||||
},
|
||||
{
|
||||
.type = M41T00_TYPE_M41T85,
|
||||
.name = "m41t85",
|
||||
.read_limit = 1,
|
||||
.sec = 1,
|
||||
.min = 2,
|
||||
.hour = 3,
|
||||
.day = 5,
|
||||
.mon = 6,
|
||||
.year = 7,
|
||||
.alarm_mon = 0xa,
|
||||
.alarm_hour = 0xc,
|
||||
.sqw = 0x13,
|
||||
},
|
||||
};
|
||||
static struct m41t00_chip_info *m41t00_chip;
|
||||
|
||||
ulong
|
||||
m41t00_get_rtc_time(void)
|
||||
{
|
||||
s32 sec, min, hour, day, mon, year;
|
||||
s32 sec1, min1, hour1, day1, mon1, year1;
|
||||
ulong limit = 10;
|
||||
s32 sec, min, hour, day, mon, year;
|
||||
s32 sec1, min1, hour1, day1, mon1, year1;
|
||||
u8 reads = 0;
|
||||
u8 buf[8], msgbuf[1] = { 0 }; /* offset into rtc's regs */
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = save_client->addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = msgbuf,
|
||||
},
|
||||
{
|
||||
.addr = save_client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 8,
|
||||
.buf = buf,
|
||||
},
|
||||
};
|
||||
|
||||
sec = min = hour = day = mon = year = 0;
|
||||
sec1 = min1 = hour1 = day1 = mon1 = year1 = 0;
|
||||
|
||||
mutex_lock(&m41t00_mutex);
|
||||
do {
|
||||
if (((sec = i2c_smbus_read_byte_data(save_client, 0)) >= 0)
|
||||
&& ((min = i2c_smbus_read_byte_data(save_client, 1))
|
||||
>= 0)
|
||||
&& ((hour = i2c_smbus_read_byte_data(save_client, 2))
|
||||
>= 0)
|
||||
&& ((day = i2c_smbus_read_byte_data(save_client, 4))
|
||||
>= 0)
|
||||
&& ((mon = i2c_smbus_read_byte_data(save_client, 5))
|
||||
>= 0)
|
||||
&& ((year = i2c_smbus_read_byte_data(save_client, 6))
|
||||
>= 0)
|
||||
&& ((sec == sec1) && (min == min1) && (hour == hour1)
|
||||
&& (day == day1) && (mon == mon1)
|
||||
&& (year == year1)))
|
||||
|
||||
break;
|
||||
if (i2c_transfer(save_client->adapter, msgs, 2) < 0)
|
||||
goto read_err;
|
||||
|
||||
sec1 = sec;
|
||||
min1 = min;
|
||||
|
@ -81,69 +130,88 @@ m41t00_get_rtc_time(void)
|
|||
day1 = day;
|
||||
mon1 = mon;
|
||||
year1 = year;
|
||||
} while (--limit > 0);
|
||||
mutex_unlock(&m41t00_mutex);
|
||||
|
||||
if (limit == 0) {
|
||||
dev_warn(&save_client->dev,
|
||||
"m41t00: can't read rtc chip\n");
|
||||
sec = min = hour = day = mon = year = 0;
|
||||
}
|
||||
sec = buf[m41t00_chip->sec] & 0x7f;
|
||||
min = buf[m41t00_chip->min] & 0x7f;
|
||||
hour = buf[m41t00_chip->hour] & 0x3f;
|
||||
day = buf[m41t00_chip->day] & 0x3f;
|
||||
mon = buf[m41t00_chip->mon] & 0x1f;
|
||||
year = buf[m41t00_chip->year];
|
||||
} while ((++reads < m41t00_chip->read_limit) && ((sec != sec1)
|
||||
|| (min != min1) || (hour != hour1) || (day != day1)
|
||||
|| (mon != mon1) || (year != year1)));
|
||||
|
||||
sec &= 0x7f;
|
||||
min &= 0x7f;
|
||||
hour &= 0x3f;
|
||||
day &= 0x3f;
|
||||
mon &= 0x1f;
|
||||
year &= 0xff;
|
||||
if ((m41t00_chip->read_limit > 1) && ((sec != sec1) || (min != min1)
|
||||
|| (hour != hour1) || (day != day1) || (mon != mon1)
|
||||
|| (year != year1)))
|
||||
goto read_err;
|
||||
|
||||
BCD_TO_BIN(sec);
|
||||
BCD_TO_BIN(min);
|
||||
BCD_TO_BIN(hour);
|
||||
BCD_TO_BIN(day);
|
||||
BCD_TO_BIN(mon);
|
||||
BCD_TO_BIN(year);
|
||||
sec = BCD2BIN(sec);
|
||||
min = BCD2BIN(min);
|
||||
hour = BCD2BIN(hour);
|
||||
day = BCD2BIN(day);
|
||||
mon = BCD2BIN(mon);
|
||||
year = BCD2BIN(year);
|
||||
|
||||
year += 1900;
|
||||
if (year < 1970)
|
||||
year += 100;
|
||||
|
||||
return mktime(year, mon, day, hour, min, sec);
|
||||
|
||||
read_err:
|
||||
dev_err(&save_client->dev, "m41t00_get_rtc_time: Read error\n");
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(m41t00_get_rtc_time);
|
||||
|
||||
static void
|
||||
m41t00_set(void *arg)
|
||||
{
|
||||
struct rtc_time tm;
|
||||
ulong nowtime = *(ulong *)arg;
|
||||
int nowtime = *(int *)arg;
|
||||
s32 sec, min, hour, day, mon, year;
|
||||
u8 wbuf[9], *buf = &wbuf[1], msgbuf[1] = { 0 };
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = save_client->addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = msgbuf,
|
||||
},
|
||||
{
|
||||
.addr = save_client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 8,
|
||||
.buf = buf,
|
||||
},
|
||||
};
|
||||
|
||||
to_tm(nowtime, &tm);
|
||||
tm.tm_year = (tm.tm_year - 1900) % 100;
|
||||
|
||||
BIN_TO_BCD(tm.tm_sec);
|
||||
BIN_TO_BCD(tm.tm_min);
|
||||
BIN_TO_BCD(tm.tm_hour);
|
||||
BIN_TO_BCD(tm.tm_mon);
|
||||
BIN_TO_BCD(tm.tm_mday);
|
||||
BIN_TO_BCD(tm.tm_year);
|
||||
sec = BIN2BCD(tm.tm_sec);
|
||||
min = BIN2BCD(tm.tm_min);
|
||||
hour = BIN2BCD(tm.tm_hour);
|
||||
day = BIN2BCD(tm.tm_mday);
|
||||
mon = BIN2BCD(tm.tm_mon);
|
||||
year = BIN2BCD(tm.tm_year);
|
||||
|
||||
mutex_lock(&m41t00_mutex);
|
||||
if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0)
|
||||
|| (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f)
|
||||
< 0)
|
||||
|| (i2c_smbus_write_byte_data(save_client, 2, tm.tm_hour & 0x3f)
|
||||
< 0)
|
||||
|| (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x3f)
|
||||
< 0)
|
||||
|| (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x1f)
|
||||
< 0)
|
||||
|| (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0xff)
|
||||
< 0))
|
||||
/* Read reg values into buf[0..7]/wbuf[1..8] */
|
||||
if (i2c_transfer(save_client->adapter, msgs, 2) < 0) {
|
||||
dev_err(&save_client->dev, "m41t00_set: Read error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n");
|
||||
wbuf[0] = 0; /* offset into rtc's regs */
|
||||
buf[m41t00_chip->sec] = (buf[m41t00_chip->sec] & ~0x7f) | (sec & 0x7f);
|
||||
buf[m41t00_chip->min] = (buf[m41t00_chip->min] & ~0x7f) | (min & 0x7f);
|
||||
buf[m41t00_chip->hour] = (buf[m41t00_chip->hour] & ~0x3f) | (hour& 0x3f);
|
||||
buf[m41t00_chip->day] = (buf[m41t00_chip->day] & ~0x3f) | (day & 0x3f);
|
||||
buf[m41t00_chip->mon] = (buf[m41t00_chip->mon] & ~0x1f) | (mon & 0x1f);
|
||||
|
||||
mutex_unlock(&m41t00_mutex);
|
||||
return;
|
||||
if (i2c_master_send(save_client, wbuf, 9) < 0)
|
||||
dev_err(&save_client->dev, "m41t00_set: Write error\n");
|
||||
}
|
||||
|
||||
static ulong new_time;
|
||||
|
@ -162,6 +230,48 @@ m41t00_set_rtc_time(ulong nowtime)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(m41t00_set_rtc_time);
|
||||
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* platform_data Driver Interface
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
static int __init
|
||||
m41t00_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct m41t00_platform_data *pdata;
|
||||
int i;
|
||||
|
||||
if (pdev && (pdata = pdev->dev.platform_data)) {
|
||||
normal_addr[0] = pdata->i2c_addr;
|
||||
|
||||
for (i=0; i<ARRAY_SIZE(m41t00_chip_info_tbl); i++)
|
||||
if (m41t00_chip_info_tbl[i].type == pdata->type) {
|
||||
m41t00_chip = &m41t00_chip_info_tbl[i];
|
||||
m41t00_chip->sqw_freq = pdata->sqw_freq;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int __exit
|
||||
m41t00_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver m41t00_platform_driver = {
|
||||
.probe = m41t00_platform_probe,
|
||||
.remove = m41t00_platform_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = M41T00_DRV_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
*****************************************************************************
|
||||
|
@ -176,23 +286,71 @@ m41t00_probe(struct i2c_adapter *adap, int addr, int kind)
|
|||
struct i2c_client *client;
|
||||
int rc;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_I2C
|
||||
| I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return 0;
|
||||
|
||||
client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
|
||||
if (!client)
|
||||
return -ENOMEM;
|
||||
|
||||
strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE);
|
||||
strlcpy(client->name, m41t00_chip->name, I2C_NAME_SIZE);
|
||||
client->addr = addr;
|
||||
client->adapter = adap;
|
||||
client->driver = &m41t00_driver;
|
||||
|
||||
if ((rc = i2c_attach_client(client)) != 0) {
|
||||
kfree(client);
|
||||
return rc;
|
||||
if ((rc = i2c_attach_client(client)))
|
||||
goto attach_err;
|
||||
|
||||
if (m41t00_chip->type != M41T00_TYPE_M41T00) {
|
||||
/* If asked, disable SQW, set SQW frequency & re-enable */
|
||||
if (m41t00_chip->sqw_freq)
|
||||
if (((rc = i2c_smbus_read_byte_data(client,
|
||||
m41t00_chip->alarm_mon)) < 0)
|
||||
|| ((rc = i2c_smbus_write_byte_data(client,
|
||||
m41t00_chip->alarm_mon, rc & ~0x40)) <0)
|
||||
|| ((rc = i2c_smbus_write_byte_data(client,
|
||||
m41t00_chip->sqw,
|
||||
m41t00_chip->sqw_freq)) < 0)
|
||||
|| ((rc = i2c_smbus_write_byte_data(client,
|
||||
m41t00_chip->alarm_mon, rc | 0x40)) <0))
|
||||
goto sqw_err;
|
||||
|
||||
/* Make sure HT (Halt Update) bit is cleared */
|
||||
if ((rc = i2c_smbus_read_byte_data(client,
|
||||
m41t00_chip->alarm_hour)) < 0)
|
||||
goto ht_err;
|
||||
|
||||
if (rc & 0x40)
|
||||
if ((rc = i2c_smbus_write_byte_data(client,
|
||||
m41t00_chip->alarm_hour, rc & ~0x40))<0)
|
||||
goto ht_err;
|
||||
}
|
||||
|
||||
m41t00_wq = create_singlethread_workqueue("m41t00");
|
||||
/* Make sure ST (stop) bit is cleared */
|
||||
if ((rc = i2c_smbus_read_byte_data(client, m41t00_chip->sec)) < 0)
|
||||
goto st_err;
|
||||
|
||||
if (rc & 0x80)
|
||||
if ((rc = i2c_smbus_write_byte_data(client, m41t00_chip->sec,
|
||||
rc & ~0x80)) < 0)
|
||||
goto st_err;
|
||||
|
||||
m41t00_wq = create_singlethread_workqueue(m41t00_chip->name);
|
||||
save_client = client;
|
||||
return 0;
|
||||
|
||||
st_err:
|
||||
dev_err(&client->dev, "m41t00_probe: Can't clear ST bit\n");
|
||||
goto attach_err;
|
||||
ht_err:
|
||||
dev_err(&client->dev, "m41t00_probe: Can't clear HT bit\n");
|
||||
goto attach_err;
|
||||
sqw_err:
|
||||
dev_err(&client->dev, "m41t00_probe: Can't set SQW Frequency\n");
|
||||
attach_err:
|
||||
kfree(client);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -204,7 +362,7 @@ m41t00_attach(struct i2c_adapter *adap)
|
|||
static int
|
||||
m41t00_detach(struct i2c_client *client)
|
||||
{
|
||||
int rc;
|
||||
int rc;
|
||||
|
||||
if ((rc = i2c_detach_client(client)) == 0) {
|
||||
kfree(client);
|
||||
|
@ -225,14 +383,18 @@ static struct i2c_driver m41t00_driver = {
|
|||
static int __init
|
||||
m41t00_init(void)
|
||||
{
|
||||
return i2c_add_driver(&m41t00_driver);
|
||||
int rc;
|
||||
|
||||
if (!(rc = platform_driver_register(&m41t00_platform_driver)))
|
||||
rc = i2c_add_driver(&m41t00_driver);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit
|
||||
m41t00_exit(void)
|
||||
{
|
||||
i2c_del_driver(&m41t00_driver);
|
||||
return;
|
||||
platform_driver_unregister(&m41t00_platform_driver);
|
||||
}
|
||||
|
||||
module_init(m41t00_init);
|
||||
|
|
|
@ -916,7 +916,7 @@ s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
|
|||
}
|
||||
|
||||
s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command,
|
||||
u8 length, u8 *values)
|
||||
u8 length, const u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
|
||||
|
@ -944,7 +944,7 @@ s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *val
|
|||
}
|
||||
|
||||
s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command,
|
||||
u8 length, u8 *values)
|
||||
u8 length, const u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
|
||||
|
|
|
@ -426,10 +426,7 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap)
|
|||
|
||||
/* register this i2c device with the driver core */
|
||||
i2c_dev->adap = adap;
|
||||
if (adap->dev.parent == &platform_bus)
|
||||
dev = &adap->dev;
|
||||
else
|
||||
dev = adap->dev.parent;
|
||||
dev = &adap->dev;
|
||||
i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL,
|
||||
MKDEV(I2C_MAJOR, i2c_dev->minor),
|
||||
dev, "i2c-%d", i2c_dev->minor);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* i2c-ocores.h - definitions for the i2c-ocores interface
|
||||
*
|
||||
* Peter Korsgaard <jacmet@sunsite.dk>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_OCORES_H
|
||||
#define _LINUX_I2C_OCORES_H
|
||||
|
||||
struct ocores_i2c_platform_data {
|
||||
u32 regstep; /* distance between registers */
|
||||
u32 clock_khz; /* input clock in kHz */
|
||||
};
|
||||
|
||||
#endif /* _LINUX_I2C_OCORES_H */
|
|
@ -97,13 +97,13 @@ extern s32 i2c_smbus_write_word_data(struct i2c_client * client,
|
|||
u8 command, u16 value);
|
||||
extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
|
||||
u8 command, u8 length,
|
||||
u8 *values);
|
||||
const u8 *values);
|
||||
/* Returns the number of read bytes */
|
||||
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
|
||||
u8 command, u8 *values);
|
||||
extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client,
|
||||
u8 command, u8 length,
|
||||
u8 *values);
|
||||
const u8 *values);
|
||||
|
||||
/*
|
||||
* A driver is capable of handling one or more physical devices present on
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Definitions for the ST M41T00 family of i2c rtc chips.
|
||||
*
|
||||
* Author: Mark A. Greer <mgreer@mvista.com>
|
||||
*
|
||||
* 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
|
||||
#ifndef _M41T00_H
|
||||
#define _M41T00_H
|
||||
|
||||
#define M41T00_DRV_NAME "m41t00"
|
||||
#define M41T00_I2C_ADDR 0x68
|
||||
|
||||
#define M41T00_TYPE_M41T00 0
|
||||
#define M41T00_TYPE_M41T81 81
|
||||
#define M41T00_TYPE_M41T85 85
|
||||
|
||||
struct m41t00_platform_data {
|
||||
u8 type;
|
||||
u8 i2c_addr;
|
||||
u8 sqw_freq;
|
||||
};
|
||||
|
||||
/* SQW output disabled, this is default value by power on */
|
||||
#define M41T00_SQW_DISABLE (0)
|
||||
|
||||
#define M41T00_SQW_32KHZ (1<<4) /* 32.768 KHz */
|
||||
#define M41T00_SQW_8KHZ (2<<4) /* 8.192 KHz */
|
||||
#define M41T00_SQW_4KHZ (3<<4) /* 4.096 KHz */
|
||||
#define M41T00_SQW_2KHZ (4<<4) /* 2.048 KHz */
|
||||
#define M41T00_SQW_1KHZ (5<<4) /* 1.024 KHz */
|
||||
#define M41T00_SQW_512HZ (6<<4) /* 512 Hz */
|
||||
#define M41T00_SQW_256HZ (7<<4) /* 256 Hz */
|
||||
#define M41T00_SQW_128HZ (8<<4) /* 128 Hz */
|
||||
#define M41T00_SQW_64HZ (9<<4) /* 64 Hz */
|
||||
#define M41T00_SQW_32HZ (10<<4) /* 32 Hz */
|
||||
#define M41T00_SQW_16HZ (11<<4) /* 16 Hz */
|
||||
#define M41T00_SQW_8HZ (12<<4) /* 8 Hz */
|
||||
#define M41T00_SQW_4HZ (13<<4) /* 4 Hz */
|
||||
#define M41T00_SQW_2HZ (14<<4) /* 2 Hz */
|
||||
#define M41T00_SQW_1HZ (15<<4) /* 1 Hz */
|
||||
|
||||
extern ulong m41t00_get_rtc_time(void);
|
||||
extern int m41t00_set_rtc_time(ulong nowtime);
|
||||
|
||||
#endif /* _M41T00_H */
|
|
@ -352,8 +352,11 @@
|
|||
#define PCI_DEVICE_ID_ATI_RS480 0x5950
|
||||
/* ATI IXP Chipset */
|
||||
#define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349
|
||||
#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353
|
||||
#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363
|
||||
#define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369
|
||||
#define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e
|
||||
#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372
|
||||
#define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376
|
||||
#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379
|
||||
#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a
|
||||
|
@ -1133,9 +1136,11 @@
|
|||
#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258
|
||||
#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259
|
||||
#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E
|
||||
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F
|
||||
|
|
Loading…
Reference in New Issue