Commit d588fcbe authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents eaa85689 4941b395
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.
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.
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: ...@@ -7,6 +7,10 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the National Semiconductor website Datasheet: Publicly available at the National Semiconductor website
http://www.national.com/pf/LM/LM83.html 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> Author: Jean Delvare <khali@linux-fr.org>
...@@ -15,10 +19,11 @@ Description ...@@ -15,10 +19,11 @@ Description
----------- -----------
The LM83 is a digital temperature sensor. It senses its own temperature as 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 well as the temperature of up to three external diodes. The LM82 is
with many other devices such as the LM84 and all other ADM1021 clones. a stripped down version of the LM83 that only supports one external diode.
The main difference between the LM83 and the LM84 in that the later can Both are compatible with many other devices such as the LM84 and all
only sense the temperature of one external diode. 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 Using the adm1021 driver for a LM83 should work, but only two temperatures
will be reported instead of four. will be reported instead of four.
...@@ -30,12 +35,16 @@ contact us. Note that the LM90 can easily be misdetected as a LM83. ...@@ -30,12 +35,16 @@ contact us. Note that the LM90 can easily be misdetected as a LM83.
Confirmed motherboards: Confirmed motherboards:
SBS P014 SBS P014
SBS PSL09
Unconfirmed motherboards: Unconfirmed motherboards:
Gigabyte GA-8IK1100 Gigabyte GA-8IK1100
Iwill MPX2 Iwill MPX2
Soltek SL-75DRV5 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 The driver has been successfully tested by Magnus Forsström, who I'd
like to thank here. More testers will be of course welcome. like to thank here. More testers will be of course welcome.
......
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 ...@@ -3,15 +3,15 @@ Naming and data format standards for sysfs files
The libsensors library offers an interface to the raw sensors data The libsensors library offers an interface to the raw sensors data
through the sysfs interface. See libsensors documentation and source for through the sysfs interface. See libsensors documentation and source for
more further information. As of writing this document, libsensors further information. As of writing this document, libsensors
(from lm_sensors 2.8.3) is heavily chip-dependant. Adding or updating (from lm_sensors 2.8.3) is heavily chip-dependent. Adding or updating
support for any given chip requires modifying the library's code. support for any given chip requires modifying the library's code.
This is because libsensors was written for the procfs interface This is because libsensors was written for the procfs interface
older kernel modules were using, which wasn't standardized enough. older kernel modules were using, which wasn't standardized enough.
Recent versions of libsensors (from lm_sensors 2.8.2 and later) have Recent versions of libsensors (from lm_sensors 2.8.2 and later) have
support for the sysfs interface, though. 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. possible.
Note that motherboards vary widely in the connections to sensor chips. 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 ...@@ -24,7 +24,7 @@ range using external resistors. Since the values of these resistors
can change from motherboard to motherboard, the conversions cannot be can change from motherboard to motherboard, the conversions cannot be
hard coded into the driver and have to be done in user space. 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 still require a configuration file (e.g. /etc/sensors.conf) for proper
values conversion, labeling of inputs and hiding of unused inputs. 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 ...@@ -39,15 +39,16 @@ If you are developing a userspace application please send us feedback on
this standard. this standard.
Note that this standard isn't completely established yet, so it is subject 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 to changes. If you are writing a new hardware monitoring driver those
of accessing sysfs files directly. 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 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 find all sensor chips, it is easier to follow the device symlinks from
/sys/i2c/devices/ /sys/class/hwmon/hwmon*.
All sysfs values are fixed point numbers. To get the true value of some All sysfs values are fixed point numbers.
of the values, you should divide by the specified value.
There is only one value per file, unlike the older /proc specification. There is only one value per file, unlike the older /proc specification.
The common scheme for files naming is: <type><number>_<item>. Usual The common scheme for files naming is: <type><number>_<item>. Usual
...@@ -69,28 +70,40 @@ to cause an alarm) is chip-dependent. ...@@ -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 * * Voltages *
************ ************
in[0-8]_min Voltage min value. in[0-*]_min Voltage min value.
Unit: millivolt Unit: millivolt
Read/Write RW
in[0-8]_max Voltage max value. in[0-*]_max Voltage max value.
Unit: millivolt Unit: millivolt
Read/Write RW
in[0-8]_input Voltage input value. in[0-*]_input Voltage input value.
Unit: millivolt Unit: millivolt
Read only RO
Voltage measured on the chip pin.
Actual voltage depends on the scaling resistors on the Actual voltage depends on the scaling resistors on the
motherboard, as recommended in the chip datasheet. motherboard, as recommended in the chip datasheet.
This varies by chip and by motherboard. This varies by chip and by motherboard.
Because of this variation, values are generally NOT scaled Because of this variation, values are generally NOT scaled
by the chip driver, and must be done by the application. by the chip driver, and must be done by the application.
However, some drivers (notably lm87 and via686a) 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. These drivers will output the actual voltage.
Typical usage: Typical usage:
...@@ -104,58 +117,72 @@ in[0-8]_input Voltage input value. ...@@ -104,58 +117,72 @@ in[0-8]_input Voltage input value.
in7_* varies in7_* varies
in8_* varies in8_* varies
cpu[0-1]_vid CPU core reference voltage. cpu[0-*]_vid CPU core reference voltage.
Unit: millivolt Unit: millivolt
Read only. RO
Not always correct. Not always correct.
vrm Voltage Regulator Module version number. vrm Voltage Regulator Module version number.
Read only. RW (but changing it should no more be necessary)
Two digit number, first is major version, second is Originally the VRM standard version multiplied by 10, but now
minor version. an arbitrary number, as not all standards have a version
number.
Affects the way the driver calculates the CPU core reference Affects the way the driver calculates the CPU core reference
voltage from the vid pins. voltage from the vid pins.
Also see the Alarms section for status flags associated with voltages.
******** ********
* Fans * * Fans *
******** ********
fan[1-3]_min Fan minimum value fan[1-*]_min Fan minimum value
Unit: revolution/min (RPM) Unit: revolution/min (RPM)
Read/Write. RW
fan[1-3]_input Fan input value. fan[1-*]_input Fan input value.
Unit: revolution/min (RPM) 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). 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. Some chips only support values 1, 2, 4 and 8.
Note that this is actually an internal clock divisor, which Note that this is actually an internal clock divisor, which
affects the measurable speed range, not the read value. affects the measurable speed range, not the read value.
Also see the Alarms section for status flags associated with fans.
******* *******
* PWM * * PWM *
******* *******
pwm[1-3] Pulse width modulation fan control. pwm[1-*] Pulse width modulation fan control.
Integer value in the range 0 to 255 Integer value in the range 0 to 255
Read/Write RW
255 is max or 100%. 255 is max or 100%.
pwm[1-3]_enable pwm[1-*]_enable
Switch PWM on and off. Switch PWM on and off.
Not always present even if fan*_pwm is. Not always present even if fan*_pwm is.
0 to turn off 0: turn off
1 to turn on in manual mode 1: turn on in manual mode
2 to turn on in automatic mode 2+: turn on in automatic mode
Read/Write 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 pwm[1-*]_auto_channels_temp
Select which temperature channels affect this PWM output in Select which temperature channels affect this PWM output in
auto mode. Bitfield, 1 is temp1, 2 is temp2, 4 is temp3 etc... auto mode. Bitfield, 1 is temp1, 2 is temp2, 4 is temp3 etc...
Which values are possible depend on the chip used. Which values are possible depend on the chip used.
RW
pwm[1-*]_auto_point[1-*]_pwm pwm[1-*]_auto_point[1-*]_pwm
pwm[1-*]_auto_point[1-*]_temp pwm[1-*]_auto_point[1-*]_temp
...@@ -163,6 +190,7 @@ pwm[1-*]_auto_point[1-*]_temp_hyst ...@@ -163,6 +190,7 @@ pwm[1-*]_auto_point[1-*]_temp_hyst
Define the PWM vs temperature curve. Number of trip points is Define the PWM vs temperature curve. Number of trip points is
chip-dependent. Use this for chips which associate trip points chip-dependent. Use this for chips which associate trip points
to PWM output channels. to PWM output channels.
RW
OR OR
...@@ -172,50 +200,57 @@ temp[1-*]_auto_point[1-*]_temp_hyst ...@@ -172,50 +200,57 @@ temp[1-*]_auto_point[1-*]_temp_hyst
Define the PWM vs temperature curve. Number of trip points is Define the PWM vs temperature curve. Number of trip points is
chip-dependent. Use this for chips which associate trip points chip-dependent. Use this for chips which associate trip points
to temperature channels. to temperature channels.
RW
**************** ****************
* Temperatures * * Temperatures *
**************** ****************
temp[1-3]_type Sensor type selection. temp[1-*]_type Sensor type selection.
Integers 1 to 4 or thermistor Beta value (typically 3435) Integers 1 to 4 or thermistor Beta value (typically 3435)
Read/Write. RW
1: PII/Celeron Diode 1: PII/Celeron Diode
2: 3904 transistor 2: 3904 transistor
3: thermal diode 3: thermal diode
4: thermistor (default/unknown Beta) 4: thermistor (default/unknown Beta)
Not all types are supported by all chips Not all types are supported by all chips
temp[1-4]_max Temperature max value. temp[1-*]_max Temperature max value.
Unit: millidegree Celcius Unit: millidegree Celsius (or millivolt, see below)
Read/Write value. RW
temp[1-3]_min Temperature min value. temp[1-*]_min Temperature min value.
Unit: millidegree Celcius Unit: millidegree Celsius
Read/Write value. RW
temp[1-3]_max_hyst temp[1-*]_max_hyst
Temperature hysteresis value for max limit. Temperature hysteresis value for max limit.
Unit: millidegree Celcius Unit: millidegree Celsius
Must be reported as an absolute temperature, NOT a delta Must be reported as an absolute temperature, NOT a delta
from the max value. from the max value.
Read/Write value. RW
temp[1-4]_input Temperature input value. temp[1-*]_input Temperature input value.
Unit: millidegree Celcius Unit: millidegree Celsius
Read only value. RO
temp[1-4]_crit Temperature critical value, typically greater than temp[1-*]_crit Temperature critical value, typically greater than
corresponding temp_max values. corresponding temp_max values.
Unit: millidegree Celcius Unit: millidegree Celsius
Read/Write value. RW
temp[1-2]_crit_hyst temp[1-*]_crit_hyst
Temperature hysteresis value for critical limit. Temperature hysteresis value for critical limit.
Unit: millidegree Celcius Unit: millidegree Celsius
Must be reported as an absolute temperature, NOT a delta Must be reported as an absolute temperature, NOT a delta
from the critical value. 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. Read/Write value.
If there are multiple temperature sensors, temp1_* is If there are multiple temperature sensors, temp1_* is
...@@ -225,6 +260,17 @@ temp[1-2]_crit_hyst ...@@ -225,6 +260,17 @@ temp[1-2]_crit_hyst
itself, for example the thermal diode inside the CPU or itself, for example the thermal diode inside the CPU or
a thermistor nearby. 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 * * Currents *
...@@ -233,25 +279,88 @@ temp[1-2]_crit_hyst ...@@ -233,25 +279,88 @@ temp[1-2]_crit_hyst
Note that no known chip provides current measurements as of writing, Note that no known chip provides current measurements as of writing,
so this part is theoretical, so to say. so this part is theoretical, so to say.
curr[1-n]_max Current max value curr[1-*]_max Current max value
Unit: milliampere Unit: milliampere
Read/Write. RW
curr[1-n]_min Current min value. curr[1-*]_min Current min value.
Unit: milliampere Unit: milliampere
Read/Write. RW
curr[1-n]_input Current input value curr[1-*]_input Current input value
Unit: milliampere 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. alarms Alarm bitmask.
Read only. RO
Integer representation of one to four bytes. Integer representation of one to four bytes.
A '1' bit means an alarm. A '1' bit means an alarm.
Chips should be programmed for 'comparator' mode so that Chips should be programmed for 'comparator' mode so that
...@@ -259,35 +368,26 @@ alarms Alarm bitmask. ...@@ -259,35 +368,26 @@ alarms Alarm bitmask.
if it is still valid. if it is still valid.
Generally a direct representation of a chip's internal Generally a direct representation of a chip's internal
alarm registers; there is no standard for the position 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. Bits are defined in kernel/include/sensors.h.
alarms_in Alarm bitmask relative to in (voltage) channels beep_mask Bitmask for beep.
Read only Same format as 'alarms' with the same bit locations,
A '1' bit means an alarm, LSB corresponds to in0 and so on use discouraged for the same reason. Use individual
Prefered to 'alarms' for newer chips *_beep files instead.
RW
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. * Other *
Read/Write *********
eeprom Raw EEPROM data in binary form. eeprom Raw EEPROM data in binary form.
Read only. RO
pec Enable or disable PEC (SMBus only) 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 ...@@ -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. are also connected directly through the ISA bus.
The kernel drivers make the data from the sensor chips available in the /sys 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 virtual filesystem. Userspace tools are then used to display the measured
data in a more friendly manner. values or configure the chips in a more friendly manner.
Lm-sensors 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 setup monitoring limits etc. You can get them on their homepage
http://www.lm-sensors.nu/ or as a package from your Linux distribution. http://www.lm-sensors.nu/ or as a package from your Linux distribution.
If from website: If from website:
Get lmsensors from project web site. Please note, you need only userspace Get lm-sensors from project web site. Please note, you need only userspace
part, so compile with "make user_install" target. part, so compile with "make user" and install with "make user_install".
General hints to get things working: General hints to get things working:
0) get lm-sensors userspace utils 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. 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. 3) load them and run "sensors" command, you should see some results.
4) fix sensors.conf, labels, limits, fan divisors 4) fix sensors.conf, labels, limits, fan divisors
5) if any more problems consult FAQ, or documentation 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 If you want some graphical indicators of system health look for applications
like: gkrellm, ksensors, xsensors, wmtemp, wmsensors, wmgtemp, ksysguardd, like: gkrellm, ksensors, xsensors, wmtemp, wmsensors, wmgtemp, ksysguardd,
......
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: ...@@ -21,8 +21,7 @@ Authors:
Module Parameters Module Parameters
----------------- -----------------
* force_addr: int None.
Forcibly enable the ICH at the given address. EXTREMELY DANGEROUS!
Description Description
......
...@@ -7,6 +7,8 @@ Supported adapters: ...@@ -7,6 +7,8 @@ Supported adapters:
* nForce3 250Gb MCP 10de:00E4 * nForce3 250Gb MCP 10de:00E4
* nForce4 MCP 10de:0052 * nForce4 MCP 10de:0052
* nForce4 MCP-04 10de:0034 * nForce4 MCP-04 10de:0034
* nForce4 MCP51 10de:0264
* nForce4 MCP55 10de:0368
Datasheet: not publically available, but seems to be similar to the Datasheet: not publically available, but seems to be similar to the
AMD-8111 SMBus 2.0 adapter. AMD-8111 SMBus 2.0 adapter.
......
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: ...@@ -6,6 +6,8 @@ Supported adapters:
Datasheet: Publicly available at the Intel website Datasheet: Publicly available at the Intel website
* ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges * ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges
Datasheet: Only available via NDA from ServerWorks Datasheet: Only available via NDA from ServerWorks
* ATI IXP southbridges IXP200, IXP300, IXP400
Datasheet: Not publicly available
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
Datasheet: Publicly available at the SMSC website http://www.smsc.com Datasheet: Publicly available at the SMSC website http://www.smsc.com
...@@ -21,8 +23,6 @@ Module Parameters ...@@ -21,8 +23,6 @@ Module Parameters
Forcibly enable the PIIX4. DANGEROUS! Forcibly enable the PIIX4. DANGEROUS!
* force_addr: int * force_addr: int
Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS! Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS!
* fix_hstcfg: int
Fix config register. Needed on some boards (Force CPCI735).
Description Description
...@@ -63,10 +63,36 @@ The PIIX4E is just an new version of the PIIX4; it is supported as well. ...@@ -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 The PIIX/PIIX3 does not implement an SMBus or I2C bus, so you can't use
this driver on those mainboards. 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. identical to the PIIX4 in I2C/SMBus support.
A few OSB4 southbridges are known to be misconfigured by the BIOS. In this If you own Force CPCI735 motherboard or other OSB4 based systems you may need
case, you have you use the fix_hstcfg module parameter. Do not use it to change the SMBus Interrupt Select register so the SMBus controller uses
unless you know you have to, because in some cases it also breaks the SMI mode.
configuration on southbridges that don't need it.
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 ...@@ -2,14 +2,31 @@ Kernel driver scx200_acb
Author: Christer Weinigel <wingel@nano-system.com> Author: Christer Weinigel <wingel@nano-system.com>
The driver supersedes the older, never merged driver named i2c-nscacb.
Module Parameters Module Parameters
----------------- -----------------
* base: int * base: up to 4 ints
Base addresses for the ACCESS.bus controllers on SCx200 and SC1100 devices 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 Description
----------- -----------
Enable the use of the ACCESS.bus controller on the Geode SCx200 and Enable the use of the ACCESS.bus controller on the Geode SCx200 and
SC1100 processors and the CS5535 and CS5536 Geode companion devices. 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
...@@ -181,6 +181,12 @@ M: bcrl@kvack.org ...@@ -181,6 +181,12 @@ M: bcrl@kvack.org
L: linux-aio@kvack.org L: linux-aio@kvack.org
S: Supported 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 ACENIC DRIVER
P: Jes Sorensen P: Jes Sorensen
M: jes@trained-monkey.org M: jes@trained-monkey.org
...@@ -2057,6 +2063,12 @@ M: adaplas@pol.net ...@@ -2057,6 +2063,12 @@ M: adaplas@pol.net
L: linux-fbdev-devel@lists.sourceforge.net L: linux-fbdev-devel@lists.sourceforge.net
S: Maintained 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) ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
P: Mark Fasheh P: Mark Fasheh
M: mark.fasheh@oracle.com M: mark.fasheh@oracle.com
...@@ -2528,12 +2540,6 @@ M: thomas@winischhofer.net ...@@ -2528,12 +2540,6 @@ M: thomas@winischhofer.net
W: http://www.winischhofer.at/linuxsisusbvga.shtml W: http://www.winischhofer.at/linuxsisusbvga.shtml
S: Maintained 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 SMB FILESYSTEM
P: Urban Widmark P: Urban Widmark
M: urban@teststation.com M: urban@teststation.com
...@@ -3146,12 +3152,6 @@ L: wbsd-devel@list.drzeus.cx ...@@ -3146,12 +3152,6 @@ L: wbsd-devel@list.drzeus.cx
W: http://projects.drzeus.cx/wbsd W: http://projects.drzeus.cx/wbsd
S: Maintained 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 WATCHDOG DEVICE DRIVERS
P: Wim Van Sebroeck P: Wim Van Sebroeck
M: wim@iguana.be M: wim@iguana.be
......
# #
# I2C Sensor chip drivers configuration # Hardware monitoring chip drivers configuration
# #
menu "Hardware Monitoring support" menu "Hardware Monitoring support"
...@@ -16,6 +16,10 @@ config HWMON ...@@ -16,6 +16,10 @@ config HWMON
should say Y here and also to the specific driver(s) for your should say Y here and also to the specific driver(s) for your
sensors chip(s) below. 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 This support can also be built as a module. If so, the module
will be called hwmon. will be called hwmon.
...@@ -23,6 +27,18 @@ config HWMON_VID ...@@ -23,6 +27,18 @@ config HWMON_VID
tristate tristate
default n 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 config SENSORS_ADM1021
tristate "Analog Devices ADM1021 and compatibles" tristate "Analog Devices ADM1021 and compatibles"
depends on HWMON && I2C depends on HWMON && I2C
...@@ -188,6 +204,16 @@ config SENSORS_LM63 ...@@ -188,6 +204,16 @@ config SENSORS_LM63
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called lm63. 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 config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles" tristate "National Semiconductor LM75 and compatibles"
depends on HWMON && I2C depends on HWMON && I2C
...@@ -236,11 +262,11 @@ config SENSORS_LM80 ...@@ -236,11 +262,11 @@ config SENSORS_LM80
will be called lm80. will be called lm80.
config SENSORS_LM83 config SENSORS_LM83
tristate "National Semiconductor LM83" tristate "National Semiconductor LM83 and compatibles"
depends on HWMON && I2C depends on HWMON && I2C
help help
If you say yes here you get support for National Semiconductor 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 This driver can also be built as a module. If so, the module
will be called lm83. will be called lm83.
...@@ -333,11 +359,32 @@ config SENSORS_SMSC47M1 ...@@ -333,11 +359,32 @@ config SENSORS_SMSC47M1
help help
If you say yes here you get support for the integrated fan If you say yes here you get support for the integrated fan
monitoring and control capabilities of the SMSC LPC47B27x, 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 This driver can also be built as a module. If so, the module
will be called smsc47m1. 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 config SENSORS_SMSC47B397
tristate "SMSC LPC47B397-NC" tristate "SMSC LPC47B397-NC"
depends on HWMON && I2C && EXPERIMENTAL depends on HWMON && I2C && EXPERIMENTAL
...@@ -385,6 +432,16 @@ config SENSORS_W83781D ...@@ -385,6 +432,16 @@ config SENSORS_W83781D
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called w83781d. 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 config SENSORS_W83792D
tristate "Winbond W83792D" tristate "Winbond W83792D"
depends on HWMON && I2C && EXPERIMENTAL depends on HWMON && I2C && EXPERIMENTAL
......
...@@ -10,7 +10,9 @@ obj-$(CONFIG_SENSORS_ASB100) += asb100.o ...@@ -10,7 +10,9 @@ obj-$(CONFIG_SENSORS_ASB100) += asb100.o
obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
obj-$(CONFIG_SENSORS_W83792D) += w83792d.o obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.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_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
...@@ -26,6 +28,7 @@ obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o ...@@ -26,6 +28,7 @@ obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM63) += lm63.o
obj-$(CONFIG_SENSORS_LM70) += lm70.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_LM77) += lm77.o obj-$(CONFIG_SENSORS_LM77) += lm77.o
obj-$(CONFIG_SENSORS_LM78) += lm78.o obj-$(CONFIG_SENSORS_LM78) += lm78.o
...@@ -40,6 +43,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o ...@@ -40,6 +43,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_VT8231) += vt8231.o obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
......
/*
abituguru.c Copyright (c) 2005-2006 Hans de Goede <j.w.r.degoede@hhs.nl>
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.
*/
/*
This driver supports the sensor part of the custom Abit uGuru chip found
on Abit uGuru motherboards. Note: because of lack of specs the CPU / RAM /
etc voltage & frequency control is not supported!
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <asm/io.h>
/* Banks */
#define ABIT_UGURU_ALARM_BANK 0x20 /* 1x 3 bytes */
#define ABIT_UGURU_SENSOR_BANK1 0x21 /* 16x volt and temp */
#define ABIT_UGURU_FAN_PWM 0x24 /* 3x 5 bytes */
#define ABIT_UGURU_SENSOR_BANK2 0x26 /* fans */
/* max nr of sensors in bank1, a bank1 sensor can be in, temp or nc */
#define ABIT_UGURU_MAX_BANK1_SENSORS 16
/* Warning if you increase one of the 2 MAX defines below to 10 or higher you
should adjust the belonging _NAMES_LENGTH macro for the 2 digit number! */
/* max nr of sensors in bank2, currently mb's with max 6 fans are known */
#define ABIT_UGURU_MAX_BANK2_SENSORS 6
/* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */
#define ABIT_UGURU_MAX_PWMS 5
/* uGuru sensor bank 1 flags */ /* Alarm if: */
#define ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE 0x01 /* temp over warn */
#define ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE 0x02 /* volt over max */
#define ABIT_UGURU_VOLT_LOW_ALARM_ENABLE 0x04 /* volt under min */
#define ABIT_UGURU_TEMP_HIGH_ALARM_FLAG 0x10 /* temp is over warn */
#define ABIT_UGURU_VOLT_HIGH_ALARM_FLAG 0x20 /* volt is over max */
#define ABIT_UGURU_VOLT_LOW_ALARM_FLAG 0x40 /* volt is under min */
/* uGuru sensor bank 2 flags */ /* Alarm if: */
#define ABIT_UGURU_FAN_LOW_ALARM_ENABLE 0x01 /* fan under min */
/* uGuru sensor bank common flags */
#define ABIT_UGURU_BEEP_ENABLE 0x08 /* beep if alarm */
#define ABIT_UGURU_SHUTDOWN_ENABLE 0x80 /* shutdown if alarm */
/* uGuru fan PWM (speed control) flags */
#define ABIT_UGURU_FAN_PWM_ENABLE 0x80 /* enable speed control */
/* Values used for conversion */
#define ABIT_UGURU_FAN_MAX 15300 /* RPM */
/* Bank1 sensor types */
#define ABIT_UGURU_IN_SENSOR 0
#define ABIT_UGURU_TEMP_SENSOR 1
#define ABIT_UGURU_NC 2
/* Timeouts / Retries, if these turn out to need a lot of fiddling we could
convert them to params. */
/* 250 was determined by trial and error, 200 works most of the time, but not
always. I assume this is cpu-speed independent, since the ISA-bus and not
the CPU should be the bottleneck. Note that 250 sometimes is still not
enough (only reported on AN7 mb) this is handled by a higher layer. */
#define ABIT_UGURU_WAIT_TIMEOUT 250
/* Normally all expected status in abituguru_ready, are reported after the
first read, but sometimes not and we need to poll, 5 polls was not enough
50 sofar is. */
#define ABIT_UGURU_READY_TIMEOUT 50
/* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */
#define ABIT_UGURU_MAX_RETRIES 3
#define ABIT_UGURU_RETRY_DELAY (HZ/5)
/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is an error */
#define ABIT_UGURU_MAX_TIMEOUTS 2
/* utility macros */
#define ABIT_UGURU_NAME "abituguru"
#define ABIT_UGURU_DEBUG(level, format, arg...) \
if (level <= verbose) \
printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg)
/* Macros to help calculate the sysfs_names array length */
/* sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0,
in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0 */
#define ABITUGURU_IN_NAMES_LENGTH (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14)
/* sum of strlen of: temp??_input\0, temp??_max\0, temp??_crit\0,
temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0 */
#define ABITUGURU_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16)
/* sum of strlen of: fan?_input\0, fan?_min\0, fan?_alarm\0,
fan?_alarm_enable\0, fan?_beep\0, fan?_shutdown\0 */
#define ABITUGURU_FAN_NAMES_LENGTH (11 + 9 + 11 + 18 + 10 + 14)
/* sum of strlen of: pwm?_enable\0, pwm?_auto_channels_temp\0,
pwm?_auto_point{1,2}_pwm\0, pwm?_auto_point{1,2}_temp\0 */
#define ABITUGURU_PWM_NAMES_LENGTH (12 + 24 + 2 * 21 + 2 * 22)
/* IN_NAMES_LENGTH > TEMP_NAMES_LENGTH so assume all bank1 sensors are in */
#define ABITUGURU_SYSFS_NAMES_LENGTH ( \
ABIT_UGURU_MAX_BANK1_SENSORS * ABITUGURU_IN_NAMES_LENGTH + \
ABIT_UGURU_MAX_BANK2_SENSORS * ABITUGURU_FAN_NAMES_LENGTH + \
ABIT_UGURU_MAX_PWMS * ABITUGURU_PWM_NAMES_LENGTH)
/* All the macros below are named identical to the oguru and oguru2 programs
reverse engineered by Olle Sandberg, hence the names might not be 100%
logical. I could come up with better names, but I prefer keeping the names
identical so that this driver can be compared with his work more easily. */
/* Two i/o-ports are used by uGuru */
#define ABIT_UGURU_BASE 0x00E0
/* Used to tell uGuru what to read and to read the actual data */
#define ABIT_UGURU_CMD 0x00
/* Mostly used to check if uGuru is busy */
#define ABIT_UGURU_DATA 0x04
#define ABIT_UGURU_REGION_LENGTH 5
/* uGuru status' */
#define ABIT_UGURU_STATUS_WRITE 0x00 /* Ready to be written */
#define ABIT_UGURU_STATUS_READ 0x01 /* Ready to be read */
#define ABIT_UGURU_STATUS_INPUT 0x08 /* More input */
#define ABIT_UGURU_STATUS_READY 0x09 /* Ready to be written */
/* Constants */
/* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */
static const int abituguru_bank1_max_value[2] = { 3494, 255000 };
/* Min / Max allowed values for sensor2 (fan) alarm threshold, these values
correspond to 300-3000 RPM */
static const u8 abituguru_bank2_min_threshold = 5;
static const u8 abituguru_bank2_max_threshold = 50;
/* Register 0 is a bitfield, 1 and 2 are pwm settings (255 = 100%), 3 and 4
are temperature trip points. */
static const int abituguru_pwm_settings_multiplier[5] = { 0, 1, 1, 1000, 1000 };
/* Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a
special case the minium allowed pwm% setting for this is 30% (77) on
some MB's this special case is handled in the code! */
static const u8 abituguru_pwm_min[5] = { 0, 170, 170, 25, 25 };
static const u8 abituguru_pwm_max[5] = { 0, 255, 255, 75, 75 };
/* Insmod parameters */
static int force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Set to one to force detection.");
static int fan_sensors;
module_param(fan_sensors, int, 0);
MODULE_PARM_DESC(fan_sensors, "Number of fan sensors on the uGuru "
"(0 = autodetect)");
static int pwms;
module_param(pwms, int, 0);
MODULE_PARM_DESC(pwms, "Number of PWMs on the uGuru "
"(0 = autodetect)");
/* Default verbose is 2, since this driver is still in the testing phase */
static int verbose = 2;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, "How verbose should the driver be? (0-3):\n"
" 0 normal output\n"
" 1 + verbose error reporting\n"
" 2 + sensors type probing info\n"
" 3 + retryable error reporting");
/* For the Abit uGuru, we need to keep some data in memory.
The structure is dynamically allocated, at the same time when a new
abituguru device is allocated. */
struct abituguru_data {
struct class_device *class_dev; /* hwmon registered device */
struct mutex update_lock; /* protect access to data and uGuru */
unsigned long last_updated; /* In jiffies */
unsigned short addr; /* uguru base address */
char uguru_ready; /* is the uguru in ready state? */
unsigned char update_timeouts; /* number of update timeouts since last
successful update */
/* The sysfs attr and their names are generated automatically, for bank1
we cannot use a predefined array because we don't know beforehand
of a sensor is a volt or a temp sensor, for bank2 and the pwms its
easier todo things the same way. For in sensors we have 9 (temp 7)
sysfs entries per sensor, for bank2 and pwms 6. */
struct sensor_device_attribute_2 sysfs_attr[
ABIT_UGURU_MAX_BANK1_SENSORS * 9 +
ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6];
/* Buffer to store the dynamically generated sysfs names */
char sysfs_names[ABITUGURU_SYSFS_NAMES_LENGTH];
/* Bank 1 data */
/* number of and addresses of [0] in, [1] temp sensors */
u8 bank1_sensors[2];
u8 bank1_address[2][ABIT_UGURU_MAX_BANK1_SENSORS];
u8 bank1_value[ABIT_UGURU_MAX_BANK1_SENSORS];
/* This array holds 3 entries per sensor for the bank 1 sensor settings
(flags, min, max for voltage / flags, warn, shutdown for temp). */
u8 bank1_settings[ABIT_UGURU_MAX_BANK1_SENSORS][3];
/* Maximum value for each sensor used for scaling in mV/millidegrees
Celsius. */
int bank1_max_value[ABIT_UGURU_MAX_BANK1_SENSORS];
/* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */
u8 bank2_sensors; /* actual number of bank2 sensors found */
u8 bank2_value[ABIT_UGURU_MAX_BANK2_SENSORS];
u8 bank2_settings[ABIT_UGURU_MAX_BANK2_SENSORS][2]; /* flags, min */
/* Alarms 2 bytes for bank1, 1 byte for bank2 */
u8 alarms[3];
/* Fan PWM (speed control) 5 bytes per PWM */
u8 pwms; /* actual number of pwms found */
u8 pwm_settings[ABIT_UGURU_MAX_PWMS][5];
};
/* wait till the uguru is in the specified state */
static int abituguru_wait(struct abituguru_data *data, u8 state)
{
int timeout = ABIT_UGURU_WAIT_TIMEOUT;
while (inb_p(data->addr + ABIT_UGURU_DATA) != state) {
timeout--;
if (timeout == 0)
return -EBUSY;
}
return 0;
}
/* Put the uguru in ready for input state */
static int abituguru_ready(struct abituguru_data *data)
{
int timeout = ABIT_UGURU_READY_TIMEOUT;
if (data->uguru_ready)
return 0;
/* Reset? / Prepare for next read/write cycle */
outb(0x00, data->addr + ABIT_UGURU_DATA);
/* Wait till the uguru is ready */
if (abituguru_wait(data, ABIT_UGURU_STATUS_READY)) {
ABIT_UGURU_DEBUG(1,
"timeout exceeded waiting for ready state\n");
return -EIO;
}
/* Cmd port MUST be read now and should contain 0xAC */
while (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) {
timeout--;
if (timeout == 0) {
ABIT_UGURU_DEBUG(1,
"CMD reg does not hold 0xAC after ready command\n");
return -EIO;
}
}
/* After this the ABIT_UGURU_DATA port should contain
ABIT_UGURU_STATUS_INPUT */
timeout = ABIT_UGURU_READY_TIMEOUT;
while (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) {
timeout--;
if (timeout == 0) {
ABIT_UGURU_DEBUG(1,
"state != more input after ready command\n");
return -EIO;
}
}
data->uguru_ready = 1;
return 0;
}
/* Send the bank and then sensor address to the uGuru for the next read/write
cycle. This function gets called as the first part of a read/write by
abituguru_read and abituguru_write. This function should never be
called by any other function. */
static int abituguru_send_address(struct abituguru_data *data,
u8 bank_addr, u8 sensor_addr, int retries)
{
/* assume the caller does error handling itself if it has not requested
any retries, and thus be quiet. */
int report_errors = retries;
for (;;) {
/* Make sure the uguru is ready and then send the bank address,
after this the uguru is no longer "ready". */
if (abituguru_ready(data) != 0)
return -EIO;
outb(bank_addr, data->addr + ABIT_UGURU_DATA);
data->uguru_ready = 0;
/* Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again
and send the sensor addr */
if (abituguru_wait(data, ABIT_UGURU_STATUS_INPUT)) {
if (retries) {
ABIT_UGURU_DEBUG(3, "timeout exceeded "
"waiting for more input state, %d "
"tries remaining\n", retries);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(ABIT_UGURU_RETRY_DELAY);
retries--;
continue;
}
if (report_errors)
ABIT_UGURU_DEBUG(1, "timeout exceeded "
"waiting for more input state "
"(bank: %d)\n", (int)bank_addr);
return -EBUSY;
}
outb(sensor_addr, data->addr + ABIT_UGURU_CMD);
return 0;
}
}
/* Read count bytes from sensor sensor_addr in bank bank_addr and store the
result in buf, retry the send address part of the read retries times. */
static int abituguru_read(struct abituguru_data *data,
u8 bank_addr, u8 sensor_addr, u8 *buf, int count, int retries)
{
int i;
/* Send the address */
i = abituguru_send_address(data, bank_addr, sensor_addr, retries);
if (i)
return i;
/* And read the data */
for (i = 0; i < count; i++) {
if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) {
ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for "
"read state (bank: %d, sensor: %d)\n",
(int)bank_addr, (int)sensor_addr);
break;
}
buf[i] = inb(data->addr + ABIT_UGURU_CMD);
}
/* Last put the chip back in ready state */
abituguru_ready(data);
return i;
}
/* Write count bytes from buf to sensor sensor_addr in bank bank_addr, the send
address part of the write is always retried ABIT_UGURU_MAX_RETRIES times. */
static int abituguru_write(struct abituguru_data *data,
u8 bank_addr, u8 sensor_addr, u8 *buf, int count)
{
int i;
/* Send the address */
i = abituguru_send_address(data, bank_addr, sensor_addr,
ABIT_UGURU_MAX_RETRIES);
if (i)
return i;
/* And write the data */
for (i = 0; i < count; i++) {
if (abituguru_wait(data, ABIT_UGURU_STATUS_WRITE)) {
ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for "
"write state (bank: %d, sensor: %d)\n",
(int)bank_addr, (int)sensor_addr);
break;
}
outb(buf[i], data->addr + ABIT_UGURU_CMD);
}
/* Now we need to wait till the chip is ready to be read again,
don't ask why */
if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) {
ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for read state "
"after write (bank: %d, sensor: %d)\n", (int)bank_addr,
(int)sensor_addr);
return -EIO;
}
/* Cmd port MUST be read now and should contain 0xAC */
if (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) {
ABIT_UGURU_DEBUG(1, "CMD reg does not hold 0xAC after write "
"(bank: %d, sensor: %d)\n", (int)bank_addr,
(int)sensor_addr);
return -EIO;
}
/* Last put the chip back in ready state */
abituguru_ready(data);
return i;
}
/* Detect sensor type. Temp and Volt sensors are enabled with
different masks and will ignore enable masks not meant for them.
This enables us to test what kind of sensor we're dealing with.
By setting the alarm thresholds so that we will always get an
alarm for sensor type X and then enabling the sensor as sensor type
X, if we then get an alarm it is a sensor of type X. */
static int __devinit
abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
u8 sensor_addr)
{
u8 val, buf[3];
int ret = ABIT_UGURU_NC;
/* First read the sensor and the current settings */
if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val,
1, ABIT_UGURU_MAX_RETRIES) != 1)
return -ENODEV;
/* Test val is sane / usable for sensor type detection. */
if ((val < 10u) || (val > 240u)) {
printk(KERN_WARNING ABIT_UGURU_NAME
": bank1-sensor: %d reading (%d) too close to limits, "
"unable to determine sensor type, skipping sensor\n",
(int)sensor_addr, (int)val);
/* assume no sensor is there for sensors for which we can't
determine the sensor type because their reading is too close
to their limits, this usually means no sensor is there. */
return ABIT_UGURU_NC;
}
ABIT_UGURU_DEBUG(2, "testing bank1 sensor %d\n", (int)sensor_addr);
/* Volt sensor test, enable volt low alarm, set min value ridicously
high. If its a volt sensor this should always give us an alarm. */
buf[0] = ABIT_UGURU_VOLT_LOW_ALARM_ENABLE;
buf[1] = 245;
buf[2] = 250;
if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
buf, 3) != 3)
return -ENODEV;
/* Now we need 20 ms to give the uguru time to read the sensors
and raise a voltage alarm */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/50);
/* Check for alarm and check the alarm is a volt low alarm. */
if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
ABIT_UGURU_MAX_RETRIES) != 3)
return -ENODEV;
if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
sensor_addr, buf, 3,
ABIT_UGURU_MAX_RETRIES) != 3)
return -ENODEV;
if (buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) {
/* Restore original settings */
if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2,
sensor_addr,
data->bank1_settings[sensor_addr],
3) != 3)
return -ENODEV;
ABIT_UGURU_DEBUG(2, " found volt sensor\n");
return ABIT_UGURU_IN_SENSOR;
} else
ABIT_UGURU_DEBUG(2, " alarm raised during volt "
"sensor test, but volt low flag not set\n");
} else
ABIT_UGURU_DEBUG(2, " alarm not raised during volt sensor "
"test\n");
/* Temp sensor test, enable sensor as a temp sensor, set beep value
ridicously low (but not too low, otherwise uguru ignores it).
If its a temp sensor this should always give us an alarm. */
buf[0] = ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE;
buf[1] = 5;
buf[2] = 10;
if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
buf, 3) != 3)
return -ENODEV;
/* Now we need 50 ms to give the uguru time to read the sensors
and raise a temp alarm */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/20);
/* Check for alarm and check the alarm is a temp high alarm. */
if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
ABIT_UGURU_MAX_RETRIES) != 3)
return -ENODEV;
if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
sensor_addr, buf, 3,
ABIT_UGURU_MAX_RETRIES) != 3)
return -ENODEV;
if (buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) {
ret = ABIT_UGURU_TEMP_SENSOR;
ABIT_UGURU_DEBUG(2, " found temp sensor\n");
} else
ABIT_UGURU_DEBUG(2, " alarm raised during temp "
"sensor test, but temp high flag not set\n");
} else
ABIT_UGURU_DEBUG(2, " alarm not raised during temp sensor "
"test\n");
/* Restore original settings */
if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
data->bank1_settings[sensor_addr], 3) != 3)
return -ENODEV;
return ret;
}
/* These functions try to find out how many sensors there are in bank2 and how
many pwms there are. The purpose of this is to make sure that we don't give
the user the possibility to change settings for non-existent sensors / pwm.
The uGuru will happily read / write whatever memory happens to be after the
memory storing the PWM settings when reading/writing to a PWM which is not
there. Notice even if we detect a PWM which doesn't exist we normally won't
write to it, unless the user tries to change the settings.
Although the uGuru allows reading (settings) from non existing bank2
sensors, my version of the uGuru does seem to stop writing to them, the
write function above aborts in this case with:
"CMD reg does not hold 0xAC after write"
Notice these 2 tests are non destructive iow read-only tests, otherwise
they would defeat their purpose. Although for the bank2_sensors detection a
read/write test would be feasible because of the reaction above, I've
however opted to stay on the safe side. */
static void __devinit
abituguru_detect_no_bank2_sensors(struct abituguru_data *data)
{
int i;
if (fan_sensors) {
data->bank2_sensors = fan_sensors;
ABIT_UGURU_DEBUG(2, "assuming %d fan sensors because of "
"\"fan_sensors\" module param\n",
(int)data->bank2_sensors);
return;
}
ABIT_UGURU_DEBUG(2, "detecting number of fan sensors\n");
for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) {
/* 0x89 are the known used bits:
-0x80 enable shutdown
-0x08 enable beep
-0x01 enable alarm
All other bits should be 0, but on some motherboards
0x40 (bit 6) is also high for some of the fans?? */
if (data->bank2_settings[i][0] & ~0xC9) {
ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem "
"to be a fan sensor: settings[0] = %02X\n",
i, (unsigned int)data->bank2_settings[i][0]);
break;
}
/* check if the threshold is within the allowed range */
if (data->bank2_settings[i][1] <
abituguru_bank2_min_threshold) {
ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem "
"to be a fan sensor: the threshold (%d) is "
"below the minimum (%d)\n", i,
(int)data->bank2_settings[i][1],
(int)abituguru_bank2_min_threshold);
break;
}
if (data->bank2_settings[i][1] >
abituguru_bank2_max_threshold) {
ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem "
"to be a fan sensor: the threshold (%d) is "
"above the maximum (%d)\n", i,
(int)data->bank2_settings[i][1],
(int)abituguru_bank2_max_threshold);
break;
}
}
data->bank2_sensors = i;
ABIT_UGURU_DEBUG(2, " found: %d fan sensors\n",
(int)data->bank2_sensors);
}
static void __devinit
abituguru_detect_no_pwms(struct abituguru_data *data)
{
int i, j;
if (pwms) {
data->pwms = pwms;
ABIT_UGURU_DEBUG(2, "assuming %d PWM outputs because of "
"\"pwms\" module param\n", (int)data->pwms);
return;
}
ABIT_UGURU_DEBUG(2, "detecting number of PWM outputs\n");
for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) {
/* 0x80 is the enable bit and the low
nibble is which temp sensor to use,
the other bits should be 0 */
if (data->pwm_settings[i][0] & ~0x8F) {
ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
"to be a pwm channel: settings[0] = %02X\n",
i, (unsigned int)data->pwm_settings[i][0]);
break;
}
/* the low nibble must correspond to one of the temp sensors
we've found */
for (j = 0; j < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR];
j++) {
if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][j] ==
(data->pwm_settings[i][0] & 0x0F))
break;
}
if (j == data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) {
ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
"to be a pwm channel: %d is not a valid temp "
"sensor address\n", i,
data->pwm_settings[i][0] & 0x0F);
break;
}
/* check if all other settings are within the allowed range */
for (j = 1; j < 5; j++) {
u8 min;
/* special case pwm1 min pwm% */
if ((i == 0) && ((j == 1) || (j == 2)))
min = 77;
else
min = abituguru_pwm_min[j];
if (data->pwm_settings[i][j] < min) {
ABIT_UGURU_DEBUG(2, " pwm channel %d does "
"not seem to be a pwm channel: "
"setting %d (%d) is below the minimum "
"value (%d)\n", i, j,
(int)data->pwm_settings[i][j],
(int)min);
goto abituguru_detect_no_pwms_exit;
}
if (data->pwm_settings[i][j] > abituguru_pwm_max[j]) {
ABIT_UGURU_DEBUG(2, " pwm channel %d does "
"not seem to be a pwm channel: "
"setting %d (%d) is above the maximum "
"value (%d)\n", i, j,
(int)data->pwm_settings[i][j],
(int)abituguru_pwm_max[j]);
goto abituguru_detect_no_pwms_exit;
}
}
/* check that min temp < max temp and min pwm < max pwm */
if (data->pwm_settings[i][1] >= data->pwm_settings[i][2]) {
ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
"to be a pwm channel: min pwm (%d) >= "
"max pwm (%d)\n", i,
(int)data->pwm_settings[i][1],
(int)data->pwm_settings[i][2]);
break;
}
if (data->pwm_settings[i][3] >= data->pwm_settings[i][4]) {
ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
"to be a pwm channel: min temp (%d) >= "
"max temp (%d)\n", i,
(int)data->pwm_settings[i][3],
(int)data->pwm_settings[i][4]);
break;
}
}
abituguru_detect_no_pwms_exit:
data->pwms = i;
ABIT_UGURU_DEBUG(2, " found: %d PWM outputs\n", (int)data->pwms);
}
/* Following are the sysfs callback functions. These functions expect:
sensor_device_attribute_2->index: sensor address/offset in the bank
sensor_device_attribute_2->nr: register offset, bitmask or NA. */
static struct abituguru_data *abituguru_update_device(struct device *dev);
static ssize_t show_bank1_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
if (!data)
return -EIO;
return sprintf(buf, "%d\n", (data->bank1_value[attr->index] *
data->bank1_max_value[attr->index] + 128) / 255);
}
static ssize_t show_bank1_setting(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n",
(data->bank1_settings[attr->index][attr->nr] *
data->bank1_max_value[attr->index] + 128) / 255);
}
static ssize_t show_bank2_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
if (!data)
return -EIO;
return sprintf(buf, "%d\n", (data->bank2_value[attr->index] *
ABIT_UGURU_FAN_MAX + 128) / 255);
}
static ssize_t show_bank2_setting(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n",
(data->bank2_settings[attr->index][attr->nr] *
ABIT_UGURU_FAN_MAX + 128) / 255);
}
static ssize_t store_bank1_setting(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
u8 val = (simple_strtoul(buf, NULL, 10) * 255 +
data->bank1_max_value[attr->index]/2) /
data->bank1_max_value[attr->index];
ssize_t ret = count;
mutex_lock(&data->update_lock);
if (data->bank1_settings[attr->index][attr->nr] != val) {
u8 orig_val = data->bank1_settings[attr->index][attr->nr];
data->bank1_settings[attr->index][attr->nr] = val;
if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2,
attr->index, data->bank1_settings[attr->index],
3) <= attr->nr) {
data->bank1_settings[attr->index][attr->nr] = orig_val;
ret = -EIO;
}
}
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t store_bank2_setting(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
u8 val = (simple_strtoul(buf, NULL, 10)*255 + ABIT_UGURU_FAN_MAX/2) /
ABIT_UGURU_FAN_MAX;
ssize_t ret = count;
/* this check can be done before taking the lock */
if ((val < abituguru_bank2_min_threshold) ||
(val > abituguru_bank2_max_threshold))
return -EINVAL;
mutex_lock(&data->update_lock);
if (data->bank2_settings[attr->index][attr->nr] != val) {
u8 orig_val = data->bank2_settings[attr->index][attr->nr];
data->bank2_settings[attr->index][attr->nr] = val;
if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK2 + 2,
attr->index, data->bank2_settings[attr->index],
2) <= attr->nr) {
data->bank2_settings[attr->index][attr->nr] = orig_val;
ret = -EIO;
}
}
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_bank1_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
if (!data)
return -EIO;
/* See if the alarm bit for this sensor is set, and if the
alarm matches the type of alarm we're looking for (for volt
it can be either low or high). The type is stored in a few
readonly bits in the settings part of the relevant sensor.
The bitmask of the type is passed to us in attr->nr. */
if ((data->alarms[attr->index / 8] & (0x01 << (attr->index % 8))) &&
(data->bank1_settings[attr->index][0] & attr->nr))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static ssize_t show_bank2_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
if (!data)
return -EIO;
if (data->alarms[2] & (0x01 << attr->index))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static ssize_t show_bank1_mask(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
if (data->bank1_settings[attr->index][0] & attr->nr)
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static ssize_t show_bank2_mask(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
if (data->bank2_settings[attr->index][0] & attr->nr)
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static ssize_t store_bank1_mask(struct device *dev,
struct device_attribute *devattr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
int mask = simple_strtoul(buf, NULL, 10);
ssize_t ret = count;
u8 orig_val;
mutex_lock(&data->update_lock);
orig_val = data->bank1_settings[attr->index][0];
if (mask)
data->bank1_settings[attr->index][0] |= attr->nr;
else
data->bank1_settings[attr->index][0] &= ~attr->nr;
if ((data->bank1_settings[attr->index][0] != orig_val) &&
(abituguru_write(data,
ABIT_UGURU_SENSOR_BANK1 + 2, attr->index,
data->bank1_settings[attr->index], 3) < 1)) {
data->bank1_settings[attr->index][0] = orig_val;
ret = -EIO;
}
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t store_bank2_mask(struct device *dev,
struct device_attribute *devattr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
int mask = simple_strtoul(buf, NULL, 10);
ssize_t ret = count;
u8 orig_val;
mutex_lock(&data->update_lock);
orig_val = data->bank2_settings[attr->index][0];
if (mask)
data->bank2_settings[attr->index][0] |= attr->nr;
else
data->bank2_settings[attr->index][0] &= ~attr->nr;
if ((data->bank2_settings[attr->index][0] != orig_val) &&
(abituguru_write(data,
ABIT_UGURU_SENSOR_BANK2 + 2, attr->index,
data->bank2_settings[attr->index], 2) < 1)) {
data->bank2_settings[attr->index][0] = orig_val;
ret = -EIO;
}
mutex_unlock(&data->update_lock);
return ret;
}
/* Fan PWM (speed control) */
static ssize_t show_pwm_setting(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->pwm_settings[attr->index][attr->nr] *
abituguru_pwm_settings_multiplier[attr->nr]);
}
static ssize_t store_pwm_setting(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
u8 min, val = (simple_strtoul(buf, NULL, 10) +
abituguru_pwm_settings_multiplier[attr->nr]/2) /
abituguru_pwm_settings_multiplier[attr->nr];
ssize_t ret = count;
/* special case pwm1 min pwm% */
if ((attr->index == 0) && ((attr->nr == 1) || (attr->nr == 2)))
min = 77;
else
min = abituguru_pwm_min[attr->nr];
/* this check can be done before taking the lock */
if ((val < min) || (val > abituguru_pwm_max[attr->nr]))
return -EINVAL;
mutex_lock(&data->update_lock);
/* this check needs to be done after taking the lock */
if ((attr->nr & 1) &&
(val >= data->pwm_settings[attr->index][attr->nr + 1]))
ret = -EINVAL;
else if (!(attr->nr & 1) &&
(val <= data->pwm_settings[attr->index][attr->nr - 1]))
ret = -EINVAL;
else if (data->pwm_settings[attr->index][attr->nr] != val) {
u8 orig_val = data->pwm_settings[attr->index][attr->nr];
data->pwm_settings[attr->index][attr->nr] = val;
if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
attr->index, data->pwm_settings[attr->index],
5) <= attr->nr) {
data->pwm_settings[attr->index][attr->nr] =
orig_val;
ret = -EIO;
}
}
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_pwm_sensor(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
int i;
/* We need to walk to the temp sensor addresses to find what
the userspace id of the configured temp sensor is. */
for (i = 0; i < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; i++)
if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][i] ==
(data->pwm_settings[attr->index][0] & 0x0F))
return sprintf(buf, "%d\n", i+1);
return -ENXIO;
}
static ssize_t store_pwm_sensor(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
unsigned long val = simple_strtoul(buf, NULL, 10) - 1;
ssize_t ret = count;
mutex_lock(&data->update_lock);
if (val < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) {
u8 orig_val = data->pwm_settings[attr->index][0];
u8 address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val];
data->pwm_settings[attr->index][0] &= 0xF0;
data->pwm_settings[attr->index][0] |= address;
if (data->pwm_settings[attr->index][0] != orig_val) {
if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
attr->index,
data->pwm_settings[attr->index],
5) < 1) {
data->pwm_settings[attr->index][0] = orig_val;
ret = -EIO;
}
}
}
else
ret = -EINVAL;
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_pwm_enable(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
int res = 0;
if (data->pwm_settings[attr->index][0] & ABIT_UGURU_FAN_PWM_ENABLE)
res = 2;
return sprintf(buf, "%d\n", res);
}
static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct abituguru_data *data = dev_get_drvdata(dev);
u8 orig_val, user_val = simple_strtoul(buf, NULL, 10);
ssize_t ret = count;
mutex_lock(&data->update_lock);
orig_val = data->pwm_settings[attr->index][0];
switch (user_val) {
case 0:
data->pwm_settings[attr->index][0] &=
~ABIT_UGURU_FAN_PWM_ENABLE;
break;
case 2:
data->pwm_settings[attr->index][0] |=
ABIT_UGURU_FAN_PWM_ENABLE;
break;
default:
ret = -EINVAL;
}
if ((data->pwm_settings[attr->index][0] != orig_val) &&
(abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
attr->index, data->pwm_settings[attr->index],
5) < 1)) {
data->pwm_settings[attr->index][0] = orig_val;
ret = -EIO;
}
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return sprintf(buf, "%s\n", ABIT_UGURU_NAME);
}
/* Sysfs attr templates, the real entries are generated automatically. */
static const
struct sensor_device_attribute_2 abituguru_sysfs_bank1_templ[2][9] = {
{
SENSOR_ATTR_2(in%d_input, 0444, show_bank1_value, NULL, 0, 0),
SENSOR_ATTR_2(in%d_min, 0644, show_bank1_setting,
store_bank1_setting, 1, 0),
SENSOR_ATTR_2(in%d_min_alarm, 0444, show_bank1_alarm, NULL,
ABIT_UGURU_VOLT_LOW_ALARM_FLAG, 0),
SENSOR_ATTR_2(in%d_max, 0644, show_bank1_setting,
store_bank1_setting, 2, 0),
SENSOR_ATTR_2(in%d_max_alarm, 0444, show_bank1_alarm, NULL,
ABIT_UGURU_VOLT_HIGH_ALARM_FLAG, 0),
SENSOR_ATTR_2(in%d_beep, 0644, show_bank1_mask,
store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0),
SENSOR_ATTR_2(in%d_shutdown, 0644, show_bank1_mask,
store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
SENSOR_ATTR_2(in%d_min_alarm_enable, 0644, show_bank1_mask,
store_bank1_mask, ABIT_UGURU_VOLT_LOW_ALARM_ENABLE, 0),
SENSOR_ATTR_2(in%d_max_alarm_enable, 0644, show_bank1_mask,
store_bank1_mask, ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE, 0),
}, {
SENSOR_ATTR_2(temp%d_input, 0444, show_bank1_value, NULL, 0, 0),
SENSOR_ATTR_2(temp%d_alarm, 0444, show_bank1_alarm, NULL,
ABIT_UGURU_TEMP_HIGH_ALARM_FLAG, 0),
SENSOR_ATTR_2(temp%d_max, 0644, show_bank1_setting,
store_bank1_setting, 1, 0),
SENSOR_ATTR_2(temp%d_crit, 0644, show_bank1_setting,
store_bank1_setting, 2, 0),
SENSOR_ATTR_2(temp%d_beep, 0644, show_bank1_mask,
store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0),
SENSOR_ATTR_2(temp%d_shutdown, 0644, show_bank1_mask,
store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
SENSOR_ATTR_2(temp%d_alarm_enable, 0644, show_bank1_mask,
store_bank1_mask, ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE, 0),
}
};
static const struct sensor_device_attribute_2 abituguru_sysfs_fan_templ[6] = {
SENSOR_ATTR_2(fan%d_input, 0444, show_bank2_value, NULL, 0, 0),
SENSOR_ATTR_2(fan%d_alarm, 0444, show_bank2_alarm, NULL, 0, 0),
SENSOR_ATTR_2(fan%d_min, 0644, show_bank2_setting,
store_bank2_setting, 1, 0),
SENSOR_ATTR_2(fan%d_beep, 0644, show_bank2_mask,
store_bank2_mask, ABIT_UGURU_BEEP_ENABLE, 0),
SENSOR_ATTR_2(fan%d_shutdown, 0644, show_bank2_mask,
store_bank2_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
SENSOR_ATTR_2(fan%d_alarm_enable, 0644, show_bank2_mask,
store_bank2_mask, ABIT_UGURU_FAN_LOW_ALARM_ENABLE, 0),
};
static const struct sensor_device_attribute_2 abituguru_sysfs_pwm_templ[6] = {
SENSOR_ATTR_2(pwm%d_enable, 0644, show_pwm_enable,
store_pwm_enable, 0, 0),
SENSOR_ATTR_2(pwm%d_auto_channels_temp, 0644, show_pwm_sensor,
store_pwm_sensor, 0, 0),
SENSOR_ATTR_2(pwm%d_auto_point1_pwm, 0644, show_pwm_setting,
store_pwm_setting, 1, 0),
SENSOR_ATTR_2(pwm%d_auto_point2_pwm, 0644, show_pwm_setting,
store_pwm_setting, 2, 0),
SENSOR_ATTR_2(pwm%d_auto_point1_temp, 0644, show_pwm_setting,
store_pwm_setting, 3, 0),
SENSOR_ATTR_2(pwm%d_auto_point2_temp, 0644, show_pwm_setting,
store_pwm_setting, 4, 0),
};
static struct sensor_device_attribute_2 abituguru_sysfs_attr[] = {
SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0),
};
static int __devinit abituguru_probe(struct platform_device *pdev)
{
struct abituguru_data *data;
int i, j, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV;
char *sysfs_filename;
/* El weirdo probe order, to keep the sysfs order identical to the
BIOS and window-appliction listing order. */
const u8 probe_order[ABIT_UGURU_MAX_BANK1_SENSORS] = {
0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02,
0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C };
if (!(data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL)))
return -ENOMEM;
data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
/* See if the uGuru is ready */
if (inb_p(data->addr + ABIT_UGURU_DATA) == ABIT_UGURU_STATUS_INPUT)
data->uguru_ready = 1;
/* Completely read the uGuru this has 2 purposes:
- testread / see if one really is there.
- make an in memory copy of all the uguru settings for future use. */
if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3)
goto abituguru_probe_error;
for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, i,
&data->bank1_value[i], 1,
ABIT_UGURU_MAX_RETRIES) != 1)
goto abituguru_probe_error;
if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1+1, i,
data->bank1_settings[i], 3,
ABIT_UGURU_MAX_RETRIES) != 3)
goto abituguru_probe_error;
}
/* Note: We don't know how many bank2 sensors / pwms there really are,
but in order to "detect" this we need to read the maximum amount
anyways. If we read sensors/pwms not there we'll just read crap
this can't hurt. We need the detection because we don't want
unwanted writes, which will hurt! */
for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) {
if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i,
&data->bank2_value[i], 1,
ABIT_UGURU_MAX_RETRIES) != 1)
goto abituguru_probe_error;
if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2+1, i,
data->bank2_settings[i], 2,
ABIT_UGURU_MAX_RETRIES) != 2)
goto abituguru_probe_error;
}
for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) {
if (abituguru_read(data, ABIT_UGURU_FAN_PWM, i,
data->pwm_settings[i], 5,
ABIT_UGURU_MAX_RETRIES) != 5)
goto abituguru_probe_error;
}
data->last_updated = jiffies;
/* Detect sensor types and fill the sysfs attr for bank1 */
sysfs_attr_i = 0;
sysfs_filename = data->sysfs_names;
sysfs_names_free = ABITUGURU_SYSFS_NAMES_LENGTH;
for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
res = abituguru_detect_bank1_sensor_type(data, probe_order[i]);
if (res < 0)
goto abituguru_probe_error;
if (res == ABIT_UGURU_NC)
continue;
/* res 1 (temp) sensors have 7 sysfs entries, 0 (in) 9 */
for (j = 0; j < (res ? 7 : 9); j++) {
used = snprintf(sysfs_filename, sysfs_names_free,
abituguru_sysfs_bank1_templ[res][j].dev_attr.
attr.name, data->bank1_sensors[res] + res)
+ 1;
data->sysfs_attr[sysfs_attr_i] =
abituguru_sysfs_bank1_templ[res][j];
data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
sysfs_filename;
data->sysfs_attr[sysfs_attr_i].index = probe_order[i];
sysfs_filename += used;
sysfs_names_free -= used;
sysfs_attr_i++;
}
data->bank1_max_value[probe_order[i]] =
abituguru_bank1_max_value[res];
data->bank1_address[res][data->bank1_sensors[res]] =
probe_order[i];
data->bank1_sensors[res]++;
}
/* Detect number of sensors and fill the sysfs attr for bank2 (fans) */
abituguru_detect_no_bank2_sensors(data);
for (i = 0; i < data->bank2_sensors; i++) {
for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_fan_templ); j++) {
used = snprintf(sysfs_filename, sysfs_names_free,
abituguru_sysfs_fan_templ[j].dev_attr.attr.name,
i + 1) + 1;
data->sysfs_attr[sysfs_attr_i] =
abituguru_sysfs_fan_templ[j];
data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
sysfs_filename;
data->sysfs_attr[sysfs_attr_i].index = i;
sysfs_filename += used;
sysfs_names_free -= used;
sysfs_attr_i++;
}
}
/* Detect number of sensors and fill the sysfs attr for pwms */
abituguru_detect_no_pwms(data);
for (i = 0; i < data->pwms; i++) {
for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_pwm_templ); j++) {
used = snprintf(sysfs_filename, sysfs_names_free,
abituguru_sysfs_pwm_templ[j].dev_attr.attr.name,
i + 1) + 1;
data->sysfs_attr[sysfs_attr_i] =
abituguru_sysfs_pwm_templ[j];
data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
sysfs_filename;
data->sysfs_attr[sysfs_attr_i].index = i;
sysfs_filename += used;
sysfs_names_free -= used;
sysfs_attr_i++;
}
}
/* Fail safe check, this should never happen! */
if (sysfs_names_free < 0) {
printk(KERN_ERR ABIT_UGURU_NAME ": Fatal error ran out of "
"space for sysfs attr names. This should never "
"happen please report to the abituguru maintainer "
"(see MAINTAINERS)\n");
res = -ENAMETOOLONG;
goto abituguru_probe_error;
}
printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n");
/* Register sysfs hooks */
data->class_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->class_dev)) {
res = PTR_ERR(data->class_dev);
goto abituguru_probe_error;
}
for (i = 0; i < sysfs_attr_i; i++)
device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
device_create_file(&pdev->dev,
&abituguru_sysfs_attr[i].dev_attr);
return 0;
abituguru_probe_error:
kfree(data);
return res;
}
static int __devexit abituguru_remove(struct platform_device *pdev)
{
struct abituguru_data *data = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
hwmon_device_unregister(data->class_dev);
kfree(data);
return 0;
}
static struct abituguru_data *abituguru_update_device(struct device *dev)
{
int i, err;
struct abituguru_data *data = dev_get_drvdata(dev);
/* fake a complete successful read if no update necessary. */
char success = 1;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ)) {
success = 0;
if ((err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
data->alarms, 3, 0)) != 3)
goto LEAVE_UPDATE;
for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
if ((err = abituguru_read(data,
ABIT_UGURU_SENSOR_BANK1, i,
&data->bank1_value[i], 1, 0)) != 1)
goto LEAVE_UPDATE;
if ((err = abituguru_read(data,
ABIT_UGURU_SENSOR_BANK1 + 1, i,
data->bank1_settings[i], 3, 0)) != 3)
goto LEAVE_UPDATE;
}
for (i = 0; i < data->bank2_sensors; i++)
if ((err = abituguru_read(data,
ABIT_UGURU_SENSOR_BANK2, i,
&data->bank2_value[i], 1, 0)) != 1)
goto LEAVE_UPDATE;
/* success! */
success = 1;
data->update_timeouts = 0;
LEAVE_UPDATE:
/* handle timeout condition */
if (err == -EBUSY) {
/* No overflow please */
if (data->update_timeouts < 255u)
data->update_timeouts++;
if (data->update_timeouts <= ABIT_UGURU_MAX_TIMEOUTS) {
ABIT_UGURU_DEBUG(3, "timeout exceeded, will "
"try again next update\n");
/* Just a timeout, fake a successful read */
success = 1;
} else
ABIT_UGURU_DEBUG(1, "timeout exceeded %d "
"times waiting for more input state\n",
(int)data->update_timeouts);
}
/* On success set last_updated */
if (success)
data->last_updated = jiffies;
}
mutex_unlock(&data->update_lock);
if (success)
return data;
else
return NULL;
}
static struct platform_driver abituguru_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ABIT_UGURU_NAME,
},
.probe = abituguru_probe,
.remove = __devexit_p(abituguru_remove),
};
static int __init abituguru_detect(void)
{
/* See if there is an uguru there. After a reboot uGuru will hold 0x00
at DATA and 0xAC, when this driver has already been loaded once
DATA will hold 0x08. For most uGuru's CMD will hold 0xAC in either
scenario but some will hold 0x00.
Some uGuru's initally hold 0x09 at DATA and will only hold 0x08
after reading CMD first, so CMD must be read first! */
u8 cmd_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_CMD);
u8 data_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_DATA);
if (((data_val == 0x00) || (data_val == 0x08)) &&
((cmd_val == 0x00) || (cmd_val == 0xAC)))
return ABIT_UGURU_BASE;
ABIT_UGURU_DEBUG(2, "no Abit uGuru found, data = 0x%02X, cmd = "
"0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val);
if (force) {
printk(KERN_INFO ABIT_UGURU_NAME ": Assuming Abit uGuru is "
"present because of \"force\" parameter\n");
return ABIT_UGURU_BASE;
}
/* No uGuru found */
return -ENODEV;
}
static struct platform_device *abituguru_pdev;
static int __init abituguru_init(void)
{
int address, err;
struct resource res = { .flags = IORESOURCE_IO };
address = abituguru_detect();
if (address < 0)
return address;
err = platform_driver_register(&abituguru_driver);
if (err)
goto exit;
abituguru_pdev = platform_device_alloc(ABIT_UGURU_NAME, address);
if (!abituguru_pdev) {
printk(KERN_ERR ABIT_UGURU_NAME
": Device allocation failed\n");
err = -ENOMEM;
goto exit_driver_unregister;
}
res.start = address;
res.end = address + ABIT_UGURU_REGION_LENGTH - 1;
res.name = ABIT_UGURU_NAME;
err = platform_device_add_resources(abituguru_pdev, &res, 1);
if (err) {
printk(KERN_ERR ABIT_UGURU_NAME
": Device resource addition failed (%d)\n", err);
goto exit_device_put;
}
err = platform_device_add(abituguru_pdev);
if (err) {
printk(KERN_ERR ABIT_UGURU_NAME
": Device addition failed (%d)\n", err);
goto exit_device_put;
}
return 0;
exit_device_put:
platform_device_put(abituguru_pdev);
exit_driver_unregister:
platform_driver_unregister(&abituguru_driver);
exit:
return err;
}
static void __exit abituguru_exit(void)
{
platform_device_unregister(abituguru_pdev);
platform_driver_unregister(&abituguru_driver);
}
MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
MODULE_DESCRIPTION("Abit uGuru Sensor device");
MODULE_LICENSE("GPL");
module_init(abituguru_init);
module_exit(abituguru_exit);
...@@ -99,10 +99,6 @@ superio_exit(int base) ...@@ -99,10 +99,6 @@ superio_exit(int base)
#define ADDR_REG_OFFSET 0 #define ADDR_REG_OFFSET 0
#define DATA_REG_OFFSET 1 #define DATA_REG_OFFSET 1
static struct resource f71805f_resource __initdata = {
.flags = IORESOURCE_IO,
};
/* /*
* Registers * Registers
*/ */
...@@ -782,6 +778,11 @@ static struct platform_driver f71805f_driver = { ...@@ -782,6 +778,11 @@ static struct platform_driver f71805f_driver = {
static int __init f71805f_device_add(unsigned short address) static int __init f71805f_device_add(unsigned short address)
{ {
struct resource res = {
.start = address,
.end = address + REGION_LENGTH - 1,
.flags = IORESOURCE_IO,
};
int err; int err;
pdev = platform_device_alloc(DRVNAME, address); pdev = platform_device_alloc(DRVNAME, address);
...@@ -791,10 +792,8 @@ static int __init f71805f_device_add(unsigned short address) ...@@ -791,10 +792,8 @@ static int __init f71805f_device_add(unsigned short address)
goto exit; goto exit;
} }
f71805f_resource.start = address; res.name = pdev->name;
f71805f_resource.end = address + REGION_LENGTH - 1; err = platform_device_add_resources(pdev, &res, 1);
f71805f_resource.name = pdev->name;
err = platform_device_add_resources(pdev, &f71805f_resource, 1);
if (err) { if (err) {
printk(KERN_ERR DRVNAME ": Device resource addition failed " printk(KERN_ERR DRVNAME ": Device resource addition failed "
"(%d)\n", err); "(%d)\n", err);
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
#define HDAPS_PORT_STATE 0x1611 /* device state */ #define HDAPS_PORT_STATE 0x1611 /* device state */
#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ #define HDAPS_PORT_YPOS 0x1612 /* y-axis position */
#define HDAPS_PORT_XPOS 0x1614 /* x-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_YVAR 0x1617 /* y-axis variance (what is this?) */
#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ #define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */
#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ #define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */
...@@ -522,13 +522,15 @@ static int __init hdaps_init(void) ...@@ -522,13 +522,15 @@ static int __init hdaps_init(void)
{ {
int ret; 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[] = { struct dmi_system_id hdaps_whitelist[] = {
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), HDAPS_DMI_MATCH_NORMAL("ThinkPad H"),
HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), /* R52 (1846AQG) */
HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"), HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"), HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"),
HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"),
...@@ -536,9 +538,9 @@ static int __init hdaps_init(void) ...@@ -536,9 +538,9 @@ static int __init hdaps_init(void)
HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"),
HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"), HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad X41 Tablet"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"),
HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"), HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad Z60m"),
{ .ident = NULL } { .ident = NULL }
}; };
......
...@@ -58,11 +58,20 @@ ...@@ -58,11 +58,20 @@
doesn't seem to be any named specification for these. The conversion doesn't seem to be any named specification for these. The conversion
tables are detailed directly in the various Pentium M datasheets: tables are detailed directly in the various Pentium M datasheets:
http://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm 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. /* vrm is the VRM/VRD document version multiplied by 10.
val is the 4-, 5- or 6-bit VID code. val is the 4-bit or more VID code.
Returned value is in mV to avoid floating point in the kernel. */ 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_from_reg(int val, u8 vrm)
{ {
int vid; int vid;
...@@ -70,26 +79,36 @@ int vid_from_reg(int val, u8 vrm) ...@@ -70,26 +79,36 @@ int vid_from_reg(int val, u8 vrm)
switch(vrm) { switch(vrm) {
case 100: /* VRD 10.0 */ case 100: /* VRD 10.0 */
/* compute in uV, round to mV */
val &= 0x3f;
if((val & 0x1f) == 0x1f) if((val & 0x1f) == 0x1f)
return 0; return 0;
if((val & 0x1f) <= 0x09 || val == 0x0a) if((val & 0x1f) <= 0x09 || val == 0x0a)
vid = 10875 - (val & 0x1f) * 250; vid = 1087500 - (val & 0x1f) * 25000;
else else
vid = 18625 - (val & 0x1f) * 250; vid = 1862500 - (val & 0x1f) * 25000;
if(val & 0x20) if(val & 0x20)
vid -= 125; vid -= 12500;
vid /= 10; /* only return 3 dec. places for now */ return((vid + 500) / 1000);
return vid;
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 */ case 24: /* Opteron processor */
val &= 0x1f;
return(val == 0x1f ? 0 : 1550 - val * 25); return(val == 0x1f ? 0 : 1550 - val * 25);
case 91: /* VRM 9.1 */ case 91: /* VRM 9.1 */
case 90: /* VRM 9.0 */ case 90: /* VRM 9.0 */
val &= 0x1f;
return(val == 0x1f ? 0 : return(val == 0x1f ? 0 :
1850 - val * 25); 1850 - val * 25);
case 85: /* VRM 8.5 */ case 85: /* VRM 8.5 */
val &= 0x1f;
return((val & 0x10 ? 25 : 0) + return((val & 0x10 ? 25 : 0) +
((val & 0x0f) > 0x04 ? 2050 : 1250) - ((val & 0x0f) > 0x04 ? 2050 : 1250) -
((val & 0x0f) * 50)); ((val & 0x0f) * 50));
...@@ -98,14 +117,21 @@ int vid_from_reg(int val, u8 vrm) ...@@ -98,14 +117,21 @@ int vid_from_reg(int val, u8 vrm)
val &= 0x0f; val &= 0x0f;
/* fall through */ /* fall through */
case 82: /* VRM 8.2 */ case 82: /* VRM 8.2 */
val &= 0x1f;
return(val == 0x1f ? 0 : return(val == 0x1f ? 0 :
val & 0x10 ? 5100 - (val) * 100 : val & 0x10 ? 5100 - (val) * 100 :
2050 - (val) * 50); 2050 - (val) * 50);
case 17: /* Intel IMVP-II */ case 17: /* Intel IMVP-II */
val &= 0x1f;
return(val & 0x10 ? 975 - (val & 0xF) * 25 : return(val & 0x10 ? 975 - (val & 0xF) * 25 :
1750 - val * 50); 1750 - val * 50);
case 13: 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 */ default: /* report 0 for unknown */
printk(KERN_INFO "hwmon-vid: requested unknown VRM version\n"); printk(KERN_INFO "hwmon-vid: requested unknown VRM version\n");
return 0; return 0;
...@@ -138,6 +164,8 @@ static struct vrm_model vrm_models[] = { ...@@ -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, 0x9, ANY, 13}, /* Pentium M (130 nm) */
{X86_VENDOR_INTEL, 0x6, 0xB, ANY, 85}, /* Tualatin */ {X86_VENDOR_INTEL, 0x6, 0xB, ANY, 85}, /* Tualatin */
{X86_VENDOR_INTEL, 0x6, 0xD, ANY, 13}, /* Pentium M (90 nm) */ {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, 0x6, ANY, ANY, 82}, /* any P6 */
{X86_VENDOR_INTEL, 0x7, ANY, ANY, 0}, /* Itanium */ {X86_VENDOR_INTEL, 0x7, ANY, ANY, 0}, /* Itanium */
{X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90}, /* P4 */ {X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90}, /* P4 */
......
/*
* 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 @@ ...@@ -12,6 +12,10 @@
* Since the datasheet omits to give the chip stepping code, I give it * Since the datasheet omits to give the chip stepping code, I give it
* here: 0x03 (at register 0xff). * 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 * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
...@@ -52,7 +56,7 @@ static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, ...@@ -52,7 +56,7 @@ static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
* Insmod parameters * Insmod parameters
*/ */
I2C_CLIENT_INSMOD_1(lm83); I2C_CLIENT_INSMOD_2(lm83, lm82);
/* /*
* The LM83 registers * The LM83 registers
...@@ -283,6 +287,9 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -283,6 +287,9 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
if (man_id == 0x01) { /* National Semiconductor */ if (man_id == 0x01) { /* National Semiconductor */
if (chip_id == 0x03) { if (chip_id == 0x03) {
kind = lm83; 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) ...@@ -296,6 +303,9 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
if (kind == lm83) { if (kind == lm83) {
name = "lm83"; name = "lm83";
} else
if (kind == lm82) {
name = "lm82";
} }
/* We can fill in the remaining client fields */ /* We can fill in the remaining client fields */
...@@ -319,32 +329,46 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -319,32 +329,46 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_detach; 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, device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_input.dev_attr); &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, device_create_file(&new_client->dev,
&sensor_dev_attr_temp3_input.dev_attr); &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, device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_max.dev_attr); &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, device_create_file(&new_client->dev,
&sensor_dev_attr_temp3_max.dev_attr); &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, device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_crit.dev_attr); &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, device_create_file(&new_client->dev,
&sensor_dev_attr_temp3_crit.dev_attr); &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); 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; return 0;
exit_detach: exit_detach:
......
/*
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 @@ ...@@ -30,10 +30,7 @@
Supports the following chips: Supports the following chips:
Chip #vin #fan #pwm #temp chip_id man_id Chip #vin #fan #pwm #temp chip_id man_id
w83627ehf - 5 - 3 0x88 0x5ca3 w83627ehf 10 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.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -121,6 +118,14 @@ superio_exit(void) ...@@ -121,6 +118,14 @@ superio_exit(void)
static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; 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 0x27
#define W83627EHF_REG_TEMP1_HYST 0x3a #define W83627EHF_REG_TEMP1_HYST 0x3a
#define W83627EHF_REG_TEMP1_OVER 0x39 #define W83627EHF_REG_TEMP1_OVER 0x39
...@@ -136,6 +141,10 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 }; ...@@ -136,6 +141,10 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
#define W83627EHF_REG_DIODE 0x59 #define W83627EHF_REG_DIODE 0x59
#define W83627EHF_REG_SMI_OVT 0x4C #define W83627EHF_REG_SMI_OVT 0x4C
#define W83627EHF_REG_ALARM1 0x459
#define W83627EHF_REG_ALARM2 0x45A
#define W83627EHF_REG_ALARM3 0x45B
/* /*
* Conversions * Conversions
*/ */
...@@ -172,6 +181,20 @@ temp1_to_reg(int temp) ...@@ -172,6 +181,20 @@ temp1_to_reg(int temp)
return (temp + 500) / 1000; 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 * Data structures and manipulation thereof
*/ */
...@@ -186,6 +209,9 @@ struct w83627ehf_data { ...@@ -186,6 +209,9 @@ struct w83627ehf_data {
unsigned long last_updated; /* In jiffies */ unsigned long last_updated; /* In jiffies */
/* Register values */ /* Register values */
u8 in[10]; /* Register value */
u8 in_max[10]; /* Register value */
u8 in_min[10]; /* Register value */
u8 fan[5]; u8 fan[5];
u8 fan_min[5]; u8 fan_min[5];
u8 fan_div[5]; u8 fan_div[5];
...@@ -196,6 +222,7 @@ struct w83627ehf_data { ...@@ -196,6 +222,7 @@ struct w83627ehf_data {
s16 temp[2]; s16 temp[2];
s16 temp_max[2]; s16 temp_max[2];
s16 temp_max_hyst[2]; s16 temp_max_hyst[2];
u32 alarms;
}; };
static inline int is_word_sized(u16 reg) static inline int is_word_sized(u16 reg)
...@@ -349,6 +376,16 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) ...@@ -349,6 +376,16 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
data->fan_div[3] |= (i >> 5) & 0x04; 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 */ /* Measured fan speeds and limits */
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
if (!(data->has_fan & (1 << i))) if (!(data->has_fan & (1 << i)))
...@@ -395,6 +432,13 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) ...@@ -395,6 +432,13 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
W83627EHF_REG_TEMP_HYST[i]); 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->last_updated = jiffies;
data->valid = 1; data->valid = 1;
} }
...@@ -406,6 +450,109 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) ...@@ -406,6 +450,109 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
/* /*
* Sysfs callback functions * 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) \ #define show_fan_reg(reg) \
static ssize_t \ static ssize_t \
...@@ -505,6 +652,14 @@ static struct sensor_device_attribute sda_fan_input[] = { ...@@ -505,6 +652,14 @@ static struct sensor_device_attribute sda_fan_input[] = {
SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), 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[] = { static struct sensor_device_attribute sda_fan_min[] = {
SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
store_fan_min, 0), store_fan_min, 0),
...@@ -529,6 +684,7 @@ static struct sensor_device_attribute sda_fan_div[] = { ...@@ -529,6 +684,7 @@ static struct sensor_device_attribute sda_fan_div[] = {
static void device_create_file_fan(struct device *dev, int i) 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_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_div[i].dev_attr);
device_create_file(dev, &sda_fan_min[i].dev_attr); device_create_file(dev, &sda_fan_min[i].dev_attr);
} }
...@@ -616,6 +772,9 @@ static struct sensor_device_attribute sda_temp[] = { ...@@ -616,6 +772,9 @@ static struct sensor_device_attribute sda_temp[] = {
store_temp_max_hyst, 0), store_temp_max_hyst, 0),
SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
store_temp_max_hyst, 1), 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) ...@@ -705,6 +864,9 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
goto exit_detach; goto exit_detach;
} }
for (i = 0; i < 10; i++)
device_create_file_in(dev, i);
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
if (data->has_fan & (1 << i)) if (data->has_fan & (1 << i))
device_create_file_fan(dev, i); device_create_file_fan(dev, i);
......
/*
w83791d.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (C) 2006 Charles Spirakis <bezaur@gmail.com>
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.
*/
/*
Supports following chips:
Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
w83791d 10 5 3 3 0x71 0x5ca3 yes no
The w83791d chip appears to be part way between the 83781d and the
83792d. Thus, this file is derived from both the w83792d.c and
w83781d.c files, but its output is more along the lines of the
83781d (which means there are no changes to the user-mode sensors
program which treats the 83791d as an 83781d).
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#define NUMBER_OF_VIN 10
#define NUMBER_OF_FANIN 5
#define NUMBER_OF_TEMPIN 3
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD_1(w83791d);
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
static int reset;
module_param(reset, bool, 0);
MODULE_PARM_DESC(reset, "Set to one to force a hardware chip reset");
static int init;
module_param(init, bool, 0);
MODULE_PARM_DESC(init, "Set to one to force extra software initialization");
/* The W83791D registers */
static const u8 W83791D_REG_IN[NUMBER_OF_VIN] = {
0x20, /* VCOREA in DataSheet */
0x21, /* VINR0 in DataSheet */
0x22, /* +3.3VIN in DataSheet */
0x23, /* VDD5V in DataSheet */
0x24, /* +12VIN in DataSheet */
0x25, /* -12VIN in DataSheet */
0x26, /* -5VIN in DataSheet */
0xB0, /* 5VSB in DataSheet */
0xB1, /* VBAT in DataSheet */
0xB2 /* VINR1 in DataSheet */
};
static const u8 W83791D_REG_IN_MAX[NUMBER_OF_VIN] = {
0x2B, /* VCOREA High Limit in DataSheet */
0x2D, /* VINR0 High Limit in DataSheet */
0x2F, /* +3.3VIN High Limit in DataSheet */
0x31, /* VDD5V High Limit in DataSheet */
0x33, /* +12VIN High Limit in DataSheet */
0x35, /* -12VIN High Limit in DataSheet */
0x37, /* -5VIN High Limit in DataSheet */
0xB4, /* 5VSB High Limit in DataSheet */
0xB6, /* VBAT High Limit in DataSheet */
0xB8 /* VINR1 High Limit in DataSheet */
};
static const u8 W83791D_REG_IN_MIN[NUMBER_OF_VIN] = {
0x2C, /* VCOREA Low Limit in DataSheet */
0x2E, /* VINR0 Low Limit in DataSheet */
0x30, /* +3.3VIN Low Limit in DataSheet */
0x32, /* VDD5V Low Limit in DataSheet */
0x34, /* +12VIN Low Limit in DataSheet */
0x36, /* -12VIN Low Limit in DataSheet */
0x38, /* -5VIN Low Limit in DataSheet */
0xB5, /* 5VSB Low Limit in DataSheet */
0xB7, /* VBAT Low Limit in DataSheet */
0xB9 /* VINR1 Low Limit in DataSheet */
};
static const u8 W83791D_REG_FAN[NUMBER_OF_FANIN] = {
0x28, /* FAN 1 Count in DataSheet */
0x29, /* FAN 2 Count in DataSheet */
0x2A, /* FAN 3 Count in DataSheet */
0xBA, /* FAN 4 Count in DataSheet */
0xBB, /* FAN 5 Count in DataSheet */
};
static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = {
0x3B, /* FAN 1 Count Low Limit in DataSheet */
0x3C, /* FAN 2 Count Low Limit in DataSheet */
0x3D, /* FAN 3 Count Low Limit in DataSheet */
0xBC, /* FAN 4 Count Low Limit in DataSheet */
0xBD, /* FAN 5 Count Low Limit in DataSheet */
};
static const u8 W83791D_REG_FAN_CFG[2] = {
0x84, /* FAN 1/2 configuration */
0x95, /* FAN 3 configuration */
};
static const u8 W83791D_REG_FAN_DIV[3] = {
0x47, /* contains FAN1 and FAN2 Divisor */
0x4b, /* contains FAN3 Divisor */
0x5C, /* contains FAN4 and FAN5 Divisor */
};
#define W83791D_REG_BANK 0x4E
#define W83791D_REG_TEMP2_CONFIG 0xC2
#define W83791D_REG_TEMP3_CONFIG 0xCA
static const u8 W83791D_REG_TEMP1[3] = {
0x27, /* TEMP 1 in DataSheet */
0x39, /* TEMP 1 Over in DataSheet */
0x3A, /* TEMP 1 Hyst in DataSheet */
};
static const u8 W83791D_REG_TEMP_ADD[2][6] = {
{0xC0, /* TEMP 2 in DataSheet */
0xC1, /* TEMP 2(0.5 deg) in DataSheet */
0xC5, /* TEMP 2 Over High part in DataSheet */
0xC6, /* TEMP 2 Over Low part in DataSheet */
0xC3, /* TEMP 2 Thyst High part in DataSheet */
0xC4}, /* TEMP 2 Thyst Low part in DataSheet */
{0xC8, /* TEMP 3 in DataSheet */
0xC9, /* TEMP 3(0.5 deg) in DataSheet */
0xCD, /* TEMP 3 Over High part in DataSheet */
0xCE, /* TEMP 3 Over Low part in DataSheet */
0xCB, /* TEMP 3 Thyst High part in DataSheet */
0xCC} /* TEMP 3 Thyst Low part in DataSheet */
};
#define W83791D_REG_BEEP_CONFIG 0x4D
static const u8 W83791D_REG_BEEP_CTRL[3] = {
0x56, /* BEEP Control Register 1 */
0x57, /* BEEP Control Register 2 */
0xA3, /* BEEP Control Register 3 */
};
#define W83791D_REG_CONFIG 0x40
#define W83791D_REG_VID_FANDIV 0x47
#define W83791D_REG_DID_VID4 0x49
#define W83791D_REG_WCHIPID 0x58
#define W83791D_REG_CHIPMAN 0x4F
#define W83791D_REG_PIN 0x4B
#define W83791D_REG_I2C_SUBADDR 0x4A
#define W83791D_REG_ALARM1 0xA9 /* realtime status register1 */
#define W83791D_REG_ALARM2 0xAA /* realtime status register2 */
#define W83791D_REG_ALARM3 0xAB /* realtime status register3 */
#define W83791D_REG_VBAT 0x5D
#define W83791D_REG_I2C_ADDR 0x48
/* The SMBus locks itself. The Winbond W83791D has a bank select register
(index 0x4e), but the driver only accesses registers in bank 0. Since
we don't switch banks, we don't need any special code to handle
locking access between bank switches */
static inline int w83791d_read(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static inline int w83791d_write(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/* The analog voltage inputs have 16mV LSB. Since the sysfs output is
in mV as would be measured on the chip input pin, need to just
multiply/divide by 16 to translate from/to register values. */
#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255))
#define IN_FROM_REG(val) ((val) * 16)
static u8 fan_to_reg(long rpm, int div)
{
if (rpm == 0)
return 255;
rpm = SENSORS_LIMIT(rpm, 1, 1000000);
return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
}
#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \
((val) == 255 ? 0 : \
1350000 / ((val) * (div))))
/* for temp1 which is 8-bit resolution, LSB = 1 degree Celsius */
#define TEMP1_FROM_REG(val) ((val) * 1000)
#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
/* for temp2 and temp3 which are 9-bit resolution, LSB = 0.5 degree Celsius
Assumes the top 8 bits are the integral amount and the bottom 8 bits
are the fractional amount. Since we only have 0.5 degree resolution,
the bottom 7 bits will always be zero */
#define TEMP23_FROM_REG(val) ((val) / 128 * 500)
#define TEMP23_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
(val) >= 127500 ? 0x7F80 : \
(val) < 0 ? ((val) - 250) / 500 * 128 : \
((val) + 250) / 500 * 128)
#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
#define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff)
#define DIV_FROM_REG(val) (1 << (val))
static u8 div_to_reg(int nr, long val)
{
int i;
int max;
/* first three fan's divisor max out at 8, rest max out at 128 */
max = (nr < 3) ? 8 : 128;
val = SENSORS_LIMIT(val, 1, max) >> 1;
for (i = 0; i < 7; i++) {
if (val == 0)
break;
val >>= 1;
}
return (u8) i;
}
struct w83791d_data {
struct i2c_client client;
struct class_device *class_dev;
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* array of 2 pointers to subclients */
struct i2c_client *lm75[2];
/* volts */
u8 in[NUMBER_OF_VIN]; /* Register value */
u8 in_max[NUMBER_OF_VIN]; /* Register value */
u8 in_min[NUMBER_OF_VIN]; /* Register value */
/* fans */
u8 fan[NUMBER_OF_FANIN]; /* Register value */
u8 fan_min[NUMBER_OF_FANIN]; /* Register value */
u8 fan_div[NUMBER_OF_FANIN]; /* Register encoding, shifted right */
/* Temperature sensors */
s8 temp1[3]; /* current, over, thyst */
s16 temp_add[2][3]; /* fixed point value. Top 8 bits are the
integral part, bottom 8 bits are the
fractional part. We only use the top
9 bits as the resolution is only
to the 0.5 degree C...
two sensors with three values
(cur, over, hyst) */
/* Misc */
u32 alarms; /* realtime status register encoding,combined */
u8 beep_enable; /* Global beep enable */
u32 beep_mask; /* Mask off specific beeps */
u8 vid; /* Register encoding, combined */
u8 vrm; /* hwmon-vid */
};
static int w83791d_attach_adapter(struct i2c_adapter *adapter);
static int w83791d_detect(struct i2c_adapter *adapter, int address, int kind);
static int w83791d_detach_client(struct i2c_client *client);
static int w83791d_read(struct i2c_client *client, u8 register);
static int w83791d_write(struct i2c_client *client, u8 register, u8 value);
static struct w83791d_data *w83791d_update_device(struct device *dev);
#ifdef DEBUG
static void w83791d_print_debug(struct w83791d_data *data, struct device *dev);
#endif
static void w83791d_init_client(struct i2c_client *client);
static struct i2c_driver w83791d_driver = {
.driver = {
.name = "w83791d",
},
.attach_adapter = w83791d_attach_adapter,
.detach_client = w83791d_detach_client,
};
/* following are the sysfs callback functions */
#define show_in_reg(reg) \
static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
struct w83791d_data *data = w83791d_update_device(dev); \
int nr = sensor_attr->index; \
return sprintf(buf,"%d\n", IN_FROM_REG(data->reg[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 sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
struct i2c_client *client = to_i2c_client(dev); \
struct w83791d_data *data = i2c_get_clientdata(client); \
unsigned long val = simple_strtoul(buf, NULL, 10); \
int nr = sensor_attr->index; \
\
mutex_lock(&data->update_lock); \
data->in_##reg[nr] = IN_TO_REG(val); \
w83791d_write(client, W83791D_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 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_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),
};
#define show_fan_reg(reg) \
static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
struct w83791d_data *data = w83791d_update_device(dev); \
int nr = sensor_attr->index; \
return sprintf(buf,"%d\n", \
FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
}
show_fan_reg(fan);
show_fan_reg(fan_min);
static ssize_t store_fan_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);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
int nr = sensor_attr->index;
mutex_lock(&data->update_lock);
data->fan_min[nr] = fan_to_reg(val, DIV_FROM_REG(data->fan_div[nr]));
w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t show_fan_div(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 w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr]));
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
least suprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
unsigned long min;
u8 tmp_fan_div;
u8 fan_div_reg;
int indx = 0;
u8 keep_mask = 0;
u8 new_shift = 0;
/* Save fan_min */
min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
mutex_lock(&data->update_lock);
data->fan_div[nr] = div_to_reg(nr, simple_strtoul(buf, NULL, 10));
switch (nr) {
case 0:
indx = 0;
keep_mask = 0xcf;
new_shift = 4;
break;
case 1:
indx = 0;
keep_mask = 0x3f;
new_shift = 6;
break;
case 2:
indx = 1;
keep_mask = 0x3f;
new_shift = 6;
break;
case 3:
indx = 2;
keep_mask = 0xf8;
new_shift = 0;
break;
case 4:
indx = 2;
keep_mask = 0x8f;
new_shift = 4;
break;
#ifdef DEBUG
default:
dev_warn(dev, "store_fan_div: Unexpected nr seen: %d\n", nr);
count = -EINVAL;
goto err_exit;
#endif
}
fan_div_reg = w83791d_read(client, W83791D_REG_FAN_DIV[indx])
& keep_mask;
tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask;
w83791d_write(client, W83791D_REG_FAN_DIV[indx],
fan_div_reg | tmp_fan_div);
/* Restore fan_min */
data->fan_min[nr] = fan_to_reg(min, DIV_FROM_REG(data->fan_div[nr]));
w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]);
#ifdef DEBUG
err_exit:
#endif
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_fan_input[] = {
SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
};
static struct sensor_device_attribute sda_fan_min[] = {
SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO,
show_fan_min, store_fan_min, 0),
SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO,
show_fan_min, store_fan_min, 1),
SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO,
show_fan_min, store_fan_min, 2),
SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO,
show_fan_min, store_fan_min, 3),
SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO,
show_fan_min, store_fan_min, 4),
};
static struct sensor_device_attribute sda_fan_div[] = {
SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO,
show_fan_div, store_fan_div, 0),
SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO,
show_fan_div, store_fan_div, 1),
SENSOR_ATTR(fan3_div, S_IWUSR | S_IRUGO,
show_fan_div, store_fan_div, 2),
SENSOR_ATTR(fan4_div, S_IWUSR | S_IRUGO,
show_fan_div, store_fan_div, 3),
SENSOR_ATTR(fan5_div, S_IWUSR | S_IRUGO,
show_fan_div, store_fan_div, 4),
};
/* read/write the temperature1, includes measured value and limits */
static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp1[attr->index]));
}
static ssize_t store_temp1(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
int nr = attr->index;
mutex_lock(&data->update_lock);
data->temp1[nr] = TEMP1_TO_REG(val);
w83791d_write(client, W83791D_REG_TEMP1[nr], data->temp1[nr]);
mutex_unlock(&data->update_lock);
return count;
}
/* read/write temperature2-3, includes measured value and limits */
static ssize_t show_temp23(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct w83791d_data *data = w83791d_update_device(dev);
int nr = attr->nr;
int index = attr->index;
return sprintf(buf, "%d\n", TEMP23_FROM_REG(data->temp_add[nr][index]));
}
static ssize_t store_temp23(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
int nr = attr->nr;
int index = attr->index;
mutex_lock(&data->update_lock);
data->temp_add[nr][index] = TEMP23_TO_REG(val);
w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2],
data->temp_add[nr][index] >> 8);
w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2 + 1],
data->temp_add[nr][index] & 0x80);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute_2 sda_temp_input[] = {
SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0),
SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0),
SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0),
};
static struct sensor_device_attribute_2 sda_temp_max[] = {
SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
show_temp1, store_temp1, 0, 1),
SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
show_temp23, store_temp23, 0, 1),
SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR,
show_temp23, store_temp23, 1, 1),
};
static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
show_temp1, store_temp1, 0, 2),
SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
show_temp23, store_temp23, 0, 2),
SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR,
show_temp23, store_temp23, 1, 2),
};
/* get reatime status of all sensors items: voltage, temp, fan */
static ssize_t show_alarms_reg(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
/* Beep control */
#define GLOBAL_BEEP_ENABLE_SHIFT 15
#define GLOBAL_BEEP_ENABLE_MASK (1 << GLOBAL_BEEP_ENABLE_SHIFT)
static ssize_t show_beep_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%d\n", data->beep_enable);
}
static ssize_t show_beep_mask(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%d\n", BEEP_MASK_FROM_REG(data->beep_mask));
}
static ssize_t store_beep_mask(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
int i;
mutex_lock(&data->update_lock);
/* The beep_enable state overrides any enabling request from
the masks */
data->beep_mask = BEEP_MASK_TO_REG(val) & ~GLOBAL_BEEP_ENABLE_MASK;
data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT);
val = data->beep_mask;
for (i = 0; i < 3; i++) {
w83791d_write(client, W83791D_REG_BEEP_CTRL[i], (val & 0xff));
val >>= 8;
}
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t store_beep_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->beep_enable = val ? 1 : 0;
/* Keep the full mask value in sync with the current enable */
data->beep_mask &= ~GLOBAL_BEEP_ENABLE_MASK;
data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT);
/* The global control is in the second beep control register
so only need to update that register */
val = (data->beep_mask >> 8) & 0xff;
w83791d_write(client, W83791D_REG_BEEP_CTRL[1], val);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_beep_ctrl[] = {
SENSOR_ATTR(beep_enable, S_IRUGO | S_IWUSR,
show_beep_enable, store_beep_enable, 0),
SENSOR_ATTR(beep_mask, S_IRUGO | S_IWUSR,
show_beep_mask, store_beep_mask, 1)
};
/* cpu voltage regulation information */
static ssize_t show_vid_reg(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
static ssize_t show_vrm_reg(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%d\n", data->vrm);
}
static ssize_t store_vrm_reg(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
/* No lock needed as vrm is internal to the driver
(not read from a chip register) and so is not
updated in w83791d_update_device() */
data->vrm = val;
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
/* This function is called when:
* w83791d_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and w83791d_driver is still present) */
static int w83791d_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_probe(adapter, &addr_data, w83791d_detect);
}
static int w83791d_create_subclient(struct i2c_adapter *adapter,
struct i2c_client *client, int addr,
struct i2c_client **sub_cli)
{
int err;
struct i2c_client *sub_client;
(*sub_cli) = sub_client =
kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!(sub_client)) {
return -ENOMEM;
}
sub_client->addr = 0x48 + addr;
i2c_set_clientdata(sub_client, NULL);
sub_client->adapter = adapter;
sub_client->driver = &w83791d_driver;
strlcpy(sub_client->name, "w83791d subclient", I2C_NAME_SIZE);
if ((err = i2c_attach_client(sub_client))) {
dev_err(&client->dev, "subclient registration "
"at address 0x%x failed\n", sub_client->addr);
kfree(sub_client);
return err;
}
return 0;
}
static int w83791d_detect_subclients(struct i2c_adapter *adapter, int address,
int kind, struct i2c_client *client)
{
struct w83791d_data *data = i2c_get_clientdata(client);
int i, id, err;
u8 val;
id = i2c_adapter_id(adapter);
if (force_subclients[0] == id && force_subclients[1] == address) {
for (i = 2; i <= 3; i++) {
if (force_subclients[i] < 0x48 ||
force_subclients[i] > 0x4f) {
dev_err(&client->dev,
"invalid subclient "
"address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -ENODEV;
goto error_sc_0;
}
}
w83791d_write(client, W83791D_REG_I2C_SUBADDR,
(force_subclients[2] & 0x07) |
((force_subclients[3] & 0x07) << 4));
}
val = w83791d_read(client, W83791D_REG_I2C_SUBADDR);
if (!(val & 0x08)) {
err = w83791d_create_subclient(adapter, client,
val & 0x7, &data->lm75[0]);
if (err < 0)
goto error_sc_0;
}
if (!(val & 0x80)) {
if ((data->lm75[0] != NULL) &&
((val & 0x7) == ((val >> 4) & 0x7))) {
dev_err(&client->dev,
"duplicate addresses 0x%x, "
"use force_subclient\n",
data->lm75[0]->addr);
err = -ENODEV;
goto error_sc_1;
}
err = w83791d_create_subclient(adapter, client,
(val >> 4) & 0x7, &data->lm75[1]);
if (err < 0)
goto error_sc_1;
}
return 0;
/* Undo inits in case of errors */
error_sc_1:
if (data->lm75[0] != NULL) {
i2c_detach_client(data->lm75[0]);
kfree(data->lm75[0]);
}
error_sc_0:
return err;
}
static int w83791d_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
struct device *dev;
struct w83791d_data *data;
int i, val1, val2;
int err = 0;
const char *client_name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
goto error0;
}
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access w83791d_{read,write}_value. */
if (!(data = kzalloc(sizeof(struct w83791d_data), GFP_KERNEL))) {
err = -ENOMEM;
goto error0;
}
client = &data->client;
dev = &client->dev;
i2c_set_clientdata(client, data);
client->addr = address;
client->adapter = adapter;
client->driver = &w83791d_driver;
mutex_init(&data->update_lock);
/* Now, we do the remaining detection. */
/* The w83791d may be stuck in some other bank than bank 0. This may
make reading other information impossible. Specify a force=...
parameter, and the Winbond will be reset to the right bank. */
if (kind < 0) {
if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) {
dev_dbg(dev, "Detection failed at step 1\n");
goto error1;
}
val1 = w83791d_read(client, W83791D_REG_BANK);
val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
/* Check for Winbond ID if in bank 0 */
if (!(val1 & 0x07)) {
/* yes it is Bank0 */
if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
((val1 & 0x80) && (val2 != 0x5c))) {
dev_dbg(dev, "Detection failed at step 2\n");
goto error1;
}
}
/* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR
should match */
if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) {
dev_dbg(dev, "Detection failed at step 3\n");
goto error1;
}
}
/* We either have a force parameter or we have reason to
believe it is a Winbond chip. Either way, we want bank 0 and
Vendor ID high byte */
val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78;
w83791d_write(client, W83791D_REG_BANK, val1 | 0x80);
/* Verify it is a Winbond w83791d */
if (kind <= 0) {
/* get vendor ID */
val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
if (val2 != 0x5c) { /* the vendor is NOT Winbond */
dev_dbg(dev, "Detection failed at step 4\n");
goto error1;
}
val1 = w83791d_read(client, W83791D_REG_WCHIPID);
if (val1 == 0x71) {
kind = w83791d;
} else {
if (kind == 0)
dev_warn(dev,
"w83791d: Ignoring 'force' parameter "
"for unknown chip at adapter %d, "
"address 0x%02x\n",
i2c_adapter_id(adapter), address);
goto error1;
}
}
if (kind == w83791d) {
client_name = "w83791d";
} else {
dev_err(dev, "w83791d: Internal error: unknown kind (%d)?!?",
kind);
goto error1;
}
#ifdef DEBUG
val1 = w83791d_read(client, W83791D_REG_DID_VID4);
dev_dbg(dev, "Device ID version: %d.%d (0x%02x)\n",
(val1 >> 5) & 0x07, (val1 >> 1) & 0x0f, val1);
#endif
/* Fill in the remaining client fields and put into the global list */
strlcpy(client->name, client_name, I2C_NAME_SIZE);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(client)))
goto error1;
if ((err = w83791d_detect_subclients(adapter, address, kind, client)))
goto error2;
/* Initialize the chip */
w83791d_init_client(client);
/* If the fan_div is changed, make sure there is a rational
fan_min in place */
for (i = 0; i < NUMBER_OF_FANIN; i++) {
data->fan_min[i] = w83791d_read(client, W83791D_REG_FAN_MIN[i]);
}
/* Register sysfs hooks */
data->class_dev = hwmon_device_register(dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
goto error3;
}
for (i = 0; i < NUMBER_OF_VIN; i++) {
device_create_file(dev, &sda_in_input[i].dev_attr);
device_create_file(dev, &sda_in_min[i].dev_attr);
device_create_file(dev, &sda_in_max[i].dev_attr);
}
for (i = 0; i < NUMBER_OF_FANIN; i++) {
device_create_file(dev, &sda_fan_input[i].dev_attr);
device_create_file(dev, &sda_fan_div[i].dev_attr);
device_create_file(dev, &sda_fan_min[i].dev_attr);
}
for (i = 0; i < NUMBER_OF_TEMPIN; i++) {
device_create_file(dev, &sda_temp_input[i].dev_attr);
device_create_file(dev, &sda_temp_max[i].dev_attr);
device_create_file(dev, &sda_temp_max_hyst[i].dev_attr);
}
device_create_file(dev, &dev_attr_alarms);
for (i = 0; i < ARRAY_SIZE(sda_beep_ctrl); i++) {
device_create_file(dev, &sda_beep_ctrl[i].dev_attr);
}
device_create_file(dev, &dev_attr_cpu0_vid);
device_create_file(dev, &dev_attr_vrm);
return 0;
error3:
if (data->lm75[0] != NULL) {
i2c_detach_client(data->lm75[0]);
kfree(data->lm75[0]);
}
if (data->lm75[1] != NULL) {
i2c_detach_client(data->lm75[1]);
kfree(data->lm75[1]);
}
error2:
i2c_detach_client(client);
error1:
kfree(data);
error0:
return err;
}
static int w83791d_detach_client(struct i2c_client *client)
{
struct w83791d_data *data = i2c_get_clientdata(client);
int err;
/* main client */
if (data)
hwmon_device_unregister(data->class_dev);
if ((err = i2c_detach_client(client)))
return err;
/* main client */
if (data)
kfree(data);
/* subclient */
else
kfree(client);
return 0;
}
static void w83791d_init_client(struct i2c_client *client)
{
struct w83791d_data *data = i2c_get_clientdata(client);
u8 tmp;
u8 old_beep;
/* The difference between reset and init is that reset
does a hard reset of the chip via index 0x40, bit 7,
but init simply forces certain registers to have "sane"
values. The hope is that the BIOS has done the right
thing (which is why the default is reset=0, init=0),
but if not, reset is the hard hammer and init
is the soft mallet both of which are trying to whack
things into place...
NOTE: The data sheet makes a distinction between
"power on defaults" and "reset by MR". As far as I can tell,
the hard reset puts everything into a power-on state so I'm
not sure what "reset by MR" means or how it can happen.
*/
if (reset || init) {
/* keep some BIOS settings when we... */
old_beep = w83791d_read(client, W83791D_REG_BEEP_CONFIG);
if (reset) {
/* ... reset the chip and ... */
w83791d_write(client, W83791D_REG_CONFIG, 0x80);
}
/* ... disable power-on abnormal beep */
w83791d_write(client, W83791D_REG_BEEP_CONFIG, old_beep | 0x80);
/* disable the global beep (not done by hard reset) */
tmp = w83791d_read(client, W83791D_REG_BEEP_CTRL[1]);
w83791d_write(client, W83791D_REG_BEEP_CTRL[1], tmp & 0xef);
if (init) {
/* Make sure monitoring is turned on for add-ons */
tmp = w83791d_read(client, W83791D_REG_TEMP2_CONFIG);
if (tmp & 1) {
w83791d_write(client, W83791D_REG_TEMP2_CONFIG,
tmp & 0xfe);
}
tmp = w83791d_read(client, W83791D_REG_TEMP3_CONFIG);
if (tmp & 1) {
w83791d_write(client, W83791D_REG_TEMP3_CONFIG,
tmp & 0xfe);
}
/* Start monitoring */
tmp = w83791d_read(client, W83791D_REG_CONFIG) & 0xf7;
w83791d_write(client, W83791D_REG_CONFIG, tmp | 0x01);
}
}
data->vrm = vid_which_vrm();
}
static struct w83791d_data *w83791d_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int i, j;
u8 reg_array_tmp[3];
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + (HZ * 3))
|| !data->valid) {
dev_dbg(dev, "Starting w83791d device update\n");
/* Update the voltages measured value and limits */
for (i = 0; i < NUMBER_OF_VIN; i++) {
data->in[i] = w83791d_read(client,
W83791D_REG_IN[i]);
data->in_max[i] = w83791d_read(client,
W83791D_REG_IN_MAX[i]);
data->in_min[i] = w83791d_read(client,
W83791D_REG_IN_MIN[i]);
}
/* Update the fan counts and limits */
for (i = 0; i < NUMBER_OF_FANIN; i++) {
/* Update the Fan measured value and limits */
data->fan[i] = w83791d_read(client,
W83791D_REG_FAN[i]);
data->fan_min[i] = w83791d_read(client,
W83791D_REG_FAN_MIN[i]);
}
/* Update the fan divisor */
for (i = 0; i < 3; i++) {
reg_array_tmp[i] = w83791d_read(client,
W83791D_REG_FAN_DIV[i]);
}
data->fan_div[0] = (reg_array_tmp[0] >> 4) & 0x03;
data->fan_div[1] = (reg_array_tmp[0] >> 6) & 0x03;
data->fan_div[2] = (reg_array_tmp[1] >> 6) & 0x03;
data->fan_div[3] = reg_array_tmp[2] & 0x07;
data->fan_div[4] = (reg_array_tmp[2] >> 4) & 0x07;
/* Update the first temperature sensor */
for (i = 0; i < 3; i++) {
data->temp1[i] = w83791d_read(client,
W83791D_REG_TEMP1[i]);
}
/* Update the rest of the temperature sensors */
for (i = 0; i < 2; i++) {
for (j = 0; j < 3; j++) {
data->temp_add[i][j] =
(w83791d_read(client,
W83791D_REG_TEMP_ADD[i][j * 2]) << 8) |
w83791d_read(client,
W83791D_REG_TEMP_ADD[i][j * 2 + 1]);
}
}
/* Update the realtime status */
data->alarms =
w83791d_read(client, W83791D_REG_ALARM1) +
(w83791d_read(client, W83791D_REG_ALARM2) << 8) +
(w83791d_read(client, W83791D_REG_ALARM3) << 16);
/* Update the beep configuration information */
data->beep_mask =
w83791d_read(client, W83791D_REG_BEEP_CTRL[0]) +
(w83791d_read(client, W83791D_REG_BEEP_CTRL[1]) << 8) +
(w83791d_read(client, W83791D_REG_BEEP_CTRL[2]) << 16);
data->beep_enable =
(data->beep_mask >> GLOBAL_BEEP_ENABLE_SHIFT) & 0x01;
/* Update the cpu voltage information */
i = w83791d_read(client, W83791D_REG_VID_FANDIV);
data->vid = i & 0x0f;
data->vid |= (w83791d_read(client, W83791D_REG_DID_VID4) & 0x01)
<< 4;
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
#ifdef DEBUG
w83791d_print_debug(data, dev);
#endif
return data;
}
#ifdef DEBUG
static void w83791d_print_debug(struct w83791d_data *data, struct device *dev)
{
int i = 0, j = 0;
dev_dbg(dev, "======Start of w83791d debug values======\n");
dev_dbg(dev, "%d set of Voltages: ===>\n", NUMBER_OF_VIN);
for (i = 0; i < NUMBER_OF_VIN; i++) {
dev_dbg(dev, "vin[%d] is: 0x%02x\n", i, data->in[i]);
dev_dbg(dev, "vin[%d] min is: 0x%02x\n", i, data->in_min[i]);
dev_dbg(dev, "vin[%d] max is: 0x%02x\n", i, data->in_max[i]);
}
dev_dbg(dev, "%d set of Fan Counts/Divisors: ===>\n", NUMBER_OF_FANIN);
for (i = 0; i < NUMBER_OF_FANIN; i++) {
dev_dbg(dev, "fan[%d] is: 0x%02x\n", i, data->fan[i]);
dev_dbg(dev, "fan[%d] min is: 0x%02x\n", i, data->fan_min[i]);
dev_dbg(dev, "fan_div[%d] is: 0x%02x\n", i, data->fan_div[i]);
}
/* temperature math is signed, but only print out the
bits that matter */
dev_dbg(dev, "%d set of Temperatures: ===>\n", NUMBER_OF_TEMPIN);
for (i = 0; i < 3; i++) {
dev_dbg(dev, "temp1[%d] is: 0x%02x\n", i, (u8) data->temp1[i]);
}
for (i = 0; i < 2; i++) {
for (j = 0; j < 3; j++) {
dev_dbg(dev, "temp_add[%d][%d] is: 0x%04x\n", i, j,
(u16) data->temp_add[i][j]);
}
}
dev_dbg(dev, "Misc Information: ===>\n");
dev_dbg(dev, "alarm is: 0x%08x\n", data->alarms);
dev_dbg(dev, "beep_mask is: 0x%08x\n", data->beep_mask);
dev_dbg(dev, "beep_enable is: %d\n", data->beep_enable);
dev_dbg(dev, "vid is: 0x%02x\n", data->vid);
dev_dbg(dev, "vrm is: 0x%02x\n", data->vrm);
dev_dbg(dev, "=======End of w83791d debug values========\n");
dev_dbg(dev, "\n");
}
#endif
static int __init sensors_w83791d_init(void)
{
return i2c_add_driver(&w83791d_driver);
}
static void __exit sensors_w83791d_exit(void)
{
i2c_del_driver(&w83791d_driver);
}
MODULE_AUTHOR("Charles Spirakis <bezaur@gmail.com>");
MODULE_DESCRIPTION("W83791D driver");
MODULE_LICENSE("GPL");
module_init(sensors_w83791d_init);
module_exit(sensors_w83791d_exit);
...@@ -250,8 +250,6 @@ FAN_TO_REG(long rpm, int div) ...@@ -250,8 +250,6 @@ FAN_TO_REG(long rpm, int div)
: (val)) / 1000, 0, 0xff)) : (val)) / 1000, 0, 0xff))
#define TEMP_ADD_TO_REG_LOW(val) ((val%1000) ? 0x80 : 0x00) #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)) #define DIV_FROM_REG(val) (1 << (val))
static inline u8 static inline u8
...@@ -291,7 +289,6 @@ struct w83792d_data { ...@@ -291,7 +289,6 @@ struct w83792d_data {
u8 pwm[7]; /* We only consider the first 3 set of pwm, u8 pwm[7]; /* We only consider the first 3 set of pwm,
although 792 chip has 7 set of pwm. */ although 792 chip has 7 set of pwm. */
u8 pwmenable[3]; u8 pwmenable[3];
u8 pwm_mode[7]; /* indicates PWM or DC mode: 1->PWM; 0->DC */
u32 alarms; /* realtime status register encoding,combined */ u32 alarms; /* realtime status register encoding,combined */
u8 chassis; /* Chassis status */ u8 chassis; /* Chassis status */
u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */ u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */
...@@ -375,8 +372,10 @@ static ssize_t store_in_##reg (struct device *dev, \ ...@@ -375,8 +372,10 @@ static ssize_t store_in_##reg (struct device *dev, \
u32 val; \ u32 val; \
\ \
val = simple_strtoul(buf, NULL, 10); \ 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); \ 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]); \ w83792d_write_value(client, W83792D_REG_IN_##REG[nr], data->in_##reg[nr]); \
mutex_unlock(&data->update_lock); \
\ \
return count; \ return count; \
} }
...@@ -443,9 +442,11 @@ store_fan_min(struct device *dev, struct device_attribute *attr, ...@@ -443,9 +442,11 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
u32 val; u32 val;
val = simple_strtoul(buf, NULL, 10); 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])); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], w83792d_write_value(client, W83792D_REG_FAN_MIN[nr],
data->fan_min[nr]); data->fan_min[nr]);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -478,6 +479,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr, ...@@ -478,6 +479,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
u8 tmp_fan_div; u8 tmp_fan_div;
/* Save fan_min */ /* Save fan_min */
mutex_lock(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr], min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr])); DIV_FROM_REG(data->fan_div[nr]));
...@@ -493,6 +495,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr, ...@@ -493,6 +495,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
/* Restore fan_min */ /* Restore fan_min */
data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); 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]); w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], data->fan_min[nr]);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -547,10 +550,11 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *attr, ...@@ -547,10 +550,11 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *attr,
s32 val; s32 val;
val = simple_strtol(buf, NULL, 10); val = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->temp1[nr] = TEMP1_TO_REG(val); data->temp1[nr] = TEMP1_TO_REG(val);
w83792d_write_value(client, W83792D_REG_TEMP1[nr], w83792d_write_value(client, W83792D_REG_TEMP1[nr],
data->temp1[nr]); data->temp1[nr]);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -580,13 +584,14 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr, ...@@ -580,13 +584,14 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr,
s32 val; s32 val;
val = simple_strtol(buf, NULL, 10); 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] = TEMP_ADD_TO_REG_HIGH(val);
data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val); data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val);
w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index], w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index],
data->temp_add[nr][index]); data->temp_add[nr][index]);
w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index+1], w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index+1],
data->temp_add[nr][index+1]); data->temp_add[nr][index+1]);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -627,7 +632,7 @@ show_pwm(struct device *dev, struct device_attribute *attr, ...@@ -627,7 +632,7 @@ show_pwm(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index; int nr = sensor_attr->index;
struct w83792d_data *data = w83792d_update_device(dev); 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 static ssize_t
...@@ -659,14 +664,16 @@ store_pwm(struct device *dev, struct device_attribute *attr, ...@@ -659,14 +664,16 @@ store_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); 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 i2c_client *client = to_i2c_client(dev);
struct w83792d_data *data = i2c_get_clientdata(client); 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); mutex_lock(&data->update_lock);
data->pwm[nr] = PWM_TO_REG(val); 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]); w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -683,6 +690,10 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, ...@@ -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; u8 fan_cfg_tmp, cfg1_tmp, cfg2_tmp, cfg3_tmp, cfg4_tmp;
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
if (val < 1 || val > 3)
return -EINVAL;
mutex_lock(&data->update_lock);
switch (val) { switch (val) {
case 1: case 1:
data->pwmenable[nr] = 0; /* manual mode */ data->pwmenable[nr] = 0; /* manual mode */
...@@ -693,8 +704,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, ...@@ -693,8 +704,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
case 3: case 3:
data->pwmenable[nr] = 1; /* thermal cruise/Smart Fan I */ data->pwmenable[nr] = 1; /* thermal cruise/Smart Fan I */
break; break;
default:
return -EINVAL;
} }
cfg1_tmp = data->pwmenable[0]; cfg1_tmp = data->pwmenable[0];
cfg2_tmp = (data->pwmenable[1]) << 2; cfg2_tmp = (data->pwmenable[1]) << 2;
...@@ -702,14 +711,15 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, ...@@ -702,14 +711,15 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
cfg4_tmp = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0; cfg4_tmp = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0;
fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp; fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp;
w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp); w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp);
mutex_unlock(&data->update_lock);
return count; return count;
} }
static struct sensor_device_attribute sda_pwm[] = { static struct sensor_device_attribute sda_pwm[] = {
SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2), SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3), SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
}; };
static struct sensor_device_attribute sda_pwm_enable[] = { static struct sensor_device_attribute sda_pwm_enable[] = {
SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
...@@ -728,7 +738,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, ...@@ -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); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index; int nr = sensor_attr->index;
struct w83792d_data *data = w83792d_update_device(dev); 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 static ssize_t
...@@ -736,29 +746,35 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, ...@@ -736,29 +746,35 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); 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 i2c_client *client = to_i2c_client(dev);
struct w83792d_data *data = i2c_get_clientdata(client); struct w83792d_data *data = i2c_get_clientdata(client);
u32 val; u32 val;
u8 pwm_mode_mask = 0;
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
data->pwm_mode[nr] = SENSORS_LIMIT(val, 0, 1); if (val != 0 && val != 1)
pwm_mode_mask = w83792d_read_value(client, return -EINVAL;
W83792D_REG_PWM[nr]) & 0x7f;
w83792d_write_value(client, W83792D_REG_PWM[nr], mutex_lock(&data->update_lock);
((data->pwm_mode[nr]) << 7) | pwm_mode_mask); 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; return count;
} }
static struct sensor_device_attribute sda_pwm_mode[] = { static struct sensor_device_attribute sda_pwm_mode[] = {
SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, 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, 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, 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, ...@@ -789,12 +805,13 @@ store_chassis_clear(struct device *dev, struct device_attribute *attr,
u8 temp1 = 0, temp2 = 0; u8 temp1 = 0, temp2 = 0;
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->chassis_clear = SENSORS_LIMIT(val, 0 ,1); data->chassis_clear = SENSORS_LIMIT(val, 0 ,1);
temp1 = ((data->chassis_clear) << 7) & 0x80; temp1 = ((data->chassis_clear) << 7) & 0x80;
temp2 = w83792d_read_value(client, temp2 = w83792d_read_value(client,
W83792D_REG_CHASSIS_CLR) & 0x7f; W83792D_REG_CHASSIS_CLR) & 0x7f;
w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2); w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -827,10 +844,12 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr, ...@@ -827,10 +844,12 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr,
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
target_tmp = val; target_tmp = val;
target_tmp = target_tmp & 0x7f; target_tmp = target_tmp & 0x7f;
mutex_lock(&data->update_lock);
target_mask = w83792d_read_value(client, W83792D_REG_THERMAL[nr]) & 0x80; target_mask = w83792d_read_value(client, W83792D_REG_THERMAL[nr]) & 0x80;
data->thermal_cruise[nr] = SENSORS_LIMIT(target_tmp, 0, 255); data->thermal_cruise[nr] = SENSORS_LIMIT(target_tmp, 0, 255);
w83792d_write_value(client, W83792D_REG_THERMAL[nr], w83792d_write_value(client, W83792D_REG_THERMAL[nr],
(data->thermal_cruise[nr]) | target_mask); (data->thermal_cruise[nr]) | target_mask);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -867,6 +886,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr, ...@@ -867,6 +886,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
u8 tol_tmp, tol_mask; u8 tol_tmp, tol_mask;
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
tol_mask = w83792d_read_value(client, tol_mask = w83792d_read_value(client,
W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0); W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0);
tol_tmp = SENSORS_LIMIT(val, 0, 15); tol_tmp = SENSORS_LIMIT(val, 0, 15);
...@@ -877,6 +897,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr, ...@@ -877,6 +897,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
} }
w83792d_write_value(client, W83792D_REG_TOLERANCE[nr], w83792d_write_value(client, W83792D_REG_TOLERANCE[nr],
tol_mask | tol_tmp); tol_mask | tol_tmp);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -915,11 +936,13 @@ store_sf2_point(struct device *dev, struct device_attribute *attr, ...@@ -915,11 +936,13 @@ store_sf2_point(struct device *dev, struct device_attribute *attr,
u8 mask_tmp = 0; u8 mask_tmp = 0;
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->sf2_points[index][nr] = SENSORS_LIMIT(val, 0, 127); data->sf2_points[index][nr] = SENSORS_LIMIT(val, 0, 127);
mask_tmp = w83792d_read_value(client, mask_tmp = w83792d_read_value(client,
W83792D_REG_POINTS[index][nr]) & 0x80; W83792D_REG_POINTS[index][nr]) & 0x80;
w83792d_write_value(client, W83792D_REG_POINTS[index][nr], w83792d_write_value(client, W83792D_REG_POINTS[index][nr],
mask_tmp|data->sf2_points[index][nr]); mask_tmp|data->sf2_points[index][nr]);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -979,6 +1002,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr, ...@@ -979,6 +1002,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
u8 mask_tmp=0, level_tmp=0; u8 mask_tmp=0, level_tmp=0;
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->sf2_levels[index][nr] = SENSORS_LIMIT((val * 15) / 100, 0, 15); data->sf2_levels[index][nr] = SENSORS_LIMIT((val * 15) / 100, 0, 15);
mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr]) mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr])
& ((nr==3) ? 0xf0 : 0x0f); & ((nr==3) ? 0xf0 : 0x0f);
...@@ -988,6 +1012,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr, ...@@ -988,6 +1012,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
level_tmp = data->sf2_levels[index][nr] << 4; level_tmp = data->sf2_levels[index][nr] << 4;
} }
w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], level_tmp | mask_tmp); w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], level_tmp | mask_tmp);
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -1373,7 +1398,7 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) ...@@ -1373,7 +1398,7 @@ static struct w83792d_data *w83792d_update_device(struct device *dev)
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct w83792d_data *data = i2c_get_clientdata(client); struct w83792d_data *data = i2c_get_clientdata(client);
int i, j; 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); mutex_lock(&data->update_lock);
...@@ -1402,10 +1427,8 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) ...@@ -1402,10 +1427,8 @@ static struct w83792d_data *w83792d_update_device(struct device *dev)
data->fan_min[i] = w83792d_read_value(client, data->fan_min[i] = w83792d_read_value(client,
W83792D_REG_FAN_MIN[i]); W83792D_REG_FAN_MIN[i]);
/* Update the PWM/DC Value and PWM/DC flag */ /* 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]); 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); 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) ...@@ -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] 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, "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[%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"); dev_dbg(dev, "3 set of Temperatures: =====>\n");
for (i=0; i<3; i++) { for (i=0; i<3; i++) {
......
...@@ -163,7 +163,7 @@ config I2C_PXA_SLAVE ...@@ -163,7 +163,7 @@ config I2C_PXA_SLAVE
I2C bus. I2C bus.
config I2C_PIIX4 config I2C_PIIX4
tristate "Intel PIIX4" tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)"
depends on I2C && PCI depends on I2C && PCI
help help
If you say yes to this option, support will be included for the Intel If you say yes to this option, support will be included for the Intel
...@@ -172,6 +172,9 @@ config I2C_PIIX4 ...@@ -172,6 +172,9 @@ config I2C_PIIX4
of Broadcom): of Broadcom):
Intel PIIX4 Intel PIIX4
Intel 440MX Intel 440MX
ATI IXP200
ATI IXP300
ATI IXP400
Serverworks OSB4 Serverworks OSB4
Serverworks CSB5 Serverworks CSB5
Serverworks CSB6 Serverworks CSB6
...@@ -273,6 +276,17 @@ config I2C_NFORCE2 ...@@ -273,6 +276,17 @@ config I2C_NFORCE2
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-nforce2. 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 config I2C_PARPORT
tristate "Parallel port adapter" tristate "Parallel port adapter"
depends on I2C && PARPORT depends on I2C && PARPORT
...@@ -500,6 +514,7 @@ config I2C_PCA_ISA ...@@ -500,6 +514,7 @@ config I2C_PCA_ISA
tristate "PCA9564 on an ISA bus" tristate "PCA9564 on an ISA bus"
depends on I2C depends on I2C
select I2C_ALGOPCA select I2C_ALGOPCA
default n
help help
This driver supports ISA boards using the Philips PCA 9564 This driver supports ISA boards using the Philips PCA 9564
Parallel bus to I2C bus controller Parallel bus to I2C bus controller
...@@ -507,6 +522,11 @@ config I2C_PCA_ISA ...@@ -507,6 +522,11 @@ config I2C_PCA_ISA
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-pca-isa. 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 config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller" tristate "Marvell mv64xxx I2C Controller"
depends on I2C && MV64X60 && EXPERIMENTAL depends on I2C && MV64X60 && EXPERIMENTAL
......
...@@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o ...@@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.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) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
......
/* /*
i801.c - Part of lm_sensors, Linux kernel modules for hardware i2c-i801.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
This driver supports several versions of Intel's I/O Controller Hubs (ICH). 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 For SMBus support, they are similar to the PIIX4 and are part
of Intel's '810' and other chipsets. 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. I2C Block Read and Process Call are not supported.
*/ */
...@@ -66,9 +66,8 @@ ...@@ -66,9 +66,8 @@
#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ #define SMBAUXCTL (13 + i801_smba) /* ICH4 only */
/* PCI Address Constants */ /* PCI Address Constants */
#define SMBBA 0x020 #define SMBBAR 4
#define SMBHSTCFG 0x040 #define SMBHSTCFG 0x040
#define SMBREV 0x008
/* Host configuration bits for SMBHSTCFG */ /* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN 1 #define SMBHSTCFG_HST_EN 1
...@@ -92,92 +91,16 @@ ...@@ -92,92 +91,16 @@
#define I801_START 0x40 #define I801_START 0x40
#define I801_PEC_EN 0x80 /* ICH4 only */ #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_transaction(void);
static int i801_block_transaction(union i2c_smbus_data *data, char read_write, static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
int command, int hwpec); int command, int hwpec);
static unsigned short i801_smba; static unsigned long i801_smba;
static struct pci_driver i801_driver; static struct pci_driver i801_driver;
static struct pci_dev *I801_dev; static struct pci_dev *I801_dev;
static int isich4; 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) static int i801_transaction(void)
{ {
int temp; int temp;
...@@ -334,8 +257,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, ...@@ -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! */ /* We will always wait for a fraction of a second! */
timeout = 0; timeout = 0;
do { do {
temp = inb_p(SMBHSTSTS);
msleep(1); msleep(1);
temp = inb_p(SMBHSTSTS);
} }
while ((!(temp & 0x80)) while ((!(temp & 0x80))
&& (timeout++ < MAX_TIMEOUT)); && (timeout++ < MAX_TIMEOUT));
...@@ -393,8 +316,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, ...@@ -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 */ /* wait for INTR bit as advised by Intel */
timeout = 0; timeout = 0;
do { do {
temp = inb_p(SMBHSTSTS);
msleep(1); msleep(1);
temp = inb_p(SMBHSTSTS);
} while ((!(temp & 0x02)) } while ((!(temp & 0x02))
&& (timeout++ < MAX_TIMEOUT)); && (timeout++ < MAX_TIMEOUT));
...@@ -541,25 +464,76 @@ MODULE_DEVICE_TABLE (pci, i801_ids); ...@@ -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) static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{ {
unsigned char temp;
int err;
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;
}
if (i801_setup(dev)) { pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
dev_warn(&dev->dev, temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
"I801 not detected, module not inserted.\n"); if (!(temp & SMBHSTCFG_HST_EN)) {
return -ENODEV; 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 */ /* set up the driverfs linkage to our parent device */
i801_adapter.dev.parent = &dev->dev; i801_adapter.dev.parent = &dev->dev;
snprintf(i801_adapter.name, I2C_NAME_SIZE, snprintf(i801_adapter.name, I2C_NAME_SIZE,
"SMBus I801 adapter at %04x", i801_smba); "SMBus I801 adapter at %04lx", i801_smba);
return i2c_add_adapter(&i801_adapter); 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) static void __devexit i801_remove(struct pci_dev *dev)
{ {
i2c_del_adapter(&i801_adapter); 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 = { static struct pci_driver i801_driver = {
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
nForce3 250Gb MCP 00E4 nForce3 250Gb MCP 00E4
nForce4 MCP 0052 nForce4 MCP 0052
nForce4 MCP-04 0034 nForce4 MCP-04 0034
nForce4 MCP51 0264
nForce4 MCP55 0368
This driver supports the 2 SMBuses that are included in the MCP of the This driver supports the 2 SMBuses that are included in the MCP of the
nForce2/3/4 chipsets. nForce2/3/4 chipsets.
...@@ -64,6 +66,7 @@ struct nforce2_smbus { ...@@ -64,6 +66,7 @@ struct nforce2_smbus {
/* /*
* nVidia nForce2 SMBus control register definitions * nVidia nForce2 SMBus control register definitions
* (Newer incarnations use standard BARs 4 and 5 instead)
*/ */
#define NFORCE_PCI_SMB1 0x50 #define NFORCE_PCI_SMB1 0x50
#define NFORCE_PCI_SMB2 0x54 #define NFORCE_PCI_SMB2 0x54
...@@ -259,6 +262,8 @@ static struct pci_device_id nforce2_ids[] = { ...@@ -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_NFORCE3S_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_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_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 } { 0 }
}; };
...@@ -266,19 +271,29 @@ static struct pci_device_id nforce2_ids[] = { ...@@ -266,19 +271,29 @@ static struct pci_device_id nforce2_ids[] = {
MODULE_DEVICE_TABLE (pci, nforce2_ids); MODULE_DEVICE_TABLE (pci, nforce2_ids);
static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg, static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar,
struct nforce2_smbus *smbus, char *name) int alt_reg, struct nforce2_smbus *smbus, const char *name)
{ {
u16 iobase;
int error; int error;
if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) { smbus->base = pci_resource_start(dev, bar);
dev_err(&smbus->adapter.dev, "Error reading PCI config for %s\n", name); if (smbus->base) {
return -1; 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->dev = dev;
smbus->base = iobase & 0xfffc;
smbus->size = 8;
if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) { if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) {
dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n", 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_ ...@@ -313,12 +328,13 @@ static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_
pci_set_drvdata(dev, smbuses); pci_set_drvdata(dev, smbuses);
/* SMBus adapter 1 */ /* 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) { if (res1 < 0) {
dev_err(&dev->dev, "Error probing SMB1.\n"); dev_err(&dev->dev, "Error probing SMB1.\n");
smbuses[0].base = 0; /* to have a check value */ 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) { if (res2 < 0) {
dev_err(&dev->dev, "Error probing SMB2.\n"); dev_err(&dev->dev, "Error probing SMB2.\n");
smbuses[1].base = 0; /* to have a check value */ smbuses[1].base = 0; /* to have a check value */
......
/*
* 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, ...@@ -102,13 +102,6 @@ MODULE_PARM_DESC(force_addr,
"Forcibly enable the PIIX4 at the given address. " "Forcibly enable the PIIX4 at the given address. "
"EXTREMELY DANGEROUS!"); "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 int piix4_transaction(void);
static unsigned short piix4_smba; static unsigned short piix4_smba;
...@@ -137,7 +130,7 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, ...@@ -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 */ /* Don't access SMBus on IBM systems which get corrupted eeproms */
if (dmi_check_system(piix4_dmi_table) && if (dmi_check_system(piix4_dmi_table) &&
PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { 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 " "may corrupt your serial eeprom! Refusing to load "
"module!\n"); "module!\n");
return -EPERM; return -EPERM;
...@@ -166,22 +159,6 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, ...@@ -166,22 +159,6 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); 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 /* If force_addr is set, we program the new address here. Just to make
sure, we disable the PIIX4 first. */ sure, we disable the PIIX4 first. */
if (force_addr) { if (force_addr) {
...@@ -214,7 +191,7 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, ...@@ -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"); dev_dbg(&PIIX4_dev->dev, "Using Interrupt 9 for SMBus.\n");
else if ((temp & 0x0E) == 0) else if ((temp & 0x0E) == 0)
dev_dbg(&PIIX4_dev->dev, "Using Interrupt SMI# for SMBus.\n"); dev_dbg(&PIIX4_dev->dev, "Using Interrupt SMI# for SMBus.\n");
...@@ -413,6 +390,12 @@ static struct i2c_adapter piix4_adapter = { ...@@ -413,6 +390,12 @@ static struct i2c_adapter piix4_adapter = {
static struct pci_device_id piix4_ids[] = { static struct pci_device_id piix4_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3), { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3),
.driver_data = 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), { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4),
.driver_data = 0 }, .driver_data = 0 },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5), { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5),
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/msr.h>
#include <linux/scx200.h> #include <linux/scx200.h>
...@@ -85,6 +84,10 @@ struct scx200_acb_iface { ...@@ -85,6 +84,10 @@ struct scx200_acb_iface {
u8 *ptr; u8 *ptr;
char needs_reset; char needs_reset;
unsigned len; unsigned len;
/* PCI device info */
struct pci_dev *pdev;
int bar;
}; };
/* Register Definitions */ /* Register Definitions */
...@@ -381,7 +384,7 @@ static struct i2c_algorithm scx200_acb_algorithm = { ...@@ -381,7 +384,7 @@ static struct i2c_algorithm scx200_acb_algorithm = {
static struct scx200_acb_iface *scx200_acb_list; static struct scx200_acb_iface *scx200_acb_list;
static DECLARE_MUTEX(scx200_acb_list_mutex); 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; u8 val;
...@@ -417,17 +420,16 @@ static int scx200_acb_probe(struct scx200_acb_iface *iface) ...@@ -417,17 +420,16 @@ static int scx200_acb_probe(struct scx200_acb_iface *iface)
return 0; 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 scx200_acb_iface *iface;
struct i2c_adapter *adapter; struct i2c_adapter *adapter;
int rc;
iface = kzalloc(sizeof(*iface), GFP_KERNEL); iface = kzalloc(sizeof(*iface), GFP_KERNEL);
if (!iface) { if (!iface) {
printk(KERN_ERR NAME ": can't allocate memory\n"); printk(KERN_ERR NAME ": can't allocate memory\n");
rc = -ENOMEM; return NULL;
goto errout;
} }
adapter = &iface->adapter; adapter = &iface->adapter;
...@@ -440,26 +442,27 @@ static int __init scx200_acb_create(const char *text, int base, int index) ...@@ -440,26 +442,27 @@ static int __init scx200_acb_create(const char *text, int base, int index)
mutex_init(&iface->mutex); mutex_init(&iface->mutex);
if (!request_region(base, 8, adapter->name)) { return iface;
printk(KERN_ERR NAME ": can't allocate io 0x%x-0x%x\n", }
base, base + 8-1);
rc = -EBUSY; static int __init scx200_acb_create(struct scx200_acb_iface *iface)
goto errout_free; {
} struct i2c_adapter *adapter;
iface->base = base; int rc;
adapter = &iface->adapter;
rc = scx200_acb_probe(iface); rc = scx200_acb_probe(iface);
if (rc) { if (rc) {
printk(KERN_WARNING NAME ": probe failed\n"); printk(KERN_WARNING NAME ": probe failed\n");
goto errout_release; return rc;
} }
scx200_acb_reset(iface); scx200_acb_reset(iface);
if (i2c_add_adapter(adapter) < 0) { if (i2c_add_adapter(adapter) < 0) {
printk(KERN_ERR NAME ": failed to register\n"); printk(KERN_ERR NAME ": failed to register\n");
rc = -ENODEV; return -ENODEV;
goto errout_release;
} }
down(&scx200_acb_list_mutex); down(&scx200_acb_list_mutex);
...@@ -468,64 +471,148 @@ static int __init scx200_acb_create(const char *text, int base, int index) ...@@ -468,64 +471,148 @@ static int __init scx200_acb_create(const char *text, int base, int index)
up(&scx200_acb_list_mutex); up(&scx200_acb_list_mutex);
return 0; return 0;
}
errout_release: static __init int scx200_create_pci(const char *text, struct pci_dev *pdev,
release_region(iface->base, 8); 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: errout_free:
kfree(iface); kfree(iface);
errout:
return rc; return rc;
} }
static struct pci_device_id scx200[] = { static int __init scx200_create_isa(const char *text, unsigned long base,
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) }, int index)
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) }, {
{ }, struct scx200_acb_iface *iface;
}; int rc;
iface = scx200_create_iface(text, index);
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;
}
iface->base = base;
rc = scx200_acb_create(iface);
if (rc == 0)
return 0;
static struct pci_device_id divil_pci[] = { release_region(base, 8);
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, errout_free:
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, kfree(iface);
{ } /* NULL entry */ 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 }
}; };
#define MSR_LBAR_SMB 0x5140000B static struct {
const char *name;
int bar;
} scx200_data[] = {
{ "SCx200", -1 },
{ "CS5535", 0 },
{ "CS5536", 0 }
};
static __init int scx200_add_cs553x(void) static __init int scx200_scan_pci(void)
{ {
u32 low, hi; int data, dev;
u32 smb_base; int rc = -ENODEV;
struct pci_dev *pdev;
/* Grab & reserve the SMB I/O range */
rdmsr(MSR_LBAR_SMB, low, hi); 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);
}
}
/* Check the IO mask and whether SMB is enabled */ break;
if (hi != 0x0000F001) {
printk(KERN_WARNING NAME ": SMBus not enabled\n");
return -ENODEV;
} }
/* SMBus IO size is 8 bytes */ return rc;
smb_base = low & 0x0000FFF8;
return scx200_acb_create("CS5535", smb_base, 0);
} }
static int __init scx200_acb_init(void) static int __init scx200_acb_init(void)
{ {
int i; int rc;
int rc = -ENODEV;
pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n"); pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
/* Verify that this really is a SCx200 processor */ rc = scx200_scan_pci();
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();
/* If at least one bus was created, init must succeed */ /* If at least one bus was created, init must succeed */
if (scx200_acb_list) if (scx200_acb_list)
...@@ -543,7 +630,14 @@ static void __exit scx200_acb_cleanup(void) ...@@ -543,7 +630,14 @@ static void __exit scx200_acb_cleanup(void)
up(&scx200_acb_list_mutex); up(&scx200_acb_list_mutex);
i2c_del_adapter(&iface->adapter); 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); kfree(iface);
down(&scx200_acb_list_mutex); down(&scx200_acb_list_mutex);
} }
......
...@@ -39,6 +39,7 @@ config SENSORS_EEPROM ...@@ -39,6 +39,7 @@ config SENSORS_EEPROM
config SENSORS_PCF8574 config SENSORS_PCF8574
tristate "Philips PCF8574 and PCF8574A" tristate "Philips PCF8574 and PCF8574A"
depends on I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
default n
help help
If you say yes here you get support for Philips PCF8574 and If you say yes here you get support for Philips PCF8574 and
PCF8574A chips. PCF8574A chips.
...@@ -46,6 +47,9 @@ config SENSORS_PCF8574 ...@@ -46,6 +47,9 @@ config SENSORS_PCF8574
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called pcf8574. will be called pcf8574.
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
config SENSORS_PCA9539 config SENSORS_PCA9539
tristate "Philips PCA9539 16-bit I/O port" tristate "Philips PCA9539 16-bit I/O port"
depends on I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
...@@ -59,12 +63,16 @@ config SENSORS_PCA9539 ...@@ -59,12 +63,16 @@ config SENSORS_PCA9539
config SENSORS_PCF8591 config SENSORS_PCF8591
tristate "Philips PCF8591" tristate "Philips PCF8591"
depends on I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
default n
help help
If you say yes here you get support for Philips PCF8591 chips. 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 This driver can also be built as a module. If so, the module
will be called pcf8591. will be called pcf8591.
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
config ISP1301_OMAP config ISP1301_OMAP
tristate "Philips ISP1301 with OMAP OTG" tristate "Philips ISP1301 with OMAP OTG"
depends on I2C && ARCH_OMAP_OTG depends on I2C && ARCH_OMAP_OTG
......
/* /*
* drivers/i2c/chips/m41t00.c * I2C client/driver for the ST M41T00 family of i2c rtc chips.
*
* I2C client/driver for the ST M41T00 Real-Time Clock chip.
* *
* Author: Mark A. Greer <mgreer@mvista.com> * 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 * the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express * is licensed "as is" without any warranty of any kind, whether express
* or implied. * or implied.
...@@ -13,9 +11,6 @@ ...@@ -13,9 +11,6 @@
/* /*
* This i2c client/driver wedges between the drivers/char/genrtc.c RTC * This i2c client/driver wedges between the drivers/char/genrtc.c RTC
* interface and the SMBus interface of the i2c subsystem. * 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> #include <linux/kernel.h>
...@@ -24,56 +19,110 @@ ...@@ -24,56 +19,110 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/mutex.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/m41t00.h>
#include <asm/time.h> #include <asm/time.h>
#include <asm/rtc.h> #include <asm/rtc.h>
#define M41T00_DRV_NAME "m41t00"
static DEFINE_MUTEX(m41t00_mutex);
static struct i2c_driver m41t00_driver; static struct i2c_driver m41t00_driver;
static struct i2c_client *save_client; static struct i2c_client *save_client;
static unsigned short ignore[] = { I2C_CLIENT_END }; 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 = { static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr, .normal_i2c = normal_addr,
.probe = ignore, .probe = ignore,
.ignore = 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 ulong
m41t00_get_rtc_time(void) m41t00_get_rtc_time(void)
{ {
s32 sec, min, hour, day, mon, year; s32 sec, min, hour, day, mon, year;
s32 sec1, min1, hour1, day1, mon1, year1; s32 sec1, min1, hour1, day1, mon1, year1;
ulong limit = 10; 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; sec = min = hour = day = mon = year = 0;
sec1 = min1 = hour1 = day1 = mon1 = year1 = 0;
mutex_lock(&m41t00_mutex);
do { do {
if (((sec = i2c_smbus_read_byte_data(save_client, 0)) >= 0) if (i2c_transfer(save_client->adapter, msgs, 2) < 0)
&& ((min = i2c_smbus_read_byte_data(save_client, 1)) goto read_err;
>= 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;
sec1 = sec; sec1 = sec;
min1 = min; min1 = min;
...@@ -81,69 +130,88 @@ m41t00_get_rtc_time(void) ...@@ -81,69 +130,88 @@ m41t00_get_rtc_time(void)
day1 = day; day1 = day;
mon1 = mon; mon1 = mon;
year1 = year; 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 &= 0x7f;
min &= 0x7f;
hour &= 0x3f;
day &= 0x3f;
mon &= 0x1f;
year &= 0xff;
BCD_TO_BIN(sec); sec = buf[m41t00_chip->sec] & 0x7f;
BCD_TO_BIN(min); min = buf[m41t00_chip->min] & 0x7f;
BCD_TO_BIN(hour); hour = buf[m41t00_chip->hour] & 0x3f;
BCD_TO_BIN(day); day = buf[m41t00_chip->day] & 0x3f;
BCD_TO_BIN(mon); mon = buf[m41t00_chip->mon] & 0x1f;
BCD_TO_BIN(year); year = buf[m41t00_chip->year];
} while ((++reads < m41t00_chip->read_limit) && ((sec != sec1)
|| (min != min1) || (hour != hour1) || (day != day1)
|| (mon != mon1) || (year != year1)));
if ((m41t00_chip->read_limit > 1) && ((sec != sec1) || (min != min1)
|| (hour != hour1) || (day != day1) || (mon != mon1)
|| (year != year1)))
goto read_err;
sec = BCD2BIN(sec);
min = BCD2BIN(min);
hour = BCD2BIN(hour);
day = BCD2BIN(day);
mon = BCD2BIN(mon);
year = BCD2BIN(year);
year += 1900; year += 1900;
if (year < 1970) if (year < 1970)
year += 100; year += 100;
return mktime(year, mon, day, hour, min, sec); 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 static void
m41t00_set(void *arg) m41t00_set(void *arg)
{ {
struct rtc_time tm; 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); to_tm(nowtime, &tm);
tm.tm_year = (tm.tm_year - 1900) % 100; tm.tm_year = (tm.tm_year - 1900) % 100;
BIN_TO_BCD(tm.tm_sec); sec = BIN2BCD(tm.tm_sec);
BIN_TO_BCD(tm.tm_min); min = BIN2BCD(tm.tm_min);
BIN_TO_BCD(tm.tm_hour); hour = BIN2BCD(tm.tm_hour);
BIN_TO_BCD(tm.tm_mon); day = BIN2BCD(tm.tm_mday);
BIN_TO_BCD(tm.tm_mday); mon = BIN2BCD(tm.tm_mon);
BIN_TO_BCD(tm.tm_year); year = BIN2BCD(tm.tm_year);
mutex_lock(&m41t00_mutex); /* Read reg values into buf[0..7]/wbuf[1..8] */
if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0) if (i2c_transfer(save_client->adapter, msgs, 2) < 0) {
|| (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f) dev_err(&save_client->dev, "m41t00_set: Read error\n");
< 0) return;
|| (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) wbuf[0] = 0; /* offset into rtc's regs */
< 0) buf[m41t00_chip->sec] = (buf[m41t00_chip->sec] & ~0x7f) | (sec & 0x7f);
|| (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x1f) buf[m41t00_chip->min] = (buf[m41t00_chip->min] & ~0x7f) | (min & 0x7f);
< 0) buf[m41t00_chip->hour] = (buf[m41t00_chip->hour] & ~0x3f) | (hour& 0x3f);
|| (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0xff) buf[m41t00_chip->day] = (buf[m41t00_chip->day] & ~0x3f) | (day & 0x3f);
< 0)) buf[m41t00_chip->mon] = (buf[m41t00_chip->mon] & ~0x1f) | (mon & 0x1f);
dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n"); if (i2c_master_send(save_client, wbuf, 9) < 0)
dev_err(&save_client->dev, "m41t00_set: Write error\n");
mutex_unlock(&m41t00_mutex);
return;
} }
static ulong new_time; static ulong new_time;
...@@ -162,6 +230,48 @@ m41t00_set_rtc_time(ulong nowtime) ...@@ -162,6 +230,48 @@ m41t00_set_rtc_time(ulong nowtime)
return 0; 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) ...@@ -176,23 +286,71 @@ m41t00_probe(struct i2c_adapter *adap, int addr, int kind)
struct i2c_client *client; struct i2c_client *client;
int rc; 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); client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!client) if (!client)
return -ENOMEM; return -ENOMEM;
strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE); strlcpy(client->name, m41t00_chip->name, I2C_NAME_SIZE);
client->addr = addr; client->addr = addr;
client->adapter = adap; client->adapter = adap;
client->driver = &m41t00_driver; client->driver = &m41t00_driver;
if ((rc = i2c_attach_client(client)) != 0) { if ((rc = i2c_attach_client(client)))
kfree(client); goto attach_err;
return rc;
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; save_client = client;
return 0; 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 static int
...@@ -204,7 +362,7 @@ m41t00_attach(struct i2c_adapter *adap) ...@@ -204,7 +362,7 @@ m41t00_attach(struct i2c_adapter *adap)
static int static int
m41t00_detach(struct i2c_client *client) m41t00_detach(struct i2c_client *client)
{ {
int rc; int rc;
if ((rc = i2c_detach_client(client)) == 0) { if ((rc = i2c_detach_client(client)) == 0) {
kfree(client); kfree(client);
...@@ -225,14 +383,18 @@ static struct i2c_driver m41t00_driver = { ...@@ -225,14 +383,18 @@ static struct i2c_driver m41t00_driver = {
static int __init static int __init
m41t00_init(void) 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 static void __exit
m41t00_exit(void) m41t00_exit(void)
{ {
i2c_del_driver(&m41t00_driver); i2c_del_driver(&m41t00_driver);
return; platform_driver_unregister(&m41t00_platform_driver);
} }
module_init(m41t00_init); module_init(m41t00_init);
......
...@@ -916,7 +916,7 @@ s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value) ...@@ -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, 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; union i2c_smbus_data data;
...@@ -944,7 +944,7 @@ s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *val ...@@ -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, 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; union i2c_smbus_data data;
......
...@@ -426,10 +426,7 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap) ...@@ -426,10 +426,7 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap)
/* register this i2c device with the driver core */ /* register this i2c device with the driver core */
i2c_dev->adap = adap; i2c_dev->adap = adap;
if (adap->dev.parent == &platform_bus) dev = &adap->dev;
dev = &adap->dev;
else
dev = adap->dev.parent;
i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL, i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL,
MKDEV(I2C_MAJOR, i2c_dev->minor), MKDEV(I2C_MAJOR, i2c_dev->minor),
dev, "i2c-%d", i2c_dev->minor); dev, "i2c-%d", i2c_dev->minor);
......
/*
* 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, ...@@ -97,13 +97,13 @@ extern s32 i2c_smbus_write_word_data(struct i2c_client * client,
u8 command, u16 value); u8 command, u16 value);
extern s32 i2c_smbus_write_block_data(struct i2c_client * client, extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
u8 command, u8 length, u8 command, u8 length,
u8 *values); const u8 *values);
/* Returns the number of read bytes */ /* Returns the number of read bytes */
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client, extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
u8 command, u8 *values); u8 command, u8 *values);
extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client,
u8 command, u8 length, u8 command, u8 length,
u8 *values); const u8 *values);
/* /*
* A driver is capable of handling one or more physical devices present on * A driver is capable of handling one or more physical devices present on
......
/*
* 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 @@ ...@@ -352,8 +352,11 @@
#define PCI_DEVICE_ID_ATI_RS480 0x5950 #define PCI_DEVICE_ID_ATI_RS480 0x5950
/* ATI IXP Chipset */ /* ATI IXP Chipset */
#define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 #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_IDE 0x4369
#define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e #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_IDE 0x4376
#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 #define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379
#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a #define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a
...@@ -1133,9 +1136,11 @@ ...@@ -1133,9 +1136,11 @@
#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258
#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259
#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B #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_IDE 0x0265
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 #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_IDE 0x036E
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment