Commit bdbf0ac7 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6

* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6: (47 commits)
  hwmon: (adm1026) Prevent log spamming
  hwmon: (adm1026) Fix debug messages
  hwmon: (adm1029) Use mask for fan_div value
  hwmon: (adt7470) Add documentation
  hwmon: (ibmaem) Automatically load on IBM systems via DMI
  hwmon: (ibmpex) Automatically load on IBM systems via DMI
  hwmon: (w83781d) Use new style driver binding
  hwmon: (w83781d) Stop abusing struct i2c_client for ISA devices
  hwmon: (w83781d) Make ISA interface depend on CONFIG_ISA
  hwmon: (w83781d) Additional information about AS99127F PWM
  hwmon: (w83781d) Detect alias chips
  hwmon: (w83781d) Refactor beep enable handling
  hwmon: Drop dead links to old National Semiconductor chip datasheets
  hwmon: (w83791d) add support for thermal cruise mode
  hwmon: (w83791d) add pwm_enable support
  hwmon: (w83791d) add manual PWM support
  hwmon: (w83791d) fan 4/5 pins can also be used for gpio
  hwmon: (max1619) Use inline functions instead of macros
  hwmon: (it87) Fix thermal sensor type values
  hwmon: (lm78) Convert to a new-style i2c driver
  ...
