Merge branch 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6
* 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6: (44 commits) i2c: Delete the i2c-isa pseudo bus driver hwmon: refuse to load abituguru driver on non-Abit boards hwmon: fix Abit Uguru3 driver detection on some motherboards hwmon/w83627ehf: Be quiet when no chip is found hwmon/w83627ehf: No need to initialize fan_min hwmon/w83627ehf: Export the thermal sensor types hwmon/w83627ehf: Enable VBAT monitoring hwmon/w83627ehf: Add support for the VID inputs hwmon/w83627ehf: Fix timing issues hwmon/w83627ehf: Add error messages for two error cases hwmon/w83627ehf: Convert to a platform driver hwmon/w83627ehf: Update the Kconfig entry make coretemp_device_remove() static hwmon: Add LM93 support hwmon: Improve the pwmN_enable documentation hwmon/smsc47b397: Don't report missing fans as spinning at 82 RPM hwmon: Add support for newer uGuru's hwmon/f71805f: Add temperature-tracking fan control mode hwmon/w83627ehf: Preserve speed reading when changing fan min hwmon: fix detection of abituguru volt inputs ... Manual fixup of trivial conflict in MAINTAINERS file
This commit is contained in:
Коммит
40b42f1ebf
|
@ -164,15 +164,6 @@ Who: Kay Sievers <kay.sievers@suse.de>
|
|||
|
||||
---------------------------
|
||||
|
||||
What: i2c-isa
|
||||
When: December 2006
|
||||
Why: i2c-isa is a non-sense and doesn't fit in the device driver
|
||||
model. Drivers relying on it are better implemented as platform
|
||||
drivers.
|
||||
Who: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: i2c_adapter.list
|
||||
When: July 2007
|
||||
Why: Superfluous, this list duplicates the one maintained by the driver
|
||||
|
|
|
@ -2,7 +2,7 @@ Kernel driver abituguru
|
|||
=======================
|
||||
|
||||
Supported chips:
|
||||
* Abit uGuru revision 1-3 (Hardware Monitor part only)
|
||||
* Abit uGuru revision 1 & 2 (Hardware Monitor part only)
|
||||
Prefix: 'abituguru'
|
||||
Addresses scanned: ISA 0x0E0
|
||||
Datasheet: Not available, this driver is based on reverse engineering.
|
||||
|
@ -20,8 +20,8 @@ Supported chips:
|
|||
uGuru 2.1.0.0 ~ 2.1.2.8 (AS8, AV8, AA8, AG8, AA8XE, AX8)
|
||||
uGuru 2.2.0.0 ~ 2.2.0.6 (AA8 Fatal1ty)
|
||||
uGuru 2.3.0.0 ~ 2.3.0.9 (AN8)
|
||||
uGuru 3.0.0.0 ~ 3.0.1.2 (AW8, AL8, NI8)
|
||||
uGuru 4.xxxxx? (AT8 32X) (2)
|
||||
uGuru 3.0.0.0 ~ 3.0.x.x (AW8, AL8, AT8, NI8 SLI, AT8 32X, AN8 32X,
|
||||
AW9D-MAX) (2)
|
||||
1) For revisions 2 and 3 uGuru's the driver can autodetect the
|
||||
sensortype (Volt or Temp) for bank1 sensors, for revision 1 uGuru's
|
||||
this doesnot always work. For these uGuru's the autodection can
|
||||
|
@ -30,8 +30,9 @@ Supported chips:
|
|||
bank1_types=1,1,0,0,0,0,0,2,0,0,0,0,2,0,0,1
|
||||
You may also need to specify the fan_sensors option for these boards
|
||||
fan_sensors=5
|
||||
2) The current version of the abituguru driver is known to NOT work
|
||||
on these Motherboards
|
||||
2) There is a seperate abituguru3 driver for these motherboards,
|
||||
the abituguru (without the 3 !) driver will not work on these
|
||||
motherboards (and visa versa)!
|
||||
|
||||
Authors:
|
||||
Hans de Goede <j.w.r.degoede@hhs.nl>,
|
||||
|
@ -43,8 +44,10 @@ 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.
|
||||
detection to be skipped, and thus the insmod to
|
||||
succeed. If the uGuru can't be read the actual hwmon
|
||||
driver will not load and thus no hwmon device will get
|
||||
registered.
|
||||
* bank1_types: int[] Bank1 sensortype autodetection override:
|
||||
-1 autodetect (default)
|
||||
0 volt sensor
|
||||
|
@ -69,13 +72,15 @@ 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).
|
||||
This driver supports the hardware monitoring features of the first and
|
||||
second revision 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.
|
||||
The first and second revision of 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
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
Kernel driver abituguru3
|
||||
========================
|
||||
|
||||
Supported chips:
|
||||
* Abit uGuru revision 3 (Hardware Monitor part, reading only)
|
||||
Prefix: 'abituguru3'
|
||||
Addresses scanned: ISA 0x0E0
|
||||
Datasheet: Not available, this driver is based on reverse engineering.
|
||||
Note:
|
||||
The uGuru is a microcontroller with onboard firmware which programs
|
||||
it to behave as a hwmon IC. There are many different revisions of the
|
||||
firmware and thus effectivly many different revisions of the uGuru.
|
||||
Below is an incomplete list with which revisions are used for which
|
||||
Motherboards:
|
||||
uGuru 1.00 ~ 1.24 (AI7, KV8-MAX3, AN7)
|
||||
uGuru 2.0.0.0 ~ 2.0.4.2 (KV8-PRO)
|
||||
uGuru 2.1.0.0 ~ 2.1.2.8 (AS8, AV8, AA8, AG8, AA8XE, AX8)
|
||||
uGuru 2.3.0.0 ~ 2.3.0.9 (AN8)
|
||||
uGuru 3.0.0.0 ~ 3.0.x.x (AW8, AL8, AT8, NI8 SLI, AT8 32X, AN8 32X,
|
||||
AW9D-MAX)
|
||||
The abituguru3 driver is only for revison 3.0.x.x motherboards,
|
||||
this driver will not work on older motherboards. For older
|
||||
motherboards use the abituguru (without the 3 !) driver.
|
||||
|
||||
Authors:
|
||||
Hans de Goede <j.w.r.degoede@hhs.nl>,
|
||||
(Initial reverse engineering done by Louis Kruger)
|
||||
|
||||
|
||||
Module Parameters
|
||||
-----------------
|
||||
|
||||
* force: bool Force detection. Note this parameter only causes the
|
||||
detection to be skipped, and thus the insmod to
|
||||
succeed. If the uGuru can't be read the actual hwmon
|
||||
driver will not load and thus no hwmon device will get
|
||||
registered.
|
||||
* verbose: bool Should the driver be verbose?
|
||||
0/off/false normal output
|
||||
1/on/true + verbose error reporting (default)
|
||||
Default: 1 (the driver is still in the testing phase)
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports the hardware monitoring features of the third revision of
|
||||
the Abit uGuru chip, found on recent Abit uGuru featuring motherboards.
|
||||
|
||||
The 3rd revision of the uGuru chip in reality is a Winbond W83L951G.
|
||||
Unfortunatly this doesn't help since the W83L951G is a generic microcontroller
|
||||
with a custom Abit application running on it.
|
||||
|
||||
Despite Abit not releasing any information regarding the uGuru revision 3,
|
||||
Louis Kruger 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,
|
||||
neither is writing any of the sensor settings and writing / reading the
|
||||
fanspeed control registers (FanEQ)
|
||||
|
||||
If you encounter any problems please mail me <j.w.r.degoede@hhs.nl> and
|
||||
include the output of: "dmesg | grep abituguru"
|
|
@ -0,0 +1,257 @@
|
|||
Kernel driver dme1737
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* SMSC DME1737 and compatibles (like Asus A8000)
|
||||
Prefix: 'dme1737'
|
||||
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
|
||||
Datasheet: Provided by SMSC upon request and under NDA
|
||||
|
||||
Authors:
|
||||
Juerg Haefliger <juergh@gmail.com>
|
||||
|
||||
|
||||
Module Parameters
|
||||
-----------------
|
||||
|
||||
* force_start: bool Enables the monitoring of voltage, fan and temp inputs
|
||||
and PWM output control functions. Using this parameter
|
||||
shouldn't be required since the BIOS usually takes care
|
||||
of this.
|
||||
|
||||
Note that there is no need to use this parameter if the driver loads without
|
||||
complaining. The driver will say so if it is necessary.
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the hardware monitoring capabilities of the
|
||||
SMSC DME1737 and Asus A8000 (which are the same) Super-I/O chips. This chip
|
||||
features monitoring of 3 temp sensors temp[1-3] (2 remote diodes and 1
|
||||
internal), 7 voltages in[0-6] (6 external and 1 internal) and 6 fan speeds
|
||||
fan[1-6]. Additionally, the chip implements 5 PWM outputs pwm[1-3,5-6] for
|
||||
controlling fan speeds both manually and automatically.
|
||||
|
||||
Fan[3-6] and pwm[3,5-6] are optional features and their availability is
|
||||
dependent on the configuration of the chip. The driver will detect which
|
||||
features are present during initialization and create the sysfs attributes
|
||||
accordingly.
|
||||
|
||||
|
||||
Voltage Monitoring
|
||||
------------------
|
||||
|
||||
The voltage inputs are sampled with 12-bit resolution and have internal
|
||||
scaling resistors. The values returned by the driver therefore reflect true
|
||||
millivolts and don't need scaling. The voltage inputs are mapped as follows
|
||||
(the last column indicates the input ranges):
|
||||
|
||||
in0: +5VTR (+5V standby) 0V - 6.64V
|
||||
in1: Vccp (processor core) 0V - 3V
|
||||
in2: VCC (internal +3.3V) 0V - 4.38V
|
||||
in3: +5V 0V - 6.64V
|
||||
in4: +12V 0V - 16V
|
||||
in5: VTR (+3.3V standby) 0V - 4.38V
|
||||
in6: Vbat (+3.0V) 0V - 4.38V
|
||||
|
||||
Each voltage input has associated min and max limits which trigger an alarm
|
||||
when crossed.
|
||||
|
||||
|
||||
Temperature Monitoring
|
||||
----------------------
|
||||
|
||||
Temperatures are measured with 12-bit resolution and reported in millidegree
|
||||
Celsius. The chip also features offsets for all 3 temperature inputs which -
|
||||
when programmed - get added to the input readings. The chip does all the
|
||||
scaling by itself and the driver therefore reports true temperatures that don't
|
||||
need any user-space adjustments. The temperature inputs are mapped as follows
|
||||
(the last column indicates the input ranges):
|
||||
|
||||
temp1: Remote diode 1 (3904 type) temperature -127C - +127C
|
||||
temp2: DME1737 internal temperature -127C - +127C
|
||||
temp3: Remote diode 2 (3904 type) temperature -127C - +127C
|
||||
|
||||
Each temperature input has associated min and max limits which trigger an alarm
|
||||
when crossed. Additionally, each temperature input has a fault attribute that
|
||||
returns 1 when a faulty diode or an unconnected input is detected and 0
|
||||
otherwise.
|
||||
|
||||
|
||||
Fan Monitoring
|
||||
--------------
|
||||
|
||||
Fan RPMs are measured with 16-bit resolution. The chip provides inputs for 6
|
||||
fan tachometers. All 6 inputs have an associated min limit which triggers an
|
||||
alarm when crossed. Fan inputs 1-4 provide type attributes that need to be set
|
||||
to the number of pulses per fan revolution that the connected tachometer
|
||||
generates. Supported values are 1, 2, and 4. Fan inputs 5-6 only support fans
|
||||
that generate 2 pulses per revolution. Fan inputs 5-6 also provide a max
|
||||
attribute that needs to be set to the maximum attainable RPM (fan at 100% duty-
|
||||
cycle) of the input. The chip adjusts the sampling rate based on this value.
|
||||
|
||||
|
||||
PWM Output Control
|
||||
------------------
|
||||
|
||||
This chip features 5 PWM outputs. PWM outputs 1-3 are associated with fan
|
||||
inputs 1-3 and PWM outputs 5-6 are associated with fan inputs 5-6. PWM outputs
|
||||
1-3 can be configured to operate either in manual or automatic mode by setting
|
||||
the appropriate enable attribute accordingly. PWM outputs 5-6 can only operate
|
||||
in manual mode, their enable attributes are therefore read-only. When set to
|
||||
manual mode, the fan speed is set by writing the duty-cycle value to the
|
||||
appropriate PWM attribute. In automatic mode, the PWM attribute returns the
|
||||
current duty-cycle as set by the fan controller in the chip. All PWM outputs
|
||||
support the setting of the output frequency via the freq attribute.
|
||||
|
||||
In automatic mode, the chip supports the setting of the PWM ramp rate which
|
||||
defines how fast the PWM output is adjusting to changes of the associated
|
||||
temperature input. Associating PWM outputs to temperature inputs is done via
|
||||
temperature zones. The chip features 3 zones whose assignments to temperature
|
||||
inputs is static and determined during initialization. These assignments can
|
||||
be retrieved via the zone[1-3]_auto_channels_temp attributes. Each PWM output
|
||||
is assigned to one (or hottest of multiple) temperature zone(s) through the
|
||||
pwm[1-3]_auto_channels_zone attributes. Each PWM output has 3 distinct output
|
||||
duty-cycles: full, low, and min. Full is internally hard-wired to 255 (100%)
|
||||
and low and min can be programmed via pwm[1-3]_auto_point1_pwm and
|
||||
pwm[1-3]_auto_pwm_min, respectively. The thermal thresholds of the zones are
|
||||
programmed via zone[1-3]_auto_point[1-3]_temp and
|
||||
zone[1-3]_auto_point1_temp_hyst:
|
||||
|
||||
pwm[1-3]_auto_point2_pwm full-speed duty-cycle (255, i.e., 100%)
|
||||
pwm[1-3]_auto_point1_pwm low-speed duty-cycle
|
||||
pwm[1-3]_auto_pwm_min min-speed duty-cycle
|
||||
|
||||
zone[1-3]_auto_point3_temp full-speed temp (all outputs)
|
||||
zone[1-3]_auto_point2_temp full-speed temp
|
||||
zone[1-3]_auto_point1_temp low-speed temp
|
||||
zone[1-3]_auto_point1_temp_hyst min-speed temp
|
||||
|
||||
The chip adjusts the output duty-cycle linearly in the range of auto_point1_pwm
|
||||
to auto_point2_pwm if the temperature of the associated zone is between
|
||||
auto_point1_temp and auto_point2_temp. If the temperature drops below the
|
||||
auto_point1_temp_hyst value, the output duty-cycle is set to the auto_pwm_min
|
||||
value which only supports two values: 0 or auto_point1_pwm. That means that the
|
||||
fan either turns completely off or keeps spinning with the low-speed
|
||||
duty-cycle. If any of the temperatures rise above the auto_point3_temp value,
|
||||
all PWM outputs are set to 100% duty-cycle.
|
||||
|
||||
Following is another representation of how the chip sets the output duty-cycle
|
||||
based on the temperature of the associated thermal zone:
|
||||
|
||||
Duty-Cycle Duty-Cycle
|
||||
Temperature Rising Temp Falling Temp
|
||||
----------- ----------- ------------
|
||||
full-speed full-speed full-speed
|
||||
|
||||
< linearly adjusted duty-cycle >
|
||||
|
||||
low-speed low-speed low-speed
|
||||
min-speed low-speed
|
||||
min-speed min-speed min-speed
|
||||
min-speed min-speed
|
||||
|
||||
|
||||
Sysfs Attributes
|
||||
----------------
|
||||
|
||||
Following is a list of all sysfs attributes that the driver provides, their
|
||||
permissions and a short description:
|
||||
|
||||
Name Perm Description
|
||||
---- ---- -----------
|
||||
cpu0_vid RO CPU core reference voltage in
|
||||
millivolts.
|
||||
vrm RW Voltage regulator module version
|
||||
number.
|
||||
|
||||
in[0-6]_input RO Measured voltage in millivolts.
|
||||
in[0-6]_min RW Low limit for voltage input.
|
||||
in[0-6]_max RW High limit for voltage input.
|
||||
in[0-6]_alarm RO Voltage input alarm. Returns 1 if
|
||||
voltage input is or went outside the
|
||||
associated min-max range, 0 otherwise.
|
||||
|
||||
temp[1-3]_input RO Measured temperature in millidegree
|
||||
Celsius.
|
||||
temp[1-3]_min RW Low limit for temp input.
|
||||
temp[1-3]_max RW High limit for temp input.
|
||||
temp[1-3]_offset RW Offset for temp input. This value will
|
||||
be added by the chip to the measured
|
||||
temperature.
|
||||
temp[1-3]_alarm RO Alarm for temp input. Returns 1 if temp
|
||||
input is or went outside the associated
|
||||
min-max range, 0 otherwise.
|
||||
temp[1-3]_fault RO Temp input fault. Returns 1 if the chip
|
||||
detects a faulty thermal diode or an
|
||||
unconnected temp input, 0 otherwise.
|
||||
|
||||
zone[1-3]_auto_channels_temp RO Temperature zone to temperature input
|
||||
mapping. This attribute is a bitfield
|
||||
and supports the following values:
|
||||
1: temp1
|
||||
2: temp2
|
||||
4: temp3
|
||||
zone[1-3]_auto_point1_temp_hyst RW Auto PWM temp point1 hysteresis. The
|
||||
output of the corresponding PWM is set
|
||||
to the pwm_auto_min value if the temp
|
||||
falls below the auto_point1_temp_hyst
|
||||
value.
|
||||
zone[1-3]_auto_point[1-3]_temp RW Auto PWM temp points. Auto_point1 is
|
||||
the low-speed temp, auto_point2 is the
|
||||
full-speed temp, and auto_point3 is the
|
||||
temp at which all PWM outputs are set
|
||||
to full-speed (100% duty-cycle).
|
||||
|
||||
fan[1-6]_input RO Measured fan speed in RPM.
|
||||
fan[1-6]_min RW Low limit for fan input.
|
||||
fan[1-6]_alarm RO Alarm for fan input. Returns 1 if fan
|
||||
input is or went below the associated
|
||||
min value, 0 otherwise.
|
||||
fan[1-4]_type RW Type of attached fan. Expressed in
|
||||
number of pulses per revolution that
|
||||
the fan generates. Supported values are
|
||||
1, 2, and 4.
|
||||
fan[5-6]_max RW Max attainable RPM at 100% duty-cycle.
|
||||
Required for chip to adjust the
|
||||
sampling rate accordingly.
|
||||
|
||||
pmw[1-3,5-6] RO/RW Duty-cycle of PWM output. Supported
|
||||
values are 0-255 (0%-100%). Only
|
||||
writeable if the associated PWM is in
|
||||
manual mode.
|
||||
pwm[1-3]_enable RW Enable of PWM outputs 1-3. Supported
|
||||
values are:
|
||||
0: turned off (output @ 100%)
|
||||
1: manual mode
|
||||
2: automatic mode
|
||||
pwm[5-6]_enable RO Enable of PWM outputs 5-6. Always
|
||||
returns 1 since these 2 outputs are
|
||||
hard-wired to manual mode.
|
||||
pmw[1-3,5-6]_freq RW Frequency of PWM output. Supported
|
||||
values are in the range 11Hz-30000Hz
|
||||
(default is 25000Hz).
|
||||
pmw[1-3]_ramp_rate RW Ramp rate of PWM output. Determines how
|
||||
fast the PWM duty-cycle will change
|
||||
when the PWM is in automatic mode.
|
||||
Expressed in ms per PWM step. Supported
|
||||
values are in the range 0ms-206ms
|
||||
(default is 0, which means the duty-
|
||||
cycle changes instantly).
|
||||
pwm[1-3]_auto_channels_zone RW PWM output to temperature zone mapping.
|
||||
This attribute is a bitfield and
|
||||
supports the following values:
|
||||
1: zone1
|
||||
2: zone2
|
||||
4: zone3
|
||||
6: highest of zone[2-3]
|
||||
7: highest of zone[1-3]
|
||||
pwm[1-3]_auto_pwm_min RW Auto PWM min pwm. Minimum PWM duty-
|
||||
cycle. Supported values are 0 or
|
||||
auto_point1_pwm.
|
||||
pwm[1-3]_auto_point1_pwm RW Auto PWM pwm point. Auto_point1 is the
|
||||
low-speed duty-cycle.
|
||||
pwm[1-3]_auto_point2_pwm RO Auto PWM pwm point. Auto_point2 is the
|
||||
full-speed duty-cycle which is hard-
|
||||
wired to 255 (100% duty-cycle).
|
|
@ -5,11 +5,11 @@ Supported chips:
|
|||
* Fintek F71805F/FG
|
||||
Prefix: 'f71805f'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Provided by Fintek on request
|
||||
Datasheet: Available from the Fintek website
|
||||
* Fintek F71872F/FG
|
||||
Prefix: 'f71872f'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Provided by Fintek on request
|
||||
Datasheet: Available from the Fintek website
|
||||
|
||||
Author: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
|
@ -128,7 +128,9 @@ it.
|
|||
When the PWM method is used, you can select the operating frequency,
|
||||
from 187.5 kHz (default) to 31 Hz. The best frequency depends on the
|
||||
fan model. As a rule of thumb, lower frequencies seem to give better
|
||||
control, but may generate annoying high-pitch noise. Fintek recommends
|
||||
control, but may generate annoying high-pitch noise. So a frequency just
|
||||
above the audible range, such as 25 kHz, may be a good choice; if this
|
||||
doesn't give you good linear control, try reducing it. Fintek recommends
|
||||
not going below 1 kHz, as the fan tachometers get confused by lower
|
||||
frequencies as well.
|
||||
|
||||
|
@ -136,16 +138,23 @@ When the DC method is used, Fintek recommends not going below 5 V, which
|
|||
corresponds to a pwm value of 106 for the driver. The driver doesn't
|
||||
enforce this limit though.
|
||||
|
||||
Three different fan control modes are supported:
|
||||
Three different fan control modes are supported; the mode number is written
|
||||
to the pwm<n>_enable file.
|
||||
|
||||
* Manual mode
|
||||
You ask for a specific PWM duty cycle or DC voltage.
|
||||
* 1: Manual mode
|
||||
You ask for a specific PWM duty cycle or DC voltage by writing to the
|
||||
pwm<n> file.
|
||||
|
||||
* Fan speed mode
|
||||
You ask for a specific fan speed. This mode assumes that pwm1
|
||||
corresponds to fan1, pwm2 to fan2 and pwm3 to fan3.
|
||||
* 2: Temperature mode
|
||||
You define 3 temperature/fan speed trip points using the
|
||||
pwm<n>_auto_point<m>_temp and _fan files. These define a staircase
|
||||
relationship between temperature and fan speed with two additional points
|
||||
interpolated between the values that you define. When the temperature
|
||||
is below auto_point1_temp the fan is switched off.
|
||||
|
||||
* Temperature mode
|
||||
You define 3 temperature/fan speed trip points, and the fan speed is
|
||||
adjusted depending on the measured temperature, using interpolation.
|
||||
This mode is not yet supported by the driver.
|
||||
* 3: Fan speed mode
|
||||
You ask for a specific fan speed by writing to the fan<n>_target file.
|
||||
|
||||
Both of the automatic modes require that pwm1 corresponds to fan1, pwm2 to
|
||||
fan2 and pwm3 to fan3. Temperature mode also requires that temp1 corresponds
|
||||
to pwm1 and fan1, etc.
|
||||
|
|
|
@ -12,11 +12,12 @@ Supported chips:
|
|||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Publicly available at the ITE website
|
||||
http://www.ite.com.tw/
|
||||
* IT8716F
|
||||
* IT8716F/IT8726F
|
||||
Prefix: 'it8716'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Publicly available at the ITE website
|
||||
http://www.ite.com.tw/product_info/file/pc/IT8716F_V0.3.ZIP
|
||||
http://www.ite.com.tw/product_info/file/pc/IT8726F_V0.3.pdf
|
||||
* IT8718F
|
||||
Prefix: 'it8718'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
|
@ -68,7 +69,7 @@ Description
|
|||
-----------
|
||||
|
||||
This driver implements support for the IT8705F, IT8712F, IT8716F,
|
||||
IT8718F and SiS950 chips.
|
||||
IT8718F, IT8726F and SiS950 chips.
|
||||
|
||||
These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
|
||||
joysticks and other miscellaneous stuff. For hardware monitoring, they
|
||||
|
@ -97,6 +98,10 @@ clock divider mess) but not compatible with the older chips and
|
|||
revisions. For now, the driver only uses the 16-bit mode on the
|
||||
IT8716F and IT8718F.
|
||||
|
||||
The IT8726F is just bit enhanced IT8716F with additional hardware
|
||||
for AMD power sequencing. Therefore the chip will appear as IT8716F
|
||||
to userspace applications.
|
||||
|
||||
Temperatures are measured in degrees Celsius. An alarm is triggered once
|
||||
when the Overtemperature Shutdown limit is crossed.
|
||||
|
||||
|
|
|
@ -48,6 +48,18 @@ Supported chips:
|
|||
Addresses scanned: I2C 0x4c, 0x4d (unsupported 0x4e)
|
||||
Datasheet: Publicly available at the Maxim website
|
||||
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
|
||||
* Maxim MAX6680
|
||||
Prefix: 'max6680'
|
||||
Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
|
||||
0x4c, 0x4d and 0x4e
|
||||
Datasheet: Publicly available at the Maxim website
|
||||
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370
|
||||
* Maxim MAX6681
|
||||
Prefix: 'max6680'
|
||||
Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
|
||||
0x4c, 0x4d and 0x4e
|
||||
Datasheet: Publicly available at the Maxim website
|
||||
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370
|
||||
|
||||
|
||||
Author: Jean Delvare <khali@linux-fr.org>
|
||||
|
@ -59,11 +71,15 @@ Description
|
|||
The LM90 is a digital temperature sensor. It senses its own temperature as
|
||||
well as the temperature of up to one external diode. It is compatible
|
||||
with many other devices such as the LM86, the LM89, the LM99, the ADM1032,
|
||||
the MAX6657, MAX6658 and the MAX6659 all of which are supported by this driver.
|
||||
Note that there is no easy way to differentiate between the last three
|
||||
variants. The extra address and features of the MAX6659 are not supported by
|
||||
this driver. Additionally, the ADT7461 is supported if found in ADM1032
|
||||
compatibility mode.
|
||||
the MAX6657, MAX6658, MAX6659, MAX6680 and the MAX6681 all of which are
|
||||
supported by this driver.
|
||||
|
||||
Note that there is no easy way to differentiate between the MAX6657,
|
||||
MAX6658 and MAX6659 variants. The extra address and features of the
|
||||
MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only
|
||||
differ in their pinout, therefore they obviously can't (and don't need to)
|
||||
be distinguished. Additionally, the ADT7461 is supported if found in
|
||||
ADM1032 compatibility mode.
|
||||
|
||||
The specificity of this family of chipsets over the ADM1021/LM84
|
||||
family is that it features critical limits with hysteresis, and an
|
||||
|
@ -93,18 +109,22 @@ ADM1032:
|
|||
* ALERT is triggered by open remote sensor.
|
||||
* SMBus PEC support for Write Byte and Receive Byte transactions.
|
||||
|
||||
ADT7461
|
||||
ADT7461:
|
||||
* Extended temperature range (breaks compatibility)
|
||||
* Lower resolution for remote temperature
|
||||
|
||||
MAX6657 and MAX6658:
|
||||
* Remote sensor type selection
|
||||
|
||||
MAX6659
|
||||
MAX6659:
|
||||
* Selectable address
|
||||
* Second critical temperature limit
|
||||
* Remote sensor type selection
|
||||
|
||||
MAX6680 and MAX6681:
|
||||
* Selectable address
|
||||
* Remote sensor type selection
|
||||
|
||||
All temperature values are given in degrees Celsius. Resolution
|
||||
is 1.0 degree for the local temperature, 0.125 degree for the remote
|
||||
temperature.
|
||||
|
@ -141,7 +161,7 @@ SMBus Read Byte, and PEC will work properly.
|
|||
Additionally, the ADM1032 doesn't support SMBus Send Byte with PEC.
|
||||
Instead, it will try to write the PEC value to the register (because the
|
||||
SMBus Send Byte transaction with PEC is similar to a Write Byte transaction
|
||||
without PEC), which is not what we want. Thus, PEC is explicitely disabled
|
||||
without PEC), which is not what we want. Thus, PEC is explicitly disabled
|
||||
on SMBus Send Byte transactions in the lm90 driver.
|
||||
|
||||
PEC on byte data transactions represents a significant increase in bandwidth
|
||||
|
|
|
@ -0,0 +1,412 @@
|
|||
Kernel driver lm93
|
||||
==================
|
||||
|
||||
Supported chips:
|
||||
* National Semiconductor LM93
|
||||
Prefix 'lm93'
|
||||
Addresses scanned: I2C 0x2c-0x2e
|
||||
Datasheet: http://www.national.com/ds.cgi/LM/LM93.pdf
|
||||
|
||||
Author:
|
||||
Mark M. Hoffman <mhoffman@lightlink.com>
|
||||
Ported to 2.6 by Eric J. Bowersox <ericb@aspsys.com>
|
||||
Adapted to 2.6.20 by Carsten Emde <ce@osadl.org>
|
||||
Modified for mainline integration by Hans J. Koch <hjk@linutronix.de>
|
||||
|
||||
Module Parameters
|
||||
-----------------
|
||||
|
||||
(specific to LM93)
|
||||
* init: integer
|
||||
Set to non-zero to force some initializations (default is 0).
|
||||
* disable_block: integer
|
||||
A "0" allows SMBus block data transactions if the host supports them. A "1"
|
||||
disables SMBus block data transactions. The default is 0.
|
||||
* vccp_limit_type: integer array (2)
|
||||
Configures in7 and in8 limit type, where 0 means absolute and non-zero
|
||||
means relative. "Relative" here refers to "Dynamic Vccp Monitoring using
|
||||
VID" from the datasheet. It greatly simplifies the interface to allow
|
||||
only one set of limits (absolute or relative) to be in operation at a
|
||||
time (even though the hardware is capable of enabling both). There's
|
||||
not a compelling use case for enabling both at once, anyway. The default
|
||||
is "0,0".
|
||||
* vid_agtl: integer
|
||||
A "0" configures the VID pins for V(ih) = 2.1V min, V(il) = 0.8V max.
|
||||
A "1" configures the VID pins for V(ih) = 0.8V min, V(il) = 0.4V max.
|
||||
(The latter setting is referred to as AGTL+ Compatible in the datasheet.)
|
||||
I.e. this parameter controls the VID pin input thresholds; if your VID
|
||||
inputs are not working, try changing this. The default value is "0".
|
||||
|
||||
(common among sensor drivers)
|
||||
* force: short array (min = 1, max = 48)
|
||||
List of adapter,address pairs to assume to be present. Autodetection
|
||||
of the target device will still be attempted. Use one of the more
|
||||
specific force directives below if this doesn't detect the device.
|
||||
* force_lm93: short array (min = 1, max = 48)
|
||||
List of adapter,address pairs which are unquestionably assumed to contain
|
||||
a 'lm93' chip
|
||||
* ignore: short array (min = 1, max = 48)
|
||||
List of adapter,address pairs not to scan
|
||||
* ignore_range: short array (min = 1, max = 48)
|
||||
List of adapter,start-addr,end-addr triples not to scan
|
||||
* probe: short array (min = 1, max = 48)
|
||||
List of adapter,address pairs to scan additionally
|
||||
* probe_range: short array (min = 1, max = 48)
|
||||
List of adapter,start-addr,end-addr triples to scan additionally
|
||||
|
||||
|
||||
Hardware Description
|
||||
--------------------
|
||||
|
||||
(from the datasheet)
|
||||
|
||||
The LM93, hardware monitor, has a two wire digital interface compatible with
|
||||
SMBus 2.0. Using an 8-bit ADC, the LM93 measures the temperature of two remote
|
||||
diode connected transistors as well as its own die and 16 power supply
|
||||
voltages. To set fan speed, the LM93 has two PWM outputs that are each
|
||||
controlled by up to four temperature zones. The fancontrol algorithm is lookup
|
||||
table based. The LM93 includes a digital filter that can be invoked to smooth
|
||||
temperature readings for better control of fan speed. The LM93 has four
|
||||
tachometer inputs to measure fan speed. Limit and status registers for all
|
||||
measured values are included. The LM93 builds upon the functionality of
|
||||
previous motherboard management ASICs and uses some of the LM85 s features
|
||||
(i.e. smart tachometer mode). It also adds measurement and control support
|
||||
for dynamic Vccp monitoring and PROCHOT. It is designed to monitor a dual
|
||||
processor Xeon class motherboard with a minimum of external components.
|
||||
|
||||
|
||||
Driver Description
|
||||
------------------
|
||||
|
||||
This driver implements support for the National Semiconductor LM93.
|
||||
|
||||
|
||||
User Interface
|
||||
--------------
|
||||
|
||||
#PROCHOT:
|
||||
|
||||
The LM93 can monitor two #PROCHOT signals. The results are found in the
|
||||
sysfs files prochot1, prochot2, prochot1_avg, prochot2_avg, prochot1_max,
|
||||
and prochot2_max. prochot1_max and prochot2_max contain the user limits
|
||||
for #PROCHOT1 and #PROCHOT2, respectively. prochot1 and prochot2 contain
|
||||
the current readings for the most recent complete time interval. The
|
||||
value of prochot1_avg and prochot2_avg is something like a 2 period
|
||||
exponential moving average (but not quite - check the datasheet). Note
|
||||
that this third value is calculated by the chip itself. All values range
|
||||
from 0-255 where 0 indicates no throttling, and 255 indicates > 99.6%.
|
||||
|
||||
The monitoring intervals for the two #PROCHOT signals is also configurable.
|
||||
These intervals can be found in the sysfs files prochot1_interval and
|
||||
prochot2_interval. The values in these files specify the intervals for
|
||||
#P1_PROCHOT and #P2_PROCHOT, respectively. Selecting a value not in this
|
||||
list will cause the driver to use the next largest interval. The available
|
||||
intervals are:
|
||||
|
||||
#PROCHOT intervals: 0.73, 1.46, 2.9, 5.8, 11.7, 23.3, 46.6, 93.2, 186, 372
|
||||
|
||||
It is possible to configure the LM93 to logically short the two #PROCHOT
|
||||
signals. I.e. when #P1_PROCHOT is asserted, the LM93 will automatically
|
||||
assert #P2_PROCHOT, and vice-versa. This mode is enabled by writing a
|
||||
non-zero integer to the sysfs file prochot_short.
|
||||
|
||||
The LM93 can also override the #PROCHOT pins by driving a PWM signal onto
|
||||
one or both of them. When overridden, the signal has a period of 3.56 mS,
|
||||
a minimum pulse width of 5 clocks (at 22.5kHz => 6.25% duty cycle), and
|
||||
a maximum pulse width of 80 clocks (at 22.5kHz => 99.88% duty cycle).
|
||||
|
||||
The sysfs files prochot1_override and prochot2_override contain boolean
|
||||
intgers which enable or disable the override function for #P1_PROCHOT and
|
||||
#P2_PROCHOT, respectively. The sysfs file prochot_override_duty_cycle
|
||||
contains a value controlling the duty cycle for the PWM signal used when
|
||||
the override function is enabled. This value ranges from 0 to 15, with 0
|
||||
indicating minimum duty cycle and 15 indicating maximum.
|
||||
|
||||
#VRD_HOT:
|
||||
|
||||
The LM93 can monitor two #VRD_HOT signals. The results are found in the
|
||||
sysfs files vrdhot1 and vrdhot2. There is one value per file: a boolean for
|
||||
which 1 indicates #VRD_HOT is asserted and 0 indicates it is negated. These
|
||||
files are read-only.
|
||||
|
||||
Smart Tach Mode:
|
||||
|
||||
(from the datasheet)
|
||||
|
||||
If a fan is driven using a low-side drive PWM, the tachometer
|
||||
output of the fan is corrupted. The LM93 includes smart tachometer
|
||||
circuitry that allows an accurate tachometer reading to be
|
||||
achieved despite the signal corruption. In smart tach mode all
|
||||
four signals are measured within 4 seconds.
|
||||
|
||||
Smart tach mode is enabled by the driver by writing 1 or 2 (associating the
|
||||
the fan tachometer with a pwm) to the sysfs file fan<n>_smart_tach. A zero
|
||||
will disable the function for that fan. Note that Smart tach mode cannot be
|
||||
enabled if the PWM output frequency is 22500 Hz (see below).
|
||||
|
||||
Manual PWM:
|
||||
|
||||
The LM93 has a fixed or override mode for the two PWM outputs (although, there
|
||||
are still some conditions that will override even this mode - see section
|
||||
15.10.6 of the datasheet for details.) The sysfs files pwm1_override
|
||||
and pwm2_override are used to enable this mode; each is a boolean integer
|
||||
where 0 disables and 1 enables the manual control mode. The sysfs files pwm1
|
||||
and pwm2 are used to set the manual duty cycle; each is an integer (0-255)
|
||||
where 0 is 0% duty cycle, and 255 is 100%. Note that the duty cycle values
|
||||
are constrained by the hardware. Selecting a value which is not available
|
||||
will cause the driver to use the next largest value. Also note: when manual
|
||||
PWM mode is disabled, the value of pwm1 and pwm2 indicates the current duty
|
||||
cycle chosen by the h/w.
|
||||
|
||||
PWM Output Frequency:
|
||||
|
||||
The LM93 supports several different frequencies for the PWM output channels.
|
||||
The sysfs files pwm1_freq and pwm2_freq are used to select the frequency. The
|
||||
frequency values are constrained by the hardware. Selecting a value which is
|
||||
not available will cause the driver to use the next largest value. Also note
|
||||
that this parameter has implications for the Smart Tach Mode (see above).
|
||||
|
||||
PWM Output Frequencies: 12, 36, 48, 60, 72, 84, 96, 22500 (h/w default)
|
||||
|
||||
Automatic PWM:
|
||||
|
||||
The LM93 is capable of complex automatic fan control, with many different
|
||||
points of configuration. To start, each PWM output can be bound to any
|
||||
combination of eight control sources. The final PWM is the largest of all
|
||||
individual control sources to which the PWM output is bound.
|
||||
|
||||
The eight control sources are: temp1-temp4 (aka "zones" in the datasheet),
|
||||
#PROCHOT 1 & 2, and #VRDHOT 1 & 2. The bindings are expressed as a bitmask
|
||||
in the sysfs files pwm<n>_auto_channels, where a "1" enables the binding, and
|
||||
a "0" disables it. The h/w default is 0x0f (all temperatures bound).
|
||||
|
||||
0x01 - Temp 1
|
||||
0x02 - Temp 2
|
||||
0x04 - Temp 3
|
||||
0x08 - Temp 4
|
||||
0x10 - #PROCHOT 1
|
||||
0x20 - #PROCHOT 2
|
||||
0x40 - #VRDHOT 1
|
||||
0x80 - #VRDHOT 2
|
||||
|
||||
The function y = f(x) takes a source temperature x to a PWM output y. This
|
||||
function of the LM93 is derived from a base temperature and a table of 12
|
||||
temperature offsets. The base temperature is expressed in degrees C in the
|
||||
sysfs files temp<n>_auto_base. The offsets are expressed in cumulative
|
||||
degrees C, with the value of offset <i> for temperature value <n> being
|
||||
contained in the file temp<n>_auto_offset<i>. E.g. if the base temperature
|
||||
is 40C:
|
||||
|
||||
offset # temp<n>_auto_offset<i> range pwm
|
||||
1 0 - 25.00%
|
||||
2 0 - 28.57%
|
||||
3 1 40C - 41C 32.14%
|
||||
4 1 41C - 42C 35.71%
|
||||
5 2 42C - 44C 39.29%
|
||||
6 2 44C - 46C 42.86%
|
||||
7 2 48C - 50C 46.43%
|
||||
8 2 50C - 52C 50.00%
|
||||
9 2 52C - 54C 53.57%
|
||||
10 2 54C - 56C 57.14%
|
||||
11 2 56C - 58C 71.43%
|
||||
12 2 58C - 60C 85.71%
|
||||
> 60C 100.00%
|
||||
|
||||
Valid offsets are in the range 0C <= x <= 7.5C in 0.5C increments.
|
||||
|
||||
There is an independent base temperature for each temperature channel. Note,
|
||||
however, there are only two tables of offsets: one each for temp[12] and
|
||||
temp[34]. Therefore, any change to e.g. temp1_auto_offset<i> will also
|
||||
affect temp2_auto_offset<i>.
|
||||
|
||||
The LM93 can also apply hysteresis to the offset table, to prevent unwanted
|
||||
oscillation between two steps in the offsets table. These values are found in
|
||||
the sysfs files temp<n>_auto_offset_hyst. The value in this file has the
|
||||
same representation as in temp<n>_auto_offset<i>.
|
||||
|
||||
If a temperature reading falls below the base value for that channel, the LM93
|
||||
will use the minimum PWM value. These values are found in the sysfs files
|
||||
temp<n>_auto_pwm_min. Note, there are only two minimums: one each for temp[12]
|
||||
and temp[34]. Therefore, any change to e.g. temp1_auto_pwm_min will also
|
||||
affect temp2_auto_pwm_min.
|
||||
|
||||
PWM Spin-Up Cycle:
|
||||
|
||||
A spin-up cycle occurs when a PWM output is commanded from 0% duty cycle to
|
||||
some value > 0%. The LM93 supports a minimum duty cycle during spin-up. These
|
||||
values are found in the sysfs files pwm<n>_auto_spinup_min. The value in this
|
||||
file has the same representation as other PWM duty cycle values. The
|
||||
duration of the spin-up cycle is also configurable. These values are found in
|
||||
the sysfs files pwm<n>_auto_spinup_time. The value in this file is
|
||||
the spin-up time in seconds. The available spin-up times are constrained by
|
||||
the hardware. Selecting a value which is not available will cause the driver
|
||||
to use the next largest value.
|
||||
|
||||
Spin-up Durations: 0 (disabled, h/w default), 0.1, 0.25, 0.4, 0.7, 1.0,
|
||||
2.0, 4.0
|
||||
|
||||
#PROCHOT and #VRDHOT PWM Ramping:
|
||||
|
||||
If the #PROCHOT or #VRDHOT signals are asserted while bound to a PWM output
|
||||
channel, the LM93 will ramp the PWM output up to 100% duty cycle in discrete
|
||||
steps. The duration of each step is configurable. There are two files, with
|
||||
one value each in seconds: pwm_auto_prochot_ramp and pwm_auto_vrdhot_ramp.
|
||||
The available ramp times are constrained by the hardware. Selecting a value
|
||||
which is not available will cause the driver to use the next largest value.
|
||||
|
||||
Ramp Times: 0 (disabled, h/w default) to 0.75 in 0.05 second intervals
|
||||
|
||||
Fan Boost:
|
||||
|
||||
For each temperature channel, there is a boost temperature: if the channel
|
||||
exceeds this limit, the LM93 will immediately drive both PWM outputs to 100%.
|
||||
This limit is expressed in degrees C in the sysfs files temp<n>_auto_boost.
|
||||
There is also a hysteresis temperature for this function: after the boost
|
||||
limit is reached, the temperature channel must drop below this value before
|
||||
the boost function is disabled. This temperature is also expressed in degrees
|
||||
C in the sysfs files temp<n>_auto_boost_hyst.
|
||||
|
||||
GPIO Pins:
|
||||
|
||||
The LM93 can monitor the logic level of four dedicated GPIO pins as well as the
|
||||
four tach input pins. GPIO0-GPIO3 correspond to (fan) tach 1-4, respectively.
|
||||
All eight GPIOs are read by reading the bitmask in the sysfs file gpio. The
|
||||
LSB is GPIO0, and the MSB is GPIO7.
|
||||
|
||||
|
||||
LM93 Unique sysfs Files
|
||||
-----------------------
|
||||
|
||||
file description
|
||||
-------------------------------------------------------------
|
||||
|
||||
prochot<n> current #PROCHOT %
|
||||
|
||||
prochot<n>_avg moving average #PROCHOT %
|
||||
|
||||
prochot<n>_max limit #PROCHOT %
|
||||
|
||||
prochot_short enable or disable logical #PROCHOT pin short
|
||||
|
||||
prochot<n>_override force #PROCHOT assertion as PWM
|
||||
|
||||
prochot_override_duty_cycle
|
||||
duty cycle for the PWM signal used when
|
||||
#PROCHOT is overridden
|
||||
|
||||
prochot<n>_interval #PROCHOT PWM sampling interval
|
||||
|
||||
vrdhot<n> 0 means negated, 1 means asserted
|
||||
|
||||
fan<n>_smart_tach enable or disable smart tach mode
|
||||
|
||||
pwm<n>_auto_channels select control sources for PWM outputs
|
||||
|
||||
pwm<n>_auto_spinup_min minimum duty cycle during spin-up
|
||||
|
||||
pwm<n>_auto_spinup_time duration of spin-up
|
||||
|
||||
pwm_auto_prochot_ramp ramp time per step when #PROCHOT asserted
|
||||
|
||||
pwm_auto_vrdhot_ramp ramp time per step when #VRDHOT asserted
|
||||
|
||||
temp<n>_auto_base temperature channel base
|
||||
|
||||
temp<n>_auto_offset[1-12]
|
||||
temperature channel offsets
|
||||
|
||||
temp<n>_auto_offset_hyst
|
||||
temperature channel offset hysteresis
|
||||
|
||||
temp<n>_auto_boost temperature channel boost (PWMs to 100%) limit
|
||||
|
||||
temp<n>_auto_boost_hyst temperature channel boost hysteresis
|
||||
|
||||
gpio input state of 8 GPIO pins; read-only
|
||||
|
||||
|
||||
Sample Configuration File
|
||||
-------------------------
|
||||
|
||||
Here is a sample LM93 chip config for sensors.conf:
|
||||
|
||||
---------- cut here ----------
|
||||
chip "lm93-*"
|
||||
|
||||
# VOLTAGE INPUTS
|
||||
|
||||
# labels and scaling based on datasheet recommendations
|
||||
label in1 "+12V1"
|
||||
compute in1 @ * 12.945, @ / 12.945
|
||||
set in1_min 12 * 0.90
|
||||
set in1_max 12 * 1.10
|
||||
|
||||
label in2 "+12V2"
|
||||
compute in2 @ * 12.945, @ / 12.945
|
||||
set in2_min 12 * 0.90
|
||||
set in2_max 12 * 1.10
|
||||
|
||||
label in3 "+12V3"
|
||||
compute in3 @ * 12.945, @ / 12.945
|
||||
set in3_min 12 * 0.90
|
||||
set in3_max 12 * 1.10
|
||||
|
||||
label in4 "FSB_Vtt"
|
||||
|
||||
label in5 "3GIO"
|
||||
|
||||
label in6 "ICH_Core"
|
||||
|
||||
label in7 "Vccp1"
|
||||
|
||||
label in8 "Vccp2"
|
||||
|
||||
label in9 "+3.3V"
|
||||
set in9_min 3.3 * 0.90
|
||||
set in9_max 3.3 * 1.10
|
||||
|
||||
label in10 "+5V"
|
||||
set in10_min 5.0 * 0.90
|
||||
set in10_max 5.0 * 1.10
|
||||
|
||||
label in11 "SCSI_Core"
|
||||
|
||||
label in12 "Mem_Core"
|
||||
|
||||
label in13 "Mem_Vtt"
|
||||
|
||||
label in14 "Gbit_Core"
|
||||
|
||||
# Assuming R1/R2 = 4.1143, and 3.3V reference
|
||||
# -12V = (4.1143 + 1) * (@ - 3.3) + 3.3
|
||||
label in15 "-12V"
|
||||
compute in15 @ * 5.1143 - 13.57719, (@ + 13.57719) / 5.1143
|
||||
set in15_min -12 * 0.90
|
||||
set in15_max -12 * 1.10
|
||||
|
||||
label in16 "+3.3VSB"
|
||||
set in16_min 3.3 * 0.90
|
||||
set in16_max 3.3 * 1.10
|
||||
|
||||
# TEMPERATURE INPUTS
|
||||
|
||||
label temp1 "CPU1"
|
||||
label temp2 "CPU2"
|
||||
label temp3 "LM93"
|
||||
|
||||
# TACHOMETER INPUTS
|
||||
|
||||
label fan1 "Fan1"
|
||||
set fan1_min 3000
|
||||
label fan2 "Fan2"
|
||||
set fan2_min 3000
|
||||
label fan3 "Fan3"
|
||||
set fan3_min 3000
|
||||
label fan4 "Fan4"
|
||||
set fan4_min 3000
|
||||
|
||||
# PWM OUTPUTS
|
||||
|
||||
label pwm1 "CPU1"
|
||||
label pwm2 "CPU2"
|
||||
|
|
@ -4,6 +4,7 @@ Kernel driver smsc47b397
|
|||
Supported chips:
|
||||
* SMSC LPC47B397-NC
|
||||
* SMSC SCH5307-NS
|
||||
* SMSC SCH5317
|
||||
Prefix: 'smsc47b397'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: In this file
|
||||
|
@ -18,8 +19,8 @@ The following specification describes the SMSC LPC47B397-NC[1] sensor chip
|
|||
provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected
|
||||
by Mark M. Hoffman <mhoffman@lightlink.com>.
|
||||
|
||||
[1] And SMSC SCH5307-NS, which has a different device ID but is otherwise
|
||||
compatible.
|
||||
[1] And SMSC SCH5307-NS and SCH5317, which have different device IDs but are
|
||||
otherwise compatible.
|
||||
|
||||
* * * * *
|
||||
|
||||
|
@ -131,7 +132,7 @@ OUT DX,AL
|
|||
The registers of interest for identifying the SIO on the dc7100 are Device ID
|
||||
(0x20) and Device Rev (0x21).
|
||||
|
||||
The Device ID will read 0x6F (for SCH5307-NS, 0x81)
|
||||
The Device ID will read 0x6F (0x81 for SCH5307-NS, and 0x85 for SCH5317)
|
||||
The Device Rev currently reads 0x01
|
||||
|
||||
Obtaining the HWM Base Address.
|
||||
|
|
|
@ -172,11 +172,10 @@ pwm[1-*] Pulse width modulation fan control.
|
|||
255 is max or 100%.
|
||||
|
||||
pwm[1-*]_enable
|
||||
Switch PWM on and off.
|
||||
Not always present even if pwmN is.
|
||||
0: turn off
|
||||
1: turn on in manual mode
|
||||
2+: turn on in automatic mode
|
||||
Fan speed control method:
|
||||
0: no fan speed control (i.e. fan at full speed)
|
||||
1: manual fan speed control enabled (using pwm[1-*])
|
||||
2+: automatic fan speed control enabled
|
||||
Check individual chip documentation files for automatic mode
|
||||
details.
|
||||
RW
|
||||
|
@ -343,9 +342,9 @@ 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
|
||||
in[0-*]_fault
|
||||
fan[1-*]_fault
|
||||
temp[1-*]_fault
|
||||
Input fault condition
|
||||
0: no fault occured
|
||||
1: fault condition
|
||||
|
|
|
@ -22,9 +22,9 @@ This driver implements support for the Winbond W83627EHF, W83627EHG, and
|
|||
W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
|
||||
|
||||
The chips implement three temperature sensors, five fan rotation
|
||||
speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
|
||||
with beep warnings (control unimplemented), and some automatic fan regulation
|
||||
strategies (plus manual fan control mode).
|
||||
speed sensors, ten analog voltage sensors (only nine for the 627DHG), one
|
||||
VID (6 pins), alarms with beep warnings (control unimplemented), and
|
||||
some automatic fan regulation strategies (plus manual fan control mode).
|
||||
|
||||
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
|
||||
|
|
36
MAINTAINERS
36
MAINTAINERS
|
@ -607,6 +607,12 @@ W: http://sourceforge.net/projects/acpi4asus
|
|||
W: http://xf.iksaif.net/acpi4asus
|
||||
S: Maintained
|
||||
|
||||
ASUS ASB100 HARDWARE MONITOR DRIVER
|
||||
P: Mark M. Hoffman
|
||||
M: mhoffman@lightlink.com
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
|
||||
ASUS LAPTOP EXTRAS DRIVER
|
||||
P: Corentin Chary
|
||||
M: corentincj@iksaif.net
|
||||
|
@ -1273,6 +1279,12 @@ M: shannon.nelson@intel.com
|
|||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
DME1737 HARDWARE MONITOR DRIVER
|
||||
P: Juerg Haefliger
|
||||
M: juergh@gmail.com
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
|
||||
DOCBOOK FOR DOCUMENTATION
|
||||
P: Randy Dunlap
|
||||
M: rdunlap@xenotime.net
|
||||
|
@ -1623,11 +1635,11 @@ W: http://gigaset307x.sourceforge.net/
|
|||
S: Maintained
|
||||
|
||||
HARDWARE MONITORING
|
||||
P: Jean Delvare
|
||||
M: khali@linux-fr.org
|
||||
P: Mark M. Hoffman
|
||||
M: mhoffman@lightlink.com
|
||||
L: lm-sensors@lm-sensors.org
|
||||
W: http://www.lm-sensors.org/
|
||||
T: quilt http://khali.linux-fr.org/devel/linux-2.6/jdelvare-hwmon/
|
||||
T: git lm-sensors.org:/kernel/mhoffman/hwmon-2.6.git
|
||||
S: Maintained
|
||||
|
||||
HARDWARE RANDOM NUMBER GENERATOR CORE
|
||||
|
@ -1763,6 +1775,12 @@ P: William Irwin
|
|||
M: wli@holomorphy.com
|
||||
S: Maintained
|
||||
|
||||
I2C/SMBUS STUB DRIVER
|
||||
P: Mark M. Hoffman
|
||||
M: mhoffman@lightlink.com
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
|
||||
I2C SUBSYSTEM
|
||||
P: Jean Delvare
|
||||
M: khali@linux-fr.org
|
||||
|
@ -3292,6 +3310,12 @@ W: http://www.brownhat.org/sis900.html
|
|||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
SIS 96X I2C/SMBUS DRIVER
|
||||
P: Mark M. Hoffman
|
||||
M: mhoffman@lightlink.com
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
|
||||
SIS FRAMEBUFFER DRIVER
|
||||
P: Thomas Winischhofer
|
||||
M: thomas@winischhofer.net
|
||||
|
@ -3309,6 +3333,12 @@ P: Nicolas Pitre
|
|||
M: nico@cam.org
|
||||
S: Maintained
|
||||
|
||||
SMSC47B397 HARDWARE MONITOR DRIVER
|
||||
P: Mark M. Hoffman
|
||||
M: mhoffman@lightlink.com
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
|
||||
SOFTMAC LAYER (IEEE 802.11)
|
||||
P: Johannes Berg
|
||||
M: johannes@sipsolutions.net
|
||||
|
|
|
@ -708,7 +708,6 @@ CONFIG_I2C_ALGOPCF=m
|
|||
# I2C Hardware Bus support
|
||||
#
|
||||
CONFIG_I2C_ELEKTOR=m
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_PARPORT is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_STUB is not set
|
||||
|
|
|
@ -536,7 +536,6 @@ CONFIG_I2C_ALGOBIT=y
|
|||
# I2C Hardware Bus support
|
||||
#
|
||||
# CONFIG_I2C_ELEKTOR is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_PARPORT is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_PCA_ISA is not set
|
||||
|
|
|
@ -748,7 +748,6 @@ CONFIG_I2C=m
|
|||
# CONFIG_I2C_ELEKTOR is not set
|
||||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
|
|
|
@ -698,7 +698,6 @@ CONFIG_I2C_ALGOBIT=y
|
|||
# I2C Hardware Bus support
|
||||
#
|
||||
# CONFIG_I2C_ELEKTOR is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_STUB is not set
|
||||
# CONFIG_I2C_PCA_ISA is not set
|
||||
|
|
|
@ -735,7 +735,6 @@ CONFIG_I2C_CHARDEV=m
|
|||
# I2C Hardware Bus support
|
||||
#
|
||||
CONFIG_I2C_AT91=m
|
||||
CONFIG_I2C_ISA=m
|
||||
# CONFIG_I2C_OCORES is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_STUB is not set
|
||||
|
|
|
@ -558,7 +558,6 @@ CONFIG_I2C_ALGOBIT=y
|
|||
#
|
||||
# I2C Hardware Bus support
|
||||
#
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_PARPORT is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_STUB is not set
|
||||
|
|
|
@ -826,7 +826,6 @@ CONFIG_I2C_ALGOBIT=m
|
|||
# I2C Hardware Bus support
|
||||
#
|
||||
# CONFIG_I2C_ELEKTOR is not set
|
||||
CONFIG_I2C_ISA=m
|
||||
# CONFIG_I2C_OCORES is not set
|
||||
# CONFIG_I2C_PARPORT is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
|
|
|
@ -699,7 +699,6 @@ CONFIG_I2C_ALGOPCF=m
|
|||
# I2C Hardware Bus support
|
||||
#
|
||||
CONFIG_I2C_ELEKTOR=m
|
||||
CONFIG_I2C_ISA=m
|
||||
# CONFIG_I2C_OCORES is not set
|
||||
# CONFIG_I2C_PARPORT is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
|
|
|
@ -531,7 +531,6 @@ CONFIG_I2C_CHARDEV=m
|
|||
# CONFIG_I2C_AMD8111 is not set
|
||||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_PIIX4 is not set
|
||||
|
|
|
@ -452,7 +452,6 @@ CONFIG_I2C_CHARDEV=y
|
|||
# CONFIG_I2C_AMD8111 is not set
|
||||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
CONFIG_I2C_MPC=y
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
|
|
|
@ -413,7 +413,6 @@ CONFIG_I2C_CHARDEV=y
|
|||
#
|
||||
# I2C Hardware Bus support
|
||||
#
|
||||
# CONFIG_I2C_ISA is not set
|
||||
CONFIG_I2C_MPC=y
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_PCA_ISA is not set
|
||||
|
|
|
@ -518,7 +518,6 @@ CONFIG_I2C_CHARDEV=y
|
|||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_PIIX4 is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
CONFIG_I2C_MPC=y
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
|
|
|
@ -489,7 +489,6 @@ CONFIG_I2C_CHARDEV=y
|
|||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_PIIX4 is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
CONFIG_I2C_MPC=y
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
|
|
|
@ -710,7 +710,6 @@ CONFIG_I2C_CHARDEV=y
|
|||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_PIIX4 is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_MPC is not set
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
|
|
|
@ -661,7 +661,6 @@ CONFIG_I2C_ALGOBIT=m
|
|||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_PIIX4 is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_MPC is not set
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT is not set
|
||||
|
|
|
@ -461,7 +461,6 @@ CONFIG_I2C_CHARDEV=y
|
|||
# CONFIG_I2C_I801 is not set
|
||||
# CONFIG_I2C_I810 is not set
|
||||
# CONFIG_I2C_IBM_IIC is not set
|
||||
# CONFIG_I2C_ISA is not set
|
||||
# CONFIG_I2C_NFORCE2 is not set
|
||||
# CONFIG_I2C_PARPORT_LIGHT is not set
|
||||
# CONFIG_I2C_PIIX4 is not set
|
||||
|
|
|
@ -29,17 +29,34 @@ config HWMON_VID
|
|||
default n
|
||||
|
||||
config SENSORS_ABITUGURU
|
||||
tristate "Abit uGuru"
|
||||
tristate "Abit uGuru (rev 1 & 2)"
|
||||
depends on 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).
|
||||
If you say yes here you get support for the sensor part of the first
|
||||
and second revision of the Abit uGuru chip. 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 from before end 2005). For more info and a list
|
||||
of which motherboards have which revision see
|
||||
Documentation/hwmon/abituguru
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called abituguru.
|
||||
|
||||
config SENSORS_ABITUGURU3
|
||||
tristate "Abit uGuru (rev 3)"
|
||||
depends on HWMON && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the sensor part of the
|
||||
third revision of the Abit uGuru chip. Only reading the sensors
|
||||
and their settings is supported. The third revision of the Abit
|
||||
uGuru chip can be found on recent Abit motherboards (since end
|
||||
2005). For more info and a list of which motherboards have which
|
||||
revision see Documentation/hwmon/abituguru3
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called abituguru3.
|
||||
|
||||
config SENSORS_AD7418
|
||||
tristate "Analog Devices AD7416, AD7417 and AD7418"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
|
@ -250,12 +267,10 @@ config SENSORS_CORETEMP
|
|||
|
||||
config SENSORS_IT87
|
||||
tristate "ITE IT87xx and compatibles"
|
||||
depends on I2C
|
||||
select I2C_ISA
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for ITE IT8705F, IT8712F,
|
||||
IT8716F and IT8718F sensor chips, and the SiS960 clone.
|
||||
IT8716F, IT8718F and IT8726F sensor chips, and the SiS960 clone.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called it87.
|
||||
|
@ -365,8 +380,8 @@ config SENSORS_LM90
|
|||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for National Semiconductor LM90,
|
||||
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and
|
||||
MAX6658 sensor chips.
|
||||
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657,
|
||||
MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips.
|
||||
|
||||
The Analog Devices ADT7461 sensor chip is also supported, but only
|
||||
if found in ADM1032 compatibility mode.
|
||||
|
@ -384,6 +399,17 @@ config SENSORS_LM92
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called lm92.
|
||||
|
||||
config SENSORS_LM93
|
||||
tristate "National Semiconductor LM93 and compatibles"
|
||||
depends on HWMON && I2C
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for National Semiconductor LM93
|
||||
sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm93.
|
||||
|
||||
config SENSORS_MAX1619
|
||||
tristate "Maxim MAX1619 sensor chip"
|
||||
depends on I2C
|
||||
|
@ -405,8 +431,6 @@ config SENSORS_MAX6650
|
|||
|
||||
config SENSORS_PC87360
|
||||
tristate "National Semiconductor PC87360 family"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
select I2C_ISA
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get access to the hardware monitoring
|
||||
|
@ -433,8 +457,7 @@ config SENSORS_PC87427
|
|||
|
||||
config SENSORS_SIS5595
|
||||
tristate "Silicon Integrated Systems Corp. SiS5595"
|
||||
depends on I2C && PCI && EXPERIMENTAL
|
||||
select I2C_ISA
|
||||
depends on PCI
|
||||
help
|
||||
If you say yes here you get support for the integrated sensors in
|
||||
SiS5595 South Bridges.
|
||||
|
@ -442,6 +465,18 @@ config SENSORS_SIS5595
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called sis5595.
|
||||
|
||||
config SENSORS_DME1737
|
||||
tristate "SMSC DME1737 and compatibles"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for the hardware monitoring
|
||||
and fan control features of the SMSC DME1737 (and compatibles
|
||||
like the Asus A8000) Super-I/O chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called dme1737.
|
||||
|
||||
config SENSORS_SMSC47M1
|
||||
tristate "SMSC LPC47M10x and compatibles"
|
||||
help
|
||||
|
@ -487,8 +522,7 @@ config SENSORS_SMSC47B397
|
|||
|
||||
config SENSORS_VIA686A
|
||||
tristate "VIA686A"
|
||||
depends on I2C && PCI
|
||||
select I2C_ISA
|
||||
depends on PCI
|
||||
help
|
||||
If you say yes here you get support for the integrated sensors in
|
||||
Via 686A/B South Bridges.
|
||||
|
@ -509,9 +543,8 @@ config SENSORS_VT1211
|
|||
|
||||
config SENSORS_VT8231
|
||||
tristate "VIA VT8231"
|
||||
depends on I2C && PCI && EXPERIMENTAL
|
||||
depends on PCI
|
||||
select HWMON_VID
|
||||
select I2C_ISA
|
||||
help
|
||||
If you say yes here then you get support for the integrated sensors
|
||||
in the VIA VT8231 device.
|
||||
|
@ -584,17 +617,16 @@ config SENSORS_W83627HF
|
|||
will be called w83627hf.
|
||||
|
||||
config SENSORS_W83627EHF
|
||||
tristate "Winbond W83627EHF"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
select I2C_ISA
|
||||
tristate "Winbond W83627EHF/DHG"
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get preliminary support for the hardware
|
||||
If you say yes here you get support for the hardware
|
||||
monitoring functionality of the Winbond W83627EHF Super-I/O chip.
|
||||
Only fan and temperature inputs are supported at the moment, while
|
||||
the chip does much more than that.
|
||||
|
||||
This driver also supports the W83627EHG, which is the lead-free
|
||||
version of the W83627EHF.
|
||||
version of the W83627EHF, and the W83627DHG, which is a similar
|
||||
chip suited for specific Intel processors that use PECI such as
|
||||
the Core 2 Duo.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called w83627ehf.
|
||||
|
|
|
@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
|
|||
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
||||
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
||||
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
|
||||
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
|
||||
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
|
||||
|
@ -25,6 +26,7 @@ obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
|
|||
obj-$(CONFIG_SENSORS_AMS) += ams/
|
||||
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
|
||||
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
||||
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
|
||||
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
|
||||
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
|
||||
obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
|
||||
|
@ -45,6 +47,7 @@ obj-$(CONFIG_SENSORS_LM85) += lm85.o
|
|||
obj-$(CONFIG_SENSORS_LM87) += lm87.o
|
||||
obj-$(CONFIG_SENSORS_LM90) += lm90.o
|
||||
obj-$(CONFIG_SENSORS_LM92) += lm92.o
|
||||
obj-$(CONFIG_SENSORS_LM93) += lm93.o
|
||||
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
|
||||
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
||||
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
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!
|
||||
This driver supports the sensor part of the first and second revision of
|
||||
the custom Abit uGuru chip found on Abit uGuru motherboards. Note: because
|
||||
of lack of specs the CPU/RAM voltage & frequency control is not supported!
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -31,6 +31,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* Banks */
|
||||
|
@ -418,7 +419,7 @@ static int __devinit
|
|||
abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
|
||||
u8 sensor_addr)
|
||||
{
|
||||
u8 val, buf[3];
|
||||
u8 val, test_flag, buf[3];
|
||||
int i, ret = -ENODEV; /* error is the most common used retval :| */
|
||||
|
||||
/* If overriden by the user return the user selected type */
|
||||
|
@ -436,7 +437,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
|
|||
return -ENODEV;
|
||||
|
||||
/* Test val is sane / usable for sensor type detection. */
|
||||
if ((val < 10u) || (val > 240u)) {
|
||||
if ((val < 10u) || (val > 250u)) {
|
||||
printk(KERN_WARNING ABIT_UGURU_NAME
|
||||
": bank1-sensor: %d reading (%d) too close to limits, "
|
||||
"unable to determine sensor type, skipping sensor\n",
|
||||
|
@ -449,10 +450,20 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
|
|||
|
||||
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;
|
||||
high, or vica versa if the reading is very high. If its a volt
|
||||
sensor this should always give us an alarm. */
|
||||
if (val <= 240u) {
|
||||
buf[0] = ABIT_UGURU_VOLT_LOW_ALARM_ENABLE;
|
||||
buf[1] = 245;
|
||||
buf[2] = 250;
|
||||
test_flag = ABIT_UGURU_VOLT_LOW_ALARM_FLAG;
|
||||
} else {
|
||||
buf[0] = ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE;
|
||||
buf[1] = 5;
|
||||
buf[2] = 10;
|
||||
test_flag = ABIT_UGURU_VOLT_HIGH_ALARM_FLAG;
|
||||
}
|
||||
|
||||
if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
|
||||
buf, 3) != 3)
|
||||
goto abituguru_detect_bank1_sensor_type_exit;
|
||||
|
@ -469,13 +480,13 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
|
|||
sensor_addr, buf, 3,
|
||||
ABIT_UGURU_MAX_RETRIES) != 3)
|
||||
goto abituguru_detect_bank1_sensor_type_exit;
|
||||
if (buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) {
|
||||
if (buf[0] & test_flag) {
|
||||
ABIT_UGURU_DEBUG(2, " found volt sensor\n");
|
||||
ret = ABIT_UGURU_IN_SENSOR;
|
||||
goto abituguru_detect_bank1_sensor_type_exit;
|
||||
} else
|
||||
ABIT_UGURU_DEBUG(2, " alarm raised during volt "
|
||||
"sensor test, but volt low flag not set\n");
|
||||
"sensor test, but volt range flag not set\n");
|
||||
} else
|
||||
ABIT_UGURU_DEBUG(2, " alarm not raised during volt sensor "
|
||||
"test\n");
|
||||
|
@ -1287,6 +1298,7 @@ abituguru_probe_error:
|
|||
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
|
||||
device_remove_file(&pdev->dev,
|
||||
&abituguru_sysfs_attr[i].dev_attr);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
return res;
|
||||
}
|
||||
|
@ -1296,13 +1308,13 @@ static int __devexit abituguru_remove(struct platform_device *pdev)
|
|||
int i;
|
||||
struct abituguru_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
|
||||
device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
|
||||
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
|
||||
device_remove_file(&pdev->dev,
|
||||
&abituguru_sysfs_attr[i].dev_attr);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
|
@ -1436,6 +1448,15 @@ static int __init abituguru_init(void)
|
|||
int address, err;
|
||||
struct resource res = { .flags = IORESOURCE_IO };
|
||||
|
||||
#ifdef CONFIG_DMI
|
||||
char *board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
|
||||
|
||||
/* safety check, refuse to load on non Abit motherboards */
|
||||
if (!force && (!board_vendor ||
|
||||
strcmp(board_vendor, "http://www.abit.com.tw/")))
|
||||
return -ENODEV;
|
||||
#endif
|
||||
|
||||
address = abituguru_detect();
|
||||
if (address < 0)
|
||||
return address;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -318,7 +318,7 @@ exit:
|
|||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
void coretemp_device_remove(unsigned int cpu)
|
||||
static void coretemp_device_remove(unsigned int cpu)
|
||||
{
|
||||
struct pdev_entry *p, *n;
|
||||
mutex_lock(&pdev_list_mutex);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -27,6 +27,7 @@
|
|||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
@ -52,9 +53,11 @@ MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low")
|
|||
#define DS1621_REG_CONFIG_DONE 0x80
|
||||
|
||||
/* The DS1621 registers */
|
||||
#define DS1621_REG_TEMP 0xAA /* word, RO */
|
||||
#define DS1621_REG_TEMP_MIN 0xA2 /* word, RW */
|
||||
#define DS1621_REG_TEMP_MAX 0xA1 /* word, RW */
|
||||
static const u8 DS1621_REG_TEMP[3] = {
|
||||
0xAA, /* input, word, RO */
|
||||
0xA2, /* min, word, RW */
|
||||
0xA1, /* max, word, RW */
|
||||
};
|
||||
#define DS1621_REG_CONF 0xAC /* byte, RW */
|
||||
#define DS1621_COM_START 0xEE /* no data */
|
||||
#define DS1621_COM_STOP 0x22 /* no data */
|
||||
|
@ -63,10 +66,7 @@ MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low")
|
|||
#define DS1621_ALARM_TEMP_HIGH 0x40
|
||||
#define DS1621_ALARM_TEMP_LOW 0x20
|
||||
|
||||
/* Conversions. Rounding and limit checking is only done on the TO_REG
|
||||
variants. Note that you should be a bit careful with which arguments
|
||||
these macros are called: arguments may be evaluated more than once.
|
||||
Fixing this is just not worth it. */
|
||||
/* Conversions */
|
||||
#define ALARMS_FROM_REG(val) ((val) & \
|
||||
(DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
|
||||
|
||||
|
@ -78,7 +78,7 @@ struct ds1621_data {
|
|||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u16 temp, temp_min, temp_max; /* Register values, word */
|
||||
u16 temp[3]; /* Register values, word */
|
||||
u8 conf; /* Register encoding, combined */
|
||||
};
|
||||
|
||||
|
@ -101,7 +101,7 @@ static struct i2c_driver ds1621_driver = {
|
|||
|
||||
/* All registers are word-sized, except for the configuration register.
|
||||
DS1621 uses a high-byte first convention, which is exactly opposite to
|
||||
the usual practice. */
|
||||
the SMBus standard. */
|
||||
static int ds1621_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
if (reg == DS1621_REG_CONF)
|
||||
|
@ -110,9 +110,6 @@ static int ds1621_read_value(struct i2c_client *client, u8 reg)
|
|||
return swab16(i2c_smbus_read_word_data(client, reg));
|
||||
}
|
||||
|
||||
/* All registers are word-sized, except for the configuration register.
|
||||
DS1621 uses a high-byte first convention, which is exactly opposite to
|
||||
the usual practice. */
|
||||
static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
if (reg == DS1621_REG_CONF)
|
||||
|
@ -139,50 +136,61 @@ static void ds1621_init_client(struct i2c_client *client)
|
|||
i2c_smbus_write_byte(client, DS1621_COM_START);
|
||||
}
|
||||
|
||||
#define show(value) \
|
||||
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct ds1621_data *data = ds1621_update_client(dev); \
|
||||
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ds1621_data *data = ds1621_update_client(dev);
|
||||
return sprintf(buf, "%d\n",
|
||||
LM75_TEMP_FROM_REG(data->temp[attr->index]));
|
||||
}
|
||||
|
||||
show(temp);
|
||||
show(temp_min);
|
||||
show(temp_max);
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds1621_data *data = ds1621_update_client(dev);
|
||||
u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10));
|
||||
|
||||
#define set_temp(suffix, value, reg) \
|
||||
static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \
|
||||
size_t count) \
|
||||
{ \
|
||||
struct i2c_client *client = to_i2c_client(dev); \
|
||||
struct ds1621_data *data = ds1621_update_client(dev); \
|
||||
u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \
|
||||
\
|
||||
mutex_lock(&data->update_lock); \
|
||||
data->value = val; \
|
||||
ds1621_write_value(client, reg, data->value); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return count; \
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp[attr->index] = val;
|
||||
ds1621_write_value(client, DS1621_REG_TEMP[attr->index],
|
||||
data->temp[attr->index]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
set_temp(min, temp_min, DS1621_REG_TEMP_MIN);
|
||||
set_temp(max, temp_max, DS1621_REG_TEMP_MAX);
|
||||
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct ds1621_data *data = ds1621_update_client(dev);
|
||||
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->conf));
|
||||
}
|
||||
|
||||
static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ds1621_data *data = ds1621_update_client(dev);
|
||||
return sprintf(buf, "%d\n", !!(data->conf & attr->index));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL);
|
||||
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min);
|
||||
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
|
||||
DS1621_ALARM_TEMP_LOW);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
|
||||
DS1621_ALARM_TEMP_HIGH);
|
||||
|
||||
static struct attribute *ds1621_attributes[] = {
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp1_min.attr,
|
||||
&dev_attr_temp1_max.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
NULL
|
||||
};
|
||||
|
@ -204,9 +212,9 @@ static int ds1621_detect(struct i2c_adapter *adapter, int address,
|
|||
int kind)
|
||||
{
|
||||
int conf, temp;
|
||||
struct i2c_client *new_client;
|
||||
struct i2c_client *client;
|
||||
struct ds1621_data *data;
|
||||
int err = 0;
|
||||
int i, err = 0;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_WORD_DATA
|
||||
|
@ -221,55 +229,44 @@ static int ds1621_detect(struct i2c_adapter *adapter, int address,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
new_client = &data->client;
|
||||
i2c_set_clientdata(new_client, data);
|
||||
new_client->addr = address;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &ds1621_driver;
|
||||
new_client->flags = 0;
|
||||
|
||||
client = &data->client;
|
||||
i2c_set_clientdata(client, data);
|
||||
client->addr = address;
|
||||
client->adapter = adapter;
|
||||
client->driver = &ds1621_driver;
|
||||
|
||||
/* Now, we do the remaining detection. It is lousy. */
|
||||
if (kind < 0) {
|
||||
/* The NVB bit should be low if no EEPROM write has been
|
||||
requested during the latest 10ms, which is highly
|
||||
improbable in our case. */
|
||||
conf = ds1621_read_value(new_client, DS1621_REG_CONF);
|
||||
conf = ds1621_read_value(client, DS1621_REG_CONF);
|
||||
if (conf & DS1621_REG_CONFIG_NVB)
|
||||
goto exit_free;
|
||||
/* The 7 lowest bits of a temperature should always be 0. */
|
||||
temp = ds1621_read_value(new_client, DS1621_REG_TEMP);
|
||||
if (temp & 0x007f)
|
||||
goto exit_free;
|
||||
temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MIN);
|
||||
if (temp & 0x007f)
|
||||
goto exit_free;
|
||||
temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MAX);
|
||||
if (temp & 0x007f)
|
||||
goto exit_free;
|
||||
for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
|
||||
temp = ds1621_read_value(client, DS1621_REG_TEMP[i]);
|
||||
if (temp & 0x007f)
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine the chip type - only one kind supported! */
|
||||
if (kind <= 0)
|
||||
kind = ds1621;
|
||||
|
||||
/* Fill in remaining client fields and put it into the global list */
|
||||
strlcpy(new_client->name, "ds1621", I2C_NAME_SIZE);
|
||||
data->valid = 0;
|
||||
strlcpy(client->name, "ds1621", I2C_NAME_SIZE);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
if ((err = i2c_attach_client(client)))
|
||||
goto exit_free;
|
||||
|
||||
/* Initialize the DS1621 chip */
|
||||
ds1621_init_client(new_client);
|
||||
ds1621_init_client(client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
if ((err = sysfs_create_group(&new_client->dev.kobj, &ds1621_group)))
|
||||
if ((err = sysfs_create_group(&client->dev.kobj, &ds1621_group)))
|
||||
goto exit_detach;
|
||||
|
||||
data->class_dev = hwmon_device_register(&new_client->dev);
|
||||
data->class_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->class_dev)) {
|
||||
err = PTR_ERR(data->class_dev);
|
||||
goto exit_remove_files;
|
||||
|
@ -278,9 +275,9 @@ static int ds1621_detect(struct i2c_adapter *adapter, int address,
|
|||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &ds1621_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &ds1621_group);
|
||||
exit_detach:
|
||||
i2c_detach_client(new_client);
|
||||
i2c_detach_client(client);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
|
@ -314,23 +311,21 @@ static struct ds1621_data *ds1621_update_client(struct device *dev)
|
|||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
int i;
|
||||
|
||||
dev_dbg(&client->dev, "Starting ds1621 update\n");
|
||||
|
||||
data->conf = ds1621_read_value(client, DS1621_REG_CONF);
|
||||
|
||||
data->temp = ds1621_read_value(client, DS1621_REG_TEMP);
|
||||
|
||||
data->temp_min = ds1621_read_value(client,
|
||||
DS1621_REG_TEMP_MIN);
|
||||
data->temp_max = ds1621_read_value(client,
|
||||
DS1621_REG_TEMP_MAX);
|
||||
for (i = 0; i < ARRAY_SIZE(data->temp); i++)
|
||||
data->temp[i] = ds1621_read_value(client,
|
||||
DS1621_REG_TEMP[i]);
|
||||
|
||||
/* reset alarms if necessary */
|
||||
new_conf = data->conf;
|
||||
if (data->temp > data->temp_min)
|
||||
if (data->temp[0] > data->temp[1]) /* input > min */
|
||||
new_conf &= ~DS1621_ALARM_TEMP_LOW;
|
||||
if (data->temp < data->temp_max)
|
||||
if (data->temp[0] < data->temp[2]) /* input < max */
|
||||
new_conf &= ~DS1621_ALARM_TEMP_HIGH;
|
||||
if (data->conf != new_conf)
|
||||
ds1621_write_value(client, DS1621_REG_CONF,
|
||||
|
|
|
@ -127,6 +127,13 @@ superio_exit(int base)
|
|||
#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr))
|
||||
#define F71805F_REG_TEMP_HYST(nr) (0x55 + 2 * (nr))
|
||||
#define F71805F_REG_TEMP_MODE 0x01
|
||||
/* pwm/fan pwmnr from 0 to 2, auto point apnr from 0 to 2 */
|
||||
/* map Fintek numbers to our numbers as follows: 9->0, 5->1, 1->2 */
|
||||
#define F71805F_REG_PWM_AUTO_POINT_TEMP(pwmnr, apnr) \
|
||||
(0xA0 + 0x10 * (pwmnr) + (2 - (apnr)))
|
||||
#define F71805F_REG_PWM_AUTO_POINT_FAN(pwmnr, apnr) \
|
||||
(0xA4 + 0x10 * (pwmnr) + \
|
||||
2 * (2 - (apnr)))
|
||||
|
||||
#define F71805F_REG_START 0x00
|
||||
/* status nr from 0 to 2 */
|
||||
|
@ -144,6 +151,11 @@ superio_exit(int base)
|
|||
* Data structures and manipulation thereof
|
||||
*/
|
||||
|
||||
struct f71805f_auto_point {
|
||||
u8 temp[3];
|
||||
u16 fan[3];
|
||||
};
|
||||
|
||||
struct f71805f_data {
|
||||
unsigned short addr;
|
||||
const char *name;
|
||||
|
@ -170,6 +182,7 @@ struct f71805f_data {
|
|||
u8 temp_hyst[3];
|
||||
u8 temp_mode;
|
||||
unsigned long alarms;
|
||||
struct f71805f_auto_point auto_points[3];
|
||||
};
|
||||
|
||||
struct f71805f_sio_data {
|
||||
|
@ -312,7 +325,7 @@ static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val)
|
|||
static struct f71805f_data *f71805f_update_device(struct device *dev)
|
||||
{
|
||||
struct f71805f_data *data = dev_get_drvdata(dev);
|
||||
int nr;
|
||||
int nr, apnr;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
|
@ -342,6 +355,18 @@ static struct f71805f_data *f71805f_update_device(struct device *dev)
|
|||
F71805F_REG_TEMP_HYST(nr));
|
||||
}
|
||||
data->temp_mode = f71805f_read8(data, F71805F_REG_TEMP_MODE);
|
||||
for (nr = 0; nr < 3; nr++) {
|
||||
for (apnr = 0; apnr < 3; apnr++) {
|
||||
data->auto_points[nr].temp[apnr] =
|
||||
f71805f_read8(data,
|
||||
F71805F_REG_PWM_AUTO_POINT_TEMP(nr,
|
||||
apnr));
|
||||
data->auto_points[nr].fan[apnr] =
|
||||
f71805f_read16(data,
|
||||
F71805F_REG_PWM_AUTO_POINT_FAN(nr,
|
||||
apnr));
|
||||
}
|
||||
}
|
||||
|
||||
data->last_limits = jiffies;
|
||||
}
|
||||
|
@ -705,6 +730,70 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute
|
|||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pwm_auto_point_temp(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char* buf)
|
||||
{
|
||||
struct f71805f_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
|
||||
int pwmnr = attr->nr;
|
||||
int apnr = attr->index;
|
||||
|
||||
return sprintf(buf, "%ld\n",
|
||||
temp_from_reg(data->auto_points[pwmnr].temp[apnr]));
|
||||
}
|
||||
|
||||
static ssize_t set_pwm_auto_point_temp(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char* buf, size_t count)
|
||||
{
|
||||
struct f71805f_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
|
||||
int pwmnr = attr->nr;
|
||||
int apnr = attr->index;
|
||||
unsigned long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->auto_points[pwmnr].temp[apnr] = temp_to_reg(val);
|
||||
f71805f_write8(data, F71805F_REG_PWM_AUTO_POINT_TEMP(pwmnr, apnr),
|
||||
data->auto_points[pwmnr].temp[apnr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pwm_auto_point_fan(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char* buf)
|
||||
{
|
||||
struct f71805f_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
|
||||
int pwmnr = attr->nr;
|
||||
int apnr = attr->index;
|
||||
|
||||
return sprintf(buf, "%ld\n",
|
||||
fan_from_reg(data->auto_points[pwmnr].fan[apnr]));
|
||||
}
|
||||
|
||||
static ssize_t set_pwm_auto_point_fan(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char* buf, size_t count)
|
||||
{
|
||||
struct f71805f_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
|
||||
int pwmnr = attr->nr;
|
||||
int apnr = attr->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->auto_points[pwmnr].fan[apnr] = fan_to_reg(val);
|
||||
f71805f_write16(data, F71805F_REG_PWM_AUTO_POINT_FAN(pwmnr, apnr),
|
||||
data->auto_points[pwmnr].fan[apnr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
|
@ -932,6 +1021,63 @@ static SENSOR_DEVICE_ATTR(pwm3_freq, S_IRUGO | S_IWUSR,
|
|||
show_pwm_freq, set_pwm_freq, 2);
|
||||
static SENSOR_DEVICE_ATTR(pwm3_mode, S_IRUGO, show_pwm_mode, NULL, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
0, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
0, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
0, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
0, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
0, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
0, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
1, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
1, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
1, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
1, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
1, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
1, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
2, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
2, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
2, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
2, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_temp, set_pwm_auto_point_temp,
|
||||
2, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_fan, S_IRUGO | S_IWUSR,
|
||||
show_pwm_auto_point_fan, set_pwm_auto_point_fan,
|
||||
2, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
|
||||
|
@ -1014,6 +1160,25 @@ static struct attribute *f71805f_attributes[] = {
|
|||
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_type.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point2_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point3_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point1_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point2_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point3_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point1_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point2_fan.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point3_fan.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in0_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_alarm.dev_attr.attr,
|
||||
|
@ -1242,12 +1407,12 @@ static int __devexit f71805f_remove(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
int i;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
|
||||
for (i = 0; i < 4; i++)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
|
@ -1290,15 +1455,12 @@ static int __init f71805f_device_add(unsigned short address,
|
|||
goto exit_device_put;
|
||||
}
|
||||
|
||||
pdev->dev.platform_data = kmalloc(sizeof(struct f71805f_sio_data),
|
||||
GFP_KERNEL);
|
||||
if (!pdev->dev.platform_data) {
|
||||
err = -ENOMEM;
|
||||
err = platform_device_add_data(pdev, sio_data,
|
||||
sizeof(struct f71805f_sio_data));
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
|
||||
goto exit_device_put;
|
||||
}
|
||||
memcpy(pdev->dev.platform_data, sio_data,
|
||||
sizeof(struct f71805f_sio_data));
|
||||
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -364,7 +364,7 @@ static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
|
|||
/* Individual alarm files */
|
||||
static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
|
||||
|
@ -383,7 +383,7 @@ static struct attribute *lm63_attributes[] = {
|
|||
&dev_attr_temp2_crit_hyst.attr,
|
||||
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
|
|
|
@ -223,14 +223,14 @@ static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp, NULL, 8);
|
|||
/* Individual alarm files */
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp3_input_fault, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8);
|
||||
static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
|
||||
static SENSOR_DEVICE_ATTR(temp4_input_fault, S_IRUGO, show_alarm, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 12);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 13);
|
||||
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 13);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 15);
|
||||
/* Raw alarm file for compatibility */
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
|
@ -245,7 +245,7 @@ static struct attribute *lm83_attributes[] = {
|
|||
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
|
@ -266,9 +266,9 @@ static struct attribute *lm83_attributes_opt[] = {
|
|||
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_input_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
* variants. The extra address and features of the MAX6659 are not
|
||||
* supported by this driver.
|
||||
*
|
||||
* This driver also supports the MAX6680 and MAX6681, two other sensor
|
||||
* chips made by Maxim. These are quite similar to the other Maxim
|
||||
* chips. Complete datasheet can be obtained at:
|
||||
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370
|
||||
* The MAX6680 and MAX6681 only differ in the pinout so they can be
|
||||
* treated identically.
|
||||
*
|
||||
* This driver also supports the ADT7461 chip from Analog Devices but
|
||||
* only in its "compatability mode". If an ADT7461 chip is found but
|
||||
* is configured in non-compatible mode (where its temperature
|
||||
|
@ -84,20 +91,25 @@
|
|||
/*
|
||||
* Addresses to scan
|
||||
* Address is fully defined internally and cannot be changed except for
|
||||
* MAX6659.
|
||||
* MAX6659, MAX6680 and MAX6681.
|
||||
* LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658
|
||||
* have address 0x4c.
|
||||
* ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d.
|
||||
* MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported).
|
||||
* MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
|
||||
* 0x4c, 0x4d or 0x4e.
|
||||
*/
|
||||
|
||||
static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END };
|
||||
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
|
||||
0x29, 0x2a, 0x2b,
|
||||
0x4c, 0x4d, 0x4e,
|
||||
I2C_CLIENT_END };
|
||||
|
||||
/*
|
||||
* Insmod parameters
|
||||
*/
|
||||
|
||||
I2C_CLIENT_INSMOD_6(lm90, adm1032, lm99, lm86, max6657, adt7461);
|
||||
I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
|
||||
|
||||
/*
|
||||
* The LM90 registers
|
||||
|
@ -359,7 +371,7 @@ static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4);
|
|||
/* Individual alarm files */
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5);
|
||||
|
@ -381,7 +393,7 @@ static struct attribute *lm90_attributes[] = {
|
|||
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
|
@ -429,7 +441,7 @@ static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
|
|||
*/
|
||||
|
||||
/* The ADM1032 supports PEC but not on write byte transactions, so we need
|
||||
to explicitely ask for a transaction without PEC. */
|
||||
to explicitly ask for a transaction without PEC. */
|
||||
static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value)
|
||||
{
|
||||
return i2c_smbus_xfer(client->adapter, client->addr,
|
||||
|
@ -525,7 +537,8 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
|
|||
®_convrate) < 0)
|
||||
goto exit_free;
|
||||
|
||||
if (man_id == 0x01) { /* National Semiconductor */
|
||||
if ((address == 0x4C || address == 0x4D)
|
||||
&& man_id == 0x01) { /* National Semiconductor */
|
||||
u8 reg_config2;
|
||||
|
||||
if (lm90_read_reg(new_client, LM90_REG_R_CONFIG2,
|
||||
|
@ -548,7 +561,8 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
|
|||
}
|
||||
}
|
||||
} else
|
||||
if (man_id == 0x41) { /* Analog Devices */
|
||||
if ((address == 0x4C || address == 0x4D)
|
||||
&& man_id == 0x41) { /* Analog Devices */
|
||||
if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
|
||||
&& (reg_config1 & 0x3F) == 0x00
|
||||
&& reg_convrate <= 0x0A) {
|
||||
|
@ -562,18 +576,30 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
|
|||
} else
|
||||
if (man_id == 0x4D) { /* Maxim */
|
||||
/*
|
||||
* The Maxim variants do NOT have a chip_id register.
|
||||
* Reading from that address will return the last read
|
||||
* value, which in our case is those of the man_id
|
||||
* register. Likewise, the config1 register seems to
|
||||
* lack a low nibble, so the value will be those of the
|
||||
* previous read, so in our case those of the man_id
|
||||
* register.
|
||||
* The MAX6657, MAX6658 and MAX6659 do NOT have a
|
||||
* chip_id register. Reading from that address will
|
||||
* return the last read value, which in our case is
|
||||
* those of the man_id register. Likewise, the config1
|
||||
* register seems to lack a low nibble, so the value
|
||||
* will be those of the previous read, so in our case
|
||||
* those of the man_id register.
|
||||
*/
|
||||
if (chip_id == man_id
|
||||
&& (address == 0x4F || address == 0x4D)
|
||||
&& (reg_config1 & 0x1F) == (man_id & 0x0F)
|
||||
&& reg_convrate <= 0x09) {
|
||||
kind = max6657;
|
||||
} else
|
||||
/* The chip_id register of the MAX6680 and MAX6681
|
||||
* holds the revision of the chip.
|
||||
* the lowest bit of the config1 register is unused
|
||||
* and should return zero when read, so should the
|
||||
* second to last bit of config1 (software reset)
|
||||
*/
|
||||
if (chip_id == 0x01
|
||||
&& (reg_config1 & 0x03) == 0x00
|
||||
&& reg_convrate <= 0x07) {
|
||||
kind = max6680;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,6 +625,8 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
|
|||
name = "lm86";
|
||||
} else if (kind == max6657) {
|
||||
name = "max6657";
|
||||
} else if (kind == max6680) {
|
||||
name = "max6680";
|
||||
} else if (kind == adt7461) {
|
||||
name = "adt7461";
|
||||
}
|
||||
|
@ -646,7 +674,8 @@ exit:
|
|||
|
||||
static void lm90_init_client(struct i2c_client *client)
|
||||
{
|
||||
u8 config;
|
||||
u8 config, config_orig;
|
||||
struct lm90_data *data = i2c_get_clientdata(client);
|
||||
|
||||
/*
|
||||
* Start the conversions.
|
||||
|
@ -657,9 +686,20 @@ static void lm90_init_client(struct i2c_client *client)
|
|||
dev_warn(&client->dev, "Initialization failed!\n");
|
||||
return;
|
||||
}
|
||||
if (config & 0x40)
|
||||
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
|
||||
config & 0xBF); /* run */
|
||||
config_orig = config;
|
||||
|
||||
/*
|
||||
* Put MAX6680/MAX8881 into extended resolution (bit 0x10,
|
||||
* 0.125 degree resolution) and range (0x08, extend range
|
||||
* to -64 degree) mode for the remote temperature sensor.
|
||||
*/
|
||||
if (data->kind == max6680) {
|
||||
config |= 0x18;
|
||||
}
|
||||
|
||||
config &= 0xBF; /* run */
|
||||
if (config != config_orig) /* Only write if changed */
|
||||
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
|
||||
}
|
||||
|
||||
static int lm90_detach_client(struct i2c_client *client)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* pc87360.c - Part of lm_sensors, Linux kernel modules
|
||||
* for hardware monitoring
|
||||
* Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
|
||||
* Copyright (C) 2004, 2007 Jean Delvare <khali@linux-fr.org>
|
||||
*
|
||||
* Copied from smsc47m1.c:
|
||||
* Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||
|
@ -37,8 +37,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-isa.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/hwmon-vid.h>
|
||||
|
@ -47,12 +46,10 @@
|
|||
#include <asm/io.h>
|
||||
|
||||
static u8 devid;
|
||||
static unsigned short address;
|
||||
static struct platform_device *pdev;
|
||||
static unsigned short extra_isa[3];
|
||||
static u8 confreg[4];
|
||||
|
||||
enum chips { any_chip, pc87360, pc87363, pc87364, pc87365, pc87366 };
|
||||
|
||||
static int init = 1;
|
||||
module_param(init, int, 0);
|
||||
MODULE_PARM_DESC(init,
|
||||
|
@ -178,11 +175,11 @@ static inline u8 PWM_TO_REG(int val, int inv)
|
|||
((val) + 500) / 1000)
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
* Device data
|
||||
*/
|
||||
|
||||
struct pc87360_data {
|
||||
struct i2c_client client;
|
||||
const char *name;
|
||||
struct class_device *class_dev;
|
||||
struct mutex lock;
|
||||
struct mutex update_lock;
|
||||
|
@ -222,27 +219,28 @@ struct pc87360_data {
|
|||
* Functions declaration
|
||||
*/
|
||||
|
||||
static int pc87360_detect(struct i2c_adapter *adapter);
|
||||
static int pc87360_detach_client(struct i2c_client *client);
|
||||
static int pc87360_probe(struct platform_device *pdev);
|
||||
static int pc87360_remove(struct platform_device *pdev);
|
||||
|
||||
static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank,
|
||||
u8 reg);
|
||||
static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
|
||||
u8 reg, u8 value);
|
||||
static void pc87360_init_client(struct i2c_client *client, int use_thermistors);
|
||||
static void pc87360_init_device(struct platform_device *pdev,
|
||||
int use_thermistors);
|
||||
static struct pc87360_data *pc87360_update_device(struct device *dev);
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
* Driver data
|
||||
*/
|
||||
|
||||
static struct i2c_driver pc87360_driver = {
|
||||
static struct platform_driver pc87360_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "pc87360",
|
||||
},
|
||||
.attach_adapter = pc87360_detect,
|
||||
.detach_client = pc87360_detach_client,
|
||||
.probe = pc87360_probe,
|
||||
.remove = __devexit_p(pc87360_remove),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -281,8 +279,7 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *devattr,
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long fan_min = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -347,8 +344,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, con
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -410,8 +406,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *devattr,
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -425,8 +420,7 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *devattr,
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -511,8 +505,7 @@ static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char
|
|||
}
|
||||
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 pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
data->vrm = simple_strtoul(buf, NULL, 10);
|
||||
return count;
|
||||
}
|
||||
|
@ -584,8 +577,7 @@ static ssize_t set_therm_min(struct device *dev, struct device_attribute *devatt
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -599,8 +591,7 @@ static ssize_t set_therm_max(struct device *dev, struct device_attribute *devatt
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -614,8 +605,7 @@ static ssize_t set_therm_crit(struct device *dev, struct device_attribute *devat
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -715,8 +705,7 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *devattr
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -730,8 +719,7 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *devattr
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -745,8 +733,7 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *devatt
|
|||
size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -818,6 +805,14 @@ static const struct attribute_group pc8736x_temp_group = {
|
|||
.attrs = pc8736x_temp_attr_array,
|
||||
};
|
||||
|
||||
static ssize_t show_name(struct device *dev, struct device_attribute
|
||||
*devattr, char *buf)
|
||||
{
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%s\n", data->name);
|
||||
}
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
|
||||
/*
|
||||
* Device detection, registration and update
|
||||
*/
|
||||
|
@ -912,28 +907,18 @@ static int __init pc87360_find(int sioaddr, u8 *devid, unsigned short *addresses
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pc87360_detect(struct i2c_adapter *adapter)
|
||||
static int __devinit pc87360_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct i2c_client *client;
|
||||
struct pc87360_data *data;
|
||||
int err = 0;
|
||||
const char *name = "pc87360";
|
||||
int use_thermistors = 0;
|
||||
struct device *dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (!(data = kzalloc(sizeof(struct pc87360_data), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
client = &data->client;
|
||||
dev = &client->dev;
|
||||
i2c_set_clientdata(client, data);
|
||||
client->addr = address;
|
||||
mutex_init(&data->lock);
|
||||
client->adapter = adapter;
|
||||
client->driver = &pc87360_driver;
|
||||
client->flags = 0;
|
||||
|
||||
data->fannr = 2;
|
||||
data->innr = 0;
|
||||
data->tempnr = 0;
|
||||
|
@ -960,15 +945,17 @@ static int pc87360_detect(struct i2c_adapter *adapter)
|
|||
break;
|
||||
}
|
||||
|
||||
strlcpy(client->name, name, sizeof(client->name));
|
||||
data->name = name;
|
||||
data->valid = 0;
|
||||
mutex_init(&data->lock);
|
||||
mutex_init(&data->update_lock);
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (((data->address[i] = extra_isa[i]))
|
||||
&& !request_region(extra_isa[i], PC87360_EXTENT,
|
||||
pc87360_driver.driver.name)) {
|
||||
dev_err(&client->dev, "Region 0x%x-0x%x already "
|
||||
dev_err(dev, "Region 0x%x-0x%x already "
|
||||
"in use!\n", extra_isa[i],
|
||||
extra_isa[i]+PC87360_EXTENT-1);
|
||||
for (i--; i >= 0; i--)
|
||||
|
@ -982,9 +969,6 @@ static int pc87360_detect(struct i2c_adapter *adapter)
|
|||
if (data->fannr)
|
||||
data->fan_conf = confreg[0] | (confreg[1] << 8);
|
||||
|
||||
if ((err = i2c_attach_client(client)))
|
||||
goto ERROR2;
|
||||
|
||||
/* Use the correct reference voltage
|
||||
Unless both the VLM and the TMS logical devices agree to
|
||||
use an external Vref, the internal one is used. */
|
||||
|
@ -996,7 +980,7 @@ static int pc87360_detect(struct i2c_adapter *adapter)
|
|||
PC87365_REG_TEMP_CONFIG);
|
||||
}
|
||||
data->in_vref = (i&0x02) ? 3025 : 2966;
|
||||
dev_dbg(&client->dev, "Using %s reference voltage\n",
|
||||
dev_dbg(dev, "Using %s reference voltage\n",
|
||||
(i&0x02) ? "external" : "internal");
|
||||
|
||||
data->vid_conf = confreg[3];
|
||||
|
@ -1015,18 +999,18 @@ static int pc87360_detect(struct i2c_adapter *adapter)
|
|||
if (devid == 0xe9 && data->address[1]) /* PC87366 */
|
||||
use_thermistors = confreg[2] & 0x40;
|
||||
|
||||
pc87360_init_client(client, use_thermistors);
|
||||
pc87360_init_device(pdev, use_thermistors);
|
||||
}
|
||||
|
||||
/* Register all-or-nothing sysfs groups */
|
||||
|
||||
if (data->innr &&
|
||||
(err = sysfs_create_group(&client->dev.kobj,
|
||||
(err = sysfs_create_group(&dev->kobj,
|
||||
&pc8736x_vin_group)))
|
||||
goto ERROR3;
|
||||
|
||||
if (data->innr == 14 &&
|
||||
(err = sysfs_create_group(&client->dev.kobj,
|
||||
(err = sysfs_create_group(&dev->kobj,
|
||||
&pc8736x_therm_group)))
|
||||
goto ERROR3;
|
||||
|
||||
|
@ -1067,7 +1051,10 @@ static int pc87360_detect(struct i2c_adapter *adapter)
|
|||
goto ERROR3;
|
||||
}
|
||||
|
||||
data->class_dev = hwmon_device_register(&client->dev);
|
||||
if ((err = device_create_file(dev, &dev_attr_name)))
|
||||
goto ERROR3;
|
||||
|
||||
data->class_dev = hwmon_device_register(dev);
|
||||
if (IS_ERR(data->class_dev)) {
|
||||
err = PTR_ERR(data->class_dev);
|
||||
goto ERROR3;
|
||||
|
@ -1075,14 +1062,12 @@ static int pc87360_detect(struct i2c_adapter *adapter)
|
|||
return 0;
|
||||
|
||||
ERROR3:
|
||||
device_remove_file(dev, &dev_attr_name);
|
||||
/* can still remove groups whose members were added individually */
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group);
|
||||
|
||||
i2c_detach_client(client);
|
||||
ERROR2:
|
||||
sysfs_remove_group(&dev->kobj, &pc8736x_temp_group);
|
||||
sysfs_remove_group(&dev->kobj, &pc8736x_fan_group);
|
||||
sysfs_remove_group(&dev->kobj, &pc8736x_therm_group);
|
||||
sysfs_remove_group(&dev->kobj, &pc8736x_vin_group);
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (data->address[i]) {
|
||||
release_region(data->address[i], PC87360_EXTENT);
|
||||
|
@ -1093,20 +1078,18 @@ ERROR1:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int pc87360_detach_client(struct i2c_client *client)
|
||||
static int __devexit pc87360_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group);
|
||||
|
||||
if ((i = i2c_detach_client(client)))
|
||||
return i;
|
||||
device_remove_file(&pdev->dev, &dev_attr_name);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &pc8736x_temp_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &pc8736x_therm_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &pc8736x_vin_group);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (data->address[i]) {
|
||||
|
@ -1144,9 +1127,10 @@ static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
|
|||
mutex_unlock(&(data->lock));
|
||||
}
|
||||
|
||||
static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
||||
static void pc87360_init_device(struct platform_device *pdev,
|
||||
int use_thermistors)
|
||||
{
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = platform_get_drvdata(pdev);
|
||||
int i, nr;
|
||||
const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 };
|
||||
const u8 init_temp[3] = { 2, 2, 1 };
|
||||
|
@ -1155,7 +1139,7 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
if (init >= 2 && data->innr) {
|
||||
reg = pc87360_read_value(data, LD_IN, NO_BANK,
|
||||
PC87365_REG_IN_CONVRATE);
|
||||
dev_info(&client->dev, "VLM conversion set to "
|
||||
dev_info(&pdev->dev, "VLM conversion set to "
|
||||
"1s period, 160us delay\n");
|
||||
pc87360_write_value(data, LD_IN, NO_BANK,
|
||||
PC87365_REG_IN_CONVRATE,
|
||||
|
@ -1169,7 +1153,7 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
reg = pc87360_read_value(data, LD_IN, i,
|
||||
PC87365_REG_IN_STATUS);
|
||||
if (!(reg & 0x01)) {
|
||||
dev_dbg(&client->dev, "Forcibly "
|
||||
dev_dbg(&pdev->dev, "Forcibly "
|
||||
"enabling in%d\n", i);
|
||||
pc87360_write_value(data, LD_IN, i,
|
||||
PC87365_REG_IN_STATUS,
|
||||
|
@ -1193,7 +1177,7 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
reg = pc87360_read_value(data, LD_TEMP, i,
|
||||
PC87365_REG_TEMP_STATUS);
|
||||
if (!(reg & 0x01)) {
|
||||
dev_dbg(&client->dev, "Forcibly "
|
||||
dev_dbg(&pdev->dev, "Forcibly "
|
||||
"enabling temp%d\n", i+1);
|
||||
pc87360_write_value(data, LD_TEMP, i,
|
||||
PC87365_REG_TEMP_STATUS,
|
||||
|
@ -1210,7 +1194,7 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
reg = pc87360_read_value(data, LD_TEMP,
|
||||
(i-11)/2, PC87365_REG_TEMP_STATUS);
|
||||
if (reg & 0x01) {
|
||||
dev_dbg(&client->dev, "Skipping "
|
||||
dev_dbg(&pdev->dev, "Skipping "
|
||||
"temp%d, pin already in use "
|
||||
"by temp%d\n", i-7, (i-11)/2);
|
||||
continue;
|
||||
|
@ -1220,7 +1204,7 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
reg = pc87360_read_value(data, LD_IN, i,
|
||||
PC87365_REG_IN_STATUS);
|
||||
if (!(reg & 0x01)) {
|
||||
dev_dbg(&client->dev, "Forcibly "
|
||||
dev_dbg(&pdev->dev, "Forcibly "
|
||||
"enabling temp%d\n", i-7);
|
||||
pc87360_write_value(data, LD_IN, i,
|
||||
PC87365_REG_TEMP_STATUS,
|
||||
|
@ -1234,7 +1218,7 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
reg = pc87360_read_value(data, LD_IN, NO_BANK,
|
||||
PC87365_REG_IN_CONFIG);
|
||||
if (reg & 0x01) {
|
||||
dev_dbg(&client->dev, "Forcibly "
|
||||
dev_dbg(&pdev->dev, "Forcibly "
|
||||
"enabling monitoring (VLM)\n");
|
||||
pc87360_write_value(data, LD_IN, NO_BANK,
|
||||
PC87365_REG_IN_CONFIG,
|
||||
|
@ -1246,7 +1230,7 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
reg = pc87360_read_value(data, LD_TEMP, NO_BANK,
|
||||
PC87365_REG_TEMP_CONFIG);
|
||||
if (reg & 0x01) {
|
||||
dev_dbg(&client->dev, "Forcibly enabling "
|
||||
dev_dbg(&pdev->dev, "Forcibly enabling "
|
||||
"monitoring (TMS)\n");
|
||||
pc87360_write_value(data, LD_TEMP, NO_BANK,
|
||||
PC87365_REG_TEMP_CONFIG,
|
||||
|
@ -1268,9 +1252,9 @@ static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
|
|||
}
|
||||
}
|
||||
|
||||
static void pc87360_autodiv(struct i2c_client *client, int nr)
|
||||
static void pc87360_autodiv(struct device *dev, int nr)
|
||||
{
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
u8 old_min = data->fan_min[nr];
|
||||
|
||||
/* Increase clock divider if needed and possible */
|
||||
|
@ -1280,7 +1264,7 @@ static void pc87360_autodiv(struct i2c_client *client, int nr)
|
|||
data->fan_status[nr] += 0x20;
|
||||
data->fan_min[nr] >>= 1;
|
||||
data->fan[nr] >>= 1;
|
||||
dev_dbg(&client->dev, "Increasing "
|
||||
dev_dbg(dev, "Increasing "
|
||||
"clock divider to %d for fan %d\n",
|
||||
FAN_DIV_FROM_REG(data->fan_status[nr]), nr+1);
|
||||
}
|
||||
|
@ -1292,7 +1276,7 @@ static void pc87360_autodiv(struct i2c_client *client, int nr)
|
|||
data->fan_status[nr] -= 0x20;
|
||||
data->fan_min[nr] <<= 1;
|
||||
data->fan[nr] <<= 1;
|
||||
dev_dbg(&client->dev, "Decreasing "
|
||||
dev_dbg(dev, "Decreasing "
|
||||
"clock divider to %d for fan %d\n",
|
||||
FAN_DIV_FROM_REG(data->fan_status[nr]),
|
||||
nr+1);
|
||||
|
@ -1309,14 +1293,13 @@ static void pc87360_autodiv(struct i2c_client *client, int nr)
|
|||
|
||||
static struct pc87360_data *pc87360_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pc87360_data *data = i2c_get_clientdata(client);
|
||||
struct pc87360_data *data = dev_get_drvdata(dev);
|
||||
u8 i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
|
||||
dev_dbg(&client->dev, "Data update\n");
|
||||
dev_dbg(dev, "Data update\n");
|
||||
|
||||
/* Fans */
|
||||
for (i = 0; i < data->fannr; i++) {
|
||||
|
@ -1330,7 +1313,7 @@ static struct pc87360_data *pc87360_update_device(struct device *dev)
|
|||
LD_FAN, NO_BANK,
|
||||
PC87360_REG_FAN_MIN(i));
|
||||
/* Change clock divider if needed */
|
||||
pc87360_autodiv(client, i);
|
||||
pc87360_autodiv(dev, i);
|
||||
/* Clear bits and write new divider */
|
||||
pc87360_write_value(data, LD_FAN, NO_BANK,
|
||||
PC87360_REG_FAN_STATUS(i),
|
||||
|
@ -1418,9 +1401,53 @@ static struct pc87360_data *pc87360_update_device(struct device *dev)
|
|||
return data;
|
||||
}
|
||||
|
||||
static int __init pc87360_device_add(unsigned short address)
|
||||
{
|
||||
struct resource res = {
|
||||
.name = "pc87360",
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
int err, i;
|
||||
|
||||
pdev = platform_device_alloc("pc87360", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
printk(KERN_ERR "pc87360: Device allocation failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!extra_isa[i])
|
||||
continue;
|
||||
res.start = extra_isa[i];
|
||||
res.end = extra_isa[i] + PC87360_EXTENT - 1;
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR "pc87360: Device resource[%d] "
|
||||
"addition failed (%d)\n", i, err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
}
|
||||
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "pc87360: Device addition failed (%d)\n",
|
||||
err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_device_put:
|
||||
platform_device_put(pdev);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init pc87360_init(void)
|
||||
{
|
||||
int i;
|
||||
int err, i;
|
||||
unsigned short address = 0;
|
||||
|
||||
if (pc87360_find(0x2e, &devid, extra_isa)
|
||||
&& pc87360_find(0x4e, &devid, extra_isa)) {
|
||||
|
@ -1443,12 +1470,27 @@ static int __init pc87360_init(void)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
return i2c_isa_add_driver(&pc87360_driver);
|
||||
err = platform_driver_register(&pc87360_driver);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
/* Sets global pdev as a side effect */
|
||||
err = pc87360_device_add(address);
|
||||
if (err)
|
||||
goto exit_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_driver:
|
||||
platform_driver_unregister(&pc87360_driver);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit pc87360_exit(void)
|
||||
{
|
||||
i2c_isa_del_driver(&pc87360_driver);
|
||||
platform_device_unregister(pdev);
|
||||
platform_driver_unregister(&pc87360_driver);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -484,7 +484,6 @@ static int __devexit pc87427_remove(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
int i;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
device_remove_file(&pdev->dev, &dev_attr_name);
|
||||
for (i = 0; i < 8; i++) {
|
||||
|
@ -492,6 +491,7 @@ static int __devexit pc87427_remove(struct platform_device *pdev)
|
|||
continue;
|
||||
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
|
||||
}
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
|
|
|
@ -54,9 +54,9 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-isa.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
@ -72,17 +72,13 @@ module_param(force_addr, ushort, 0);
|
|||
MODULE_PARM_DESC(force_addr,
|
||||
"Initialize the base address of the sensors");
|
||||
|
||||
/* Device address
|
||||
Note that we can't determine the ISA address until we have initialized
|
||||
our module */
|
||||
static unsigned short address;
|
||||
static struct platform_device *pdev;
|
||||
|
||||
/* Many SIS5595 constants specified below */
|
||||
|
||||
/* Length of ISA address segment */
|
||||
#define SIS5595_EXTENT 8
|
||||
/* PCI Config Registers */
|
||||
#define SIS5595_REVISION_REG 0x08
|
||||
#define SIS5595_BASE_REG 0x68
|
||||
#define SIS5595_PIN_REG 0x7A
|
||||
#define SIS5595_ENABLE_REG 0x7B
|
||||
|
@ -165,7 +161,8 @@ static inline u8 DIV_TO_REG(int val)
|
|||
/* For each registered chip, we need to keep some data in memory.
|
||||
The structure is dynamically allocated. */
|
||||
struct sis5595_data {
|
||||
struct i2c_client client;
|
||||
unsigned short addr;
|
||||
const char *name;
|
||||
struct class_device *class_dev;
|
||||
struct mutex lock;
|
||||
|
||||
|
@ -189,102 +186,88 @@ struct sis5595_data {
|
|||
|
||||
static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */
|
||||
|
||||
static int sis5595_detect(struct i2c_adapter *adapter);
|
||||
static int sis5595_detach_client(struct i2c_client *client);
|
||||
static int sis5595_probe(struct platform_device *pdev);
|
||||
static int sis5595_remove(struct platform_device *pdev);
|
||||
|
||||
static int sis5595_read_value(struct i2c_client *client, u8 reg);
|
||||
static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value);
|
||||
static int sis5595_read_value(struct sis5595_data *data, u8 reg);
|
||||
static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value);
|
||||
static struct sis5595_data *sis5595_update_device(struct device *dev);
|
||||
static void sis5595_init_client(struct i2c_client *client);
|
||||
static void sis5595_init_device(struct sis5595_data *data);
|
||||
|
||||
static struct i2c_driver sis5595_driver = {
|
||||
static struct platform_driver sis5595_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "sis5595",
|
||||
},
|
||||
.attach_adapter = sis5595_detect,
|
||||
.detach_client = sis5595_detach_client,
|
||||
.probe = sis5595_probe,
|
||||
.remove = __devexit_p(sis5595_remove),
|
||||
};
|
||||
|
||||
/* 4 Voltages */
|
||||
static ssize_t show_in(struct device *dev, char *buf, int nr)
|
||||
static ssize_t show_in(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sis5595_data *data = sis5595_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
|
||||
}
|
||||
|
||||
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
|
||||
static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sis5595_data *data = sis5595_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
|
||||
}
|
||||
|
||||
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
|
||||
static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sis5595_data *data = sis5595_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
|
||||
}
|
||||
|
||||
static ssize_t set_in_min(struct device *dev, const char *buf,
|
||||
size_t count, int nr)
|
||||
static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_min[nr] = IN_TO_REG(val);
|
||||
sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
|
||||
sis5595_write_value(data, SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t set_in_max(struct device *dev, const char *buf,
|
||||
size_t count, int nr)
|
||||
static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_max[nr] = IN_TO_REG(val);
|
||||
sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
|
||||
sis5595_write_value(data, SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
#define show_in_offset(offset) \
|
||||
static ssize_t \
|
||||
show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_in(dev, buf, offset); \
|
||||
} \
|
||||
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
|
||||
show_in##offset, NULL); \
|
||||
static ssize_t \
|
||||
show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_in_min(dev, buf, offset); \
|
||||
} \
|
||||
static ssize_t \
|
||||
show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_in_max(dev, buf, offset); \
|
||||
} \
|
||||
static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_in_min(dev, buf, count, offset); \
|
||||
} \
|
||||
static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_in_max(dev, buf, count, offset); \
|
||||
} \
|
||||
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_in##offset##_min, set_in##offset##_min); \
|
||||
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
|
||||
show_in##offset##_max, set_in##offset##_max);
|
||||
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);
|
||||
|
@ -307,13 +290,12 @@ static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_over = TEMP_TO_REG(val);
|
||||
sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over);
|
||||
sis5595_write_value(data, SIS5595_REG_TEMP_OVER, data->temp_over);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -326,13 +308,12 @@ static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_hyst = TEMP_TO_REG(val);
|
||||
sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst);
|
||||
sis5595_write_value(data, SIS5595_REG_TEMP_HYST, data->temp_hyst);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -344,37 +325,47 @@ static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
|
|||
show_temp_hyst, set_temp_hyst);
|
||||
|
||||
/* 2 Fans */
|
||||
static ssize_t show_fan(struct device *dev, char *buf, int nr)
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sis5595_data *data = sis5595_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
|
||||
DIV_FROM_REG(data->fan_div[nr])) );
|
||||
}
|
||||
|
||||
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
|
||||
static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sis5595_data *data = sis5595_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
|
||||
DIV_FROM_REG(data->fan_div[nr])) );
|
||||
}
|
||||
|
||||
static ssize_t set_fan_min(struct device *dev, const char *buf,
|
||||
size_t count, int nr)
|
||||
static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
unsigned long 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]));
|
||||
sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
sis5595_write_value(data, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
|
||||
static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sis5595_data *data = sis5595_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
|
||||
}
|
||||
|
||||
|
@ -382,11 +373,12 @@ static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
|
|||
determined in part by the fan divisor. This follows the principle of
|
||||
least surprise; the user doesn't expect the fan minimum to change just
|
||||
because the divisor changed. */
|
||||
static ssize_t set_fan_div(struct device *dev, const char *buf,
|
||||
size_t count, int nr)
|
||||
static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
unsigned long min;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int reg;
|
||||
|
@ -394,7 +386,7 @@ static ssize_t set_fan_div(struct device *dev, const char *buf,
|
|||
mutex_lock(&data->update_lock);
|
||||
min = FAN_FROM_REG(data->fan_min[nr],
|
||||
DIV_FROM_REG(data->fan_div[nr]));
|
||||
reg = sis5595_read_value(client, SIS5595_REG_FANDIV);
|
||||
reg = sis5595_read_value(data, SIS5595_REG_FANDIV);
|
||||
|
||||
switch (val) {
|
||||
case 1: data->fan_div[nr] = 0; break;
|
||||
|
@ -402,7 +394,7 @@ static ssize_t set_fan_div(struct device *dev, const char *buf,
|
|||
case 4: data->fan_div[nr] = 2; break;
|
||||
case 8: data->fan_div[nr] = 3; break;
|
||||
default:
|
||||
dev_err(&client->dev, "fan_div value %ld not "
|
||||
dev_err(dev, "fan_div value %ld not "
|
||||
"supported. Choose one of 1, 2, 4 or 8!\n", val);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return -EINVAL;
|
||||
|
@ -416,55 +408,25 @@ static ssize_t set_fan_div(struct device *dev, const char *buf,
|
|||
reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
|
||||
break;
|
||||
}
|
||||
sis5595_write_value(client, SIS5595_REG_FANDIV, reg);
|
||||
sis5595_write_value(data, SIS5595_REG_FANDIV, reg);
|
||||
data->fan_min[nr] =
|
||||
FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
|
||||
sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
sis5595_write_value(data, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
#define show_fan_offset(offset) \
|
||||
static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_fan(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_fan_min(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_fan_div(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_fan_min(dev, buf, count, offset - 1); \
|
||||
} \
|
||||
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
|
||||
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_fan_##offset##_min, set_fan_##offset##_min);
|
||||
static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
|
||||
show_fan, NULL, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_fan_min, set_fan_min, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
|
||||
show_fan_div, set_fan_div, offset - 1);
|
||||
|
||||
show_fan_offset(1);
|
||||
show_fan_offset(2);
|
||||
|
||||
static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
return set_fan_div(dev, buf, count, 0) ;
|
||||
}
|
||||
|
||||
static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
return set_fan_div(dev, buf, count, 1) ;
|
||||
}
|
||||
static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
|
||||
show_fan_1_div, set_fan_1_div);
|
||||
static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
|
||||
show_fan_2_div, set_fan_2_div);
|
||||
|
||||
/* Alarms */
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -473,28 +435,37 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
|
|||
}
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
|
||||
static struct attribute *sis5595_attributes[] = {
|
||||
&dev_attr_in0_input.attr,
|
||||
&dev_attr_in0_min.attr,
|
||||
&dev_attr_in0_max.attr,
|
||||
&dev_attr_in1_input.attr,
|
||||
&dev_attr_in1_min.attr,
|
||||
&dev_attr_in1_max.attr,
|
||||
&dev_attr_in2_input.attr,
|
||||
&dev_attr_in2_min.attr,
|
||||
&dev_attr_in2_max.attr,
|
||||
&dev_attr_in3_input.attr,
|
||||
&dev_attr_in3_min.attr,
|
||||
&dev_attr_in3_max.attr,
|
||||
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%s\n", data->name);
|
||||
}
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
|
||||
&dev_attr_fan1_input.attr,
|
||||
&dev_attr_fan1_min.attr,
|
||||
&dev_attr_fan1_div.attr,
|
||||
&dev_attr_fan2_input.attr,
|
||||
&dev_attr_fan2_min.attr,
|
||||
&dev_attr_fan2_div.attr,
|
||||
static struct attribute *sis5595_attributes[] = {
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_max.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_div.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_div.dev_attr.attr,
|
||||
|
||||
&dev_attr_alarms.attr,
|
||||
&dev_attr_name.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -503,9 +474,9 @@ static const struct attribute_group sis5595_group = {
|
|||
};
|
||||
|
||||
static struct attribute *sis5595_attributes_opt[] = {
|
||||
&dev_attr_in4_input.attr,
|
||||
&dev_attr_in4_min.attr,
|
||||
&dev_attr_in4_max.attr,
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_max.dev_attr.attr,
|
||||
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp1_max.attr,
|
||||
|
@ -518,68 +489,35 @@ static const struct attribute_group sis5595_group_opt = {
|
|||
};
|
||||
|
||||
/* This is called when the module is loaded */
|
||||
static int sis5595_detect(struct i2c_adapter *adapter)
|
||||
static int __devinit sis5595_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err = 0;
|
||||
int i;
|
||||
struct i2c_client *new_client;
|
||||
struct sis5595_data *data;
|
||||
struct resource *res;
|
||||
char val;
|
||||
u16 a;
|
||||
|
||||
if (force_addr)
|
||||
address = force_addr & ~(SIS5595_EXTENT - 1);
|
||||
/* Reserve the ISA region */
|
||||
if (!request_region(address, SIS5595_EXTENT,
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!request_region(res->start, SIS5595_EXTENT,
|
||||
sis5595_driver.driver.name)) {
|
||||
err = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
if (force_addr) {
|
||||
dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address);
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(s_bridge, SIS5595_BASE_REG, address))
|
||||
goto exit_release;
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a))
|
||||
goto exit_release;
|
||||
if ((a & ~(SIS5595_EXTENT - 1)) != address)
|
||||
/* doesn't work for some chips? */
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) {
|
||||
goto exit_release;
|
||||
}
|
||||
if ((val & 0x80) == 0) {
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG,
|
||||
val | 0x80))
|
||||
goto exit_release;
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
|
||||
goto exit_release;
|
||||
if ((val & 0x80) == 0)
|
||||
/* doesn't work for some chips! */
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
if (!(data = kzalloc(sizeof(struct sis5595_data), GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
new_client = &data->client;
|
||||
new_client->addr = address;
|
||||
mutex_init(&data->lock);
|
||||
i2c_set_clientdata(new_client, data);
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &sis5595_driver;
|
||||
new_client->flags = 0;
|
||||
mutex_init(&data->update_lock);
|
||||
data->addr = res->start;
|
||||
data->name = "sis5595";
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
/* Check revision and pin registers to determine whether 4 or 5 voltages */
|
||||
pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision));
|
||||
pci_read_config_byte(s_bridge, PCI_REVISION_ID, &data->revision);
|
||||
/* 4 voltages, 1 temp */
|
||||
data->maxins = 3;
|
||||
if (data->revision >= REV2MIN) {
|
||||
|
@ -589,47 +527,37 @@ static int sis5595_detect(struct i2c_adapter *adapter)
|
|||
data->maxins = 4;
|
||||
}
|
||||
|
||||
/* Fill in the remaining client fields and put it into the global list */
|
||||
strlcpy(new_client->name, "sis5595", I2C_NAME_SIZE);
|
||||
|
||||
data->valid = 0;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto exit_free;
|
||||
|
||||
/* Initialize the SIS5595 chip */
|
||||
sis5595_init_client(new_client);
|
||||
sis5595_init_device(data);
|
||||
|
||||
/* A few vars need to be filled upon startup */
|
||||
for (i = 0; i < 2; i++) {
|
||||
data->fan_min[i] = sis5595_read_value(new_client,
|
||||
data->fan_min[i] = sis5595_read_value(data,
|
||||
SIS5595_REG_FAN_MIN(i));
|
||||
}
|
||||
|
||||
/* Register sysfs hooks */
|
||||
if ((err = sysfs_create_group(&new_client->dev.kobj, &sis5595_group)))
|
||||
goto exit_detach;
|
||||
if ((err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group)))
|
||||
goto exit_free;
|
||||
if (data->maxins == 4) {
|
||||
if ((err = device_create_file(&new_client->dev,
|
||||
&dev_attr_in4_input))
|
||||
|| (err = device_create_file(&new_client->dev,
|
||||
&dev_attr_in4_min))
|
||||
|| (err = device_create_file(&new_client->dev,
|
||||
&dev_attr_in4_max)))
|
||||
if ((err = device_create_file(&pdev->dev,
|
||||
&sensor_dev_attr_in4_input.dev_attr))
|
||||
|| (err = device_create_file(&pdev->dev,
|
||||
&sensor_dev_attr_in4_min.dev_attr))
|
||||
|| (err = device_create_file(&pdev->dev,
|
||||
&sensor_dev_attr_in4_max.dev_attr)))
|
||||
goto exit_remove_files;
|
||||
} else {
|
||||
if ((err = device_create_file(&new_client->dev,
|
||||
if ((err = device_create_file(&pdev->dev,
|
||||
&dev_attr_temp1_input))
|
||||
|| (err = device_create_file(&new_client->dev,
|
||||
|| (err = device_create_file(&pdev->dev,
|
||||
&dev_attr_temp1_max))
|
||||
|| (err = device_create_file(&new_client->dev,
|
||||
|| (err = device_create_file(&pdev->dev,
|
||||
&dev_attr_temp1_max_hyst)))
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
||||
data->class_dev = hwmon_device_register(&new_client->dev);
|
||||
data->class_dev = hwmon_device_register(&pdev->dev);
|
||||
if (IS_ERR(data->class_dev)) {
|
||||
err = PTR_ERR(data->class_dev);
|
||||
goto exit_remove_files;
|
||||
|
@ -638,32 +566,26 @@ static int sis5595_detect(struct i2c_adapter *adapter)
|
|||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &sis5595_group);
|
||||
sysfs_remove_group(&new_client->dev.kobj, &sis5595_group_opt);
|
||||
exit_detach:
|
||||
i2c_detach_client(new_client);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sis5595_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_opt);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit_release:
|
||||
release_region(address, SIS5595_EXTENT);
|
||||
release_region(res->start, SIS5595_EXTENT);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sis5595_detach_client(struct i2c_client *client)
|
||||
static int __devexit sis5595_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
int err;
|
||||
struct sis5595_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &sis5595_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &sis5595_group_opt);
|
||||
|
||||
if ((err = i2c_detach_client(client)))
|
||||
return err;
|
||||
|
||||
release_region(client->addr, SIS5595_EXTENT);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sis5595_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_opt);
|
||||
|
||||
release_region(data->addr, SIS5595_EXTENT);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
|
@ -671,41 +593,37 @@ static int sis5595_detach_client(struct i2c_client *client)
|
|||
|
||||
|
||||
/* ISA access must be locked explicitly. */
|
||||
static int sis5595_read_value(struct i2c_client *client, u8 reg)
|
||||
static int sis5595_read_value(struct sis5595_data *data, u8 reg)
|
||||
{
|
||||
int res;
|
||||
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
mutex_lock(&data->lock);
|
||||
outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
|
||||
res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET);
|
||||
outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET);
|
||||
res = inb_p(data->addr + SIS5595_DATA_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value)
|
||||
static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value)
|
||||
{
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
mutex_lock(&data->lock);
|
||||
outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
|
||||
outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET);
|
||||
outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET);
|
||||
outb_p(value, data->addr + SIS5595_DATA_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when we have found a new SIS5595. */
|
||||
static void sis5595_init_client(struct i2c_client *client)
|
||||
static void __devinit sis5595_init_device(struct sis5595_data *data)
|
||||
{
|
||||
u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG);
|
||||
u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG);
|
||||
if (!(config & 0x01))
|
||||
sis5595_write_value(client, SIS5595_REG_CONFIG,
|
||||
sis5595_write_value(data, SIS5595_REG_CONFIG,
|
||||
(config & 0xf7) | 0x01);
|
||||
}
|
||||
|
||||
static struct sis5595_data *sis5595_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sis5595_data *data = i2c_get_clientdata(client);
|
||||
struct sis5595_data *data = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -715,35 +633,35 @@ static struct sis5595_data *sis5595_update_device(struct device *dev)
|
|||
|
||||
for (i = 0; i <= data->maxins; i++) {
|
||||
data->in[i] =
|
||||
sis5595_read_value(client, SIS5595_REG_IN(i));
|
||||
sis5595_read_value(data, SIS5595_REG_IN(i));
|
||||
data->in_min[i] =
|
||||
sis5595_read_value(client,
|
||||
sis5595_read_value(data,
|
||||
SIS5595_REG_IN_MIN(i));
|
||||
data->in_max[i] =
|
||||
sis5595_read_value(client,
|
||||
sis5595_read_value(data,
|
||||
SIS5595_REG_IN_MAX(i));
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
data->fan[i] =
|
||||
sis5595_read_value(client, SIS5595_REG_FAN(i));
|
||||
sis5595_read_value(data, SIS5595_REG_FAN(i));
|
||||
data->fan_min[i] =
|
||||
sis5595_read_value(client,
|
||||
sis5595_read_value(data,
|
||||
SIS5595_REG_FAN_MIN(i));
|
||||
}
|
||||
if (data->maxins == 3) {
|
||||
data->temp =
|
||||
sis5595_read_value(client, SIS5595_REG_TEMP);
|
||||
sis5595_read_value(data, SIS5595_REG_TEMP);
|
||||
data->temp_over =
|
||||
sis5595_read_value(client, SIS5595_REG_TEMP_OVER);
|
||||
sis5595_read_value(data, SIS5595_REG_TEMP_OVER);
|
||||
data->temp_hyst =
|
||||
sis5595_read_value(client, SIS5595_REG_TEMP_HYST);
|
||||
sis5595_read_value(data, SIS5595_REG_TEMP_HYST);
|
||||
}
|
||||
i = sis5595_read_value(client, SIS5595_REG_FANDIV);
|
||||
i = sis5595_read_value(data, SIS5595_REG_FANDIV);
|
||||
data->fan_div[0] = (i >> 4) & 0x03;
|
||||
data->fan_div[1] = i >> 6;
|
||||
data->alarms =
|
||||
sis5595_read_value(client, SIS5595_REG_ALARM1) |
|
||||
(sis5595_read_value(client, SIS5595_REG_ALARM2) << 8);
|
||||
sis5595_read_value(data, SIS5595_REG_ALARM1) |
|
||||
(sis5595_read_value(data, SIS5595_REG_ALARM2) << 8);
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
@ -774,10 +692,50 @@ static int blacklist[] __devinitdata = {
|
|||
PCI_DEVICE_ID_SI_5598,
|
||||
0 };
|
||||
|
||||
static int __devinit sis5595_device_add(unsigned short address)
|
||||
{
|
||||
struct resource res = {
|
||||
.start = address,
|
||||
.end = address + SIS5595_EXTENT - 1,
|
||||
.name = "sis5595",
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
int err;
|
||||
|
||||
pdev = platform_device_alloc("sis5595", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
printk(KERN_ERR "sis5595: Device allocation failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR "sis5595: Device resource addition failed "
|
||||
"(%d)\n", err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "sis5595: Device addition failed (%d)\n",
|
||||
err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_device_put:
|
||||
platform_device_put(pdev);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit sis5595_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
u16 val;
|
||||
u16 address;
|
||||
u8 enable;
|
||||
int *i;
|
||||
|
||||
for (i = blacklist; *i != 0; i++) {
|
||||
|
@ -790,27 +748,68 @@ static int __devinit sis5595_pci_probe(struct pci_dev *dev,
|
|||
}
|
||||
}
|
||||
|
||||
force_addr &= ~(SIS5595_EXTENT - 1);
|
||||
if (force_addr) {
|
||||
dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", force_addr);
|
||||
pci_write_config_word(dev, SIS5595_BASE_REG, force_addr);
|
||||
}
|
||||
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_word(dev, SIS5595_BASE_REG, &val))
|
||||
pci_read_config_word(dev, SIS5595_BASE_REG, &address)) {
|
||||
dev_err(&dev->dev, "Failed to read ISA address\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
address = val & ~(SIS5595_EXTENT - 1);
|
||||
if (address == 0 && force_addr == 0) {
|
||||
address &= ~(SIS5595_EXTENT - 1);
|
||||
if (!address) {
|
||||
dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (force_addr && address != force_addr) {
|
||||
/* doesn't work for some chips? */
|
||||
dev_err(&dev->dev, "Failed to force ISA address\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_byte(dev, SIS5595_ENABLE_REG, &enable)) {
|
||||
dev_err(&dev->dev, "Failed to read enable register\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!(enable & 0x80)) {
|
||||
if ((PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_byte(dev, SIS5595_ENABLE_REG,
|
||||
enable | 0x80))
|
||||
|| (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_byte(dev, SIS5595_ENABLE_REG, &enable))
|
||||
|| (!(enable & 0x80))) {
|
||||
/* doesn't work for some chips! */
|
||||
dev_err(&dev->dev, "Failed to enable HWM device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (platform_driver_register(&sis5595_driver)) {
|
||||
dev_dbg(&dev->dev, "Failed to register sis5595 driver\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s_bridge = pci_dev_get(dev);
|
||||
if (i2c_isa_add_driver(&sis5595_driver)) {
|
||||
pci_dev_put(s_bridge);
|
||||
s_bridge = NULL;
|
||||
}
|
||||
/* Sets global pdev as a side effect */
|
||||
if (sis5595_device_add(address))
|
||||
goto exit_unregister;
|
||||
|
||||
/* Always return failure here. This is to allow other drivers to bind
|
||||
* to this pci device. We don't really want to have control over the
|
||||
* pci device, we only wanted to read as few register values from it.
|
||||
*/
|
||||
return -ENODEV;
|
||||
|
||||
exit_unregister:
|
||||
pci_dev_put(dev);
|
||||
platform_driver_unregister(&sis5595_driver);
|
||||
exit:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct pci_driver sis5595_pci_driver = {
|
||||
|
@ -828,7 +827,8 @@ static void __exit sm_sis5595_exit(void)
|
|||
{
|
||||
pci_unregister_driver(&sis5595_pci_driver);
|
||||
if (s_bridge != NULL) {
|
||||
i2c_isa_del_driver(&sis5595_driver);
|
||||
platform_device_unregister(pdev);
|
||||
platform_driver_unregister(&sis5595_driver);
|
||||
pci_dev_put(s_bridge);
|
||||
s_bridge = NULL;
|
||||
}
|
||||
|
|
|
@ -174,6 +174,8 @@ static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
|
|||
REG: count of 90kHz pulses / revolution */
|
||||
static int fan_from_reg(u16 reg)
|
||||
{
|
||||
if (reg == 0 || reg == 0xffff)
|
||||
return 0;
|
||||
return 90000 * 60 / reg;
|
||||
}
|
||||
|
||||
|
@ -333,7 +335,7 @@ static int __init smsc47b397_find(unsigned short *addr)
|
|||
superio_enter();
|
||||
id = superio_inb(SUPERIO_REG_DEVID);
|
||||
|
||||
if ((id != 0x6f) && (id != 0x81)) {
|
||||
if ((id != 0x6f) && (id != 0x81) && (id != 0x85)) {
|
||||
superio_exit();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -346,7 +348,8 @@ static int __init smsc47b397_find(unsigned short *addr)
|
|||
|
||||
printk(KERN_INFO DRVNAME ": found SMSC %s "
|
||||
"(base address 0x%04x, revision %u)\n",
|
||||
id == 0x81 ? "SCH5307-NS" : "LPC47B397-NC", *addr, rev);
|
||||
id == 0x81 ? "SCH5307-NS" : id == 0x85 ? "SCH5317" :
|
||||
"LPC47B397-NC", *addr, rev);
|
||||
|
||||
superio_exit();
|
||||
return 0;
|
||||
|
|
|
@ -597,6 +597,7 @@ static int __devinit smsc47m1_probe(struct platform_device *pdev)
|
|||
error_remove_files:
|
||||
sysfs_remove_group(&dev->kobj, &smsc47m1_group);
|
||||
error_free:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
error_release:
|
||||
release_region(res->start, SMSC_EXTENT);
|
||||
|
@ -608,12 +609,12 @@ static int __devexit smsc47m1_remove(struct platform_device *pdev)
|
|||
struct smsc47m1_data *data = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
release_region(res->start, SMSC_EXTENT);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
|
@ -693,15 +694,12 @@ static int __init smsc47m1_device_add(unsigned short address,
|
|||
goto exit_device_put;
|
||||
}
|
||||
|
||||
pdev->dev.platform_data = kmalloc(sizeof(struct smsc47m1_sio_data),
|
||||
GFP_KERNEL);
|
||||
if (!pdev->dev.platform_data) {
|
||||
err = -ENOMEM;
|
||||
err = platform_device_add_data(pdev, sio_data,
|
||||
sizeof(struct smsc47m1_sio_data));
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
|
||||
goto exit_device_put;
|
||||
}
|
||||
memcpy(pdev->dev.platform_data, sio_data,
|
||||
sizeof(struct smsc47m1_sio_data));
|
||||
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
|
||||
|
@ -97,7 +98,7 @@ static inline int TEMP_FROM_REG(s8 val)
|
|||
struct smsc47m192_data {
|
||||
struct i2c_client client;
|
||||
struct class_device *class_dev;
|
||||
struct semaphore update_lock;
|
||||
struct mutex update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
|
@ -164,11 +165,11 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
|
|||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
mutex_lock(&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);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -181,11 +182,11 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
|
|||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
mutex_lock(&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);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -243,11 +244,11 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr,
|
|||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
mutex_lock(&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);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -260,11 +261,11 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
|
|||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
mutex_lock(&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);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -287,7 +288,7 @@ static ssize_t set_temp_offset(struct device *dev, struct device_attribute
|
|||
u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
down(&data->update_lock);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_offset[nr] = TEMP_TO_REG(val);
|
||||
if (nr>1)
|
||||
i2c_smbus_write_byte_data(client,
|
||||
|
@ -303,7 +304,7 @@ static ssize_t set_temp_offset(struct device *dev, struct device_attribute
|
|||
} else if ((sfr & 0x10) == (nr==0 ? 0x10 : 0))
|
||||
i2c_smbus_write_byte_data(client,
|
||||
SMSC47M192_REG_TEMP_OFFSET(nr), 0);
|
||||
up(&data->update_lock);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -360,8 +361,8 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
|||
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(temp2_fault, S_IRUGO, show_alarm, NULL, 0x4000);
|
||||
static SENSOR_DEVICE_ATTR(temp3_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);
|
||||
|
@ -411,13 +412,13 @@ static struct attribute *smsc47m192_attributes[] = {
|
|||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
|
||||
&dev_attr_cpu0_vid.attr,
|
||||
&dev_attr_vrm.attr,
|
||||
|
@ -531,7 +532,7 @@ static int smsc47m192_detect(struct i2c_adapter *adapter, int address,
|
|||
/* 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);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(client)))
|
||||
|
@ -594,7 +595,7 @@ static struct smsc47m192_data *smsc47m192_update_device(struct device *dev)
|
|||
struct smsc47m192_data *data = i2c_get_clientdata(client);
|
||||
int i, config;
|
||||
|
||||
down(&data->update_lock);
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
|
@ -645,7 +646,7 @@ static struct smsc47m192_data *smsc47m192_update_device(struct device *dev)
|
|||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
|
|
@ -34,9 +34,9 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-isa.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
|
@ -51,10 +51,7 @@ module_param(force_addr, ushort, 0);
|
|||
MODULE_PARM_DESC(force_addr,
|
||||
"Initialize the base address of the sensors");
|
||||
|
||||
/* Device address
|
||||
Note that we can't determine the ISA address until we have initialized
|
||||
our module */
|
||||
static unsigned short address;
|
||||
static struct platform_device *pdev;
|
||||
|
||||
/*
|
||||
The Via 686a southbridge has a LM78-like chip integrated on the same IC.
|
||||
|
@ -295,7 +292,8 @@ static inline long TEMP_FROM_REG10(u16 val)
|
|||
/* For each registered chip, we need to keep some data in memory.
|
||||
The structure is dynamically allocated. */
|
||||
struct via686a_data {
|
||||
struct i2c_client client;
|
||||
unsigned short addr;
|
||||
const char *name;
|
||||
struct class_device *class_dev;
|
||||
struct mutex update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
|
@ -315,98 +313,85 @@ struct via686a_data {
|
|||
|
||||
static struct pci_dev *s_bridge; /* pointer to the (only) via686a */
|
||||
|
||||
static int via686a_detect(struct i2c_adapter *adapter);
|
||||
static int via686a_detach_client(struct i2c_client *client);
|
||||
static int via686a_probe(struct platform_device *pdev);
|
||||
static int via686a_remove(struct platform_device *pdev);
|
||||
|
||||
static inline int via686a_read_value(struct i2c_client *client, u8 reg)
|
||||
static inline int via686a_read_value(struct via686a_data *data, u8 reg)
|
||||
{
|
||||
return (inb_p(client->addr + reg));
|
||||
return inb_p(data->addr + reg);
|
||||
}
|
||||
|
||||
static inline void via686a_write_value(struct i2c_client *client, u8 reg,
|
||||
static inline void via686a_write_value(struct via686a_data *data, u8 reg,
|
||||
u8 value)
|
||||
{
|
||||
outb_p(value, client->addr + reg);
|
||||
outb_p(value, data->addr + reg);
|
||||
}
|
||||
|
||||
static struct via686a_data *via686a_update_device(struct device *dev);
|
||||
static void via686a_init_client(struct i2c_client *client);
|
||||
static void via686a_init_device(struct via686a_data *data);
|
||||
|
||||
/* following are the sysfs callback functions */
|
||||
|
||||
/* 7 voltage sensors */
|
||||
static ssize_t show_in(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_in(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
|
||||
}
|
||||
|
||||
static ssize_t show_in_min(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
|
||||
}
|
||||
|
||||
static ssize_t show_in_max(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
|
||||
}
|
||||
|
||||
static ssize_t set_in_min(struct device *dev, const char *buf,
|
||||
size_t count, int nr) {
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count) {
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_min[nr] = IN_TO_REG(val, nr);
|
||||
via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
|
||||
via686a_write_value(data, VIA686A_REG_IN_MIN(nr),
|
||||
data->in_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
static ssize_t set_in_max(struct device *dev, const char *buf,
|
||||
size_t count, int nr) {
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count) {
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_max[nr] = IN_TO_REG(val, nr);
|
||||
via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
|
||||
via686a_write_value(data, VIA686A_REG_IN_MAX(nr),
|
||||
data->in_max[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
#define show_in_offset(offset) \
|
||||
static ssize_t \
|
||||
show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_in(dev, buf, offset); \
|
||||
} \
|
||||
static ssize_t \
|
||||
show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_in_min(dev, buf, offset); \
|
||||
} \
|
||||
static ssize_t \
|
||||
show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_in_max(dev, buf, offset); \
|
||||
} \
|
||||
static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_in_min(dev, buf, count, offset); \
|
||||
} \
|
||||
static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_in_max(dev, buf, count, offset); \
|
||||
} \
|
||||
static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);\
|
||||
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_in##offset##_min, set_in##offset##_min); \
|
||||
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
|
||||
show_in##offset##_max, set_in##offset##_max);
|
||||
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);
|
||||
|
@ -415,150 +400,128 @@ show_in_offset(3);
|
|||
show_in_offset(4);
|
||||
|
||||
/* 3 temperatures */
|
||||
static ssize_t show_temp(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
|
||||
}
|
||||
static ssize_t show_temp_over(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
|
||||
}
|
||||
static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
|
||||
}
|
||||
static ssize_t set_temp_over(struct device *dev, const char *buf,
|
||||
size_t count, int nr) {
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count) {
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_over[nr] = TEMP_TO_REG(val);
|
||||
via686a_write_value(client, VIA686A_REG_TEMP_OVER[nr],
|
||||
via686a_write_value(data, VIA686A_REG_TEMP_OVER[nr],
|
||||
data->temp_over[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
static ssize_t set_temp_hyst(struct device *dev, const char *buf,
|
||||
size_t count, int nr) {
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count) {
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_hyst[nr] = TEMP_TO_REG(val);
|
||||
via686a_write_value(client, VIA686A_REG_TEMP_HYST[nr],
|
||||
via686a_write_value(data, VIA686A_REG_TEMP_HYST[nr],
|
||||
data->temp_hyst[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
#define show_temp_offset(offset) \
|
||||
static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_temp(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t \
|
||||
show_temp_##offset##_over (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_temp_over(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t \
|
||||
show_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_temp_hyst(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t set_temp_##offset##_over (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_temp_over(dev, buf, count, offset - 1); \
|
||||
} \
|
||||
static ssize_t set_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_temp_hyst(dev, buf, count, offset - 1); \
|
||||
} \
|
||||
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);\
|
||||
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
|
||||
show_temp_##offset##_over, set_temp_##offset##_over); \
|
||||
static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
|
||||
show_temp_##offset##_hyst, set_temp_##offset##_hyst);
|
||||
static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
|
||||
show_temp, NULL, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
|
||||
show_temp_over, set_temp_over, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
|
||||
show_temp_hyst, set_temp_hyst, offset - 1);
|
||||
|
||||
show_temp_offset(1);
|
||||
show_temp_offset(2);
|
||||
show_temp_offset(3);
|
||||
|
||||
/* 2 Fans */
|
||||
static ssize_t show_fan(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
|
||||
DIV_FROM_REG(data->fan_div[nr])) );
|
||||
}
|
||||
static ssize_t show_fan_min(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n",
|
||||
FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) );
|
||||
}
|
||||
static ssize_t show_fan_div(struct device *dev, char *buf, int nr) {
|
||||
static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
|
||||
char *buf) {
|
||||
struct via686a_data *data = via686a_update_device(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
|
||||
}
|
||||
static ssize_t set_fan_min(struct device *dev, const char *buf,
|
||||
size_t count, int nr) {
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count) {
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
|
||||
via686a_write_value(client, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
|
||||
via686a_write_value(data, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
static ssize_t set_fan_div(struct device *dev, const char *buf,
|
||||
size_t count, int nr) {
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count) {
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int nr = attr->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
int old;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
old = via686a_read_value(client, VIA686A_REG_FANDIV);
|
||||
old = via686a_read_value(data, VIA686A_REG_FANDIV);
|
||||
data->fan_div[nr] = DIV_TO_REG(val);
|
||||
old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
|
||||
via686a_write_value(client, VIA686A_REG_FANDIV, old);
|
||||
via686a_write_value(data, VIA686A_REG_FANDIV, old);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
#define show_fan_offset(offset) \
|
||||
static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_fan(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_fan_min(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_fan_div(dev, buf, offset - 1); \
|
||||
} \
|
||||
static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_fan_min(dev, buf, count, offset - 1); \
|
||||
} \
|
||||
static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
return set_fan_div(dev, buf, count, offset - 1); \
|
||||
} \
|
||||
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
|
||||
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_fan_##offset##_min, set_fan_##offset##_min); \
|
||||
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
|
||||
show_fan_##offset##_div, set_fan_##offset##_div);
|
||||
static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
|
||||
show_fan, NULL, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_fan_min, set_fan_min, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
|
||||
show_fan_div, set_fan_div, offset - 1);
|
||||
|
||||
show_fan_offset(1);
|
||||
show_fan_offset(2);
|
||||
|
@ -570,41 +533,50 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
|
|||
}
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
|
||||
static ssize_t show_name(struct device *dev, struct device_attribute
|
||||
*devattr, char *buf)
|
||||
{
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%s\n", data->name);
|
||||
}
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
|
||||
static struct attribute *via686a_attributes[] = {
|
||||
&dev_attr_in0_input.attr,
|
||||
&dev_attr_in1_input.attr,
|
||||
&dev_attr_in2_input.attr,
|
||||
&dev_attr_in3_input.attr,
|
||||
&dev_attr_in4_input.attr,
|
||||
&dev_attr_in0_min.attr,
|
||||
&dev_attr_in1_min.attr,
|
||||
&dev_attr_in2_min.attr,
|
||||
&dev_attr_in3_min.attr,
|
||||
&dev_attr_in4_min.attr,
|
||||
&dev_attr_in0_max.attr,
|
||||
&dev_attr_in1_max.attr,
|
||||
&dev_attr_in2_max.attr,
|
||||
&dev_attr_in3_max.attr,
|
||||
&dev_attr_in4_max.attr,
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_max.dev_attr.attr,
|
||||
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp2_input.attr,
|
||||
&dev_attr_temp3_input.attr,
|
||||
&dev_attr_temp1_max.attr,
|
||||
&dev_attr_temp2_max.attr,
|
||||
&dev_attr_temp3_max.attr,
|
||||
&dev_attr_temp1_max_hyst.attr,
|
||||
&dev_attr_temp2_max_hyst.attr,
|
||||
&dev_attr_temp3_max_hyst.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
|
||||
|
||||
&dev_attr_fan1_input.attr,
|
||||
&dev_attr_fan2_input.attr,
|
||||
&dev_attr_fan1_min.attr,
|
||||
&dev_attr_fan2_min.attr,
|
||||
&dev_attr_fan1_div.attr,
|
||||
&dev_attr_fan2_div.attr,
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_div.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_div.dev_attr.attr,
|
||||
|
||||
&dev_attr_alarms.attr,
|
||||
&dev_attr_name.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -612,58 +584,29 @@ static const struct attribute_group via686a_group = {
|
|||
.attrs = via686a_attributes,
|
||||
};
|
||||
|
||||
/* The driver. I choose to use type i2c_driver, as at is identical to both
|
||||
smbus_driver and isa_driver, and clients could be of either kind */
|
||||
static struct i2c_driver via686a_driver = {
|
||||
static struct platform_driver via686a_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "via686a",
|
||||
},
|
||||
.attach_adapter = via686a_detect,
|
||||
.detach_client = via686a_detach_client,
|
||||
.probe = via686a_probe,
|
||||
.remove = __devexit_p(via686a_remove),
|
||||
};
|
||||
|
||||
|
||||
/* This is called when the module is loaded */
|
||||
static int via686a_detect(struct i2c_adapter *adapter)
|
||||
static int __devinit via686a_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_client *new_client;
|
||||
struct via686a_data *data;
|
||||
int err = 0;
|
||||
const char client_name[] = "via686a";
|
||||
u16 val;
|
||||
|
||||
/* 8231 requires multiple of 256, we enforce that on 686 as well */
|
||||
if (force_addr) {
|
||||
address = force_addr & 0xFF00;
|
||||
dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n",
|
||||
address);
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(s_bridge, VIA686A_BASE_REG, address))
|
||||
return -ENODEV;
|
||||
}
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val))
|
||||
return -ENODEV;
|
||||
if (!(val & 0x0001)) {
|
||||
if (force_addr) {
|
||||
dev_info(&adapter->dev, "enabling sensors\n");
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(s_bridge, VIA686A_ENABLE_REG,
|
||||
val | 0x0001))
|
||||
return -ENODEV;
|
||||
} else {
|
||||
dev_warn(&adapter->dev, "sensors disabled - enable "
|
||||
"with force_addr=0x%x\n", address);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
/* Reserve the ISA region */
|
||||
if (!request_region(address, VIA686A_EXTENT,
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!request_region(res->start, VIA686A_EXTENT,
|
||||
via686a_driver.driver.name)) {
|
||||
dev_err(&adapter->dev, "region 0x%x already in use!\n",
|
||||
address);
|
||||
dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",
|
||||
(unsigned long)res->start, (unsigned long)res->end);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -672,30 +615,19 @@ static int via686a_detect(struct i2c_adapter *adapter)
|
|||
goto exit_release;
|
||||
}
|
||||
|
||||
new_client = &data->client;
|
||||
i2c_set_clientdata(new_client, data);
|
||||
new_client->addr = address;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &via686a_driver;
|
||||
new_client->flags = 0;
|
||||
|
||||
/* Fill in the remaining client fields and put into the global list */
|
||||
strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
|
||||
|
||||
data->valid = 0;
|
||||
platform_set_drvdata(pdev, data);
|
||||
data->addr = res->start;
|
||||
data->name = "via686a";
|
||||
mutex_init(&data->update_lock);
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto exit_free;
|
||||
|
||||
/* Initialize the VIA686A chip */
|
||||
via686a_init_client(new_client);
|
||||
via686a_init_device(data);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
if ((err = sysfs_create_group(&new_client->dev.kobj, &via686a_group)))
|
||||
goto exit_detach;
|
||||
if ((err = sysfs_create_group(&pdev->dev.kobj, &via686a_group)))
|
||||
goto exit_free;
|
||||
|
||||
data->class_dev = hwmon_device_register(&new_client->dev);
|
||||
data->class_dev = hwmon_device_register(&pdev->dev);
|
||||
if (IS_ERR(data->class_dev)) {
|
||||
err = PTR_ERR(data->class_dev);
|
||||
goto exit_remove_files;
|
||||
|
@ -704,51 +636,46 @@ static int via686a_detect(struct i2c_adapter *adapter)
|
|||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &via686a_group);
|
||||
exit_detach:
|
||||
i2c_detach_client(new_client);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit_release:
|
||||
release_region(address, VIA686A_EXTENT);
|
||||
release_region(res->start, VIA686A_EXTENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int via686a_detach_client(struct i2c_client *client)
|
||||
static int __devexit via686a_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
int err;
|
||||
struct via686a_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &via686a_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
|
||||
|
||||
if ((err = i2c_detach_client(client)))
|
||||
return err;
|
||||
|
||||
release_region(client->addr, VIA686A_EXTENT);
|
||||
release_region(data->addr, VIA686A_EXTENT);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void via686a_init_client(struct i2c_client *client)
|
||||
static void __devinit via686a_init_device(struct via686a_data *data)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
/* Start monitoring */
|
||||
reg = via686a_read_value(client, VIA686A_REG_CONFIG);
|
||||
via686a_write_value(client, VIA686A_REG_CONFIG, (reg|0x01)&0x7F);
|
||||
reg = via686a_read_value(data, VIA686A_REG_CONFIG);
|
||||
via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F);
|
||||
|
||||
/* Configure temp interrupt mode for continuous-interrupt operation */
|
||||
via686a_write_value(client, VIA686A_REG_TEMP_MODE,
|
||||
via686a_read_value(client, VIA686A_REG_TEMP_MODE) &
|
||||
!(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS));
|
||||
reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE);
|
||||
via686a_write_value(data, VIA686A_REG_TEMP_MODE,
|
||||
(reg & ~VIA686A_TEMP_MODE_MASK)
|
||||
| VIA686A_TEMP_MODE_CONTINUOUS);
|
||||
}
|
||||
|
||||
static struct via686a_data *via686a_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct via686a_data *data = i2c_get_clientdata(client);
|
||||
struct via686a_data *data = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -757,27 +684,27 @@ static struct via686a_data *via686a_update_device(struct device *dev)
|
|||
|| !data->valid) {
|
||||
for (i = 0; i <= 4; i++) {
|
||||
data->in[i] =
|
||||
via686a_read_value(client, VIA686A_REG_IN(i));
|
||||
data->in_min[i] = via686a_read_value(client,
|
||||
via686a_read_value(data, VIA686A_REG_IN(i));
|
||||
data->in_min[i] = via686a_read_value(data,
|
||||
VIA686A_REG_IN_MIN
|
||||
(i));
|
||||
data->in_max[i] =
|
||||
via686a_read_value(client, VIA686A_REG_IN_MAX(i));
|
||||
via686a_read_value(data, VIA686A_REG_IN_MAX(i));
|
||||
}
|
||||
for (i = 1; i <= 2; i++) {
|
||||
data->fan[i - 1] =
|
||||
via686a_read_value(client, VIA686A_REG_FAN(i));
|
||||
data->fan_min[i - 1] = via686a_read_value(client,
|
||||
via686a_read_value(data, VIA686A_REG_FAN(i));
|
||||
data->fan_min[i - 1] = via686a_read_value(data,
|
||||
VIA686A_REG_FAN_MIN(i));
|
||||
}
|
||||
for (i = 0; i <= 2; i++) {
|
||||
data->temp[i] = via686a_read_value(client,
|
||||
data->temp[i] = via686a_read_value(data,
|
||||
VIA686A_REG_TEMP[i]) << 2;
|
||||
data->temp_over[i] =
|
||||
via686a_read_value(client,
|
||||
via686a_read_value(data,
|
||||
VIA686A_REG_TEMP_OVER[i]);
|
||||
data->temp_hyst[i] =
|
||||
via686a_read_value(client,
|
||||
via686a_read_value(data,
|
||||
VIA686A_REG_TEMP_HYST[i]);
|
||||
}
|
||||
/* add in lower 2 bits
|
||||
|
@ -785,23 +712,23 @@ static struct via686a_data *via686a_update_device(struct device *dev)
|
|||
temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
|
||||
temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
|
||||
*/
|
||||
data->temp[0] |= (via686a_read_value(client,
|
||||
data->temp[0] |= (via686a_read_value(data,
|
||||
VIA686A_REG_TEMP_LOW1)
|
||||
& 0xc0) >> 6;
|
||||
data->temp[1] |=
|
||||
(via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
|
||||
(via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
|
||||
0x30) >> 4;
|
||||
data->temp[2] |=
|
||||
(via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
|
||||
(via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
|
||||
0xc0) >> 6;
|
||||
|
||||
i = via686a_read_value(client, VIA686A_REG_FANDIV);
|
||||
i = via686a_read_value(data, VIA686A_REG_FANDIV);
|
||||
data->fan_div[0] = (i >> 4) & 0x03;
|
||||
data->fan_div[1] = i >> 6;
|
||||
data->alarms =
|
||||
via686a_read_value(client,
|
||||
via686a_read_value(data,
|
||||
VIA686A_REG_ALARM1) |
|
||||
(via686a_read_value(client, VIA686A_REG_ALARM2) << 8);
|
||||
(via686a_read_value(data, VIA686A_REG_ALARM2) << 8);
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
@ -818,32 +745,102 @@ static struct pci_device_id via686a_pci_ids[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
|
||||
|
||||
static int __devinit via686a_device_add(unsigned short address)
|
||||
{
|
||||
struct resource res = {
|
||||
.start = address,
|
||||
.end = address + VIA686A_EXTENT - 1,
|
||||
.name = "via686a",
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
int err;
|
||||
|
||||
pdev = platform_device_alloc("via686a", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
printk(KERN_ERR "via686a: Device allocation failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR "via686a: Device resource addition failed "
|
||||
"(%d)\n", err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "via686a: Device addition failed (%d)\n",
|
||||
err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_device_put:
|
||||
platform_device_put(pdev);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit via686a_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
u16 val;
|
||||
u16 address, val;
|
||||
|
||||
if (force_addr) {
|
||||
address = force_addr & ~(VIA686A_EXTENT - 1);
|
||||
dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", address);
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(dev, VIA686A_BASE_REG, address | 1))
|
||||
return -ENODEV;
|
||||
}
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_word(dev, VIA686A_BASE_REG, &val))
|
||||
return -ENODEV;
|
||||
|
||||
address = val & ~(VIA686A_EXTENT - 1);
|
||||
if (address == 0 && force_addr == 0) {
|
||||
if (address == 0) {
|
||||
dev_err(&dev->dev, "base address not set - upgrade BIOS "
|
||||
"or use force_addr=0xaddr\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
s_bridge = pci_dev_get(dev);
|
||||
if (i2c_isa_add_driver(&via686a_driver)) {
|
||||
pci_dev_put(s_bridge);
|
||||
s_bridge = NULL;
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_word(dev, VIA686A_ENABLE_REG, &val))
|
||||
return -ENODEV;
|
||||
if (!(val & 0x0001)) {
|
||||
if (!force_addr) {
|
||||
dev_warn(&dev->dev, "Sensors disabled, enable "
|
||||
"with force_addr=0x%x\n", address);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_warn(&dev->dev, "Enabling sensors\n");
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(dev, VIA686A_ENABLE_REG,
|
||||
val | 0x0001))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (platform_driver_register(&via686a_driver))
|
||||
goto exit;
|
||||
|
||||
/* Sets global pdev as a side effect */
|
||||
if (via686a_device_add(address))
|
||||
goto exit_unregister;
|
||||
|
||||
/* Always return failure here. This is to allow other drivers to bind
|
||||
* to this pci device. We don't really want to have control over the
|
||||
* pci device, we only wanted to read as few register values from it.
|
||||
*/
|
||||
s_bridge = pci_dev_get(dev);
|
||||
return -ENODEV;
|
||||
|
||||
exit_unregister:
|
||||
platform_driver_unregister(&via686a_driver);
|
||||
exit:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -862,7 +859,8 @@ static void __exit sm_via686a_exit(void)
|
|||
{
|
||||
pci_unregister_driver(&via686a_pci_driver);
|
||||
if (s_bridge != NULL) {
|
||||
i2c_isa_del_driver(&via686a_driver);
|
||||
platform_device_unregister(pdev);
|
||||
platform_driver_unregister(&via686a_driver);
|
||||
pci_dev_put(s_bridge);
|
||||
s_bridge = NULL;
|
||||
}
|
||||
|
|
|
@ -29,8 +29,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-isa.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/hwmon-vid.h>
|
||||
|
@ -42,10 +41,7 @@ static int force_addr;
|
|||
module_param(force_addr, int, 0);
|
||||
MODULE_PARM_DESC(force_addr, "Initialize the base address of the sensors");
|
||||
|
||||
/* Device address
|
||||
Note that we can't determine the ISA address until we have initialized
|
||||
our module */
|
||||
static unsigned short isa_address;
|
||||
static struct platform_device *pdev;
|
||||
|
||||
#define VT8231_EXTENT 0x80
|
||||
#define VT8231_BASE_REG 0x70
|
||||
|
@ -148,7 +144,9 @@ static inline u8 FAN_TO_REG(long rpm, int div)
|
|||
#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : 1310720 / ((val) * (div)))
|
||||
|
||||
struct vt8231_data {
|
||||
struct i2c_client client;
|
||||
unsigned short addr;
|
||||
const char *name;
|
||||
|
||||
struct mutex update_lock;
|
||||
struct class_device *class_dev;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
|
@ -168,20 +166,20 @@ struct vt8231_data {
|
|||
};
|
||||
|
||||
static struct pci_dev *s_bridge;
|
||||
static int vt8231_detect(struct i2c_adapter *adapter);
|
||||
static int vt8231_detach_client(struct i2c_client *client);
|
||||
static int vt8231_probe(struct platform_device *pdev);
|
||||
static int vt8231_remove(struct platform_device *pdev);
|
||||
static struct vt8231_data *vt8231_update_device(struct device *dev);
|
||||
static void vt8231_init_client(struct i2c_client *client);
|
||||
static void vt8231_init_device(struct vt8231_data *data);
|
||||
|
||||
static inline int vt8231_read_value(struct i2c_client *client, u8 reg)
|
||||
static inline int vt8231_read_value(struct vt8231_data *data, u8 reg)
|
||||
{
|
||||
return inb_p(client->addr + reg);
|
||||
return inb_p(data->addr + reg);
|
||||
}
|
||||
|
||||
static inline void vt8231_write_value(struct i2c_client *client, u8 reg,
|
||||
static inline void vt8231_write_value(struct vt8231_data *data, u8 reg,
|
||||
u8 value)
|
||||
{
|
||||
outb_p(value, client->addr + reg);
|
||||
outb_p(value, data->addr + reg);
|
||||
}
|
||||
|
||||
/* following are the sysfs callback functions */
|
||||
|
@ -220,13 +218,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
|
|||
{
|
||||
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 vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_min[nr] = SENSORS_LIMIT(((val * 958) / 10000) + 3, 0, 255);
|
||||
vt8231_write_value(client, regvoltmin[nr], data->in_min[nr]);
|
||||
vt8231_write_value(data, regvoltmin[nr], data->in_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -236,13 +233,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
|
|||
{
|
||||
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 vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_max[nr] = SENSORS_LIMIT(((val * 958) / 10000) + 3, 0, 255);
|
||||
vt8231_write_value(client, regvoltmax[nr], data->in_max[nr]);
|
||||
vt8231_write_value(data, regvoltmax[nr], data->in_max[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -278,14 +274,13 @@ static ssize_t show_in5_max(struct device *dev, struct device_attribute *attr,
|
|||
static ssize_t set_in5_min(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_min[5] = SENSORS_LIMIT(((val * 958 * 34) / (10000 * 54)) + 3,
|
||||
0, 255);
|
||||
vt8231_write_value(client, regvoltmin[5], data->in_min[5]);
|
||||
vt8231_write_value(data, regvoltmin[5], data->in_min[5]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -293,14 +288,13 @@ static ssize_t set_in5_min(struct device *dev, struct device_attribute *attr,
|
|||
static ssize_t set_in5_max(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->in_max[5] = SENSORS_LIMIT(((val * 958 * 34) / (10000 * 54)) + 3,
|
||||
0, 255);
|
||||
vt8231_write_value(client, regvoltmax[5], data->in_max[5]);
|
||||
vt8231_write_value(data, regvoltmax[5], data->in_max[5]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -348,26 +342,24 @@ static ssize_t show_temp0_min(struct device *dev, struct device_attribute *attr,
|
|||
static ssize_t set_temp0_max(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_max[0] = SENSORS_LIMIT((val + 500) / 1000, 0, 255);
|
||||
vt8231_write_value(client, regtempmax[0], data->temp_max[0]);
|
||||
vt8231_write_value(data, regtempmax[0], data->temp_max[0]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
static ssize_t set_temp0_min(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_min[0] = SENSORS_LIMIT((val + 500) / 1000, 0, 255);
|
||||
vt8231_write_value(client, regtempmin[0], data->temp_min[0]);
|
||||
vt8231_write_value(data, regtempmin[0], data->temp_min[0]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -404,13 +396,12 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
|
|||
{
|
||||
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 vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_max[nr] = SENSORS_LIMIT(TEMP_MAXMIN_TO_REG(val), 0, 255);
|
||||
vt8231_write_value(client, regtempmax[nr], data->temp_max[nr]);
|
||||
vt8231_write_value(data, regtempmax[nr], data->temp_max[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -419,13 +410,12 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr,
|
|||
{
|
||||
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 vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_min[nr] = SENSORS_LIMIT(TEMP_MAXMIN_TO_REG(val), 0, 255);
|
||||
vt8231_write_value(client, regtempmin[nr], data->temp_min[nr]);
|
||||
vt8231_write_value(data, regtempmin[nr], data->temp_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -486,13 +476,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
|
|||
{
|
||||
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 vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
int 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]));
|
||||
vt8231_write_value(client, VT8231_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
vt8231_write_value(data, VT8231_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -500,12 +489,11 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
|
|||
static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int nr = sensor_attr->index;
|
||||
int old = vt8231_read_value(client, VT8231_REG_FANDIV);
|
||||
int old = vt8231_read_value(data, VT8231_REG_FANDIV);
|
||||
long min = FAN_FROM_REG(data->fan_min[nr],
|
||||
DIV_FROM_REG(data->fan_div[nr]));
|
||||
|
||||
|
@ -516,7 +504,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
|
|||
case 4: data->fan_div[nr] = 2; break;
|
||||
case 8: data->fan_div[nr] = 3; break;
|
||||
default:
|
||||
dev_err(&client->dev, "fan_div value %ld not supported."
|
||||
dev_err(dev, "fan_div value %ld not supported."
|
||||
"Choose one of 1, 2, 4 or 8!\n", val);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return -EINVAL;
|
||||
|
@ -524,10 +512,10 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
/* Correct the fan minimum speed */
|
||||
data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
|
||||
vt8231_write_value(client, VT8231_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
vt8231_write_value(data, VT8231_REG_FAN_MIN(nr), data->fan_min[nr]);
|
||||
|
||||
old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
|
||||
vt8231_write_value(client, VT8231_REG_FANDIV, old);
|
||||
vt8231_write_value(data, VT8231_REG_FANDIV, old);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
@ -551,9 +539,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
|
|||
struct vt8231_data *data = vt8231_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->alarms);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
|
||||
static ssize_t show_name(struct device *dev, struct device_attribute
|
||||
*devattr, char *buf)
|
||||
{
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%s\n", data->name);
|
||||
}
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
|
||||
static struct attribute *vt8231_attributes_temps[6][4] = {
|
||||
{
|
||||
&dev_attr_temp1_input.attr,
|
||||
|
@ -648,6 +643,7 @@ static struct attribute *vt8231_attributes[] = {
|
|||
&sensor_dev_attr_fan1_div.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_div.dev_attr.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
&dev_attr_name.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -655,13 +651,13 @@ static const struct attribute_group vt8231_group = {
|
|||
.attrs = vt8231_attributes,
|
||||
};
|
||||
|
||||
static struct i2c_driver vt8231_driver = {
|
||||
static struct platform_driver vt8231_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "vt8231",
|
||||
},
|
||||
.attach_adapter = vt8231_detect,
|
||||
.detach_client = vt8231_detach_client,
|
||||
.probe = vt8231_probe,
|
||||
.remove = __devexit_p(vt8231_remove),
|
||||
};
|
||||
|
||||
static struct pci_device_id vt8231_pci_ids[] = {
|
||||
|
@ -680,40 +676,18 @@ static struct pci_driver vt8231_pci_driver = {
|
|||
.probe = vt8231_pci_probe,
|
||||
};
|
||||
|
||||
int vt8231_detect(struct i2c_adapter *adapter)
|
||||
int vt8231_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
struct resource *res;
|
||||
struct vt8231_data *data;
|
||||
int err = 0, i;
|
||||
u16 val;
|
||||
|
||||
/* 8231 requires multiple of 256 */
|
||||
if (force_addr) {
|
||||
isa_address = force_addr & 0xFF00;
|
||||
dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n",
|
||||
isa_address);
|
||||
if (PCIBIOS_SUCCESSFUL != pci_write_config_word(s_bridge,
|
||||
VT8231_BASE_REG, isa_address))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_read_config_word(s_bridge, VT8231_ENABLE_REG, &val))
|
||||
return -ENODEV;
|
||||
|
||||
if (!(val & 0x0001)) {
|
||||
dev_warn(&adapter->dev, "enabling sensors\n");
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(s_bridge, VT8231_ENABLE_REG,
|
||||
val | 0x0001))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Reserve the ISA region */
|
||||
if (!request_region(isa_address, VT8231_EXTENT,
|
||||
vt8231_pci_driver.name)) {
|
||||
dev_err(&adapter->dev, "region 0x%x already in use!\n",
|
||||
isa_address);
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!request_region(res->start, VT8231_EXTENT,
|
||||
vt8231_driver.driver.name)) {
|
||||
dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",
|
||||
(unsigned long)res->start, (unsigned long)res->end);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -722,33 +696,23 @@ int vt8231_detect(struct i2c_adapter *adapter)
|
|||
goto exit_release;
|
||||
}
|
||||
|
||||
client = &data->client;
|
||||
i2c_set_clientdata(client, data);
|
||||
client->addr = isa_address;
|
||||
client->adapter = adapter;
|
||||
client->driver = &vt8231_driver;
|
||||
|
||||
/* Fill in the remaining client fields and put into the global list */
|
||||
strlcpy(client->name, "vt8231", I2C_NAME_SIZE);
|
||||
platform_set_drvdata(pdev, data);
|
||||
data->addr = res->start;
|
||||
data->name = "vt8231";
|
||||
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(client)))
|
||||
goto exit_free;
|
||||
|
||||
vt8231_init_client(client);
|
||||
vt8231_init_device(data);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
if ((err = sysfs_create_group(&client->dev.kobj, &vt8231_group)))
|
||||
goto exit_detach;
|
||||
if ((err = sysfs_create_group(&pdev->dev.kobj, &vt8231_group)))
|
||||
goto exit_free;
|
||||
|
||||
/* Must update device information to find out the config field */
|
||||
data->uch_config = vt8231_read_value(client, VT8231_REG_UCH_CONFIG);
|
||||
data->uch_config = vt8231_read_value(data, VT8231_REG_UCH_CONFIG);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) {
|
||||
if (ISTEMP(i, data->uch_config)) {
|
||||
if ((err = sysfs_create_group(&client->dev.kobj,
|
||||
if ((err = sysfs_create_group(&pdev->dev.kobj,
|
||||
&vt8231_group_temps[i])))
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
@ -756,13 +720,13 @@ int vt8231_detect(struct i2c_adapter *adapter)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) {
|
||||
if (ISVOLT(i, data->uch_config)) {
|
||||
if ((err = sysfs_create_group(&client->dev.kobj,
|
||||
if ((err = sysfs_create_group(&pdev->dev.kobj,
|
||||
&vt8231_group_volts[i])))
|
||||
goto exit_remove_files;
|
||||
}
|
||||
}
|
||||
|
||||
data->class_dev = hwmon_device_register(&client->dev);
|
||||
data->class_dev = hwmon_device_register(&pdev->dev);
|
||||
if (IS_ERR(data->class_dev)) {
|
||||
err = PTR_ERR(data->class_dev);
|
||||
goto exit_remove_files;
|
||||
|
@ -771,56 +735,52 @@ int vt8231_detect(struct i2c_adapter *adapter)
|
|||
|
||||
exit_remove_files:
|
||||
for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
|
||||
sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_volts[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
|
||||
sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);
|
||||
|
||||
sysfs_remove_group(&client->dev.kobj, &vt8231_group);
|
||||
exit_detach:
|
||||
i2c_detach_client(client);
|
||||
exit_free:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
exit_release:
|
||||
release_region(isa_address, VT8231_EXTENT);
|
||||
release_region(res->start, VT8231_EXTENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int vt8231_detach_client(struct i2c_client *client)
|
||||
static int vt8231_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vt8231_data *data = i2c_get_clientdata(client);
|
||||
int err, i;
|
||||
struct vt8231_data *data = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
|
||||
sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_volts[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
|
||||
sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);
|
||||
|
||||
sysfs_remove_group(&client->dev.kobj, &vt8231_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
release_region(client->addr, VT8231_EXTENT);
|
||||
release_region(data->addr, VT8231_EXTENT);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vt8231_init_client(struct i2c_client *client)
|
||||
static void vt8231_init_device(struct vt8231_data *data)
|
||||
{
|
||||
vt8231_write_value(client, VT8231_REG_TEMP1_CONFIG, 0);
|
||||
vt8231_write_value(client, VT8231_REG_TEMP2_CONFIG, 0);
|
||||
vt8231_write_value(data, VT8231_REG_TEMP1_CONFIG, 0);
|
||||
vt8231_write_value(data, VT8231_REG_TEMP2_CONFIG, 0);
|
||||
}
|
||||
|
||||
static struct vt8231_data *vt8231_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct vt8231_data *data = i2c_get_clientdata(client);
|
||||
struct vt8231_data *data = dev_get_drvdata(dev);
|
||||
int i;
|
||||
u16 low;
|
||||
|
||||
|
@ -830,41 +790,41 @@ static struct vt8231_data *vt8231_update_device(struct device *dev)
|
|||
|| !data->valid) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (ISVOLT(i, data->uch_config)) {
|
||||
data->in[i] = vt8231_read_value(client,
|
||||
data->in[i] = vt8231_read_value(data,
|
||||
regvolt[i]);
|
||||
data->in_min[i] = vt8231_read_value(client,
|
||||
data->in_min[i] = vt8231_read_value(data,
|
||||
regvoltmin[i]);
|
||||
data->in_max[i] = vt8231_read_value(client,
|
||||
data->in_max[i] = vt8231_read_value(data,
|
||||
regvoltmax[i]);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
data->fan[i] = vt8231_read_value(client,
|
||||
data->fan[i] = vt8231_read_value(data,
|
||||
VT8231_REG_FAN(i));
|
||||
data->fan_min[i] = vt8231_read_value(client,
|
||||
data->fan_min[i] = vt8231_read_value(data,
|
||||
VT8231_REG_FAN_MIN(i));
|
||||
}
|
||||
|
||||
low = vt8231_read_value(client, VT8231_REG_TEMP_LOW01);
|
||||
low = vt8231_read_value(data, VT8231_REG_TEMP_LOW01);
|
||||
low = (low >> 6) | ((low & 0x30) >> 2)
|
||||
| (vt8231_read_value(client, VT8231_REG_TEMP_LOW25) << 4);
|
||||
| (vt8231_read_value(data, VT8231_REG_TEMP_LOW25) << 4);
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (ISTEMP(i, data->uch_config)) {
|
||||
data->temp[i] = (vt8231_read_value(client,
|
||||
data->temp[i] = (vt8231_read_value(data,
|
||||
regtemp[i]) << 2)
|
||||
| ((low >> (2 * i)) & 0x03);
|
||||
data->temp_max[i] = vt8231_read_value(client,
|
||||
data->temp_max[i] = vt8231_read_value(data,
|
||||
regtempmax[i]);
|
||||
data->temp_min[i] = vt8231_read_value(client,
|
||||
data->temp_min[i] = vt8231_read_value(data,
|
||||
regtempmin[i]);
|
||||
}
|
||||
}
|
||||
|
||||
i = vt8231_read_value(client, VT8231_REG_FANDIV);
|
||||
i = vt8231_read_value(data, VT8231_REG_FANDIV);
|
||||
data->fan_div[0] = (i >> 4) & 0x03;
|
||||
data->fan_div[1] = i >> 6;
|
||||
data->alarms = vt8231_read_value(client, VT8231_REG_ALARM1) |
|
||||
(vt8231_read_value(client, VT8231_REG_ALARM2) << 8);
|
||||
data->alarms = vt8231_read_value(data, VT8231_REG_ALARM1) |
|
||||
(vt8231_read_value(data, VT8231_REG_ALARM2) << 8);
|
||||
|
||||
/* Set alarm flags correctly */
|
||||
if (!data->fan[0] && data->fan_min[0]) {
|
||||
|
@ -888,33 +848,102 @@ static struct vt8231_data *vt8231_update_device(struct device *dev)
|
|||
return data;
|
||||
}
|
||||
|
||||
static int __devinit vt8231_device_add(unsigned short address)
|
||||
{
|
||||
struct resource res = {
|
||||
.start = address,
|
||||
.end = address + VT8231_EXTENT - 1,
|
||||
.name = "vt8231",
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
int err;
|
||||
|
||||
pdev = platform_device_alloc("vt8231", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
printk(KERN_ERR "vt8231: Device allocation failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR "vt8231: Device resource addition failed "
|
||||
"(%d)\n", err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "vt8231: Device addition failed (%d)\n",
|
||||
err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_device_put:
|
||||
platform_device_put(pdev);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit vt8231_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
u16 val;
|
||||
u16 address, val;
|
||||
if (force_addr) {
|
||||
address = force_addr & 0xff00;
|
||||
dev_warn(&dev->dev, "Forcing ISA address 0x%x\n",
|
||||
address);
|
||||
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(dev, VT8231_BASE_REG, address | 1))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (PCIBIOS_SUCCESSFUL != pci_read_config_word(dev, VT8231_BASE_REG,
|
||||
&val))
|
||||
return -ENODEV;
|
||||
|
||||
isa_address = val & ~(VT8231_EXTENT - 1);
|
||||
if (isa_address == 0 && force_addr == 0) {
|
||||
address = val & ~(VT8231_EXTENT - 1);
|
||||
if (address == 0) {
|
||||
dev_err(&dev->dev, "base address not set -\
|
||||
upgrade BIOS or use force_addr=0xaddr\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
s_bridge = pci_dev_get(dev);
|
||||
if (PCIBIOS_SUCCESSFUL != pci_read_config_word(dev, VT8231_ENABLE_REG,
|
||||
&val))
|
||||
return -ENODEV;
|
||||
|
||||
if (i2c_isa_add_driver(&vt8231_driver)) {
|
||||
pci_dev_put(s_bridge);
|
||||
s_bridge = NULL;
|
||||
if (!(val & 0x0001)) {
|
||||
dev_warn(&dev->dev, "enabling sensors\n");
|
||||
if (PCIBIOS_SUCCESSFUL !=
|
||||
pci_write_config_word(dev, VT8231_ENABLE_REG,
|
||||
val | 0x0001))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (platform_driver_register(&vt8231_driver))
|
||||
goto exit;
|
||||
|
||||
/* Sets global pdev as a side effect */
|
||||
if (vt8231_device_add(address))
|
||||
goto exit_unregister;
|
||||
|
||||
/* Always return failure here. This is to allow other drivers to bind
|
||||
* to this pci device. We don't really want to have control over the
|
||||
* pci device, we only wanted to read as few register values from it.
|
||||
*/
|
||||
|
||||
/* We do, however, mark ourselves as using the PCI device to stop it
|
||||
getting unloaded. */
|
||||
s_bridge = pci_dev_get(dev);
|
||||
return -ENODEV;
|
||||
|
||||
exit_unregister:
|
||||
platform_driver_unregister(&vt8231_driver);
|
||||
exit:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -927,7 +956,8 @@ static void __exit sm_vt8231_exit(void)
|
|||
{
|
||||
pci_unregister_driver(&vt8231_pci_driver);
|
||||
if (s_bridge != NULL) {
|
||||
i2c_isa_del_driver(&vt8231_driver);
|
||||
platform_device_unregister(pdev);
|
||||
platform_driver_unregister(&vt8231_driver);
|
||||
pci_dev_put(s_bridge);
|
||||
s_bridge = NULL;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -220,6 +220,18 @@ static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2,
|
|||
#define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \
|
||||
regpwm_627hf[(nr) - 1] : regpwm[(nr) - 1])
|
||||
|
||||
#define W83627HF_REG_PWM_FREQ 0x5C /* Only for the 627HF */
|
||||
|
||||
#define W83637HF_REG_PWM_FREQ1 0x00 /* 697HF/687THF too */
|
||||
#define W83637HF_REG_PWM_FREQ2 0x02 /* 697HF/687THF too */
|
||||
#define W83637HF_REG_PWM_FREQ3 0x10 /* 687THF too */
|
||||
|
||||
static const u8 W83637HF_REG_PWM_FREQ[] = { W83637HF_REG_PWM_FREQ1,
|
||||
W83637HF_REG_PWM_FREQ2,
|
||||
W83637HF_REG_PWM_FREQ3 };
|
||||
|
||||
#define W83627HF_BASE_PWM_FREQ 46870
|
||||
|
||||
#define W83781D_REG_I2C_ADDR 0x48
|
||||
#define W83781D_REG_I2C_SUBADDR 0x4A
|
||||
|
||||
|
@ -267,6 +279,49 @@ static int TEMP_FROM_REG(u8 reg)
|
|||
|
||||
#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
|
||||
|
||||
static inline unsigned long pwm_freq_from_reg_627hf(u8 reg)
|
||||
{
|
||||
unsigned long freq;
|
||||
freq = W83627HF_BASE_PWM_FREQ >> reg;
|
||||
return freq;
|
||||
}
|
||||
static inline u8 pwm_freq_to_reg_627hf(unsigned long val)
|
||||
{
|
||||
u8 i;
|
||||
/* Only 5 dividers (1 2 4 8 16)
|
||||
Search for the nearest available frequency */
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (val > (((W83627HF_BASE_PWM_FREQ >> i) +
|
||||
(W83627HF_BASE_PWM_FREQ >> (i+1))) / 2))
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline unsigned long pwm_freq_from_reg(u8 reg)
|
||||
{
|
||||
/* Clock bit 8 -> 180 kHz or 24 MHz */
|
||||
unsigned long clock = (reg & 0x80) ? 180000UL : 24000000UL;
|
||||
|
||||
reg &= 0x7f;
|
||||
/* This should not happen but anyway... */
|
||||
if (reg == 0)
|
||||
reg++;
|
||||
return (clock / (reg << 8));
|
||||
}
|
||||
static inline u8 pwm_freq_to_reg(unsigned long val)
|
||||
{
|
||||
/* Minimum divider value is 0x01 and maximum is 0x7F */
|
||||
if (val >= 93750) /* The highest we can do */
|
||||
return 0x01;
|
||||
if (val >= 720) /* Use 24 MHz clock */
|
||||
return (24000000UL / (val << 8));
|
||||
if (val < 6) /* The lowest we can do */
|
||||
return 0xFF;
|
||||
else /* Use 180 kHz clock */
|
||||
return (0x80 | (180000UL / (val << 8)));
|
||||
}
|
||||
|
||||
#define BEEP_MASK_FROM_REG(val) (val)
|
||||
#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
|
||||
#define BEEP_ENABLE_TO_REG(val) ((val)?1:0)
|
||||
|
@ -316,6 +371,7 @@ struct w83627hf_data {
|
|||
u32 beep_mask; /* Register encoding, combined */
|
||||
u8 beep_enable; /* Boolean */
|
||||
u8 pwm[3]; /* Register value */
|
||||
u8 pwm_freq[3]; /* Register value */
|
||||
u16 sens[3]; /* 782D/783S only.
|
||||
1 = pentium diode; 2 = 3904 diode;
|
||||
3000-5000 = thermistor beta.
|
||||
|
@ -851,6 +907,64 @@ sysfs_pwm(1);
|
|||
sysfs_pwm(2);
|
||||
sysfs_pwm(3);
|
||||
|
||||
static ssize_t
|
||||
show_pwm_freq_reg(struct device *dev, char *buf, int nr)
|
||||
{
|
||||
struct w83627hf_data *data = w83627hf_update_device(dev);
|
||||
if (data->type == w83627hf)
|
||||
return sprintf(buf, "%ld\n",
|
||||
pwm_freq_from_reg_627hf(data->pwm_freq[nr - 1]));
|
||||
else
|
||||
return sprintf(buf, "%ld\n",
|
||||
pwm_freq_from_reg(data->pwm_freq[nr - 1]));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
store_pwm_freq_reg(struct device *dev, const char *buf, size_t count, int nr)
|
||||
{
|
||||
struct w83627hf_data *data = dev_get_drvdata(dev);
|
||||
static const u8 mask[]={0xF8, 0x8F};
|
||||
u32 val;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (data->type == w83627hf) {
|
||||
data->pwm_freq[nr - 1] = pwm_freq_to_reg_627hf(val);
|
||||
w83627hf_write_value(data, W83627HF_REG_PWM_FREQ,
|
||||
(data->pwm_freq[nr - 1] << ((nr - 1)*4)) |
|
||||
(w83627hf_read_value(data,
|
||||
W83627HF_REG_PWM_FREQ) & mask[nr - 1]));
|
||||
} else {
|
||||
data->pwm_freq[nr - 1] = pwm_freq_to_reg(val);
|
||||
w83627hf_write_value(data, W83637HF_REG_PWM_FREQ[nr - 1],
|
||||
data->pwm_freq[nr - 1]);
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
#define sysfs_pwm_freq(offset) \
|
||||
static ssize_t show_regs_pwm_freq_##offset(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return show_pwm_freq_reg(dev, buf, offset); \
|
||||
} \
|
||||
static ssize_t \
|
||||
store_regs_pwm_freq_##offset(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
return store_pwm_freq_reg(dev, buf, count, offset); \
|
||||
} \
|
||||
static DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR, \
|
||||
show_regs_pwm_freq_##offset, store_regs_pwm_freq_##offset);
|
||||
|
||||
sysfs_pwm_freq(1);
|
||||
sysfs_pwm_freq(2);
|
||||
sysfs_pwm_freq(3);
|
||||
|
||||
static ssize_t
|
||||
show_sensor_reg(struct device *dev, char *buf, int nr)
|
||||
{
|
||||
|
@ -1077,6 +1191,9 @@ static struct attribute *w83627hf_attributes_opt[] = {
|
|||
|
||||
&dev_attr_pwm3.attr,
|
||||
|
||||
&dev_attr_pwm1_freq.attr,
|
||||
&dev_attr_pwm2_freq.attr,
|
||||
&dev_attr_pwm3_freq.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -1139,7 +1256,9 @@ static int __devinit w83627hf_probe(struct platform_device *pdev)
|
|||
|| (err = device_create_file(dev, &dev_attr_in5_max))
|
||||
|| (err = device_create_file(dev, &dev_attr_in6_input))
|
||||
|| (err = device_create_file(dev, &dev_attr_in6_min))
|
||||
|| (err = device_create_file(dev, &dev_attr_in6_max)))
|
||||
|| (err = device_create_file(dev, &dev_attr_in6_max))
|
||||
|| (err = device_create_file(dev, &dev_attr_pwm1_freq))
|
||||
|| (err = device_create_file(dev, &dev_attr_pwm2_freq)))
|
||||
goto ERROR4;
|
||||
|
||||
if (data->type != w83697hf)
|
||||
|
@ -1169,6 +1288,12 @@ static int __devinit w83627hf_probe(struct platform_device *pdev)
|
|||
if ((err = device_create_file(dev, &dev_attr_pwm3)))
|
||||
goto ERROR4;
|
||||
|
||||
if (data->type == w83637hf || data->type == w83687thf)
|
||||
if ((err = device_create_file(dev, &dev_attr_pwm1_freq))
|
||||
|| (err = device_create_file(dev, &dev_attr_pwm2_freq))
|
||||
|| (err = device_create_file(dev, &dev_attr_pwm3_freq)))
|
||||
goto ERROR4;
|
||||
|
||||
data->class_dev = hwmon_device_register(dev);
|
||||
if (IS_ERR(data->class_dev)) {
|
||||
err = PTR_ERR(data->class_dev);
|
||||
|
@ -1181,6 +1306,7 @@ static int __devinit w83627hf_probe(struct platform_device *pdev)
|
|||
sysfs_remove_group(&dev->kobj, &w83627hf_group);
|
||||
sysfs_remove_group(&dev->kobj, &w83627hf_group_opt);
|
||||
ERROR3:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
ERROR1:
|
||||
release_region(res->start, WINB_REGION_SIZE);
|
||||
|
@ -1193,11 +1319,11 @@ static int __devexit w83627hf_remove(struct platform_device *pdev)
|
|||
struct w83627hf_data *data = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
hwmon_device_unregister(data->class_dev);
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(data);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
|
@ -1472,6 +1598,20 @@ static struct w83627hf_data *w83627hf_update_device(struct device *dev)
|
|||
(data->type == w83627hf || data->type == w83697hf))
|
||||
break;
|
||||
}
|
||||
if (data->type == w83627hf) {
|
||||
u8 tmp = w83627hf_read_value(data,
|
||||
W83627HF_REG_PWM_FREQ);
|
||||
data->pwm_freq[0] = tmp & 0x07;
|
||||
data->pwm_freq[1] = (tmp >> 4) & 0x07;
|
||||
} else if (data->type != w83627thf) {
|
||||
for (i = 1; i <= 3; i++) {
|
||||
data->pwm_freq[i - 1] =
|
||||
w83627hf_read_value(data,
|
||||
W83637HF_REG_PWM_FREQ[i - 1]);
|
||||
if (i == 2 && (data->type == w83697hf))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data->temp = w83627hf_read_value(data, W83781D_REG_TEMP(1));
|
||||
data->temp_max =
|
||||
|
@ -1548,15 +1688,12 @@ static int __init w83627hf_device_add(unsigned short address,
|
|||
goto exit_device_put;
|
||||
}
|
||||
|
||||
pdev->dev.platform_data = kmalloc(sizeof(struct w83627hf_sio_data),
|
||||
GFP_KERNEL);
|
||||
if (!pdev->dev.platform_data) {
|
||||
err = -ENOMEM;
|
||||
err = platform_device_add_data(pdev, sio_data,
|
||||
sizeof(struct w83627hf_sio_data));
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
|
||||
goto exit_device_put;
|
||||
}
|
||||
memcpy(pdev->dev.platform_data, sio_data,
|
||||
sizeof(struct w83627hf_sio_data));
|
||||
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
|
|
|
@ -237,9 +237,6 @@ config I2C_IOP3XX
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-iop3xx.
|
||||
|
||||
config I2C_ISA
|
||||
tristate
|
||||
|
||||
config I2C_IXP4XX
|
||||
tristate "IXP4xx GPIO-Based I2C Interface (DEPRECATED)"
|
||||
depends on ARCH_IXP4XX
|
||||
|
|
|
@ -18,7 +18,6 @@ obj-$(CONFIG_I2C_I801) += i2c-i801.o
|
|||
obj-$(CONFIG_I2C_I810) += i2c-i810.o
|
||||
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
|
||||
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
||||
obj-$(CONFIG_I2C_ISA) += i2c-isa.o
|
||||
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
|
||||
obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o
|
||||
obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
/*
|
||||
i2c-isa.c - an i2c-core-like thing for ISA hardware monitoring chips
|
||||
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
Based on the i2c-isa pseudo-adapter from the lm_sensors project
|
||||
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.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 implements an i2c-core-like thing for ISA hardware monitoring
|
||||
chips. Such chips are linked to the i2c subsystem for historical
|
||||
reasons (because the early ISA hardware monitoring chips such as the
|
||||
LM78 had both an I2C and an ISA interface). They used to be
|
||||
registered with the main i2c-core, but as a first step in the
|
||||
direction of a clean separation between I2C and ISA chip drivers,
|
||||
we now have this separate core for ISA ones. It is significantly
|
||||
more simple than the real one, of course, because we don't have to
|
||||
handle multiple busses: there is only one (fake) ISA adapter.
|
||||
It is worth noting that we still rely on i2c-core for some things
|
||||
at the moment - but hopefully this won't last. */
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-isa.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
/* Exported by i2c-core for i2c-isa only */
|
||||
extern void i2c_adapter_dev_release(struct device *dev);
|
||||
extern struct class i2c_adapter_class;
|
||||
|
||||
static u32 isa_func(struct i2c_adapter *adapter);
|
||||
|
||||
/* This is the actual algorithm we define */
|
||||
static const struct i2c_algorithm isa_algorithm = {
|
||||
.functionality = isa_func,
|
||||
};
|
||||
|
||||
/* There can only be one... */
|
||||
static struct i2c_adapter isa_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.id = I2C_HW_ISA,
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.algo = &isa_algorithm,
|
||||
.name = "ISA main adapter",
|
||||
};
|
||||
|
||||
/* We can't do a thing... */
|
||||
static u32 isa_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* We implement an interface which resembles i2c_{add,del}_driver,
|
||||
but for i2c-isa drivers. We don't have to remember and handle lists
|
||||
of drivers and adapters so this is much more simple, of course. */
|
||||
|
||||
int i2c_isa_add_driver(struct i2c_driver *driver)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Add the driver to the list of i2c drivers in the driver core */
|
||||
driver->driver.bus = &i2c_bus_type;
|
||||
res = driver_register(&driver->driver);
|
||||
if (res)
|
||||
return res;
|
||||
dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->driver.name);
|
||||
|
||||
/* Now look for clients */
|
||||
res = driver->attach_adapter(&isa_adapter);
|
||||
if (res) {
|
||||
dev_dbg(&isa_adapter.dev,
|
||||
"Driver %s failed to attach adapter, unregistering\n",
|
||||
driver->driver.name);
|
||||
driver_unregister(&driver->driver);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int i2c_isa_del_driver(struct i2c_driver *driver)
|
||||
{
|
||||
struct list_head *item, *_n;
|
||||
struct i2c_client *client;
|
||||
int res;
|
||||
|
||||
/* Detach all clients belonging to this one driver */
|
||||
list_for_each_safe(item, _n, &isa_adapter.clients) {
|
||||
client = list_entry(item, struct i2c_client, list);
|
||||
if (client->driver != driver)
|
||||
continue;
|
||||
dev_dbg(&isa_adapter.dev, "Detaching client %s at 0x%x\n",
|
||||
client->name, client->addr);
|
||||
if ((res = driver->detach_client(client))) {
|
||||
dev_err(&isa_adapter.dev, "Failed, driver "
|
||||
"%s not unregistered!\n",
|
||||
driver->driver.name);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the driver off the core list */
|
||||
driver_unregister(&driver->driver);
|
||||
dev_dbg(&isa_adapter.dev, "Driver %s unregistered\n", driver->driver.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __init i2c_isa_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_init(&isa_adapter.clist_lock);
|
||||
INIT_LIST_HEAD(&isa_adapter.clients);
|
||||
|
||||
isa_adapter.nr = ANY_I2C_ISA_BUS;
|
||||
isa_adapter.dev.parent = &platform_bus;
|
||||
sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr);
|
||||
isa_adapter.dev.release = &i2c_adapter_dev_release;
|
||||
isa_adapter.dev.class = &i2c_adapter_class;
|
||||
err = device_register(&isa_adapter.dev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "i2c-isa: Failed to register device\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name);
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit i2c_isa_exit(void)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
struct list_head *item, *_n;
|
||||
struct i2c_client *client = NULL;
|
||||
#endif
|
||||
|
||||
/* There should be no more active client */
|
||||
#ifdef DEBUG
|
||||
dev_dbg(&isa_adapter.dev, "Looking for clients\n");
|
||||
list_for_each_safe(item, _n, &isa_adapter.clients) {
|
||||
client = list_entry(item, struct i2c_client, list);
|
||||
dev_err(&isa_adapter.dev, "Driver %s still has an active "
|
||||
"ISA client at 0x%x\n", client->driver->driver.name,
|
||||
client->addr);
|
||||
}
|
||||
if (client != NULL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* Clean up the sysfs representation */
|
||||
dev_dbg(&isa_adapter.dev, "Unregistering from sysfs\n");
|
||||
init_completion(&isa_adapter.dev_released);
|
||||
device_unregister(&isa_adapter.dev);
|
||||
|
||||
/* Wait for sysfs to drop all references */
|
||||
dev_dbg(&isa_adapter.dev, "Waiting for sysfs completion\n");
|
||||
wait_for_completion(&isa_adapter.dev_released);
|
||||
|
||||
dev_dbg(&isa_adapter.dev, "%s unregistered\n", isa_adapter.name);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(i2c_isa_add_driver);
|
||||
EXPORT_SYMBOL(i2c_isa_del_driver);
|
||||
|
||||
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
|
||||
MODULE_DESCRIPTION("ISA bus access through i2c");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(i2c_isa_init);
|
||||
module_exit(i2c_isa_exit);
|
|
@ -288,7 +288,6 @@ void i2c_adapter_dev_release(struct device *dev)
|
|||
struct i2c_adapter *adap = to_i2c_adapter(dev);
|
||||
complete(&adap->dev_released);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_adapter_dev_release); /* exported to i2c-isa */
|
||||
|
||||
static ssize_t
|
||||
show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
|
@ -307,7 +306,6 @@ struct class i2c_adapter_class = {
|
|||
.name = "i2c-adapter",
|
||||
.dev_attrs = i2c_adapter_attrs,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(i2c_adapter_class); /* exported to i2c-isa */
|
||||
|
||||
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
|
||||
{
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* i2c-isa.h - definitions for the i2c-isa pseudo-i2c-adapter interface
|
||||
*
|
||||
* Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_ISA_H
|
||||
#define _LINUX_I2C_ISA_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
extern int i2c_isa_add_driver(struct i2c_driver *driver);
|
||||
extern int i2c_isa_del_driver(struct i2c_driver *driver);
|
||||
|
||||
/* Detect whether we are on the isa bus. This is only useful to hybrid
|
||||
(i2c+isa) drivers. */
|
||||
#define i2c_is_isa_adapter(adapptr) \
|
||||
((adapptr)->id == I2C_HW_ISA)
|
||||
#define i2c_is_isa_client(clientptr) \
|
||||
i2c_is_isa_adapter((clientptr)->adapter)
|
||||
|
||||
#endif /* _LINUX_I2C_ISA_H */
|
|
@ -368,7 +368,6 @@ struct i2c_client_address_data {
|
|||
|
||||
/* The numbers to use to set I2C bus address */
|
||||
#define ANY_I2C_BUS 0xffff
|
||||
#define ANY_I2C_ISA_BUS 9191
|
||||
|
||||
|
||||
/* ----- functions exported by i2c.o */
|
||||
|
|
Загрузка…
Ссылка в новой задаче