parents 8eb88c80 a0cf354a
Kernel driver adt7470
=====================
Supported chips:
* Analog Devices ADT7470
Prefix: 'adt7470'
Addresses scanned: I2C 0x2C, 0x2E, 0x2F
Datasheet: Publicly available at the Analog Devices website
Author: Darrick J. Wong
Description
-----------
This driver implements support for the Analog Devices ADT7470 chip. There may
be other chips that implement this interface.
The ADT7470 uses the 2-wire interface compatible with the SMBus 2.0
specification. Using an analog to digital converter it measures up to ten (10)
external temperatures. It has four (4) 16-bit counters for measuring fan speed.
There are four (4) PWM outputs that can be used to control fan speed.
A sophisticated control system for the PWM outputs is designed into the ADT7470
that allows fan speed to be adjusted automatically based on any of the ten
temperature sensors. Each PWM output is individually adjustable and
programmable. Once configured, the ADT7470 will adjust the PWM outputs in
response to the measured temperatures with further host intervention. This
feature can also be disabled for manual control of the PWM's.
Each of the measured inputs (temperature, fan speed) has corresponding high/low
limit values. The ADT7470 will signal an ALARM if any measured value exceeds
either limit.
The ADT7470 DOES NOT sample all inputs continuously. A single pin on the
ADT7470 is connected to a multitude of thermal diodes, but the chip must be
instructed explicitly to read the multitude of diodes. If you want to use
automatic fan control mode, you must manually read any of the temperature
sensors or the fan control algorithm will not run. The chip WILL NOT DO THIS
AUTOMATICALLY; this must be done from userspace. This may be a bug in the chip
design, given that many other AD chips take care of this. The driver will not
read the registers more often than once every 5 seconds. Further,
configuration data is only read once per minute.
Special Features
----------------
The ADT7470 has a 8-bit ADC and is capable of measuring temperatures with 1
degC resolution.
The Analog Devices datasheet is very detailed and describes a procedure for
determining an optimal configuration for the automatic PWM control.
Configuration Notes
-------------------
Besides standard interfaces driver adds the following:
* PWM Control
* pwm#_auto_point1_pwm and pwm#_auto_point1_temp and
* pwm#_auto_point2_pwm and pwm#_auto_point2_temp -
point1: Set the pwm speed at a lower temperature bound.
point2: Set the pwm speed at a higher temperature bound.
The ADT7470 will scale the pwm between the lower and higher pwm speed when
the temperature is between the two temperature boundaries. PWM values range
from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
temperature sensor associated with the PWM control exceeds
pwm#_auto_point2_temp.
Notes
-----
As stated above, the temperature inputs must be read periodically from
userspace in order for the automatic pwm algorithm to run.
...@@ -136,10 +136,10 @@ once-only alarms. ...@@ -136,10 +136,10 @@ once-only alarms.
The IT87xx only updates its values each 1.5 seconds; reading it more often The IT87xx only updates its values each 1.5 seconds; reading it more often
will do no harm, but will return 'old' values. will do no harm, but will return 'old' values.
To change sensor N to a thermistor, 'echo 2 > tempN_type' where N is 1, 2, To change sensor N to a thermistor, 'echo 4 > tempN_type' where N is 1, 2,
or 3. To change sensor N to a thermal diode, 'echo 3 > tempN_type'. or 3. To change sensor N to a thermal diode, 'echo 3 > tempN_type'.
Give 0 for unused sensor. Any other value is invalid. To configure this at Give 0 for unused sensor. Any other value is invalid. To configure this at
startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; startup, consult lm_sensors's /etc/sensors.conf. (4 = thermistor;
3 = thermal diode) 3 = thermal diode)
......
...@@ -163,16 +163,6 @@ configured individually according to the following options. ...@@ -163,16 +163,6 @@ configured individually according to the following options.
* pwm#_auto_pwm_min - this specifies the PWM value for temp#_auto_temp_off * pwm#_auto_pwm_min - this specifies the PWM value for temp#_auto_temp_off
temperature. (PWM value from 0 to 255) temperature. (PWM value from 0 to 255)
* pwm#_auto_pwm_freq - select base frequency of PWM output. You can select
in range of 10.0 to 94.0 Hz in .1 Hz units.
(Values 100 to 940).
The pwm#_auto_pwm_freq can be set to one of the following 8 values. Setting the
frequency to a value not on this list, will result in the next higher frequency
being selected. The actual device frequency may vary slightly from this
specification as designed by the manufacturer. Consult the datasheet for more
details. (PWM Frequency values: 100, 150, 230, 300, 380, 470, 620, 940)
* pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature * pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature
the bahaviour of fans. Write 1 to let fans spinning at the bahaviour of fans. Write 1 to let fans spinning at
pwm#_auto_pwm_min or write 0 to let them off. pwm#_auto_pwm_min or write 0 to let them off.
......
...@@ -65,11 +65,10 @@ The LM87 has four pins which can serve one of two possible functions, ...@@ -65,11 +65,10 @@ The LM87 has four pins which can serve one of two possible functions,
depending on the hardware configuration. depending on the hardware configuration.
Some functions share pins, so not all functions are available at the same Some functions share pins, so not all functions are available at the same
time. Which are depends on the hardware setup. This driver assumes that time. Which are depends on the hardware setup. This driver normally
the BIOS configured the chip correctly. In that respect, it differs from assumes that firmware configured the chip correctly. Where this is not
the original driver (from lm_sensors for Linux 2.4), which would force the the case, platform code must set the I2C client's platform_data to point
LM87 to an arbitrary, compile-time chosen mode, regardless of the actual to a u8 value to be written to the channel register.
chipset wiring.
For reference, here is the list of exclusive functions: For reference, here is the list of exclusive functions:
- in0+in5 (default) or temp3 - in0+in5 (default) or temp3
......
...@@ -11,7 +11,7 @@ Supported chips: ...@@ -11,7 +11,7 @@ Supported chips:
Prefix: 'lm99' Prefix: 'lm99'
Addresses scanned: I2C 0x4c and 0x4d Addresses scanned: I2C 0x4c and 0x4d
Datasheet: Publicly available at the National Semiconductor website Datasheet: Publicly available at the National Semiconductor website
http://www.national.com/pf/LM/LM89.html http://www.national.com/mpf/LM/LM89.html
* National Semiconductor LM99 * National Semiconductor LM99
Prefix: 'lm99' Prefix: 'lm99'
Addresses scanned: I2C 0x4c and 0x4d Addresses scanned: I2C 0x4c and 0x4d
...@@ -21,18 +21,32 @@ Supported chips: ...@@ -21,18 +21,32 @@ Supported chips:
Prefix: 'lm86' Prefix: 'lm86'
Addresses scanned: I2C 0x4c Addresses scanned: I2C 0x4c
Datasheet: Publicly available at the National Semiconductor website Datasheet: Publicly available at the National Semiconductor website
http://www.national.com/pf/LM/LM86.html http://www.national.com/mpf/LM/LM86.html
* Analog Devices ADM1032 * Analog Devices ADM1032
Prefix: 'adm1032' Prefix: 'adm1032'
Addresses scanned: I2C 0x4c and 0x4d Addresses scanned: I2C 0x4c and 0x4d
Datasheet: Publicly available at the Analog Devices website Datasheet: Publicly available at the ON Semiconductor website
http://www.analog.com/en/prod/0,2877,ADM1032,00.html http://www.onsemi.com/PowerSolutions/product.do?id=ADM1032
* Analog Devices ADT7461 * Analog Devices ADT7461
Prefix: 'adt7461' Prefix: 'adt7461'
Addresses scanned: I2C 0x4c and 0x4d Addresses scanned: I2C 0x4c and 0x4d
Datasheet: Publicly available at the Analog Devices website Datasheet: Publicly available at the ON Semiconductor website
http://www.analog.com/en/prod/0,2877,ADT7461,00.html http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461
Note: Only if in ADM1032 compatibility mode * Maxim MAX6646
Prefix: 'max6646'
Addresses scanned: I2C 0x4d
Datasheet: Publicly available at the Maxim website
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497
* Maxim MAX6647
Prefix: 'max6646'
Addresses scanned: I2C 0x4e
Datasheet: Publicly available at the Maxim website
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497
* Maxim MAX6649
Prefix: 'max6646'
Addresses scanned: I2C 0x4c
Datasheet: Publicly available at the Maxim website
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497
* Maxim MAX6657 * Maxim MAX6657
Prefix: 'max6657' Prefix: 'max6657'
Addresses scanned: I2C 0x4c Addresses scanned: I2C 0x4c
...@@ -70,25 +84,21 @@ Description ...@@ -70,25 +84,21 @@ Description
The LM90 is a digital temperature sensor. It senses its own temperature as 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 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, with many other devices, many of which are supported by this driver.
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, Note that there is no easy way to differentiate between the MAX6657,
MAX6658 and MAX6659 variants. The extra address and features of the MAX6658 and MAX6659 variants. The extra address and features of the
MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only 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) differ in their pinout, therefore they obviously can't (and don't need to)
be distinguished. Additionally, the ADT7461 is supported if found in be distinguished.
ADM1032 compatibility mode.
The specificity of this family of chipsets over the ADM1021/LM84 The specificity of this family of chipsets over the ADM1021/LM84
family is that it features critical limits with hysteresis, and an family is that it features critical limits with hysteresis, and an
increased resolution of the remote temperature measurement. increased resolution of the remote temperature measurement.
The different chipsets of the family are not strictly identical, although The different chipsets of the family are not strictly identical, although
very similar. This driver doesn't handle any specific feature for now, very similar. For reference, here comes a non-exhaustive list of specific
with the exception of SMBus PEC. For reference, here comes a non-exhaustive features:
list of specific features:
LM90: LM90:
* Filter and alert configuration register at 0xBF. * Filter and alert configuration register at 0xBF.
...@@ -114,9 +124,11 @@ ADT7461: ...@@ -114,9 +124,11 @@ ADT7461:
* Lower resolution for remote temperature * Lower resolution for remote temperature
MAX6657 and MAX6658: MAX6657 and MAX6658:
* Better local resolution
* Remote sensor type selection * Remote sensor type selection
MAX6659: MAX6659:
* Better local resolution
* Selectable address * Selectable address
* Second critical temperature limit * Second critical temperature limit
* Remote sensor type selection * Remote sensor type selection
...@@ -127,7 +139,8 @@ MAX6680 and MAX6681: ...@@ -127,7 +139,8 @@ MAX6680 and MAX6681:
All temperature values are given in degrees Celsius. Resolution All temperature values are given in degrees Celsius. Resolution
is 1.0 degree for the local temperature, 0.125 degree for the remote is 1.0 degree for the local temperature, 0.125 degree for the remote
temperature. temperature, except for the MAX6657, MAX6658 and MAX6659 which have a
resolution of 0.125 degree for both temperatures.
Each sensor has its own high and low limits, plus a critical limit. Each sensor has its own high and low limits, plus a critical limit.
Additionally, there is a relative hysteresis value common to both critical Additionally, there is a relative hysteresis value common to both critical
......
...@@ -5,12 +5,7 @@ Supported chips: ...@@ -5,12 +5,7 @@ Supported chips:
* National Semiconductor PC87360, PC87363, PC87364, PC87365 and PC87366 * National Semiconductor PC87360, PC87363, PC87364, PC87365 and PC87366
Prefixes: 'pc87360', 'pc87363', 'pc87364', 'pc87365', 'pc87366' Prefixes: 'pc87360', 'pc87363', 'pc87364', 'pc87365', 'pc87366'
Addresses scanned: none, address read from Super I/O config space Addresses scanned: none, address read from Super I/O config space
Datasheets: Datasheets: No longer available
http://www.national.com/pf/PC/PC87360.html
http://www.national.com/pf/PC/PC87363.html
http://www.national.com/pf/PC/PC87364.html
http://www.national.com/pf/PC/PC87365.html
http://www.national.com/pf/PC/PC87366.html
Authors: Jean Delvare <khali@linux-fr.org> Authors: Jean Delvare <khali@linux-fr.org>
......
...@@ -5,7 +5,7 @@ Supported chips: ...@@ -5,7 +5,7 @@ Supported chips:
* National Semiconductor PC87427 * National Semiconductor PC87427
Prefix: 'pc87427' Prefix: 'pc87427'
Addresses scanned: none, address read from Super I/O config space Addresses scanned: none, address read from Super I/O config space
Datasheet: http://www.winbond.com.tw/E-WINBONDHTM/partner/apc_007.html Datasheet: No longer available
Author: Jean Delvare <khali@linux-fr.org> Author: Jean Delvare <khali@linux-fr.org>
......
...@@ -353,7 +353,7 @@ in6=255 ...@@ -353,7 +353,7 @@ in6=255
# PWM # PWM
Additional info about PWM on the AS99127F (may apply to other Asus * Additional info about PWM on the AS99127F (may apply to other Asus
chips as well) by Jean Delvare as of 2004-04-09: chips as well) by Jean Delvare as of 2004-04-09:
AS99127F revision 2 seems to have two PWM registers at 0x59 and 0x5A, AS99127F revision 2 seems to have two PWM registers at 0x59 and 0x5A,
...@@ -396,7 +396,7 @@ Please contact us if you can figure out how it is supposed to work. As ...@@ -396,7 +396,7 @@ Please contact us if you can figure out how it is supposed to work. As
long as we don't know more, the w83781d driver doesn't handle PWM on long as we don't know more, the w83781d driver doesn't handle PWM on
AS99127F chips at all. AS99127F chips at all.
Additional info about PWM on the AS99127F rev.1 by Hector Martin: * Additional info about PWM on the AS99127F rev.1 by Hector Martin:
I've been fiddling around with the (in)famous 0x59 register and I've been fiddling around with the (in)famous 0x59 register and
found out the following values do work as a form of coarse pwm: found out the following values do work as a form of coarse pwm:
...@@ -418,3 +418,36 @@ change. ...@@ -418,3 +418,36 @@ change.
My mobo is an ASUS A7V266-E. This behavior is similar to what I got My mobo is an ASUS A7V266-E. This behavior is similar to what I got
with speedfan under Windows, where 0-15% would be off, 15-2x% (can't with speedfan under Windows, where 0-15% would be off, 15-2x% (can't
remember the exact value) would be 70% and higher would be full on. remember the exact value) would be 70% and higher would be full on.
* Additional info about PWM on the AS99127F rev.1 from lm-sensors
ticket #2350:
I conducted some experiment on Asus P3B-F motherboard with AS99127F
(Ver. 1).
I confirm that 0x59 register control the CPU_Fan Header on this
motherboard, and 0x5a register control PWR_Fan.
In order to reduce the dependency of specific fan, the measurement is
conducted with a digital scope without fan connected. I found out that
P3B-F actually output variable DC voltage on fan header center pin,
looks like PWM is filtered on this motherboard.
Here are some of measurements:
0x80 20 mV
0x81 20 mV
0x82 232 mV
0x83 1.2 V
0x84 2.31 V
0x85 3.44 V
0x86 4.62 V
0x87 5.81 V
0x88 7.01 V
9x89 8.22 V
0x8a 9.42 V
0x8b 10.6 V
0x8c 11.9 V
0x8d 12.4 V
0x8e 12.4 V
0x8f 12.4 V
...@@ -58,29 +58,35 @@ internal state that allows no clean access (Bank with ID register is not ...@@ -58,29 +58,35 @@ internal state that allows no clean access (Bank with ID register is not
currently selected). If you know the address of the chip, use a 'force' currently selected). If you know the address of the chip, use a 'force'
parameter; this will put it into a more well-behaved state first. parameter; this will put it into a more well-behaved state first.
The driver implements three temperature sensors, five fan rotation speed The driver implements three temperature sensors, ten voltage sensors,
sensors, and ten voltage sensors. five fan rotation speed sensors and manual PWM control of each fan.
Temperatures are measured in degrees Celsius and measurement resolution is 1 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 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
the temperature gets higher than the Overtemperature Shutdown value; it stays the temperature gets higher than the Overtemperature Shutdown value; it stays
on until the temperature falls below the Hysteresis value. on until the temperature falls below the Hysteresis value.
Voltage sensors (also known as IN sensors) report their values in millivolts.
An alarm is triggered if the voltage has crossed a programmable minimum
or maximum limit.
Fan rotation speeds are reported in RPM (rotations per minute). An alarm is Fan rotation speeds are reported in RPM (rotations per minute). An alarm is
triggered if the rotation speed has dropped below a programmable limit. Fan triggered if the rotation speed has dropped below a programmable limit. Fan
readings can be divided by a programmable divider (1, 2, 4, 8, 16, readings can be divided by a programmable divider (1, 2, 4, 8, 16,
32, 64 or 128 for all fans) to give the readings more range or accuracy. 32, 64 or 128 for all fans) to give the readings more range or accuracy.
Voltage sensors (also known as IN sensors) report their values in millivolts. Each fan controlled is controlled by PWM. The PWM duty cycle can be read and
An alarm is triggered if the voltage has crossed a programmable minimum set for each fan separately. Valid values range from 0 (stop) to 255 (full).
or maximum limit. PWM 1-3 support Thermal Cruise mode, in which the PWMs are automatically
regulated to keep respectively temp 1-3 at a certain target temperature.
See below for the description of the sysfs-interface.
The w83791d has a global bit used to enable beeping from the speaker when an The w83791d has a global bit used to enable beeping from the speaker when an
alarm is triggered as well as a bitmask to enable or disable the beep for alarm is triggered as well as a bitmask to enable or disable the beep for
specific alarms. You need both the global beep enable bit and the specific alarms. You need both the global beep enable bit and the
corresponding beep bit to be on for a triggered alarm to sound a beep. corresponding beep bit to be on for a triggered alarm to sound a beep.
The sysfs interface to the gloabal enable is via the sysfs beep_enable file. The sysfs interface to the global enable is via the sysfs beep_enable file.
This file is used for both legacy and new code. This file is used for both legacy and new code.
The sysfs interface to the beep bitmask has migrated from the original legacy The sysfs interface to the beep bitmask has migrated from the original legacy
...@@ -105,6 +111,27 @@ going forward. ...@@ -105,6 +111,27 @@ going forward.
The driver reads the hardware chip values at most once every three seconds. The driver reads the hardware chip values at most once every three seconds.
User mode code requesting values more often will receive cached values. User mode code requesting values more often will receive cached values.
/sys files
----------
The sysfs-interface is documented in the 'sysfs-interface' file. Only
chip-specific options are documented here.
pwm[1-3]_enable - this file controls mode of fan/temperature control for
fan 1-3. Fan/PWM 4-5 only support manual mode.
* 1 Manual mode
* 2 Thermal Cruise mode
* 3 Fan Speed Cruise mode (no further support)
temp[1-3]_target - defines the target temperature for Thermal Cruise mode.
Unit: millidegree Celsius
RW
temp[1-3]_tolerance - temperature tolerance for Thermal Cruise mode.
Specifies an interval around the target temperature
in which the fan speed is not changed.
Unit: millidegree Celsius
RW
Alarms bitmap vs. beep_mask bitmask Alarms bitmap vs. beep_mask bitmask
------------------------------------ ------------------------------------
For legacy code using the alarms and beep_mask files: For legacy code using the alarms and beep_mask files:
...@@ -132,7 +159,3 @@ tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch ...@@ -132,7 +159,3 @@ tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch
tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch
case_open : alarms: 0x001000 beep_mask: 0x001000 case_open : alarms: 0x001000 beep_mask: 0x001000
global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable)
W83791D TODO:
---------------
Provide a patch for smart-fan control (still need appropriate motherboard/fans)
...@@ -510,11 +510,9 @@ config SENSORS_LM90 ...@@ -510,11 +510,9 @@ config SENSORS_LM90
depends on I2C depends on I2C
help help
If you say yes here you get support for National Semiconductor LM90, If you say yes here you get support for National Semiconductor LM90,
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim
MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. MAX6646, MAX6647, MAX6649, 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.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called lm90. will be called lm90.
......
...@@ -279,7 +279,6 @@ struct adm1026_data { ...@@ -279,7 +279,6 @@ struct adm1026_data {
u8 fan_min[8]; /* Register value */ u8 fan_min[8]; /* Register value */
u8 fan_div[8]; /* Decoded value */ u8 fan_div[8]; /* Decoded value */
struct pwm_data pwm1; /* Pwm control values */ struct pwm_data pwm1; /* Pwm control values */
int vid; /* Decoded value */
u8 vrm; /* VRM version */ u8 vrm; /* VRM version */
u8 analog_out; /* Register value (DAC) */ u8 analog_out; /* Register value (DAC) */
long alarms; /* Register encoding, combined */ long alarms; /* Register encoding, combined */
...@@ -455,7 +454,7 @@ static void adm1026_print_gpio(struct i2c_client *client) ...@@ -455,7 +454,7 @@ static void adm1026_print_gpio(struct i2c_client *client)
struct adm1026_data *data = i2c_get_clientdata(client); struct adm1026_data *data = i2c_get_clientdata(client);
int i; int i;
dev_dbg(&client->dev, "GPIO config is:"); dev_dbg(&client->dev, "GPIO config is:\n");
for (i = 0;i <= 7;++i) { for (i = 0;i <= 7;++i) {
if (data->config2 & (1 << i)) { if (data->config2 & (1 << i)) {
dev_dbg(&client->dev, "\t%sGP%s%d\n", dev_dbg(&client->dev, "\t%sGP%s%d\n",
...@@ -697,8 +696,6 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) ...@@ -697,8 +696,6 @@ static struct adm1026_data *adm1026_update_device(struct device *dev)
data->last_config = jiffies; data->last_config = jiffies;
}; /* last_config */ }; /* last_config */
dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n");
data->vid = (data->gpio >> 11) & 0x1f;
data->valid = 1; data->valid = 1;
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return data; return data;
...@@ -1215,7 +1212,10 @@ static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, ...@@ -1215,7 +1212,10 @@ static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg,
static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct adm1026_data *data = adm1026_update_device(dev); struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid & 0x3f, data->vrm)); int vid = (data->gpio >> 11) & 0x1f;
dev_dbg(dev, "Setting VID from GPIO11-15.\n");
return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm));
} }
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
...@@ -1681,17 +1681,16 @@ static int adm1026_detect(struct i2c_client *client, int kind, ...@@ -1681,17 +1681,16 @@ static int adm1026_detect(struct i2c_client *client, int kind,
kind = adm1026; kind = adm1026;
} else if (company == ADM1026_COMPANY_ANALOG_DEV } else if (company == ADM1026_COMPANY_ANALOG_DEV
&& (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
dev_err(&adapter->dev, ": Unrecognized stepping " dev_err(&adapter->dev, "Unrecognized stepping "
"0x%02x. Defaulting to ADM1026.\n", verstep); "0x%02x. Defaulting to ADM1026.\n", verstep);
kind = adm1026; kind = adm1026;
} else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
dev_err(&adapter->dev, ": Found version/stepping " dev_err(&adapter->dev, "Found version/stepping "
"0x%02x. Assuming generic ADM1026.\n", "0x%02x. Assuming generic ADM1026.\n",
verstep); verstep);
kind = any_chip; kind = any_chip;
} else { } else {
dev_dbg(&adapter->dev, ": Autodetection " dev_dbg(&adapter->dev, "Autodetection failed\n");
"failed\n");
/* Not an ADM1026 ... */ /* Not an ADM1026 ... */
if (kind == 0) { /* User used force=x,y */ if (kind == 0) { /* User used force=x,y */
dev_err(&adapter->dev, "Generic ADM1026 not " dev_err(&adapter->dev, "Generic ADM1026 not "
......
...@@ -179,7 +179,8 @@ show_fan(struct device *dev, struct device_attribute *devattr, char *buf) ...@@ -179,7 +179,8 @@ show_fan(struct device *dev, struct device_attribute *devattr, char *buf)
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adm1029_data *data = adm1029_update_device(dev); struct adm1029_data *data = adm1029_update_device(dev);
u16 val; u16 val;
if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0 if (data->fan[attr->index] == 0
|| (data->fan_div[attr->index] & 0xC0) == 0
|| data->fan[attr->index] == 255) { || data->fan[attr->index] == 255) {
return sprintf(buf, "0\n"); return sprintf(buf, "0\n");
} }
...@@ -194,7 +195,7 @@ show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) ...@@ -194,7 +195,7 @@ show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adm1029_data *data = adm1029_update_device(dev); struct adm1029_data *data = adm1029_update_device(dev);
if (data->fan_div[attr->index] == 0) if ((data->fan_div[attr->index] & 0xC0) == 0)
return sprintf(buf, "0\n"); return sprintf(buf, "0\n");
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index])); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
} }
......
...@@ -99,39 +99,31 @@ static struct pmf_irq_client ams_shock_client = { ...@@ -99,39 +99,31 @@ static struct pmf_irq_client ams_shock_client = {
*/ */
static void ams_worker(struct work_struct *work) static void ams_worker(struct work_struct *work)
{ {
mutex_lock(&ams_info.lock); unsigned long flags;
u8 irqs_to_clear;
if (ams_info.has_device) {
unsigned long flags;
spin_lock_irqsave(&ams_info.irq_lock, flags); mutex_lock(&ams_info.lock);
if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { spin_lock_irqsave(&ams_info.irq_lock, flags);
if (verbose) irqs_to_clear = ams_info.worker_irqs;
printk(KERN_INFO "ams: freefall detected!\n");
ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
if (verbose)
printk(KERN_INFO "ams: freefall detected!\n");
/* we must call this with interrupts enabled */ ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
spin_unlock_irqrestore(&ams_info.irq_lock, flags); }
ams_info.clear_irq(AMS_IRQ_FREEFALL);
spin_lock_irqsave(&ams_info.irq_lock, flags);
}
if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
if (verbose) if (verbose)
printk(KERN_INFO "ams: shock detected!\n"); printk(KERN_INFO "ams: shock detected!\n");
ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
}
/* we must call this with interrupts enabled */ spin_unlock_irqrestore(&ams_info.irq_lock, flags);
spin_unlock_irqrestore(&ams_info.irq_lock, flags);
ams_info.clear_irq(AMS_IRQ_SHOCK);
spin_lock_irqsave(&ams_info.irq_lock, flags);
}
spin_unlock_irqrestore(&ams_info.irq_lock, flags); ams_info.clear_irq(irqs_to_clear);
}
mutex_unlock(&ams_info.lock); mutex_unlock(&ams_info.lock);
} }
...@@ -223,34 +215,28 @@ int __init ams_init(void) ...@@ -223,34 +215,28 @@ int __init ams_init(void)
void ams_exit(void) void ams_exit(void)
{ {
mutex_lock(&ams_info.lock); /* Remove input device */
ams_input_exit();
if (ams_info.has_device) {
/* Remove input device */
ams_input_exit();
/* Shut down implementation */ /* Remove attributes */
ams_info.exit(); device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
/* Flush interrupt worker
*
* We do this after ams_info.exit(), because an interrupt might
* have arrived before disabling them.
*/
flush_scheduled_work();
/* Remove attributes */ /* Shut down implementation */
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); ams_info.exit();
/* Remove device */ /* Flush interrupt worker
of_device_unregister(ams_info.of_dev); *
* We do this after ams_info.exit(), because an interrupt might
* have arrived before disabling them.
*/
flush_scheduled_work();
/* Remove handler */ /* Remove device */
pmf_unregister_irq_client(&ams_shock_client); of_device_unregister(ams_info.of_dev);
pmf_unregister_irq_client(&ams_freefall_client);
}
mutex_unlock(&ams_info.lock); /* Remove handler */
pmf_unregister_irq_client(&ams_shock_client);
pmf_unregister_irq_client(&ams_freefall_client);
} }
MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
......
...@@ -60,26 +60,34 @@ enum ams_i2c_cmd { ...@@ -60,26 +60,34 @@ enum ams_i2c_cmd {
AMS_CMD_START, AMS_CMD_START,
}; };
static int ams_i2c_attach(struct i2c_adapter *adapter); static int ams_i2c_probe(struct i2c_client *client,
static int ams_i2c_detach(struct i2c_adapter *adapter); const struct i2c_device_id *id);
static int ams_i2c_remove(struct i2c_client *client);
static const struct i2c_device_id ams_id[] = {
{ "ams", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ams_id);
static struct i2c_driver ams_i2c_driver = { static struct i2c_driver ams_i2c_driver = {
.driver = { .driver = {
.name = "ams", .name = "ams",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.attach_adapter = ams_i2c_attach, .probe = ams_i2c_probe,
.detach_adapter = ams_i2c_detach, .remove = ams_i2c_remove,
.id_table = ams_id,
}; };
static s32 ams_i2c_read(u8 reg) static s32 ams_i2c_read(u8 reg)
{ {
return i2c_smbus_read_byte_data(&ams_info.i2c_client, reg); return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
} }
static int ams_i2c_write(u8 reg, u8 value) static int ams_i2c_write(u8 reg, u8 value)
{ {
return i2c_smbus_write_byte_data(&ams_info.i2c_client, reg, value); return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
} }
static int ams_i2c_cmd(enum ams_i2c_cmd cmd) static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
...@@ -152,9 +160,9 @@ static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z) ...@@ -152,9 +160,9 @@ static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
*z = ams_i2c_read(AMS_DATAZ); *z = ams_i2c_read(AMS_DATAZ);
} }
static int ams_i2c_attach(struct i2c_adapter *adapter) static int ams_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{ {
unsigned long bus;
int vmaj, vmin; int vmaj, vmin;
int result; int result;
...@@ -162,17 +170,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) ...@@ -162,17 +170,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter)
if (unlikely(ams_info.has_device)) if (unlikely(ams_info.has_device))
return -ENODEV; return -ENODEV;
if (strncmp(adapter->name, "uni-n", 5)) ams_info.i2c_client = client;
return -ENODEV;
bus = simple_strtoul(adapter->name + 6, NULL, 10);
if (bus != ams_info.i2c_bus)
return -ENODEV;
ams_info.i2c_client.addr = ams_info.i2c_address;
ams_info.i2c_client.adapter = adapter;
ams_info.i2c_client.driver = &ams_i2c_driver;
strcpy(ams_info.i2c_client.name, "Apple Motion Sensor");
if (ams_i2c_cmd(AMS_CMD_RESET)) { if (ams_i2c_cmd(AMS_CMD_RESET)) {
printk(KERN_INFO "ams: Failed to reset the device\n"); printk(KERN_INFO "ams: Failed to reset the device\n");
...@@ -237,7 +235,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) ...@@ -237,7 +235,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter)
return 0; return 0;
} }
static int ams_i2c_detach(struct i2c_adapter *adapter) static int ams_i2c_remove(struct i2c_client *client)
{ {
if (ams_info.has_device) { if (ams_info.has_device) {
/* Disable interrupts */ /* Disable interrupts */
...@@ -261,11 +259,7 @@ static void ams_i2c_exit(void) ...@@ -261,11 +259,7 @@ static void ams_i2c_exit(void)
int __init ams_i2c_init(struct device_node *np) int __init ams_i2c_init(struct device_node *np)
{ {
char *tmp_bus;
int result; int result;
const u32 *prop;
mutex_lock(&ams_info.lock);
/* Set implementation stuff */ /* Set implementation stuff */
ams_info.of_node = np; ams_info.of_node = np;
...@@ -275,25 +269,7 @@ int __init ams_i2c_init(struct device_node *np) ...@@ -275,25 +269,7 @@ int __init ams_i2c_init(struct device_node *np)
ams_info.clear_irq = ams_i2c_clear_irq; ams_info.clear_irq = ams_i2c_clear_irq;
ams_info.bustype = BUS_I2C; ams_info.bustype = BUS_I2C;
/* look for bus either using "reg" or by path */
prop = of_get_property(ams_info.of_node, "reg", NULL);
if (!prop) {
result = -ENODEV;
goto exit;
}
tmp_bus = strstr(ams_info.of_node->full_name, "/i2c-bus@");
if (tmp_bus)
ams_info.i2c_bus = *(tmp_bus + 9) - '0';
else
ams_info.i2c_bus = ((*prop) >> 8) & 0x0f;
ams_info.i2c_address = ((*prop) & 0xff) >> 1;
result = i2c_add_driver(&ams_i2c_driver); result = i2c_add_driver(&ams_i2c_driver);
exit:
mutex_unlock(&ams_info.lock);
return result; return result;
} }
...@@ -20,13 +20,15 @@ ...@@ -20,13 +20,15 @@
#include "ams.h" #include "ams.h"
static unsigned int joystick; static unsigned int joystick;
module_param(joystick, bool, 0644); module_param(joystick, bool, S_IRUGO);
MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); MODULE_PARM_DESC(joystick, "Enable the input class device on module load");
static unsigned int invert; static unsigned int invert;
module_param(invert, bool, 0644); module_param(invert, bool, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
static DEFINE_MUTEX(ams_input_mutex);
static void ams_idev_poll(struct input_polled_dev *dev) static void ams_idev_poll(struct input_polled_dev *dev)
{ {
struct input_dev *idev = dev->input; struct input_dev *idev = dev->input;
...@@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev) ...@@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev)
} }
/* Call with ams_info.lock held! */ /* Call with ams_info.lock held! */
static void ams_input_enable(void) static int ams_input_enable(void)
{ {
struct input_dev *input; struct input_dev *input;
s8 x, y, z; s8 x, y, z;
int error;
if (ams_info.idev)
return;
ams_sensors(&x, &y, &z); ams_sensors(&x, &y, &z);
ams_info.xcalib = x; ams_info.xcalib = x;
...@@ -65,7 +65,7 @@ static void ams_input_enable(void) ...@@ -65,7 +65,7 @@ static void ams_input_enable(void)
ams_info.idev = input_allocate_polled_device(); ams_info.idev = input_allocate_polled_device();
if (!ams_info.idev) if (!ams_info.idev)
return; return -ENOMEM;
ams_info.idev->poll = ams_idev_poll; ams_info.idev->poll = ams_idev_poll;
ams_info.idev->poll_interval = 25; ams_info.idev->poll_interval = 25;
...@@ -84,14 +84,18 @@ static void ams_input_enable(void) ...@@ -84,14 +84,18 @@ static void ams_input_enable(void)
set_bit(EV_KEY, input->evbit); set_bit(EV_KEY, input->evbit);
set_bit(BTN_TOUCH, input->keybit); set_bit(BTN_TOUCH, input->keybit);
if (input_register_polled_device(ams_info.idev)) { error = input_register_polled_device(ams_info.idev);
if (error) {
input_free_polled_device(ams_info.idev); input_free_polled_device(ams_info.idev);
ams_info.idev = NULL; ams_info.idev = NULL;
return; return error;
} }
joystick = 1;
return 0;
} }
/* Call with ams_info.lock held! */
static void ams_input_disable(void) static void ams_input_disable(void)
{ {
if (ams_info.idev) { if (ams_info.idev) {
...@@ -99,6 +103,8 @@ static void ams_input_disable(void) ...@@ -99,6 +103,8 @@ static void ams_input_disable(void)
input_free_polled_device(ams_info.idev); input_free_polled_device(ams_info.idev);
ams_info.idev = NULL; ams_info.idev = NULL;
} }
joystick = 0;
} }
static ssize_t ams_input_show_joystick(struct device *dev, static ssize_t ams_input_show_joystick(struct device *dev,
...@@ -110,39 +116,42 @@ static ssize_t ams_input_show_joystick(struct device *dev, ...@@ -110,39 +116,42 @@ static ssize_t ams_input_show_joystick(struct device *dev,
static ssize_t ams_input_store_joystick(struct device *dev, static ssize_t ams_input_store_joystick(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count) struct device_attribute *attr, const char *buf, size_t count)
{ {
if (sscanf(buf, "%d\n", &joystick) != 1) unsigned long enable;
int error = 0;
if (strict_strtoul(buf, 0, &enable) || enable > 1)
return -EINVAL; return -EINVAL;
mutex_lock(&ams_info.lock); mutex_lock(&ams_input_mutex);
if (joystick) if (enable != joystick) {
ams_input_enable(); if (enable)
else error = ams_input_enable();
ams_input_disable(); else
ams_input_disable();
}
mutex_unlock(&ams_info.lock); mutex_unlock(&ams_input_mutex);
return count; return error ? error : count;
} }
static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR,
ams_input_show_joystick, ams_input_store_joystick); ams_input_show_joystick, ams_input_store_joystick);
/* Call with ams_info.lock held! */
int ams_input_init(void) int ams_input_init(void)
{ {
int result; if (joystick)
result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick);
if (!result && joystick)
ams_input_enable(); ams_input_enable();
return result;
return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick);
} }
/* Call with ams_info.lock held! */
void ams_input_exit(void) void ams_input_exit(void)
{ {
ams_input_disable();
device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick);
mutex_lock(&ams_input_mutex);
ams_input_disable();
mutex_unlock(&ams_input_mutex);
} }
...@@ -149,8 +149,6 @@ int __init ams_pmu_init(struct device_node *np) ...@@ -149,8 +149,6 @@ int __init ams_pmu_init(struct device_node *np)
const u32 *prop; const u32 *prop;
int result; int result;
mutex_lock(&ams_info.lock);
/* Set implementation stuff */ /* Set implementation stuff */
ams_info.of_node = np; ams_info.of_node = np;
ams_info.exit = ams_pmu_exit; ams_info.exit = ams_pmu_exit;
...@@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np) ...@@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np)
/* Get PMU command, should be 0x4e, but we can never know */ /* Get PMU command, should be 0x4e, but we can never know */
prop = of_get_property(ams_info.of_node, "reg", NULL); prop = of_get_property(ams_info.of_node, "reg", NULL);
if (!prop) { if (!prop)
result = -ENODEV; return -ENODEV;
goto exit;
}
ams_pmu_cmd = ((*prop) >> 8) & 0xff; ams_pmu_cmd = ((*prop) >> 8) & 0xff;
/* Disable interrupts */ /* Disable interrupts */
...@@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np) ...@@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np)
result = ams_sensor_attach(); result = ams_sensor_attach();
if (result < 0) if (result < 0)
goto exit; return result;
/* Set default values */ /* Set default values */
ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
...@@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np) ...@@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np)
printk(KERN_INFO "ams: Found PMU based motion sensor\n"); printk(KERN_INFO "ams: Found PMU based motion sensor\n");
result = 0; return 0;
exit:
mutex_unlock(&ams_info.lock);
return result;
} }
...@@ -46,9 +46,7 @@ struct ams { ...@@ -46,9 +46,7 @@ struct ams {
#ifdef CONFIG_SENSORS_AMS_I2C #ifdef CONFIG_SENSORS_AMS_I2C
/* I2C properties */ /* I2C properties */
int i2c_bus; struct i2c_client *i2c_client;
int i2c_address;
struct i2c_client i2c_client;
#endif #endif
/* Joystick emulation */ /* Joystick emulation */
......
...@@ -1118,3 +1118,10 @@ MODULE_LICENSE("GPL"); ...@@ -1118,3 +1118,10 @@ MODULE_LICENSE("GPL");
module_init(aem_init); module_init(aem_init);
module_exit(aem_exit); module_exit(aem_exit);
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
...@@ -608,3 +608,9 @@ MODULE_LICENSE("GPL"); ...@@ -608,3 +608,9 @@ MODULE_LICENSE("GPL");
module_init(ibmpex_init); module_init(ibmpex_init);
module_exit(ibmpex_exit); module_exit(ibmpex_exit);
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
...@@ -477,7 +477,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, ...@@ -477,7 +477,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
if (reg & (1 << nr)) if (reg & (1 << nr))
return sprintf(buf, "3\n"); /* thermal diode */ return sprintf(buf, "3\n"); /* thermal diode */
if (reg & (8 << nr)) if (reg & (8 << nr))
return sprintf(buf, "2\n"); /* thermistor */ return sprintf(buf, "4\n"); /* thermistor */
return sprintf(buf, "0\n"); /* disabled */ return sprintf(buf, "0\n"); /* disabled */
} }
static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, static ssize_t set_sensor(struct device *dev, struct device_attribute *attr,
...@@ -493,10 +493,15 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, ...@@ -493,10 +493,15 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr,
data->sensor &= ~(1 << nr); data->sensor &= ~(1 << nr);
data->sensor &= ~(8 << nr); data->sensor &= ~(8 << nr);
/* 3 = thermal diode; 2 = thermistor; 0 = disabled */ if (val == 2) { /* backwards compatibility */
dev_warn(dev, "Sensor type 2 is deprecated, please use 4 "
"instead\n");
val = 4;
}
/* 3 = thermal diode; 4 = thermistor; 0 = disabled */
if (val == 3) if (val == 3)
data->sensor |= 1 << nr; data->sensor |= 1 << nr;
else if (val == 2) else if (val == 4)
data->sensor |= 8 << nr; data->sensor |= 8 << nr;
else if (val != 0) { else if (val != 0) {
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
......
...@@ -114,25 +114,16 @@ static inline int TEMP_FROM_REG(s8 val) ...@@ -114,25 +114,16 @@ static inline int TEMP_FROM_REG(s8 val)
#define DIV_FROM_REG(val) (1 << (val)) #define DIV_FROM_REG(val) (1 << (val))
/* There are some complications in a module like this. First off, LM78 chips
may be both present on the SMBus and the ISA bus, and we have to handle
those cases separately at some places. Second, there might be several
LM78 chips available (well, actually, that is probably never done; but
it is a clean illustration of how to handle a case like that). Finally,
a specific chip may be attached to *both* ISA and SMBus, and we would
not like to detect it double. Fortunately, in the case of the LM78 at
least, a register tells us what SMBus address we are on, so that helps
a bit - except if there could be more than one SMBus. Groan. No solution
for this yet. */
/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
the driver field to differentiate between I2C and ISA chips. */
struct lm78_data { struct lm78_data {
struct i2c_client client; struct i2c_client *client;
struct device *hwmon_dev; struct device *hwmon_dev;
struct mutex lock; struct mutex lock;
enum chips type; enum chips type;
/* For ISA device only */
const char *name;
int isa_addr;
struct mutex update_lock; struct mutex update_lock;
char valid; /* !=0 if following fields are valid */ char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */ unsigned long last_updated; /* In jiffies */
...@@ -151,9 +142,11 @@ struct lm78_data { ...@@ -151,9 +142,11 @@ struct lm78_data {
}; };
static int lm78_attach_adapter(struct i2c_adapter *adapter); static int lm78_i2c_detect(struct i2c_client *client, int kind,
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); struct i2c_board_info *info);
static int lm78_detach_client(struct i2c_client *client); static int lm78_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int lm78_i2c_remove(struct i2c_client *client);
static int __devinit lm78_isa_probe(struct platform_device *pdev); static int __devinit lm78_isa_probe(struct platform_device *pdev);
static int __devexit lm78_isa_remove(struct platform_device *pdev); static int __devexit lm78_isa_remove(struct platform_device *pdev);
...@@ -164,12 +157,23 @@ static struct lm78_data *lm78_update_device(struct device *dev); ...@@ -164,12 +157,23 @@ static struct lm78_data *lm78_update_device(struct device *dev);
static void lm78_init_device(struct lm78_data *data); static void lm78_init_device(struct lm78_data *data);
static const struct i2c_device_id lm78_i2c_id[] = {
{ "lm78", lm78 },
{ "lm79", lm79 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
static struct i2c_driver lm78_driver = { static struct i2c_driver lm78_driver = {
.class = I2C_CLASS_HWMON,
.driver = { .driver = {
.name = "lm78", .name = "lm78",
}, },
.attach_adapter = lm78_attach_adapter, .probe = lm78_i2c_probe,
.detach_client = lm78_detach_client, .remove = lm78_i2c_remove,
.id_table = lm78_i2c_id,
.detect = lm78_i2c_detect,
.address_data = &addr_data,
}; };
static struct platform_driver lm78_isa_driver = { static struct platform_driver lm78_isa_driver = {
...@@ -454,17 +458,6 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); ...@@ -454,17 +458,6 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
/* This function is called when:
* lm78_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and lm78_driver is still present) */
static int lm78_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_probe(adapter, &addr_data, lm78_detect);
}
static struct attribute *lm78_attributes[] = { static struct attribute *lm78_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in0_min.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr,
...@@ -527,54 +520,77 @@ static ssize_t show_name(struct device *dev, struct device_attribute ...@@ -527,54 +520,77 @@ static ssize_t show_name(struct device *dev, struct device_attribute
{ {
struct lm78_data *data = dev_get_drvdata(dev); struct lm78_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->client.name); return sprintf(buf, "%s\n", data->name);
} }
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
/* This function is called by i2c_probe */ /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
{ {
int i, err; struct lm78_data *isa;
struct i2c_client *new_client; int i;
struct lm78_data *data;
const char *client_name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { if (!pdev) /* No ISA chip */
err = -ENODEV; return 0;
goto ERROR1; isa = platform_get_drvdata(pdev);
if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr)
return 0; /* Address doesn't match */
if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe))
return 0; /* Chip type doesn't match */
/* We compare all the limit registers, the config register and the
* interrupt mask registers */
for (i = 0x2b; i <= 0x3d; i++) {
if (lm78_read_value(isa, i) !=
i2c_smbus_read_byte_data(client, i))
return 0;
} }
if (lm78_read_value(isa, LM78_REG_CONFIG) !=
i2c_smbus_read_byte_data(client, LM78_REG_CONFIG))
return 0;
for (i = 0x43; i <= 0x46; i++) {
if (lm78_read_value(isa, i) !=
i2c_smbus_read_byte_data(client, i))
return 0;
}
return 1;
}
/* OK. For now, we presume we have a valid client. We now create the static int lm78_i2c_detect(struct i2c_client *client, int kind,
client structure, even though we cannot fill it completely yet. struct i2c_board_info *info)
But it allows us to access lm78_{read,write}_value. */ {
int i;
struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL;
const char *client_name;
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
err = -ENOMEM; return -ENODEV;
goto ERROR1;
}
new_client = &data->client; /* We block updates of the ISA device to minimize the risk of
i2c_set_clientdata(new_client, data); concurrent access to the same LM78 chip through different
new_client->addr = address; interfaces. */
new_client->adapter = adapter; if (isa)
new_client->driver = &lm78_driver; mutex_lock(&isa->update_lock);
/* Now, we do the remaining detection. */
if (kind < 0) { if (kind < 0) {
if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) { if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
err = -ENODEV; || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR)
goto ERROR2; != address)
} goto err_nodev;
if (lm78_read_value(data, LM78_REG_I2C_ADDR) !=
address) { /* Explicitly prevent the misdetection of Winbond chips */
err = -ENODEV; i = i2c_smbus_read_byte_data(client, 0x4f);
goto ERROR2; if (i == 0xa3 || i == 0x5c)
} goto err_nodev;
} }
/* Determine the chip type. */ /* Determine the chip type. */
if (kind <= 0) { if (kind <= 0) {
i = lm78_read_value(data, LM78_REG_CHIPID); i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
if (i == 0x00 || i == 0x20 /* LM78 */ if (i == 0x00 || i == 0x20 /* LM78 */
|| i == 0x40) /* LM78-J */ || i == 0x40) /* LM78-J */
kind = lm78; kind = lm78;
...@@ -586,33 +602,59 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -586,33 +602,59 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
"parameter for unknown chip at " "parameter for unknown chip at "
"adapter %d, address 0x%02x\n", "adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address); i2c_adapter_id(adapter), address);
err = -ENODEV; goto err_nodev;
goto ERROR2; }
if (lm78_alias_detect(client, i)) {
dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
"be the same as ISA device\n", address);
goto err_nodev;
} }
} }
if (kind == lm78) { if (isa)
client_name = "lm78"; mutex_unlock(&isa->update_lock);
} else if (kind == lm79) {
switch (kind) {
case lm79:
client_name = "lm79"; client_name = "lm79";
break;
default:
client_name = "lm78";
} }
strlcpy(info->type, client_name, I2C_NAME_SIZE);
return 0;
err_nodev:
if (isa)
mutex_unlock(&isa->update_lock);
return -ENODEV;
}
static int lm78_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lm78_data *data;
int err;
/* Fill in the remaining client fields and put into the global list */ data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL);
strlcpy(new_client->name, client_name, I2C_NAME_SIZE); if (!data)
data->type = kind; return -ENOMEM;
/* Tell the I2C layer a new client has arrived */ i2c_set_clientdata(client, data);
if ((err = i2c_attach_client(new_client))) data->client = client;
goto ERROR2; data->type = id->driver_data;
/* Initialize the LM78 chip */ /* Initialize the LM78 chip */
lm78_init_device(data); lm78_init_device(data);
/* Register sysfs hooks */ /* Register sysfs hooks */
if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group))) err = sysfs_create_group(&client->dev.kobj, &lm78_group);
if (err)
goto ERROR3; goto ERROR3;
data->hwmon_dev = hwmon_device_register(&new_client->dev); data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) { if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev); err = PTR_ERR(data->hwmon_dev);
goto ERROR4; goto ERROR4;
...@@ -621,26 +663,18 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -621,26 +663,18 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
return 0; return 0;
ERROR4: ERROR4:
sysfs_remove_group(&new_client->dev.kobj, &lm78_group); sysfs_remove_group(&client->dev.kobj, &lm78_group);
ERROR3: ERROR3:
i2c_detach_client(new_client);
ERROR2:
kfree(data); kfree(data);
ERROR1:
return err; return err;
} }
static int lm78_detach_client(struct i2c_client *client) static int lm78_i2c_remove(struct i2c_client *client)
{ {
struct lm78_data *data = i2c_get_clientdata(client); struct lm78_data *data = i2c_get_clientdata(client);
int err;
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm78_group); sysfs_remove_group(&client->dev.kobj, &lm78_group);
if ((err = i2c_detach_client(client)))
return err;
kfree(data); kfree(data);
return 0; return 0;
...@@ -651,11 +685,10 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) ...@@ -651,11 +685,10 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
int err; int err;
struct lm78_data *data; struct lm78_data *data;
struct resource *res; struct resource *res;
const char *name;
/* Reserve the ISA region */ /* Reserve the ISA region */
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start, LM78_EXTENT, "lm78")) { if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) {
err = -EBUSY; err = -EBUSY;
goto exit; goto exit;
} }
...@@ -665,18 +698,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) ...@@ -665,18 +698,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
goto exit_release_region; goto exit_release_region;
} }
mutex_init(&data->lock); mutex_init(&data->lock);
data->client.addr = res->start; data->isa_addr = res->start;
i2c_set_clientdata(&data->client, data);
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
data->type = lm79; data->type = lm79;
name = "lm79"; data->name = "lm79";
} else { } else {
data->type = lm78; data->type = lm78;
name = "lm78"; data->name = "lm78";
} }
strlcpy(data->client.name, name, I2C_NAME_SIZE);
/* Initialize the LM78 chip */ /* Initialize the LM78 chip */
lm78_init_device(data); lm78_init_device(data);
...@@ -699,7 +730,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) ...@@ -699,7 +730,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
device_remove_file(&pdev->dev, &dev_attr_name); device_remove_file(&pdev->dev, &dev_attr_name);
kfree(data); kfree(data);
exit_release_region: exit_release_region:
release_region(res->start, LM78_EXTENT); release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
exit: exit:
return err; return err;
} }
...@@ -707,13 +738,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) ...@@ -707,13 +738,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
static int __devexit lm78_isa_remove(struct platform_device *pdev) static int __devexit lm78_isa_remove(struct platform_device *pdev)
{ {
struct lm78_data *data = platform_get_drvdata(pdev); struct lm78_data *data = platform_get_drvdata(pdev);
struct resource *res;
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &lm78_group); sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
device_remove_file(&pdev->dev, &dev_attr_name); device_remove_file(&pdev->dev, &dev_attr_name);
release_region(data->client.addr, LM78_EXTENT);
kfree(data); kfree(data);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
return 0; return 0;
} }
...@@ -724,13 +758,13 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev) ...@@ -724,13 +758,13 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev)
would slow down the LM78 access and should not be necessary. */ would slow down the LM78 access and should not be necessary. */
static int lm78_read_value(struct lm78_data *data, u8 reg) static int lm78_read_value(struct lm78_data *data, u8 reg)
{ {
struct i2c_client *client = &data->client; struct i2c_client *client = data->client;
if (!client->driver) { /* ISA device */ if (!client) { /* ISA device */
int res; int res;
mutex_lock(&data->lock); mutex_lock(&data->lock);
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
res = inb_p(client->addr + LM78_DATA_REG_OFFSET); res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET);
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
return res; return res;
} else } else
...@@ -746,12 +780,12 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) ...@@ -746,12 +780,12 @@ static int lm78_read_value(struct lm78_data *data, u8 reg)
nowhere else be necessary! */ nowhere else be necessary! */
static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
{ {
struct i2c_client *client = &data->client; struct i2c_client *client = data->client;
if (!client->driver) { /* ISA device */ if (!client) { /* ISA device */
mutex_lock(&data->lock); mutex_lock(&data->lock);
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
outb_p(value, client->addr + LM78_DATA_REG_OFFSET); outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET);
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
return 0; return 0;
} else } else
...@@ -837,8 +871,17 @@ static int __init lm78_isa_found(unsigned short address) ...@@ -837,8 +871,17 @@ static int __init lm78_isa_found(unsigned short address)
{ {
int val, save, found = 0; int val, save, found = 0;
if (!request_region(address, LM78_EXTENT, "lm78")) /* We have to request the region in two parts because some
boards declare base+4 to base+7 as a PNP device */
if (!request_region(address, 4, "lm78")) {
pr_debug("lm78: Failed to request low part of region\n");
return 0; return 0;
}
if (!request_region(address + 4, 4, "lm78")) {
pr_debug("lm78: Failed to request high part of region\n");
release_region(address, 4);
return 0;
}
#define REALLY_SLOW_IO #define REALLY_SLOW_IO
/* We need the timeouts for at least some LM78-like /* We need the timeouts for at least some LM78-like
...@@ -901,7 +944,8 @@ static int __init lm78_isa_found(unsigned short address) ...@@ -901,7 +944,8 @@ static int __init lm78_isa_found(unsigned short address)
val & 0x80 ? "LM79" : "LM78", (int)address); val & 0x80 ? "LM79" : "LM78", (int)address);
release: release:
release_region(address, LM78_EXTENT); release_region(address + 4, 4);
release_region(address, 4);
return found; return found;
} }
...@@ -949,14 +993,12 @@ static int __init sm_lm78_init(void) ...@@ -949,14 +993,12 @@ static int __init sm_lm78_init(void)
{ {
int res; int res;
res = i2c_add_driver(&lm78_driver); /* We register the ISA device first, so that we can skip the
if (res) * registration of an I2C interface to the same device. */
goto exit;
if (lm78_isa_found(isa_address)) { if (lm78_isa_found(isa_address)) {
res = platform_driver_register(&lm78_isa_driver); res = platform_driver_register(&lm78_isa_driver);
if (res) if (res)
goto exit_unreg_i2c_driver; goto exit;
/* Sets global pdev as a side effect */ /* Sets global pdev as a side effect */
res = lm78_isa_device_add(isa_address); res = lm78_isa_device_add(isa_address);
...@@ -964,12 +1006,16 @@ static int __init sm_lm78_init(void) ...@@ -964,12 +1006,16 @@ static int __init sm_lm78_init(void)
goto exit_unreg_isa_driver; goto exit_unreg_isa_driver;
} }
res = i2c_add_driver(&lm78_driver);
if (res)
goto exit_unreg_isa_device;
return 0; return 0;
exit_unreg_isa_device:
platform_device_unregister(pdev);
exit_unreg_isa_driver: exit_unreg_isa_driver:
platform_driver_unregister(&lm78_isa_driver); platform_driver_unregister(&lm78_isa_driver);
exit_unreg_i2c_driver:
i2c_del_driver(&lm78_driver);
exit: exit:
return res; return res;
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com> Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
Chip details at <http://www.national.com/ds/LM/LM85.pdf> Chip details at <http://www.national.com/ds/LM/LM85.pdf>
...@@ -173,40 +174,39 @@ static int RANGE_TO_REG(int range) ...@@ -173,40 +174,39 @@ static int RANGE_TO_REG(int range)
{ {
int i; int i;
if (range >= lm85_range_map[15])
return 15;
/* Find the closest match */ /* Find the closest match */
for (i = 14; i >= 0; --i) { for (i = 0; i < 15; ++i) {
if (range >= lm85_range_map[i]) { if (range <= (lm85_range_map[i] + lm85_range_map[i + 1]) / 2)
if ((lm85_range_map[i + 1] - range) < break;
(range - lm85_range_map[i]))
return i + 1;
return i;
}
} }
return 0; return i;
} }
#define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f] #define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]
/* These are the PWM frequency encodings */ /* These are the PWM frequency encodings */
static const int lm85_freq_map[] = { /* .1 Hz */ static const int lm85_freq_map[8] = { /* 1 Hz */
100, 150, 230, 300, 380, 470, 620, 940 10, 15, 23, 30, 38, 47, 61, 94
};
static const int adm1027_freq_map[8] = { /* 1 Hz */
11, 15, 22, 29, 35, 44, 59, 88
}; };
static int FREQ_TO_REG(int freq) static int FREQ_TO_REG(const int *map, int freq)
{ {
int i; int i;
if (freq >= lm85_freq_map[7]) /* Find the closest match */
return 7;
for (i = 0; i < 7; ++i) for (i = 0; i < 7; ++i)
if (freq <= lm85_freq_map[i]) if (freq <= (map[i] + map[i + 1]) / 2)
break; break;
return i; return i;
} }
#define FREQ_FROM_REG(val) lm85_freq_map[(val) & 0x07]
static int FREQ_FROM_REG(const int *map, u8 reg)
{
return map[reg & 0x07];
}
/* Since we can't use strings, I'm abusing these numbers /* Since we can't use strings, I'm abusing these numbers
* to stand in for the following meanings: * to stand in for the following meanings:
...@@ -275,7 +275,6 @@ struct lm85_zone { ...@@ -275,7 +275,6 @@ struct lm85_zone {
struct lm85_autofan { struct lm85_autofan {
u8 config; /* Register value */ u8 config; /* Register value */
u8 freq; /* PWM frequency, encoded */
u8 min_pwm; /* Minimum PWM value, encoded */ u8 min_pwm; /* Minimum PWM value, encoded */
u8 min_off; /* Min PWM or OFF below "limit", flag */ u8 min_off; /* Min PWM or OFF below "limit", flag */
}; };
...@@ -283,8 +282,8 @@ struct lm85_autofan { ...@@ -283,8 +282,8 @@ struct lm85_autofan {
/* For each registered chip, we need to keep some data in memory. /* For each registered chip, we need to keep some data in memory.
The structure is dynamically allocated. */ The structure is dynamically allocated. */
struct lm85_data { struct lm85_data {
struct i2c_client client;
struct device *hwmon_dev; struct device *hwmon_dev;
const int *freq_map;
enum chips type; enum chips type;
struct mutex update_lock; struct mutex update_lock;
...@@ -301,6 +300,7 @@ struct lm85_data { ...@@ -301,6 +300,7 @@ struct lm85_data {
u16 fan[4]; /* Register value */ u16 fan[4]; /* Register value */
u16 fan_min[4]; /* Register value */ u16 fan_min[4]; /* Register value */
u8 pwm[3]; /* Register value */ u8 pwm[3]; /* Register value */
u8 pwm_freq[3]; /* Register encoding */
u8 temp_ext[3]; /* Decoded values */ u8 temp_ext[3]; /* Decoded values */
u8 in_ext[8]; /* Decoded values */ u8 in_ext[8]; /* Decoded values */
u8 vid; /* Register value */ u8 vid; /* Register value */
...@@ -310,22 +310,40 @@ struct lm85_data { ...@@ -310,22 +310,40 @@ struct lm85_data {
struct lm85_zone zone[3]; struct lm85_zone zone[3];
}; };
static int lm85_attach_adapter(struct i2c_adapter *adapter); static int lm85_detect(struct i2c_client *client, int kind,
static int lm85_detect(struct i2c_adapter *adapter, int address, struct i2c_board_info *info);
int kind); static int lm85_probe(struct i2c_client *client,
static int lm85_detach_client(struct i2c_client *client); const struct i2c_device_id *id);
static int lm85_remove(struct i2c_client *client);
static int lm85_read_value(struct i2c_client *client, u8 reg); static int lm85_read_value(struct i2c_client *client, u8 reg);
static void lm85_write_value(struct i2c_client *client, u8 reg, int value); static void lm85_write_value(struct i2c_client *client, u8 reg, int value);
static struct lm85_data *lm85_update_device(struct device *dev); static struct lm85_data *lm85_update_device(struct device *dev);
static const struct i2c_device_id lm85_id[] = {
{ "adm1027", adm1027 },
{ "adt7463", adt7463 },
{ "lm85", any_chip },
{ "lm85b", lm85b },
{ "lm85c", lm85c },
{ "emc6d100", emc6d100 },
{ "emc6d101", emc6d100 },
{ "emc6d102", emc6d102 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm85_id);
static struct i2c_driver lm85_driver = { static struct i2c_driver lm85_driver = {
.class = I2C_CLASS_HWMON,
.driver = { .driver = {
.name = "lm85", .name = "lm85",
}, },
.attach_adapter = lm85_attach_adapter, .probe = lm85_probe,
.detach_client = lm85_detach_client, .remove = lm85_remove,
.id_table = lm85_id,
.detect = lm85_detect,
.address_data = &addr_data,
}; };
...@@ -528,11 +546,39 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute ...@@ -528,11 +546,39 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
return count; return count;
} }
static ssize_t show_pwm_freq(struct device *dev,
struct device_attribute *attr, char *buf)
{
int nr = to_sensor_dev_attr(attr)->index;
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map,
data->pwm_freq[nr]));
}
static ssize_t set_pwm_freq(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(attr)->index;
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
(data->zone[nr].range << 4)
| data->pwm_freq[nr]);
mutex_unlock(&data->update_lock);
return count;
}
#define show_pwm_reg(offset) \ #define show_pwm_reg(offset) \
static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
show_pwm, set_pwm, offset - 1); \ show_pwm, set_pwm, offset - 1); \
static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
show_pwm_enable, set_pwm_enable, offset - 1) show_pwm_enable, set_pwm_enable, offset - 1); \
static SENSOR_DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR, \
show_pwm_freq, set_pwm_freq, offset - 1)
show_pwm_reg(1); show_pwm_reg(1);
show_pwm_reg(2); show_pwm_reg(2);
...@@ -761,31 +807,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, ...@@ -761,31 +807,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev,
return count; return count;
} }
static ssize_t show_pwm_auto_pwm_freq(struct device *dev,
struct device_attribute *attr, char *buf)
{
int nr = to_sensor_dev_attr(attr)->index;
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf, "%d\n", FREQ_FROM_REG(data->autofan[nr].freq));
}
static ssize_t set_pwm_auto_pwm_freq(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(attr)->index;
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->autofan[nr].freq = FREQ_TO_REG(val);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
(data->zone[nr].range << 4)
| data->autofan[nr].freq);
mutex_unlock(&data->update_lock);
return count;
}
#define pwm_auto(offset) \ #define pwm_auto(offset) \
static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \
S_IRUGO | S_IWUSR, show_pwm_auto_channels, \ S_IRUGO | S_IWUSR, show_pwm_auto_channels, \
...@@ -795,10 +816,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \ ...@@ -795,10 +816,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \
set_pwm_auto_pwm_min, offset - 1); \ set_pwm_auto_pwm_min, offset - 1); \
static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \
S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \ S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \
set_pwm_auto_pwm_minctl, offset - 1); \ set_pwm_auto_pwm_minctl, offset - 1)
static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_freq, \
S_IRUGO | S_IWUSR, show_pwm_auto_pwm_freq, \
set_pwm_auto_pwm_freq, offset - 1);
pwm_auto(1); pwm_auto(1);
pwm_auto(2); pwm_auto(2);
...@@ -867,7 +885,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev, ...@@ -867,7 +885,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev,
TEMP_FROM_REG(data->zone[nr].limit)); TEMP_FROM_REG(data->zone[nr].limit));
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4) ((data->zone[nr].range & 0x0f) << 4)
| (data->autofan[nr].freq & 0x07)); | (data->pwm_freq[nr] & 0x07));
/* Update temp_auto_hyst and temp_auto_off */ /* Update temp_auto_hyst and temp_auto_off */
data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG(
...@@ -910,7 +928,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev, ...@@ -910,7 +928,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev,
val - min); val - min);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4) ((data->zone[nr].range & 0x0f) << 4)
| (data->autofan[nr].freq & 0x07)); | (data->pwm_freq[nr] & 0x07));
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -957,13 +975,6 @@ temp_auto(1); ...@@ -957,13 +975,6 @@ temp_auto(1);
temp_auto(2); temp_auto(2);
temp_auto(3); temp_auto(3);
static int lm85_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_probe(adapter, &addr_data, lm85_detect);
}
static struct attribute *lm85_attributes[] = { static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr,
...@@ -984,6 +995,9 @@ static struct attribute *lm85_attributes[] = { ...@@ -984,6 +995,9 @@ static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr,
&sensor_dev_attr_pwm3_enable.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr,
&sensor_dev_attr_pwm1_freq.dev_attr.attr,
&sensor_dev_attr_pwm2_freq.dev_attr.attr,
&sensor_dev_attr_pwm3_freq.dev_attr.attr,
&sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr,
...@@ -1026,9 +1040,6 @@ static struct attribute *lm85_attributes[] = { ...@@ -1026,9 +1040,6 @@ static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_pwm_freq.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_freq.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_freq.dev_attr.attr,
&sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr, &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr, &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr,
...@@ -1103,109 +1114,74 @@ static void lm85_init_client(struct i2c_client *client) ...@@ -1103,109 +1114,74 @@ static void lm85_init_client(struct i2c_client *client)
dev_warn(&client->dev, "Device is not ready\n"); dev_warn(&client->dev, "Device is not ready\n");
} }
static int lm85_detect(struct i2c_adapter *adapter, int address, /* Return 0 if detection is successful, -ENODEV otherwise */
int kind) static int lm85_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{ {
int company, verstep; struct i2c_adapter *adapter = client->adapter;
struct i2c_client *client; int address = client->addr;
struct lm85_data *data;
int err = 0;
const char *type_name; const char *type_name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
/* We need to be able to do byte I/O */ /* We need to be able to do byte I/O */
goto ERROR0; return -ENODEV;
} }
/* OK. For now, we presume we have a valid client. We now create the /* If auto-detecting, determine the chip type */
client structure, even though we cannot fill it completely yet. if (kind < 0) {
But it allows us to access lm85_{read,write}_value. */ int company = lm85_read_value(client, LM85_REG_COMPANY);
int verstep = lm85_read_value(client, LM85_REG_VERSTEP);
if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) { dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
err = -ENOMEM; "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
goto ERROR0; address, company, verstep);
}
client = &data->client; /* All supported chips have the version in common */
i2c_set_clientdata(client, data); if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) {
client->addr = address; dev_dbg(&adapter->dev, "Autodetection failed: "
client->adapter = adapter; "unsupported version\n");
client->driver = &lm85_driver; return -ENODEV;
}
/* Now, we do the remaining detection. */ kind = any_chip;
company = lm85_read_value(client, LM85_REG_COMPANY); /* Now, refine the detection */
verstep = lm85_read_value(client, LM85_REG_VERSTEP); if (company == LM85_COMPANY_NATIONAL) {
switch (verstep) {
dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" case LM85_VERSTEP_LM85C:
" COMPANY: 0x%02x and VERSTEP: 0x%02x\n", kind = lm85c;
i2c_adapter_id(client->adapter), client->addr, break;
company, verstep); case LM85_VERSTEP_LM85B:
kind = lm85b;
/* If auto-detecting, Determine the chip type. */ break;
if (kind <= 0) { }
dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x ...\n", } else if (company == LM85_COMPANY_ANALOG_DEV) {
i2c_adapter_id(adapter), address); switch (verstep) {
if (company == LM85_COMPANY_NATIONAL case LM85_VERSTEP_ADM1027:
&& verstep == LM85_VERSTEP_LM85C) { kind = adm1027;
kind = lm85c; break;
} else if (company == LM85_COMPANY_NATIONAL case LM85_VERSTEP_ADT7463:
&& verstep == LM85_VERSTEP_LM85B) { case LM85_VERSTEP_ADT7463C:
kind = lm85b; kind = adt7463;
} else if (company == LM85_COMPANY_NATIONAL break;
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { }
dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" } else if (company == LM85_COMPANY_SMSC) {
" Defaulting to LM85.\n", verstep); switch (verstep) {
kind = any_chip; case LM85_VERSTEP_EMC6D100_A0:
} else if (company == LM85_COMPANY_ANALOG_DEV case LM85_VERSTEP_EMC6D100_A1:
&& verstep == LM85_VERSTEP_ADM1027) { /* Note: we can't tell a '100 from a '101 */
kind = adm1027; kind = emc6d100;
} else if (company == LM85_COMPANY_ANALOG_DEV break;
&& (verstep == LM85_VERSTEP_ADT7463 case LM85_VERSTEP_EMC6D102:
|| verstep == LM85_VERSTEP_ADT7463C)) { kind = emc6d102;
kind = adt7463; break;
} else if (company == LM85_COMPANY_ANALOG_DEV
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x"
" Defaulting to Generic LM85.\n", verstep);
kind = any_chip;
} else if (company == LM85_COMPANY_SMSC
&& (verstep == LM85_VERSTEP_EMC6D100_A0
|| verstep == LM85_VERSTEP_EMC6D100_A1)) {
/* Unfortunately, we can't tell a '100 from a '101
* from the registers. Since a '101 is a '100
* in a package with fewer pins and therefore no
* 3.3V, 1.5V or 1.8V inputs, perhaps if those
* inputs read 0, then it's a '101.
*/
kind = emc6d100;
} else if (company == LM85_COMPANY_SMSC
&& verstep == LM85_VERSTEP_EMC6D102) {
kind = emc6d102;
} else if (company == LM85_COMPANY_SMSC
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
dev_err(&adapter->dev, "lm85: Detected SMSC chip\n");
dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x"
" Defaulting to Generic LM85.\n", verstep);
kind = any_chip;
} else if (kind == any_chip
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n");
/* Leave kind as "any_chip" */
} else {
dev_dbg(&adapter->dev, "Autodetection failed\n");
/* Not an LM85... */
if (kind == any_chip) { /* User used force=x,y */
dev_err(&adapter->dev, "Generic LM85 Version 6 not"
" found at %d,0x%02x. Try force_lm85c.\n",
i2c_adapter_id(adapter), address);
} }
err = 0; } else {
goto ERROR1; dev_dbg(&adapter->dev, "Autodetection failed: "
"unknown vendor\n");
return -ENODEV;
} }
} }
/* Fill in the chip specific driver values */
switch (kind) { switch (kind) {
case lm85b: case lm85b:
type_name = "lm85b"; type_name = "lm85b";
...@@ -1228,16 +1204,36 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, ...@@ -1228,16 +1204,36 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
default: default:
type_name = "lm85"; type_name = "lm85";
} }
strlcpy(client->name, type_name, I2C_NAME_SIZE); strlcpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
}
/* Fill in the remaining client fields */ static int lm85_probe(struct i2c_client *client,
data->type = kind; const struct i2c_device_id *id)
{
struct lm85_data *data;
int err;
data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
data->type = id->driver_data;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
/* Tell the I2C layer a new client has arrived */ /* Fill in the chip specific driver values */
err = i2c_attach_client(client); switch (data->type) {
if (err) case adm1027:
goto ERROR1; case adt7463:
case emc6d100:
case emc6d102:
data->freq_map = adm1027_freq_map;
break;
default:
data->freq_map = lm85_freq_map;
}
/* Set the VRM version */ /* Set the VRM version */
data->vrm = vid_which_vrm(); data->vrm = vid_which_vrm();
...@@ -1248,45 +1244,42 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, ...@@ -1248,45 +1244,42 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
/* Register sysfs hooks */ /* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &lm85_group); err = sysfs_create_group(&client->dev.kobj, &lm85_group);
if (err) if (err)
goto ERROR2; goto err_kfree;
/* The ADT7463 has an optional VRM 10 mode where pin 21 is used /* The ADT7463 has an optional VRM 10 mode where pin 21 is used
as a sixth digital VID input rather than an analog input. */ as a sixth digital VID input rather than an analog input. */
data->vid = lm85_read_value(client, LM85_REG_VID); data->vid = lm85_read_value(client, LM85_REG_VID);
if (!(kind == adt7463 && (data->vid & 0x80))) if (!(data->type == adt7463 && (data->vid & 0x80)))
if ((err = sysfs_create_group(&client->dev.kobj, if ((err = sysfs_create_group(&client->dev.kobj,
&lm85_group_in4))) &lm85_group_in4)))
goto ERROR3; goto err_remove_files;
/* The EMC6D100 has 3 additional voltage inputs */ /* The EMC6D100 has 3 additional voltage inputs */
if (kind == emc6d100) if (data->type == emc6d100)
if ((err = sysfs_create_group(&client->dev.kobj, if ((err = sysfs_create_group(&client->dev.kobj,
&lm85_group_in567))) &lm85_group_in567)))
goto ERROR3; goto err_remove_files;
data->hwmon_dev = hwmon_device_register(&client->dev); data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) { if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev); err = PTR_ERR(data->hwmon_dev);
goto ERROR3; goto err_remove_files;
} }
return 0; return 0;
/* Error out and cleanup code */ /* Error out and cleanup code */
ERROR3: err_remove_files:
sysfs_remove_group(&client->dev.kobj, &lm85_group); sysfs_remove_group(&client->dev.kobj, &lm85_group);
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (kind == emc6d100) if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
ERROR2: err_kfree:
i2c_detach_client(client);
ERROR1:
kfree(data); kfree(data);
ERROR0:
return err; return err;
} }
static int lm85_detach_client(struct i2c_client *client) static int lm85_remove(struct i2c_client *client)
{ {
struct lm85_data *data = i2c_get_clientdata(client); struct lm85_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
...@@ -1294,7 +1287,6 @@ static int lm85_detach_client(struct i2c_client *client) ...@@ -1294,7 +1287,6 @@ static int lm85_detach_client(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (data->type == emc6d100) if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
i2c_detach_client(client);
kfree(data); kfree(data);
return 0; return 0;
} }
...@@ -1481,7 +1473,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) ...@@ -1481,7 +1473,7 @@ static struct lm85_data *lm85_update_device(struct device *dev)
data->autofan[i].config = data->autofan[i].config =
lm85_read_value(client, LM85_REG_AFAN_CONFIG(i)); lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i)); val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
data->autofan[i].freq = val & 0x07; data->pwm_freq[i] = val & 0x07;
data->zone[i].range = val >> 4; data->zone[i].range = val >> 4;
data->autofan[i].min_pwm = data->autofan[i].min_pwm =
lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
......
...@@ -21,11 +21,10 @@ ...@@ -21,11 +21,10 @@
* http://www.national.com/pf/LM/LM87.html * http://www.national.com/pf/LM/LM87.html
* *
* Some functions share pins, so not all functions are available at the same * Some functions share pins, so not all functions are available at the same
* time. Which are depends on the hardware setup. This driver assumes that * time. Which are depends on the hardware setup. This driver normally
* the BIOS configured the chip correctly. In that respect, it differs from * assumes that firmware configured the chip correctly. Where this is not
* the original driver (from lm_sensors for Linux 2.4), which would force the * the case, platform code must set the I2C client's platform_data to point
* LM87 to an arbitrary, compile-time chosen mode, regardless of the actual * to a u8 value to be written to the channel register.
* chipset wiring.
* For reference, here is the list of exclusive functions: * For reference, here is the list of exclusive functions:
* - in0+in5 (default) or temp3 * - in0+in5 (default) or temp3
* - fan1 (default) or in6 * - fan1 (default) or in6
...@@ -199,6 +198,7 @@ struct lm87_data { ...@@ -199,6 +198,7 @@ struct lm87_data {
unsigned long last_updated; /* In jiffies */ unsigned long last_updated; /* In jiffies */
u8 channel; /* register value */ u8 channel; /* register value */
u8 config; /* original register value */
u8 in[8]; /* register value */ u8 in[8]; /* register value */
u8 in_max[8]; /* register value */ u8 in_max[8]; /* register value */
...@@ -832,6 +832,7 @@ static int lm87_probe(struct i2c_client *new_client, ...@@ -832,6 +832,7 @@ static int lm87_probe(struct i2c_client *new_client,
sysfs_remove_group(&new_client->dev.kobj, &lm87_group); sysfs_remove_group(&new_client->dev.kobj, &lm87_group);
sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt); sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt);
exit_free: exit_free:
lm87_write_value(new_client, LM87_REG_CONFIG, data->config);
kfree(data); kfree(data);
exit: exit:
return err; return err;
...@@ -840,12 +841,17 @@ static int lm87_probe(struct i2c_client *new_client, ...@@ -840,12 +841,17 @@ static int lm87_probe(struct i2c_client *new_client,
static void lm87_init_client(struct i2c_client *client) static void lm87_init_client(struct i2c_client *client)
{ {
struct lm87_data *data = i2c_get_clientdata(client); struct lm87_data *data = i2c_get_clientdata(client);
u8 config;
data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); if (client->dev.platform_data) {
data->channel = *(u8 *)client->dev.platform_data;
lm87_write_value(client,
LM87_REG_CHANNEL_MODE, data->channel);
} else {
data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
}
data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F;
config = lm87_read_value(client, LM87_REG_CONFIG); if (!(data->config & 0x01)) {
if (!(config & 0x01)) {
int i; int i;
/* Limits are left uninitialized after power-up */ /* Limits are left uninitialized after power-up */
...@@ -867,11 +873,11 @@ static void lm87_init_client(struct i2c_client *client) ...@@ -867,11 +873,11 @@ static void lm87_init_client(struct i2c_client *client)
lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
} }
} }
if ((config & 0x81) != 0x01) {
/* Start monitoring */ /* Make sure Start is set and INT#_Clear is clear */
if ((data->config & 0x09) != 0x01)
lm87_write_value(client, LM87_REG_CONFIG, lm87_write_value(client, LM87_REG_CONFIG,
(config & 0xF7) | 0x01); (data->config & 0x77) | 0x01);
}
} }
static int lm87_remove(struct i2c_client *client) static int lm87_remove(struct i2c_client *client)
...@@ -882,6 +888,7 @@ static int lm87_remove(struct i2c_client *client) ...@@ -882,6 +888,7 @@ static int lm87_remove(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj, &lm87_group); sysfs_remove_group(&client->dev.kobj, &lm87_group);
sysfs_remove_group(&client->dev.kobj, &lm87_group_opt); sysfs_remove_group(&client->dev.kobj, &lm87_group_opt);
lm87_write_value(client, LM87_REG_CONFIG, data->config);
kfree(data); kfree(data);
return 0; return 0;
} }
......
/* /*
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware * lm90.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring * monitoring
* Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org> * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org>
* *
* Based on the lm83 driver. The LM90 is a sensor chip made by National * Based on the lm83 driver. The LM90 is a sensor chip made by National
* Semiconductor. It reports up to two temperatures (its own plus up to * Semiconductor. It reports up to two temperatures (its own plus up to
* one external one) with a 0.125 deg resolution (1 deg for local * one external one) with a 0.125 deg resolution (1 deg for local
* temperature) and a 3-4 deg accuracy. Complete datasheet can be * temperature) and a 3-4 deg accuracy.
* obtained from National's website at:
* http://www.national.com/pf/LM/LM90.html
* *
* This driver also supports the LM89 and LM99, two other sensor chips * This driver also supports the LM89 and LM99, two other sensor chips
* made by National Semiconductor. Both have an increased remote * made by National Semiconductor. Both have an increased remote
...@@ -16,48 +14,38 @@ ...@@ -16,48 +14,38 @@
* additionally shifts remote temperatures (measured and limits) by 16 * additionally shifts remote temperatures (measured and limits) by 16
* degrees, which allows for higher temperatures measurement. The * degrees, which allows for higher temperatures measurement. The
* driver doesn't handle it since it can be done easily in user-space. * driver doesn't handle it since it can be done easily in user-space.
* Complete datasheets can be obtained from National's website at:
* http://www.national.com/pf/LM/LM89.html
* http://www.national.com/pf/LM/LM99.html
* Note that there is no way to differentiate between both chips. * Note that there is no way to differentiate between both chips.
* *
* This driver also supports the LM86, another sensor chip made by * This driver also supports the LM86, another sensor chip made by
* National Semiconductor. It is exactly similar to the LM90 except it * National Semiconductor. It is exactly similar to the LM90 except it
* has a higher accuracy. * has a higher accuracy.
* Complete datasheet can be obtained from National's website at:
* http://www.national.com/pf/LM/LM86.html
* *
* This driver also supports the ADM1032, a sensor chip made by Analog * This driver also supports the ADM1032, a sensor chip made by Analog
* Devices. That chip is similar to the LM90, with a few differences * Devices. That chip is similar to the LM90, with a few differences
* that are not handled by this driver. Complete datasheet can be * that are not handled by this driver. Among others, it has a higher
* obtained from Analog's website at: * accuracy than the LM90, much like the LM86 does.
* http://www.analog.com/en/prod/0,2877,ADM1032,00.html
* Among others, it has a higher accuracy than the LM90, much like the
* LM86 does.
* *
* This driver also supports the MAX6657, MAX6658 and MAX6659 sensor * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor
* chips made by Maxim. These chips are similar to the LM86. Complete * chips made by Maxim. These chips are similar to the LM86.
* datasheet can be obtained at Maxim's website at:
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
* Note that there is no easy way to differentiate between the three * Note that there is no easy way to differentiate between the three
* variants. The extra address and features of the MAX6659 are not * variants. The extra address and features of the MAX6659 are not
* supported by this driver. These chips lack the remote temperature * supported by this driver. These chips lack the remote temperature
* offset feature. * offset feature.
* *
* This driver also supports the MAX6646, MAX6647 and MAX6649 chips
* made by Maxim. These are again similar to the LM86, but they use
* unsigned temperature values and can report temperatures from 0 to
* 145 degrees.
*
* This driver also supports the MAX6680 and MAX6681, two other sensor * This driver also supports the MAX6680 and MAX6681, two other sensor
* chips made by Maxim. These are quite similar to the other Maxim * chips made by Maxim. These are quite similar to the other Maxim
* chips. Complete datasheet can be obtained at: * chips. The MAX6680 and MAX6681 only differ in the pinout so they can
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370 * be treated identically.
* 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 * This driver also supports the ADT7461 chip from Analog Devices.
* only in its "compatability mode". If an ADT7461 chip is found but * It's supported in both compatibility and extended mode. It is mostly
* is configured in non-compatible mode (where its temperature * compatible with LM90 except for a data format difference for the
* register values are decoded differently) it is ignored by this * temperature value registers.
* driver. Complete datasheet can be obtained from Analog's website
* at:
* http://www.analog.com/en/prod/0,2877,ADT7461,00.html
* *
* Since the LM90 was the first chipset supported by this driver, most * Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and * comments will refer to this chipset, but are actually general and
...@@ -93,9 +81,10 @@ ...@@ -93,9 +81,10 @@
* Addresses to scan * Addresses to scan
* Address is fully defined internally and cannot be changed except for * Address is fully defined internally and cannot be changed except for
* MAX6659, MAX6680 and MAX6681. * MAX6659, MAX6680 and MAX6681.
* LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658 * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657
* have address 0x4c. * and MAX6658 have address 0x4c.
* ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d. * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d.
* MAX6647 has address 0x4e.
* MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported).
* MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
* 0x4c, 0x4d or 0x4e. * 0x4c, 0x4d or 0x4e.
...@@ -108,7 +97,8 @@ static const unsigned short normal_i2c[] = { ...@@ -108,7 +97,8 @@ static const unsigned short normal_i2c[] = {
* Insmod parameters * Insmod parameters
*/ */
I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680,
max6646);
/* /*
* The LM90 registers * The LM90 registers
...@@ -149,39 +139,14 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); ...@@ -149,39 +139,14 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
#define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_R_TCRIT_HYST 0x21
#define LM90_REG_W_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21
/* /* MAX6646/6647/6649/6657/6658/6659 registers */
* Conversions and various macros
* For local temperatures and limits, critical limits and the hysteresis
* value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
* For remote temperatures and limits, it uses signed 11-bit values with
* LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
*/
#define TEMP1_FROM_REG(val) ((val) * 1000) #define MAX6657_REG_R_LOCAL_TEMPL 0x11
#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
#define TEMP2_FROM_REG(val) ((val) / 32 * 125)
#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
(val) >= 127875 ? 0x7FE0 : \
(val) < 0 ? ((val) - 62) / 125 * 32 : \
((val) + 62) / 125 * 32)
#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \
((val) + 500) / 1000)
/*
* ADT7461 is almost identical to LM90 except that attempts to write
* values that are outside the range 0 < temp < 127 are treated as
* the boundary value.
*/
#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ /*
(val) >= 127000 ? 127 : \ * Device flags
((val) + 500) / 1000) */
#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ #define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */
(val) >= 127750 ? 0x7FC0 : \
((val) + 125) / 250 * 64)
/* /*
* Functions declaration * Functions declaration
...@@ -206,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = { ...@@ -206,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = {
{ "lm86", lm86 }, { "lm86", lm86 },
{ "lm89", lm99 }, { "lm89", lm99 },
{ "lm99", lm99 }, /* Missing temperature offset */ { "lm99", lm99 }, /* Missing temperature offset */
{ "max6646", max6646 },
{ "max6647", max6646 },
{ "max6649", max6646 },
{ "max6657", max6657 }, { "max6657", max6657 },
{ "max6658", max6657 }, { "max6658", max6657 },
{ "max6659", max6657 }, { "max6659", max6657 },
...@@ -237,21 +205,149 @@ struct lm90_data { ...@@ -237,21 +205,149 @@ struct lm90_data {
char valid; /* zero until following fields are valid */ char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */ unsigned long last_updated; /* in jiffies */
int kind; int kind;
int flags;
/* registers values */ /* registers values */
s8 temp8[5]; /* 0: local input s8 temp8[4]; /* 0: local low limit
1: local low limit 1: local high limit
2: local high limit 2: local critical limit
3: local critical limit 3: remote critical limit */
4: remote critical limit */ s16 temp11[5]; /* 0: remote input
s16 temp11[4]; /* 0: remote input
1: remote low limit 1: remote low limit
2: remote high limit 2: remote high limit
3: remote offset (except max6657) */ 3: remote offset (except max6646 and max6657)
4: local input */
u8 temp_hyst; u8 temp_hyst;
u8 alarms; /* bitvector */ u8 alarms; /* bitvector */
}; };
/*
* Conversions
* For local temperatures and limits, critical limits and the hysteresis
* value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
* For remote temperatures and limits, it uses signed 11-bit values with
* LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some
* Maxim chips use unsigned values.
*/
static inline int temp_from_s8(s8 val)
{
return val * 1000;
}
static inline int temp_from_u8(u8 val)
{
return val * 1000;
}
static inline int temp_from_s16(s16 val)
{
return val / 32 * 125;
}
static inline int temp_from_u16(u16 val)
{
return val / 32 * 125;
}
static s8 temp_to_s8(long val)
{
if (val <= -128000)
return -128;
if (val >= 127000)
return 127;
if (val < 0)
return (val - 500) / 1000;
return (val + 500) / 1000;
}
static u8 temp_to_u8(long val)
{
if (val <= 0)
return 0;
if (val >= 255000)
return 255;
return (val + 500) / 1000;
}
static s16 temp_to_s16(long val)
{
if (val <= -128000)
return 0x8000;
if (val >= 127875)
return 0x7FE0;
if (val < 0)
return (val - 62) / 125 * 32;
return (val + 62) / 125 * 32;
}
static u8 hyst_to_reg(long val)
{
if (val <= 0)
return 0;
if (val >= 30500)
return 31;
return (val + 500) / 1000;
}
/*
* ADT7461 in compatibility mode is almost identical to LM90 except that
* attempts to write values that are outside the range 0 < temp < 127 are
* treated as the boundary value.
*
* ADT7461 in "extended mode" operation uses unsigned integers offset by
* 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC.
*/
static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val)
{
if (data->flags & LM90_FLAG_ADT7461_EXT)
return (val - 64) * 1000;
else
return temp_from_s8(val);
}
static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val)
{
if (data->flags & LM90_FLAG_ADT7461_EXT)
return (val - 0x4000) / 64 * 250;
else
return temp_from_s16(val);
}
static u8 temp_to_u8_adt7461(struct lm90_data *data, long val)
{
if (data->flags & LM90_FLAG_ADT7461_EXT) {
if (val <= -64000)
return 0;
if (val >= 191000)
return 0xFF;
return (val + 500 + 64000) / 1000;
} else {
if (val <= 0)
return 0;
if (val >= 127000)
return 127;
return (val + 500) / 1000;
}
}
static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
{
if (data->flags & LM90_FLAG_ADT7461_EXT) {
if (val <= -64000)
return 0;
if (val >= 191750)
return 0xFFC0;
return (val + 64000 + 125) / 250 * 64;
} else {
if (val <= 0)
return 0;
if (val >= 127750)
return 0x7FC0;
return (val + 125) / 250 * 64;
}
}
/* /*
* Sysfs stuff * Sysfs stuff
*/ */
...@@ -261,7 +357,16 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, ...@@ -261,7 +357,16 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev); struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])); int temp;
if (data->kind == adt7461)
temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
else if (data->kind == max6646)
temp = temp_from_u8(data->temp8[attr->index]);
else
temp = temp_from_s8(data->temp8[attr->index]);
return sprintf(buf, "%d\n", temp);
} }
static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
...@@ -282,10 +387,12 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, ...@@ -282,10 +387,12 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
if (data->kind == adt7461) if (data->kind == adt7461)
data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); data->temp8[nr] = temp_to_u8_adt7461(data, val);
else if (data->kind == max6646)
data->temp8[nr] = temp_to_u8(val);
else else
data->temp8[nr] = TEMP1_TO_REG(val); data->temp8[nr] = temp_to_s8(val);
i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -295,7 +402,16 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, ...@@ -295,7 +402,16 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev); struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index])); int temp;
if (data->kind == adt7461)
temp = temp_from_u16_adt7461(data, data->temp11[attr->index]);
else if (data->kind == max6646)
temp = temp_from_u16(data->temp11[attr->index]);
else
temp = temp_from_s16(data->temp11[attr->index]);
return sprintf(buf, "%d\n", temp);
} }
static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
...@@ -318,13 +434,20 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, ...@@ -318,13 +434,20 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
if (data->kind == adt7461) if (data->kind == adt7461)
data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); data->temp11[nr] = temp_to_u16_adt7461(data, val);
else if (data->kind == max6657 || data->kind == max6680)
data->temp11[nr] = temp_to_s8(val) << 8;
else if (data->kind == max6646)
data->temp11[nr] = temp_to_u8(val) << 8;
else else
data->temp11[nr] = TEMP2_TO_REG(val); data->temp11[nr] = temp_to_s16(val);
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
data->temp11[nr] >> 8); data->temp11[nr] >> 8);
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], if (data->kind != max6657 && data->kind != max6680
data->temp11[nr] & 0xff); && data->kind != max6646)
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
data->temp11[nr] & 0xff);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -334,8 +457,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt ...@@ -334,8 +457,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev); struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]) int temp;
- TEMP1_FROM_REG(data->temp_hyst));
if (data->kind == adt7461)
temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
else
temp = temp_from_s8(data->temp8[attr->index]);
return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst));
} }
static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
...@@ -347,9 +476,9 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, ...@@ -347,9 +476,9 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
long hyst; long hyst;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
hyst = TEMP1_FROM_REG(data->temp8[3]) - val; hyst = temp_from_s8(data->temp8[2]) - val;
i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
HYST_TO_REG(hyst)); hyst_to_reg(hyst));
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -371,23 +500,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute ...@@ -371,23 +500,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
} }
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 1); set_temp8, 0);
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 1); set_temp11, 1);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 2); set_temp8, 1);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 2); set_temp11, 2);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 3); set_temp8, 2);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 4); set_temp8, 3);
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
set_temphyst, 3); set_temphyst, 2);
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3);
static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 3); set_temp11, 3);
...@@ -568,7 +697,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind, ...@@ -568,7 +697,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
kind = adm1032; kind = adm1032;
} else } else
if (chip_id == 0x51 /* ADT7461 */ if (chip_id == 0x51 /* ADT7461 */
&& (reg_config1 & 0x1F) == 0x00 /* check compat mode */ && (reg_config1 & 0x1B) == 0x00
&& reg_convrate <= 0x0A) { && reg_convrate <= 0x0A) {
kind = adt7461; kind = adt7461;
} }
...@@ -599,13 +728,23 @@ static int lm90_detect(struct i2c_client *new_client, int kind, ...@@ -599,13 +728,23 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
&& (reg_config1 & 0x03) == 0x00 && (reg_config1 & 0x03) == 0x00
&& reg_convrate <= 0x07) { && reg_convrate <= 0x07) {
kind = max6680; kind = max6680;
} else
/* The chip_id register of the MAX6646/6647/6649
* holds the revision of the chip.
* The lowest 6 bits of the config1 register are
* unused and should return zero when read.
*/
if (chip_id == 0x59
&& (reg_config1 & 0x3f) == 0x00
&& reg_convrate <= 0x07) {
kind = max6646;
} }
} }
if (kind <= 0) { /* identification failed */ if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev, dev_dbg(&adapter->dev,
"Unsupported chip (man_id=0x%02X, " "Unsupported chip at 0x%02x (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id); "chip_id=0x%02X)\n", address, man_id, chip_id);
return -ENODEV; return -ENODEV;
} }
} }
...@@ -629,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind, ...@@ -629,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
name = "max6680"; name = "max6680";
} else if (kind == adt7461) { } else if (kind == adt7461) {
name = "adt7461"; name = "adt7461";
} else if (kind == max6646) {
name = "max6646";
} }
strlcpy(info->type, name, I2C_NAME_SIZE); strlcpy(info->type, name, I2C_NAME_SIZE);
...@@ -668,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client, ...@@ -668,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client,
&dev_attr_pec))) &dev_attr_pec)))
goto exit_remove_files; goto exit_remove_files;
} }
if (data->kind != max6657) { if (data->kind != max6657 && data->kind != max6646) {
if ((err = device_create_file(&new_client->dev, if ((err = device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_offset.dev_attr))) &sensor_dev_attr_temp2_offset.dev_attr)))
goto exit_remove_files; goto exit_remove_files;
...@@ -707,6 +848,12 @@ static void lm90_init_client(struct i2c_client *client) ...@@ -707,6 +848,12 @@ static void lm90_init_client(struct i2c_client *client)
} }
config_orig = config; config_orig = config;
/* Check Temperature Range Select */
if (data->kind == adt7461) {
if (config & 0x04)
data->flags |= LM90_FLAG_ADT7461_EXT;
}
/* /*
* Put MAX6680/MAX8881 into extended resolution (bit 0x10, * Put MAX6680/MAX8881 into extended resolution (bit 0x10,
* 0.125 degree resolution) and range (0x08, extend range * 0.125 degree resolution) and range (0x08, extend range
...@@ -728,7 +875,7 @@ static int lm90_remove(struct i2c_client *client) ...@@ -728,7 +875,7 @@ static int lm90_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm90_group); sysfs_remove_group(&client->dev.kobj, &lm90_group);
device_remove_file(&client->dev, &dev_attr_pec); device_remove_file(&client->dev, &dev_attr_pec);
if (data->kind != max6657) if (data->kind != max6657 && data->kind != max6646)
device_remove_file(&client->dev, device_remove_file(&client->dev,
&sensor_dev_attr_temp2_offset.dev_attr); &sensor_dev_attr_temp2_offset.dev_attr);
...@@ -736,6 +883,38 @@ static int lm90_remove(struct i2c_client *client) ...@@ -736,6 +883,38 @@ static int lm90_remove(struct i2c_client *client)
return 0; return 0;
} }
static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
{
int err;
u8 oldh, newh, l;
/*
* There is a trick here. We have to read two registers to have the
* sensor temperature, but we have to beware a conversion could occur
* inbetween the readings. The datasheet says we should either use
* the one-shot conversion register, which we don't want to do
* (disables hardware monitoring) or monitor the busy bit, which is
* impossible (we can't read the values and monitor that bit at the
* exact same time). So the solution used here is to read the high
* byte once, then the low byte, then the high byte again. If the new
* high byte matches the old one, then we have a valid reading. Else
* we have to read the low byte again, and now we believe we have a
* correct reading.
*/
if ((err = lm90_read_reg(client, regh, &oldh))
|| (err = lm90_read_reg(client, regl, &l))
|| (err = lm90_read_reg(client, regh, &newh)))
return err;
if (oldh != newh) {
err = lm90_read_reg(client, regl, &l);
if (err)
return err;
}
*value = (newh << 8) | l;
return 0;
}
static struct lm90_data *lm90_update_device(struct device *dev) static struct lm90_data *lm90_update_device(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
...@@ -744,49 +923,50 @@ static struct lm90_data *lm90_update_device(struct device *dev) ...@@ -744,49 +923,50 @@ static struct lm90_data *lm90_update_device(struct device *dev)
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
u8 oldh, newh, l; u8 h, l;
dev_dbg(&client->dev, "Updating lm90 data.\n"); dev_dbg(&client->dev, "Updating lm90 data.\n");
lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]); lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]);
lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[1]); lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]);
lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[2]); lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]);
lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[3]); lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);
lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]);
lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
/* if (data->kind == max6657 || data->kind == max6646) {
* There is a trick here. We have to read two registers to lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
* have the remote sensor temperature, but we have to beware MAX6657_REG_R_LOCAL_TEMPL,
* a conversion could occur inbetween the readings. The &data->temp11[4]);
* datasheet says we should either use the one-shot } else {
* conversion register, which we don't want to do (disables if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
* hardware monitoring) or monitor the busy bit, which is &h) == 0)
* impossible (we can't read the values and monitor that bit data->temp11[4] = h << 8;
* at the exact same time). So the solution used here is to }
* read the high byte once, then the low byte, then the high lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
* byte again. If the new high byte matches the old one, LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]);
* then we have a valid reading. Else we have to read the low
* byte again, and now we believe we have a correct reading. if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) {
*/ data->temp11[1] = h << 8;
if (lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &oldh) == 0 if (data->kind != max6657 && data->kind != max6680
&& lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0 && data->kind != max6646
&& lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &newh) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL,
&& (newh == oldh &l) == 0)
|| lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0)) data->temp11[1] |= l;
data->temp11[0] = (newh << 8) | l; }
if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) {
if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &newh) == 0 data->temp11[2] = h << 8;
&& lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) if (data->kind != max6657 && data->kind != max6680
data->temp11[1] = (newh << 8) | l; && data->kind != max6646
if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &newh) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL,
&& lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) &l) == 0)
data->temp11[2] = (newh << 8) | l; data->temp11[2] |= l;
if (data->kind != max6657) { }
if (data->kind != max6657 && data->kind != max6646) {
if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH,
&newh) == 0 &h) == 0
&& lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL,
&l) == 0) &l) == 0)
data->temp11[3] = (newh << 8) | l; data->temp11[3] = (h << 8) | l;
} }
lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms);
......
...@@ -69,11 +69,18 @@ I2C_CLIENT_INSMOD_1(max1619); ...@@ -69,11 +69,18 @@ I2C_CLIENT_INSMOD_1(max1619);
#define MAX1619_REG_W_TCRIT_HYST 0x13 #define MAX1619_REG_W_TCRIT_HYST 0x13
/* /*
* Conversions and various macros * Conversions
*/ */
#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) static int temp_from_reg(int val)
#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000) {
return (val & 0x80 ? val-0x100 : val) * 1000;
}
static int temp_to_reg(int val)
{
return (val < 0 ? val+0x100*1000 : val) / 1000;
}
/* /*
* Functions declaration * Functions declaration
...@@ -135,7 +142,7 @@ struct max1619_data { ...@@ -135,7 +142,7 @@ struct max1619_data {
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \ { \
struct max1619_data *data = max1619_update_device(dev); \ struct max1619_data *data = max1619_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ return sprintf(buf, "%d\n", temp_from_reg(data->value)); \
} }
show_temp(temp_input1); show_temp(temp_input1);
show_temp(temp_input2); show_temp(temp_input2);
...@@ -153,7 +160,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co ...@@ -153,7 +160,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co
long val = simple_strtol(buf, NULL, 10); \ long val = simple_strtol(buf, NULL, 10); \
\ \
mutex_lock(&data->update_lock); \ mutex_lock(&data->update_lock); \
data->value = TEMP_TO_REG(val); \ data->value = temp_to_reg(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \ i2c_smbus_write_byte_data(client, reg, data->value); \
mutex_unlock(&data->update_lock); \ mutex_unlock(&data->update_lock); \
return count; \ return count; \
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, Philip Edelbrock <phil@netroedge.com>,
and Mark Studebaker <mdsxyz123@yahoo.com> and Mark Studebaker <mdsxyz123@yahoo.com>
Copyright (c) 2007 Jean Delvare <khali@linux-fr.org> Copyright (c) 2007 - 2008 Jean Delvare <khali@linux-fr.org>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -38,25 +38,24 @@ ...@@ -38,25 +38,24 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-vid.h> #include <linux/hwmon-vid.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#ifdef CONFIG_ISA
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <asm/io.h> #include <asm/io.h>
#include "lm75.h" #endif
/* ISA device, if found */ #include "lm75.h"
static struct platform_device *pdev;
/* Addresses to scan */ /* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, I2C_CLIENT_END }; 0x2e, 0x2f, I2C_CLIENT_END };
static unsigned short isa_address = 0x290;
/* Insmod parameters */ /* Insmod parameters */
I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f); I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f);
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
...@@ -178,9 +177,9 @@ FAN_FROM_REG(u8 val, int div) ...@@ -178,9 +177,9 @@ FAN_FROM_REG(u8 val, int div)
#define TEMP_FROM_REG(val) ((val) * 1000) #define TEMP_FROM_REG(val) ((val) * 1000)
#define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \ #define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \
(val) ^ 0x7fff : (val)) (~(val)) & 0x7fff : (val) & 0xff7fff)
#define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \ #define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \
(~(val)) & 0x7fff : (val) & 0xffffff) (~(val)) & 0x7fff : (val) & 0xff7fff)
#define DIV_FROM_REG(val) (1 << (val)) #define DIV_FROM_REG(val) (1 << (val))
...@@ -199,25 +198,16 @@ DIV_TO_REG(long val, enum chips type) ...@@ -199,25 +198,16 @@ DIV_TO_REG(long val, enum chips type)
return i; return i;
} }
/* There are some complications in a module like this. First off, W83781D chips
may be both present on the SMBus and the ISA bus, and we have to handle
those cases separately at some places. Second, there might be several
W83781D chips available (well, actually, that is probably never done; but
it is a clean illustration of how to handle a case like that). Finally,
a specific chip may be attached to *both* ISA and SMBus, and we would
not like to detect it double. Fortunately, in the case of the W83781D at
least, a register tells us what SMBus address we are on, so that helps
a bit - except if there could be more than one SMBus. Groan. No solution
for this yet. */
/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
the driver field to differentiate between I2C and ISA chips. */
struct w83781d_data { struct w83781d_data {
struct i2c_client client; struct i2c_client *client;
struct device *hwmon_dev; struct device *hwmon_dev;
struct mutex lock; struct mutex lock;
enum chips type; enum chips type;
/* For ISA device only */
const char *name;
int isa_addr;
struct mutex update_lock; struct mutex update_lock;
char valid; /* !=0 if following fields are valid */ char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */ unsigned long last_updated; /* In jiffies */
...@@ -240,7 +230,6 @@ struct w83781d_data { ...@@ -240,7 +230,6 @@ struct w83781d_data {
u8 vid; /* Register encoding, combined */ u8 vid; /* Register encoding, combined */
u32 alarms; /* Register encoding, combined */ u32 alarms; /* Register encoding, combined */
u32 beep_mask; /* Register encoding, combined */ u32 beep_mask; /* Register encoding, combined */
u8 beep_enable; /* Boolean */
u8 pwm[4]; /* Register value */ u8 pwm[4]; /* Register value */
u8 pwm2_enable; /* Boolean */ u8 pwm2_enable; /* Boolean */
u16 sens[3]; /* 782D/783S only. u16 sens[3]; /* 782D/783S only.
...@@ -249,36 +238,14 @@ struct w83781d_data { ...@@ -249,36 +238,14 @@ struct w83781d_data {
u8 vrm; u8 vrm;
}; };
static int w83781d_attach_adapter(struct i2c_adapter *adapter); static struct w83781d_data *w83781d_data_if_isa(void);
static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); static int w83781d_alias_detect(struct i2c_client *client, u8 chipid);
static int w83781d_detach_client(struct i2c_client *client);
static int __devinit w83781d_isa_probe(struct platform_device *pdev);
static int __devexit w83781d_isa_remove(struct platform_device *pdev);
static int w83781d_read_value(struct w83781d_data *data, u16 reg); static int w83781d_read_value(struct w83781d_data *data, u16 reg);
static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value);
static struct w83781d_data *w83781d_update_device(struct device *dev); static struct w83781d_data *w83781d_update_device(struct device *dev);
static void w83781d_init_device(struct device *dev); static void w83781d_init_device(struct device *dev);
static struct i2c_driver w83781d_driver = {
.driver = {
.name = "w83781d",
},
.attach_adapter = w83781d_attach_adapter,
.detach_client = w83781d_detach_client,
};
static struct platform_driver w83781d_isa_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "w83781d",
},
.probe = w83781d_isa_probe,
.remove = w83781d_isa_remove,
};
/* following are the sysfs callback functions */ /* following are the sysfs callback functions */
#define show_in_reg(reg) \ #define show_in_reg(reg) \
static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \
...@@ -513,11 +480,6 @@ static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr ...@@ -513,11 +480,6 @@ static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr
return sprintf(buf, "%ld\n", return sprintf(buf, "%ld\n",
(long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type));
} }
static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83781d_data *data = w83781d_update_device(dev);
return sprintf(buf, "%ld\n", (long)data->beep_enable);
}
static ssize_t static ssize_t
store_beep_mask(struct device *dev, struct device_attribute *attr, store_beep_mask(struct device *dev, struct device_attribute *attr,
...@@ -529,12 +491,12 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, ...@@ -529,12 +491,12 @@ store_beep_mask(struct device *dev, struct device_attribute *attr,
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->beep_mask = BEEP_MASK_TO_REG(val, data->type); data->beep_mask &= 0x8000; /* preserve beep enable */
data->beep_mask |= BEEP_MASK_TO_REG(val, data->type);
w83781d_write_value(data, W83781D_REG_BEEP_INTS1, w83781d_write_value(data, W83781D_REG_BEEP_INTS1,
data->beep_mask & 0xff); data->beep_mask & 0xff);
w83781d_write_value(data, W83781D_REG_BEEP_INTS2, w83781d_write_value(data, W83781D_REG_BEEP_INTS2,
((data->beep_mask >> 8) & 0x7f) (data->beep_mask >> 8) & 0xff);
| data->beep_enable << 7);
if (data->type != w83781d && data->type != as99127f) { if (data->type != w83781d && data->type != as99127f) {
w83781d_write_value(data, W83781D_REG_BEEP_INTS3, w83781d_write_value(data, W83781D_REG_BEEP_INTS3,
((data->beep_mask) >> 16) & 0xff); ((data->beep_mask) >> 16) & 0xff);
...@@ -544,31 +506,8 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, ...@@ -544,31 +506,8 @@ store_beep_mask(struct device *dev, struct device_attribute *attr,
return count; return count;
} }
static ssize_t
store_beep_enable(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct w83781d_data *data = dev_get_drvdata(dev);
u32 val;
val = simple_strtoul(buf, NULL, 10);
if (val != 0 && val != 1)
return -EINVAL;
mutex_lock(&data->update_lock);
data->beep_enable = val;
val = w83781d_read_value(data, W83781D_REG_BEEP_INTS2) & 0x7f;
val |= data->beep_enable << 7;
w83781d_write_value(data, W83781D_REG_BEEP_INTS2, val);
mutex_unlock(&data->update_lock);
return count;
}
static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR, static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR,
show_beep_mask, store_beep_mask); show_beep_mask, store_beep_mask);
static DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR,
show_beep_enable, store_beep_enable);
static ssize_t show_beep(struct device *dev, struct device_attribute *attr, static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
...@@ -663,6 +602,8 @@ static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR, ...@@ -663,6 +602,8 @@ static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR,
show_beep, store_beep, 5); show_beep, store_beep, 5);
static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO,
show_temp3_beep, store_beep, 13); show_temp3_beep, store_beep, 13);
static SENSOR_DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR,
show_beep, store_beep, 15);
static ssize_t static ssize_t
show_fan_div(struct device *dev, struct device_attribute *da, char *buf) show_fan_div(struct device *dev, struct device_attribute *da, char *buf)
...@@ -866,45 +807,19 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, ...@@ -866,45 +807,19 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR,
static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR,
show_sensor, store_sensor, 2); show_sensor, store_sensor, 2);
/* I2C devices get this name attribute automatically, but for ISA devices
we must create it by ourselves. */
static ssize_t
show_name(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct w83781d_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->client.name);
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
/* This function is called when:
* w83781d_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and w83781d_driver is still present) */
static int
w83781d_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_probe(adapter, &addr_data, w83781d_detect);
}
/* Assumes that adapter is of I2C, not ISA variety. /* Assumes that adapter is of I2C, not ISA variety.
* OTHERWISE DON'T CALL THIS * OTHERWISE DON'T CALL THIS
*/ */
static int static int
w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, w83781d_detect_subclients(struct i2c_client *new_client)
struct i2c_client *new_client)
{ {
int i, val1 = 0, id; int i, val1 = 0, id;
int err; int err;
const char *client_name = ""; int address = new_client->addr;
unsigned short sc_addr[2];
struct i2c_adapter *adapter = new_client->adapter;
struct w83781d_data *data = i2c_get_clientdata(new_client); struct w83781d_data *data = i2c_get_clientdata(new_client);
enum chips kind = data->type;
data->lm75[0] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!(data->lm75[0])) {
err = -ENOMEM;
goto ERROR_SC_0;
}
id = i2c_adapter_id(adapter); id = i2c_adapter_id(adapter);
...@@ -922,55 +837,35 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, ...@@ -922,55 +837,35 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
w83781d_write_value(data, W83781D_REG_I2C_SUBADDR, w83781d_write_value(data, W83781D_REG_I2C_SUBADDR,
(force_subclients[2] & 0x07) | (force_subclients[2] & 0x07) |
((force_subclients[3] & 0x07) << 4)); ((force_subclients[3] & 0x07) << 4));
data->lm75[0]->addr = force_subclients[2]; sc_addr[0] = force_subclients[2];
} else { } else {
val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR); val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR);
data->lm75[0]->addr = 0x48 + (val1 & 0x07); sc_addr[0] = 0x48 + (val1 & 0x07);
} }
if (kind != w83783s) { if (kind != w83783s) {
data->lm75[1] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!(data->lm75[1])) {
err = -ENOMEM;
goto ERROR_SC_1;
}
if (force_subclients[0] == id && if (force_subclients[0] == id &&
force_subclients[1] == address) { force_subclients[1] == address) {
data->lm75[1]->addr = force_subclients[3]; sc_addr[1] = force_subclients[3];
} else { } else {
data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07); sc_addr[1] = 0x48 + ((val1 >> 4) & 0x07);
} }
if (data->lm75[0]->addr == data->lm75[1]->addr) { if (sc_addr[0] == sc_addr[1]) {
dev_err(&new_client->dev, dev_err(&new_client->dev,
"Duplicate addresses 0x%x for subclients.\n", "Duplicate addresses 0x%x for subclients.\n",
data->lm75[0]->addr); sc_addr[0]);
err = -EBUSY; err = -EBUSY;
goto ERROR_SC_2; goto ERROR_SC_2;
} }
} }
if (kind == w83781d)
client_name = "w83781d subclient";
else if (kind == w83782d)
client_name = "w83782d subclient";
else if (kind == w83783s)
client_name = "w83783s subclient";
else if (kind == as99127f)
client_name = "as99127f subclient";
for (i = 0; i <= 1; i++) { for (i = 0; i <= 1; i++) {
/* store all data in w83781d */ data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]);
i2c_set_clientdata(data->lm75[i], NULL); if (!data->lm75[i]) {
data->lm75[i]->adapter = adapter;
data->lm75[i]->driver = &w83781d_driver;
data->lm75[i]->flags = 0;
strlcpy(data->lm75[i]->name, client_name,
I2C_NAME_SIZE);
if ((err = i2c_attach_client(data->lm75[i]))) {
dev_err(&new_client->dev, "Subclient %d " dev_err(&new_client->dev, "Subclient %d "
"registration at address 0x%x " "registration at address 0x%x "
"failed.\n", i, data->lm75[i]->addr); "failed.\n", i, sc_addr[i]);
err = -ENOMEM;
if (i == 1) if (i == 1)
goto ERROR_SC_3; goto ERROR_SC_3;
goto ERROR_SC_2; goto ERROR_SC_2;
...@@ -983,12 +878,9 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, ...@@ -983,12 +878,9 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
/* Undo inits in case of errors */ /* Undo inits in case of errors */
ERROR_SC_3: ERROR_SC_3:
i2c_detach_client(data->lm75[0]); i2c_unregister_device(data->lm75[0]);
ERROR_SC_2: ERROR_SC_2:
kfree(data->lm75[1]);
ERROR_SC_1: ERROR_SC_1:
kfree(data->lm75[0]);
ERROR_SC_0:
return err; return err;
} }
...@@ -1029,7 +921,7 @@ static struct attribute* w83781d_attributes[] = { ...@@ -1029,7 +921,7 @@ static struct attribute* w83781d_attributes[] = {
&dev_attr_vrm.attr, &dev_attr_vrm.attr,
&dev_attr_alarms.attr, &dev_attr_alarms.attr,
&dev_attr_beep_mask.attr, &dev_attr_beep_mask.attr,
&dev_attr_beep_enable.attr, &sensor_dev_attr_beep_enable.dev_attr.attr,
NULL NULL
}; };
static const struct attribute_group w83781d_group = { static const struct attribute_group w83781d_group = {
...@@ -1151,96 +1043,74 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) ...@@ -1151,96 +1043,74 @@ w83781d_create_files(struct device *dev, int kind, int is_isa)
} }
} }
if (is_isa) {
err = device_create_file(&pdev->dev, &dev_attr_name);
if (err)
return err;
}
return 0; return 0;
} }
/* Return 0 if detection is successful, -ENODEV otherwise */
static int static int
w83781d_detect(struct i2c_adapter *adapter, int address, int kind) w83781d_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{ {
int val1 = 0, val2; int val1 = 0, val2;
struct i2c_client *client; struct w83781d_data *isa = w83781d_data_if_isa();
struct device *dev; struct i2c_adapter *adapter = client->adapter;
struct w83781d_data *data; int address = client->addr;
int err;
const char *client_name = ""; const char *client_name = "";
enum vendor { winbond, asus } vendid; enum vendor { winbond, asus } vendid;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
err = -EINVAL; return -ENODEV;
goto ERROR1;
}
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access w83781d_{read,write}_value. */
if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR1;
}
client = &data->client;
i2c_set_clientdata(client, data);
client->addr = address;
mutex_init(&data->lock);
client->adapter = adapter;
client->driver = &w83781d_driver;
dev = &client->dev;
/* Now, we do the remaining detection. */ /* We block updates of the ISA device to minimize the risk of
concurrent access to the same W83781D chip through different
interfaces. */
if (isa)
mutex_lock(&isa->update_lock);
/* The w8378?d may be stuck in some other bank than bank 0. This may /* The w8378?d may be stuck in some other bank than bank 0. This may
make reading other information impossible. Specify a force=... or make reading other information impossible. Specify a force=... or
force_*=... parameter, and the Winbond will be reset to the right force_*=... parameter, and the Winbond will be reset to the right
bank. */ bank. */
if (kind < 0) { if (kind < 0) {
if (w83781d_read_value(data, W83781D_REG_CONFIG) & 0x80) { if (i2c_smbus_read_byte_data
(client, W83781D_REG_CONFIG) & 0x80) {
dev_dbg(&adapter->dev, "Detection of w83781d chip " dev_dbg(&adapter->dev, "Detection of w83781d chip "
"failed at step 3\n"); "failed at step 3\n");
err = -ENODEV; goto err_nodev;
goto ERROR2;
} }
val1 = w83781d_read_value(data, W83781D_REG_BANK); val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK);
val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
/* Check for Winbond or Asus ID if in bank 0 */ /* Check for Winbond or Asus ID if in bank 0 */
if ((!(val1 & 0x07)) && if ((!(val1 & 0x07)) &&
(((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
|| ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) {
dev_dbg(&adapter->dev, "Detection of w83781d chip " dev_dbg(&adapter->dev, "Detection of w83781d chip "
"failed at step 4\n"); "failed at step 4\n");
err = -ENODEV; goto err_nodev;
goto ERROR2;
} }
/* If Winbond SMBus, check address at 0x48. /* If Winbond SMBus, check address at 0x48.
Asus doesn't support, except for as99127f rev.2 */ Asus doesn't support, except for as99127f rev.2 */
if ((!(val1 & 0x80) && (val2 == 0xa3)) || if ((!(val1 & 0x80) && (val2 == 0xa3)) ||
((val1 & 0x80) && (val2 == 0x5c))) { ((val1 & 0x80) && (val2 == 0x5c))) {
if (w83781d_read_value if (i2c_smbus_read_byte_data
(data, W83781D_REG_I2C_ADDR) != address) { (client, W83781D_REG_I2C_ADDR) != address) {
dev_dbg(&adapter->dev, "Detection of w83781d " dev_dbg(&adapter->dev, "Detection of w83781d "
"chip failed at step 5\n"); "chip failed at step 5\n");
err = -ENODEV; goto err_nodev;
goto ERROR2;
} }
} }
} }
/* We have either had a force parameter, or we have already detected the /* We have either had a force parameter, or we have already detected the
Winbond. Put it now into bank 0 and Vendor ID High Byte */ Winbond. Put it now into bank 0 and Vendor ID High Byte */
w83781d_write_value(data, W83781D_REG_BANK, i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
(w83781d_read_value(data, W83781D_REG_BANK) (i2c_smbus_read_byte_data(client, W83781D_REG_BANK)
& 0x78) | 0x80); & 0x78) | 0x80);
/* Determine the chip type. */ /* Determine the chip type. */
if (kind <= 0) { if (kind <= 0) {
/* get vendor ID */ /* get vendor ID */
val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
if (val2 == 0x5c) if (val2 == 0x5c)
vendid = winbond; vendid = winbond;
else if (val2 == 0x12) else if (val2 == 0x12)
...@@ -1248,11 +1118,10 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -1248,11 +1118,10 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
else { else {
dev_dbg(&adapter->dev, "w83781d chip vendor is " dev_dbg(&adapter->dev, "w83781d chip vendor is "
"neither Winbond nor Asus\n"); "neither Winbond nor Asus\n");
err = -ENODEV; goto err_nodev;
goto ERROR2;
} }
val1 = w83781d_read_value(data, W83781D_REG_WCHIPID); val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID);
if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
kind = w83781d; kind = w83781d;
else if (val1 == 0x30 && vendid == winbond) else if (val1 == 0x30 && vendid == winbond)
...@@ -1266,11 +1135,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -1266,11 +1135,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
dev_warn(&adapter->dev, "Ignoring 'force' " dev_warn(&adapter->dev, "Ignoring 'force' "
"parameter for unknown chip at " "parameter for unknown chip at "
"address 0x%02x\n", address); "address 0x%02x\n", address);
err = -EINVAL; goto err_nodev;
goto ERROR2; }
if ((kind == w83781d || kind == w83782d)
&& w83781d_alias_detect(client, val1)) {
dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
"be the same as ISA device\n", address);
goto err_nodev;
} }
} }
if (isa)
mutex_unlock(&isa->update_lock);
if (kind == w83781d) { if (kind == w83781d) {
client_name = "w83781d"; client_name = "w83781d";
} else if (kind == w83782d) { } else if (kind == w83782d) {
...@@ -1281,24 +1159,46 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -1281,24 +1159,46 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
client_name = "as99127f"; client_name = "as99127f";
} }
/* Fill in the remaining client fields and put into the global list */ strlcpy(info->type, client_name, I2C_NAME_SIZE);
strlcpy(client->name, client_name, I2C_NAME_SIZE);
data->type = kind; return 0;
err_nodev:
if (isa)
mutex_unlock(&isa->update_lock);
return -ENODEV;
}
static int
w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct w83781d_data *data;
int err;
data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto ERROR1;
}
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
mutex_init(&data->update_lock);
/* Tell the I2C layer a new client has arrived */ data->type = id->driver_data;
if ((err = i2c_attach_client(client))) data->client = client;
goto ERROR2;
/* attach secondary i2c lm75-like clients */ /* attach secondary i2c lm75-like clients */
if ((err = w83781d_detect_subclients(adapter, address, err = w83781d_detect_subclients(client);
kind, client))) if (err)
goto ERROR3; goto ERROR3;
/* Initialize the chip */ /* Initialize the chip */
w83781d_init_device(dev); w83781d_init_device(dev);
/* Register sysfs hooks */ /* Register sysfs hooks */
err = w83781d_create_files(dev, kind, 0); err = w83781d_create_files(dev, data->type, 0);
if (err) if (err)
goto ERROR4; goto ERROR4;
...@@ -1314,264 +1214,113 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -1314,264 +1214,113 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
sysfs_remove_group(&dev->kobj, &w83781d_group); sysfs_remove_group(&dev->kobj, &w83781d_group);
sysfs_remove_group(&dev->kobj, &w83781d_group_opt); sysfs_remove_group(&dev->kobj, &w83781d_group_opt);
if (data->lm75[1]) { if (data->lm75[0])
i2c_detach_client(data->lm75[1]); i2c_unregister_device(data->lm75[0]);
kfree(data->lm75[1]); if (data->lm75[1])
} i2c_unregister_device(data->lm75[1]);
if (data->lm75[0]) {
i2c_detach_client(data->lm75[0]);
kfree(data->lm75[0]);
}
ERROR3: ERROR3:
i2c_detach_client(client); i2c_set_clientdata(client, NULL);
ERROR2:
kfree(data); kfree(data);
ERROR1: ERROR1:
return err; return err;
} }
static int static int
w83781d_detach_client(struct i2c_client *client) w83781d_remove(struct i2c_client *client)
{ {
struct w83781d_data *data = i2c_get_clientdata(client); struct w83781d_data *data = i2c_get_clientdata(client);
int err; struct device *dev = &client->dev;
/* main client */
if (data) {
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &w83781d_group);
sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt);
}
if ((err = i2c_detach_client(client)))
return err;
/* main client */ hwmon_device_unregister(data->hwmon_dev);
if (data)
kfree(data);
/* subclient */
else
kfree(client);
return 0;
}
static int __devinit
w83781d_isa_probe(struct platform_device *pdev)
{
int err, reg;
struct w83781d_data *data;
struct resource *res;
const char *name;
/* Reserve the ISA region */
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2,
"w83781d")) {
err = -EBUSY;
goto exit;
}
if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit_release_region;
}
mutex_init(&data->lock);
data->client.addr = res->start;
i2c_set_clientdata(&data->client, data);
platform_set_drvdata(pdev, data);
reg = w83781d_read_value(data, W83781D_REG_WCHIPID);
switch (reg) {
case 0x30:
data->type = w83782d;
name = "w83782d";
break;
default:
data->type = w83781d;
name = "w83781d";
}
strlcpy(data->client.name, name, I2C_NAME_SIZE);
/* Initialize the W83781D chip */
w83781d_init_device(&pdev->dev);
/* Register sysfs hooks */
err = w83781d_create_files(&pdev->dev, data->type, 1);
if (err)
goto exit_remove_files;
data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove_files;
}
return 0;
exit_remove_files: sysfs_remove_group(&dev->kobj, &w83781d_group);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); sysfs_remove_group(&dev->kobj, &w83781d_group_opt);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
device_remove_file(&pdev->dev, &dev_attr_name);
kfree(data);
exit_release_region:
release_region(res->start + W83781D_ADDR_REG_OFFSET, 2);
exit:
return err;
}
static int __devexit if (data->lm75[0])
w83781d_isa_remove(struct platform_device *pdev) i2c_unregister_device(data->lm75[0]);
{ if (data->lm75[1])
struct w83781d_data *data = platform_get_drvdata(pdev); i2c_unregister_device(data->lm75[1]);
hwmon_device_unregister(data->hwmon_dev); i2c_set_clientdata(client, NULL);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
device_remove_file(&pdev->dev, &dev_attr_name);
release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2);
kfree(data); kfree(data);
return 0; return 0;
} }
/* The SMBus locks itself, usually, but nothing may access the Winbond between
bank switches. ISA access must always be locked explicitly!
We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
would slow down the W83781D access and should not be necessary.
There are some ugly typecasts here, but the good news is - they should
nowhere else be necessary! */
static int static int
w83781d_read_value(struct w83781d_data *data, u16 reg) w83781d_read_value_i2c(struct w83781d_data *data, u16 reg)
{ {
struct i2c_client *client = &data->client; struct i2c_client *client = data->client;
int res, word_sized, bank; int res, bank;
struct i2c_client *cl; struct i2c_client *cl;
mutex_lock(&data->lock); bank = (reg >> 8) & 0x0f;
if (!client->driver) { /* ISA device */ if (bank > 2)
word_sized = (((reg & 0xff00) == 0x100) /* switch banks */
|| ((reg & 0xff00) == 0x200)) i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
&& (((reg & 0x00ff) == 0x50) bank);
|| ((reg & 0x00ff) == 0x53) if (bank == 0 || bank > 2) {
|| ((reg & 0x00ff) == 0x55)); res = i2c_smbus_read_byte_data(client, reg & 0xff);
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(reg >> 8,
client->addr + W83781D_DATA_REG_OFFSET);
}
outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
res = inb_p(client->addr + W83781D_DATA_REG_OFFSET);
if (word_sized) {
outb_p((reg & 0xff) + 1,
client->addr + W83781D_ADDR_REG_OFFSET);
res =
(res << 8) + inb_p(client->addr +
W83781D_DATA_REG_OFFSET);
}
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
}
} else { } else {
bank = (reg >> 8) & 0x0f; /* switch to subclient */
if (bank > 2) cl = data->lm75[bank - 1];
/* switch banks */ /* convert from ISA to LM75 I2C addresses */
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, switch (reg & 0xff) {
bank); case 0x50: /* TEMP */
if (bank == 0 || bank > 2) { res = swab16(i2c_smbus_read_word_data(cl, 0));
res = i2c_smbus_read_byte_data(client, reg & 0xff); break;
} else { case 0x52: /* CONFIG */
/* switch to subclient */ res = i2c_smbus_read_byte_data(cl, 1);
cl = data->lm75[bank - 1]; break;
/* convert from ISA to LM75 I2C addresses */ case 0x53: /* HYST */
switch (reg & 0xff) { res = swab16(i2c_smbus_read_word_data(cl, 2));
case 0x50: /* TEMP */ break;
res = swab16(i2c_smbus_read_word_data(cl, 0)); case 0x55: /* OVER */
break; default:
case 0x52: /* CONFIG */ res = swab16(i2c_smbus_read_word_data(cl, 3));
res = i2c_smbus_read_byte_data(cl, 1); break;
break;
case 0x53: /* HYST */
res = swab16(i2c_smbus_read_word_data(cl, 2));
break;
case 0x55: /* OVER */
default:
res = swab16(i2c_smbus_read_word_data(cl, 3));
break;
}
} }
if (bank > 2)
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
} }
mutex_unlock(&data->lock); if (bank > 2)
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
return res; return res;
} }
static int static int
w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value)
{ {
struct i2c_client *client = &data->client; struct i2c_client *client = data->client;
int word_sized, bank; int bank;
struct i2c_client *cl; struct i2c_client *cl;
mutex_lock(&data->lock); bank = (reg >> 8) & 0x0f;
if (!client->driver) { /* ISA device */ if (bank > 2)
word_sized = (((reg & 0xff00) == 0x100) /* switch banks */
|| ((reg & 0xff00) == 0x200)) i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
&& (((reg & 0x00ff) == 0x53) bank);
|| ((reg & 0x00ff) == 0x55)); if (bank == 0 || bank > 2) {
if (reg & 0xff00) { i2c_smbus_write_byte_data(client, reg & 0xff,
outb_p(W83781D_REG_BANK, value & 0xff);
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(reg >> 8,
client->addr + W83781D_DATA_REG_OFFSET);
}
outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
if (word_sized) {
outb_p(value >> 8,
client->addr + W83781D_DATA_REG_OFFSET);
outb_p((reg & 0xff) + 1,
client->addr + W83781D_ADDR_REG_OFFSET);
}
outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET);
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
}
} else { } else {
bank = (reg >> 8) & 0x0f; /* switch to subclient */
if (bank > 2) cl = data->lm75[bank - 1];
/* switch banks */ /* convert from ISA to LM75 I2C addresses */
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, switch (reg & 0xff) {
bank); case 0x52: /* CONFIG */
if (bank == 0 || bank > 2) { i2c_smbus_write_byte_data(cl, 1, value & 0xff);
i2c_smbus_write_byte_data(client, reg & 0xff, break;
value & 0xff); case 0x53: /* HYST */
} else { i2c_smbus_write_word_data(cl, 2, swab16(value));
/* switch to subclient */ break;
cl = data->lm75[bank - 1]; case 0x55: /* OVER */
/* convert from ISA to LM75 I2C addresses */ i2c_smbus_write_word_data(cl, 3, swab16(value));
switch (reg & 0xff) { break;
case 0x52: /* CONFIG */
i2c_smbus_write_byte_data(cl, 1, value & 0xff);
break;
case 0x53: /* HYST */
i2c_smbus_write_word_data(cl, 2, swab16(value));
break;
case 0x55: /* OVER */
i2c_smbus_write_word_data(cl, 3, swab16(value));
break;
}
} }
if (bank > 2)
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
} }
mutex_unlock(&data->lock); if (bank > 2)
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
return 0; return 0;
} }
...@@ -1678,7 +1427,7 @@ w83781d_init_device(struct device *dev) ...@@ -1678,7 +1427,7 @@ w83781d_init_device(struct device *dev)
static struct w83781d_data *w83781d_update_device(struct device *dev) static struct w83781d_data *w83781d_update_device(struct device *dev)
{ {
struct w83781d_data *data = dev_get_drvdata(dev); struct w83781d_data *data = dev_get_drvdata(dev);
struct i2c_client *client = &data->client; struct i2c_client *client = data->client;
int i; int i;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
...@@ -1775,8 +1524,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) ...@@ -1775,8 +1524,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev)
W83781D_REG_ALARM2) << 8); W83781D_REG_ALARM2) << 8);
} }
i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2);
data->beep_enable = i >> 7; data->beep_mask = (i << 8) +
data->beep_mask = ((i & 0x7f) << 8) +
w83781d_read_value(data, W83781D_REG_BEEP_INTS1); w83781d_read_value(data, W83781D_REG_BEEP_INTS1);
if ((data->type != w83781d) && (data->type != as99127f)) { if ((data->type != w83781d) && (data->type != as99127f)) {
data->beep_mask |= data->beep_mask |=
...@@ -1792,6 +1540,275 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) ...@@ -1792,6 +1540,275 @@ static struct w83781d_data *w83781d_update_device(struct device *dev)
return data; return data;
} }
static const struct i2c_device_id w83781d_ids[] = {
{ "w83781d", w83781d, },
{ "w83782d", w83782d, },
{ "w83783s", w83783s, },
{ "as99127f", as99127f },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, w83781d_ids);
static struct i2c_driver w83781d_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "w83781d",
},
.probe = w83781d_probe,
.remove = w83781d_remove,
.id_table = w83781d_ids,
.detect = w83781d_detect,
.address_data = &addr_data,
};
/*
* ISA related code
*/
#ifdef CONFIG_ISA
/* ISA device, if found */
static struct platform_device *pdev;
static unsigned short isa_address = 0x290;
/* I2C devices get this name attribute automatically, but for ISA devices
we must create it by ourselves. */
static ssize_t
show_name(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct w83781d_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static struct w83781d_data *w83781d_data_if_isa(void)
{
return pdev ? platform_get_drvdata(pdev) : NULL;
}
/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
static int w83781d_alias_detect(struct i2c_client *client, u8 chipid)
{
struct w83781d_data *isa;
int i;
if (!pdev) /* No ISA chip */
return 0;
isa = platform_get_drvdata(pdev);
if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr)
return 0; /* Address doesn't match */
if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid)
return 0; /* Chip type doesn't match */
/* We compare all the limit registers, the config register and the
* interrupt mask registers */
for (i = 0x2b; i <= 0x3d; i++) {
if (w83781d_read_value(isa, i) !=
i2c_smbus_read_byte_data(client, i))
return 0;
}
if (w83781d_read_value(isa, W83781D_REG_CONFIG) !=
i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG))
return 0;
for (i = 0x43; i <= 0x46; i++) {
if (w83781d_read_value(isa, i) !=
i2c_smbus_read_byte_data(client, i))
return 0;
}
return 1;
}
static int
w83781d_read_value_isa(struct w83781d_data *data, u16 reg)
{
int word_sized, res;
word_sized = (((reg & 0xff00) == 0x100)
|| ((reg & 0xff00) == 0x200))
&& (((reg & 0x00ff) == 0x50)
|| ((reg & 0x00ff) == 0x53)
|| ((reg & 0x00ff) == 0x55));
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
data->isa_addr + W83781D_ADDR_REG_OFFSET);
outb_p(reg >> 8,
data->isa_addr + W83781D_DATA_REG_OFFSET);
}
outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET);
res = inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET);
if (word_sized) {
outb_p((reg & 0xff) + 1,
data->isa_addr + W83781D_ADDR_REG_OFFSET);
res =
(res << 8) + inb_p(data->isa_addr +
W83781D_DATA_REG_OFFSET);
}
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
data->isa_addr + W83781D_ADDR_REG_OFFSET);
outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET);
}
return res;
}
static void
w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value)
{
int word_sized;
word_sized = (((reg & 0xff00) == 0x100)
|| ((reg & 0xff00) == 0x200))
&& (((reg & 0x00ff) == 0x53)
|| ((reg & 0x00ff) == 0x55));
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
data->isa_addr + W83781D_ADDR_REG_OFFSET);
outb_p(reg >> 8,
data->isa_addr + W83781D_DATA_REG_OFFSET);
}
outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET);
if (word_sized) {
outb_p(value >> 8,
data->isa_addr + W83781D_DATA_REG_OFFSET);
outb_p((reg & 0xff) + 1,
data->isa_addr + W83781D_ADDR_REG_OFFSET);
}
outb_p(value & 0xff, data->isa_addr + W83781D_DATA_REG_OFFSET);
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
data->isa_addr + W83781D_ADDR_REG_OFFSET);
outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET);
}
}
/* The SMBus locks itself, usually, but nothing may access the Winbond between
bank switches. ISA access must always be locked explicitly!
We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
would slow down the W83781D access and should not be necessary.
There are some ugly typecasts here, but the good news is - they should
nowhere else be necessary! */
static int
w83781d_read_value(struct w83781d_data *data, u16 reg)
{
struct i2c_client *client = data->client;
int res;
mutex_lock(&data->lock);
if (client)
res = w83781d_read_value_i2c(data, reg);
else
res = w83781d_read_value_isa(data, reg);
mutex_unlock(&data->lock);
return res;
}
static int
w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value)
{
struct i2c_client *client = data->client;
mutex_lock(&data->lock);
if (client)
w83781d_write_value_i2c(data, reg, value);
else
w83781d_write_value_isa(data, reg, value);
mutex_unlock(&data->lock);
return 0;
}
static int __devinit
w83781d_isa_probe(struct platform_device *pdev)
{
int err, reg;
struct w83781d_data *data;
struct resource *res;
/* Reserve the ISA region */
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2,
"w83781d")) {
err = -EBUSY;
goto exit;
}
data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto exit_release_region;
}
mutex_init(&data->lock);
data->isa_addr = res->start;
platform_set_drvdata(pdev, data);
reg = w83781d_read_value(data, W83781D_REG_WCHIPID);
switch (reg) {
case 0x30:
data->type = w83782d;
data->name = "w83782d";
break;
default:
data->type = w83781d;
data->name = "w83781d";
}
/* Initialize the W83781D chip */
w83781d_init_device(&pdev->dev);
/* Register sysfs hooks */
err = w83781d_create_files(&pdev->dev, data->type, 1);
if (err)
goto exit_remove_files;
err = device_create_file(&pdev->dev, &dev_attr_name);
if (err)
goto exit_remove_files;
data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove_files;
}
return 0;
exit_remove_files:
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
device_remove_file(&pdev->dev, &dev_attr_name);
kfree(data);
exit_release_region:
release_region(res->start + W83781D_ADDR_REG_OFFSET, 2);
exit:
return err;
}
static int __devexit
w83781d_isa_remove(struct platform_device *pdev)
{
struct w83781d_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
device_remove_file(&pdev->dev, &dev_attr_name);
release_region(data->isa_addr + W83781D_ADDR_REG_OFFSET, 2);
kfree(data);
return 0;
}
static struct platform_driver w83781d_isa_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "w83781d",
},
.probe = w83781d_isa_probe,
.remove = __devexit_p(w83781d_isa_remove),
};
/* return 1 if a supported chip is found, 0 otherwise */ /* return 1 if a supported chip is found, 0 otherwise */
static int __init static int __init
w83781d_isa_found(unsigned short address) w83781d_isa_found(unsigned short address)
...@@ -1928,18 +1945,14 @@ w83781d_isa_device_add(unsigned short address) ...@@ -1928,18 +1945,14 @@ w83781d_isa_device_add(unsigned short address)
} }
static int __init static int __init
sensors_w83781d_init(void) w83781d_isa_register(void)
{ {
int res; int res;
res = i2c_add_driver(&w83781d_driver);
if (res)
goto exit;
if (w83781d_isa_found(isa_address)) { if (w83781d_isa_found(isa_address)) {
res = platform_driver_register(&w83781d_isa_driver); res = platform_driver_register(&w83781d_isa_driver);
if (res) if (res)
goto exit_unreg_i2c_driver; goto exit;
/* Sets global pdev as a side effect */ /* Sets global pdev as a side effect */
res = w83781d_isa_device_add(isa_address); res = w83781d_isa_device_add(isa_address);
...@@ -1949,21 +1962,94 @@ sensors_w83781d_init(void) ...@@ -1949,21 +1962,94 @@ sensors_w83781d_init(void)
return 0; return 0;
exit_unreg_isa_driver: exit_unreg_isa_driver:
platform_driver_unregister(&w83781d_isa_driver); platform_driver_unregister(&w83781d_isa_driver);
exit_unreg_i2c_driver: exit:
i2c_del_driver(&w83781d_driver);
exit:
return res; return res;
} }
static void __exit static void __exit
sensors_w83781d_exit(void) w83781d_isa_unregister(void)
{ {
if (pdev) { if (pdev) {
platform_device_unregister(pdev); platform_device_unregister(pdev);
platform_driver_unregister(&w83781d_isa_driver); platform_driver_unregister(&w83781d_isa_driver);
} }
}
#else /* !CONFIG_ISA */
static struct w83781d_data *w83781d_data_if_isa(void)
{
return NULL;
}
static int
w83781d_alias_detect(struct i2c_client *client, u8 chipid)
{
return 0;
}
static int
w83781d_read_value(struct w83781d_data *data, u16 reg)
{
int res;
mutex_lock(&data->lock);
res = w83781d_read_value_i2c(data, reg);
mutex_unlock(&data->lock);
return res;
}
static int
w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value)
{
mutex_lock(&data->lock);
w83781d_write_value_i2c(data, reg, value);
mutex_unlock(&data->lock);
return 0;
}
static int __init
w83781d_isa_register(void)
{
return 0;
}
static void __exit
w83781d_isa_unregister(void)
{
}
#endif /* CONFIG_ISA */
static int __init
sensors_w83781d_init(void)
{
int res;
/* We register the ISA device first, so that we can skip the
* registration of an I2C interface to the same device. */
res = w83781d_isa_register();
if (res)
goto exit;
res = i2c_add_driver(&w83781d_driver);
if (res)
goto exit_unreg_isa;
return 0;
exit_unreg_isa:
w83781d_isa_unregister();
exit:
return res;
}
static void __exit
sensors_w83781d_exit(void)
{
w83781d_isa_unregister();
i2c_del_driver(&w83781d_driver); i2c_del_driver(&w83781d_driver);
} }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
Supports following chips: Supports following chips:
Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
w83791d 10 5 3 3 0x71 0x5ca3 yes no w83791d 10 5 5 3 0x71 0x5ca3 yes no
The w83791d chip appears to be part way between the 83781d and the The w83791d chip appears to be part way between the 83781d and the
83792d. Thus, this file is derived from both the w83792d.c and 83792d. Thus, this file is derived from both the w83792d.c and
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#define NUMBER_OF_VIN 10 #define NUMBER_OF_VIN 10
#define NUMBER_OF_FANIN 5 #define NUMBER_OF_FANIN 5
#define NUMBER_OF_TEMPIN 3 #define NUMBER_OF_TEMPIN 3
#define NUMBER_OF_PWM 5
/* Addresses to scan */ /* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
...@@ -116,6 +117,25 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = { ...@@ -116,6 +117,25 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = {
0xBD, /* FAN 5 Count Low Limit in DataSheet */ 0xBD, /* FAN 5 Count Low Limit in DataSheet */
}; };
static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = {
0x81, /* PWM 1 duty cycle register in DataSheet */
0x83, /* PWM 2 duty cycle register in DataSheet */
0x94, /* PWM 3 duty cycle register in DataSheet */
0xA0, /* PWM 4 duty cycle register in DataSheet */
0xA1, /* PWM 5 duty cycle register in DataSheet */
};
static const u8 W83791D_REG_TEMP_TARGET[3] = {
0x85, /* PWM 1 target temperature for temp 1 */
0x86, /* PWM 2 target temperature for temp 2 */
0x96, /* PWM 3 target temperature for temp 3 */
};
static const u8 W83791D_REG_TEMP_TOL[2] = {
0x87, /* PWM 1/2 temperature tolerance */
0x97, /* PWM 3 temperature tolerance */
};
static const u8 W83791D_REG_FAN_CFG[2] = { static const u8 W83791D_REG_FAN_CFG[2] = {
0x84, /* FAN 1/2 configuration */ 0x84, /* FAN 1/2 configuration */
0x95, /* FAN 3 configuration */ 0x95, /* FAN 3 configuration */
...@@ -160,6 +180,7 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = { ...@@ -160,6 +180,7 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = {
0xA3, /* BEEP Control Register 3 */ 0xA3, /* BEEP Control Register 3 */
}; };
#define W83791D_REG_GPIO 0x15
#define W83791D_REG_CONFIG 0x40 #define W83791D_REG_CONFIG 0x40
#define W83791D_REG_VID_FANDIV 0x47 #define W83791D_REG_VID_FANDIV 0x47
#define W83791D_REG_DID_VID4 0x49 #define W83791D_REG_DID_VID4 0x49
...@@ -224,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div) ...@@ -224,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div)
(val) < 0 ? ((val) - 250) / 500 * 128 : \ (val) < 0 ? ((val) - 250) / 500 * 128 : \
((val) + 250) / 500 * 128) ((val) + 250) / 500 * 128)
/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */
#define TARGET_TEMP_TO_REG(val) ((val) < 0 ? 0 : \
(val) >= 127000 ? 127 : \
((val) + 500) / 1000)
/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */
#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \
(val) >= 15000 ? 15 : \
((val) + 500) / 1000)
#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) #define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
#define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff) #define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff)
...@@ -275,6 +305,14 @@ struct w83791d_data { ...@@ -275,6 +305,14 @@ struct w83791d_data {
two sensors with three values two sensors with three values
(cur, over, hyst) */ (cur, over, hyst) */
/* PWMs */
u8 pwm[5]; /* pwm duty cycle */
u8 pwm_enable[3]; /* pwm enable status for fan 1-3
(fan 4-5 only support manual mode) */
u8 temp_target[3]; /* pwm 1-3 target temperature */
u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */
/* Misc */ /* Misc */
u32 alarms; /* realtime status register encoding,combined */ u32 alarms; /* realtime status register encoding,combined */
u8 beep_enable; /* Global beep enable */ u8 beep_enable; /* Global beep enable */
...@@ -652,6 +690,217 @@ static struct sensor_device_attribute sda_fan_alarm[] = { ...@@ -652,6 +690,217 @@ static struct sensor_device_attribute sda_fan_alarm[] = {
SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22), SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22),
}; };
/* read/write PWMs */
static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%u\n", data->pwm[nr]);
}
static ssize_t store_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
unsigned long val;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
mutex_lock(&data->update_lock);
data->pwm[nr] = SENSORS_LIMIT(val, 0, 255);
w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_pwm[] = {
SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO,
show_pwm, store_pwm, 0),
SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO,
show_pwm, store_pwm, 1),
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO,
show_pwm, store_pwm, 2),
SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO,
show_pwm, store_pwm, 3),
SENSOR_ATTR(pwm5, S_IWUSR | S_IRUGO,
show_pwm, store_pwm, 4),
};
static ssize_t show_pwmenable(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%u\n", data->pwm_enable[nr] + 1);
}
static ssize_t store_pwmenable(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
unsigned long val;
u8 reg_cfg_tmp;
u8 reg_idx = 0;
u8 val_shift = 0;
u8 keep_mask = 0;
int ret = strict_strtoul(buf, 10, &val);
if (ret || val < 1 || val > 3)
return -EINVAL;
mutex_lock(&data->update_lock);
data->pwm_enable[nr] = val - 1;
switch (nr) {
case 0:
reg_idx = 0;
val_shift = 2;
keep_mask = 0xf3;
break;
case 1:
reg_idx = 0;
val_shift = 4;
keep_mask = 0xcf;
break;
case 2:
reg_idx = 1;
val_shift = 2;
keep_mask = 0xf3;
break;
}
reg_cfg_tmp = w83791d_read(client, W83791D_REG_FAN_CFG[reg_idx]);
reg_cfg_tmp = (reg_cfg_tmp & keep_mask) |
data->pwm_enable[nr] << val_shift;
w83791d_write(client, W83791D_REG_FAN_CFG[reg_idx], reg_cfg_tmp);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_pwmenable[] = {
SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
show_pwmenable, store_pwmenable, 0),
SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
show_pwmenable, store_pwmenable, 1),
SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
show_pwmenable, store_pwmenable, 2),
};
/* For Smart Fan I / Thermal Cruise */
static ssize_t show_temp_target(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct w83791d_data *data = w83791d_update_device(dev);
int nr = sensor_attr->index;
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr]));
}
static ssize_t store_temp_target(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
unsigned long val;
u8 target_mask;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
mutex_lock(&data->update_lock);
data->temp_target[nr] = TARGET_TEMP_TO_REG(val);
target_mask = w83791d_read(client,
W83791D_REG_TEMP_TARGET[nr]) & 0x80;
w83791d_write(client, W83791D_REG_TEMP_TARGET[nr],
data->temp_target[nr] | target_mask);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_temp_target[] = {
SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO,
show_temp_target, store_temp_target, 0),
SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO,
show_temp_target, store_temp_target, 1),
SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO,
show_temp_target, store_temp_target, 2),
};
static ssize_t show_temp_tolerance(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct w83791d_data *data = w83791d_update_device(dev);
int nr = sensor_attr->index;
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr]));
}
static ssize_t store_temp_tolerance(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
unsigned long val;
u8 target_mask;
u8 reg_idx = 0;
u8 val_shift = 0;
u8 keep_mask = 0;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
switch (nr) {
case 0:
reg_idx = 0;
val_shift = 0;
keep_mask = 0xf0;
break;
case 1:
reg_idx = 0;
val_shift = 4;
keep_mask = 0x0f;
break;
case 2:
reg_idx = 1;
val_shift = 0;
keep_mask = 0xf0;
break;
}
mutex_lock(&data->update_lock);
data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val);
target_mask = w83791d_read(client,
W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask;
w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx],
(data->temp_tolerance[nr] << val_shift) | target_mask);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_temp_tolerance[] = {
SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO,
show_temp_tolerance, store_temp_tolerance, 0),
SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO,
show_temp_tolerance, store_temp_tolerance, 1),
SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO,
show_temp_tolerance, store_temp_tolerance, 2),
};
/* read/write the temperature1, includes measured value and limits */ /* read/write the temperature1, includes measured value and limits */
static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
...@@ -908,8 +1157,6 @@ static struct attribute *w83791d_attributes[] = { ...@@ -908,8 +1157,6 @@ static struct attribute *w83791d_attributes[] = {
FAN_UNIT_ATTRS(0), FAN_UNIT_ATTRS(0),
FAN_UNIT_ATTRS(1), FAN_UNIT_ATTRS(1),
FAN_UNIT_ATTRS(2), FAN_UNIT_ATTRS(2),
FAN_UNIT_ATTRS(3),
FAN_UNIT_ATTRS(4),
TEMP_UNIT_ATTRS(0), TEMP_UNIT_ATTRS(0),
TEMP_UNIT_ATTRS(1), TEMP_UNIT_ATTRS(1),
TEMP_UNIT_ATTRS(2), TEMP_UNIT_ATTRS(2),
...@@ -918,6 +1165,18 @@ static struct attribute *w83791d_attributes[] = { ...@@ -918,6 +1165,18 @@ static struct attribute *w83791d_attributes[] = {
&sda_beep_ctrl[1].dev_attr.attr, &sda_beep_ctrl[1].dev_attr.attr,
&dev_attr_cpu0_vid.attr, &dev_attr_cpu0_vid.attr,
&dev_attr_vrm.attr, &dev_attr_vrm.attr,
&sda_pwm[0].dev_attr.attr,
&sda_pwm[1].dev_attr.attr,
&sda_pwm[2].dev_attr.attr,
&sda_pwmenable[0].dev_attr.attr,
&sda_pwmenable[1].dev_attr.attr,
&sda_pwmenable[2].dev_attr.attr,
&sda_temp_target[0].dev_attr.attr,
&sda_temp_target[1].dev_attr.attr,
&sda_temp_target[2].dev_attr.attr,
&sda_temp_tolerance[0].dev_attr.attr,
&sda_temp_tolerance[1].dev_attr.attr,
&sda_temp_tolerance[2].dev_attr.attr,
NULL NULL
}; };
...@@ -925,6 +1184,20 @@ static const struct attribute_group w83791d_group = { ...@@ -925,6 +1184,20 @@ static const struct attribute_group w83791d_group = {
.attrs = w83791d_attributes, .attrs = w83791d_attributes,
}; };
/* Separate group of attributes for fan/pwm 4-5. Their pins can also be
in use for GPIO in which case their sysfs-interface should not be made
available */
static struct attribute *w83791d_attributes_fanpwm45[] = {
FAN_UNIT_ATTRS(3),
FAN_UNIT_ATTRS(4),
&sda_pwm[3].dev_attr.attr,
&sda_pwm[4].dev_attr.attr,
NULL
};
static const struct attribute_group w83791d_group_fanpwm45 = {
.attrs = w83791d_attributes_fanpwm45,
};
static int w83791d_detect_subclients(struct i2c_client *client) static int w83791d_detect_subclients(struct i2c_client *client)
{ {
...@@ -1056,6 +1329,7 @@ static int w83791d_probe(struct i2c_client *client, ...@@ -1056,6 +1329,7 @@ static int w83791d_probe(struct i2c_client *client,
struct w83791d_data *data; struct w83791d_data *data;
struct device *dev = &client->dev; struct device *dev = &client->dev;
int i, err; int i, err;
u8 has_fanpwm45;
#ifdef DEBUG #ifdef DEBUG
int val1; int val1;
...@@ -1090,15 +1364,27 @@ static int w83791d_probe(struct i2c_client *client, ...@@ -1090,15 +1364,27 @@ static int w83791d_probe(struct i2c_client *client,
if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group))) if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group)))
goto error3; goto error3;
/* Check if pins of fan/pwm 4-5 are in use as GPIO */
has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10;
if (has_fanpwm45) {
err = sysfs_create_group(&client->dev.kobj,
&w83791d_group_fanpwm45);
if (err)
goto error4;
}
/* Everything is ready, now register the working device */ /* Everything is ready, now register the working device */
data->hwmon_dev = hwmon_device_register(dev); data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) { if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev); err = PTR_ERR(data->hwmon_dev);
goto error4; goto error5;
} }
return 0; return 0;
error5:
if (has_fanpwm45)
sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45);
error4: error4:
sysfs_remove_group(&client->dev.kobj, &w83791d_group); sysfs_remove_group(&client->dev.kobj, &w83791d_group);
error3: error3:
...@@ -1236,6 +1522,36 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) ...@@ -1236,6 +1522,36 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04; data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04;
/* Update PWM duty cycle */
for (i = 0; i < NUMBER_OF_PWM; i++) {
data->pwm[i] = w83791d_read(client,
W83791D_REG_PWM[i]);
}
/* Update PWM enable status */
for (i = 0; i < 2; i++) {
reg_array_tmp[i] = w83791d_read(client,
W83791D_REG_FAN_CFG[i]);
}
data->pwm_enable[0] = (reg_array_tmp[0] >> 2) & 0x03;
data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03;
data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03;
/* Update PWM target temperature */
for (i = 0; i < 3; i++) {
data->temp_target[i] = w83791d_read(client,
W83791D_REG_TEMP_TARGET[i]) & 0x7f;
}
/* Update PWM temperature tolerance */
for (i = 0; i < 2; i++) {
reg_array_tmp[i] = w83791d_read(client,
W83791D_REG_TEMP_TOL[i]);
}
data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f;
data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f;
data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f;
/* Update the first temperature sensor */ /* Update the first temperature sensor */
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
data->temp1[i] = w83791d_read(client, data->temp1[i] = w83791d_read(client,
......
...@@ -259,6 +259,35 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) ...@@ -259,6 +259,35 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev)
} }
printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); printk(KERN_INFO "PowerMac i2c bus %s registered\n", name);
if (!strncmp(basename, "uni-n", 5)) {
struct device_node *np;
const u32 *prop;
struct i2c_board_info info;
/* Instantiate I2C motion sensor if present */
np = of_find_node_by_name(NULL, "accelerometer");
if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") &&
(prop = of_get_property(np, "reg", NULL))) {
int i2c_bus;
const char *tmp_bus;
/* look for bus either using "reg" or by path */
tmp_bus = strstr(np->full_name, "/i2c-bus@");
if (tmp_bus)
i2c_bus = *(tmp_bus + 9) - '0';
else
i2c_bus = ((*prop) >> 8) & 0x0f;
if (pmac_i2c_get_channel(bus) == i2c_bus) {
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = ((*prop) & 0xff) >> 1;
strlcpy(info.type, "ams", I2C_NAME_SIZE);
i2c_new_device(adapter, &info);
}
}
}
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment