提交 bdbf0ac7 编写于 作者: L 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
  ...
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,23 +99,19 @@ static struct pmf_irq_client ams_shock_client = { ...@@ -99,23 +99,19 @@ 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);
if (ams_info.has_device) {
unsigned long flags; unsigned long flags;
u8 irqs_to_clear;
mutex_lock(&ams_info.lock);
spin_lock_irqsave(&ams_info.irq_lock, flags); spin_lock_irqsave(&ams_info.irq_lock, flags);
irqs_to_clear = ams_info.worker_irqs;
if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
if (verbose) if (verbose)
printk(KERN_INFO "ams: freefall detected!\n"); printk(KERN_INFO "ams: freefall detected!\n");
ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
/* we must call this with interrupts enabled */
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) {
...@@ -123,15 +119,11 @@ static void ams_worker(struct work_struct *work) ...@@ -123,15 +119,11 @@ static void ams_worker(struct work_struct *work)
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);
ams_info.clear_irq(AMS_IRQ_SHOCK);
spin_lock_irqsave(&ams_info.irq_lock, flags);
} }
spin_unlock_irqrestore(&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,12 +215,12 @@ int __init ams_init(void) ...@@ -223,12 +215,12 @@ int __init ams_init(void)
void ams_exit(void) void ams_exit(void)
{ {
mutex_lock(&ams_info.lock);
if (ams_info.has_device) {
/* Remove input device */ /* Remove input device */
ams_input_exit(); ams_input_exit();
/* Remove attributes */
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
/* Shut down implementation */ /* Shut down implementation */
ams_info.exit(); ams_info.exit();
...@@ -239,18 +231,12 @@ void ams_exit(void) ...@@ -239,18 +231,12 @@ void ams_exit(void)
*/ */
flush_scheduled_work(); flush_scheduled_work();
/* Remove attributes */
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
/* Remove device */ /* Remove device */
of_device_unregister(ams_info.of_dev); of_device_unregister(ams_info.of_dev);
/* Remove handler */ /* Remove handler */
pmf_unregister_irq_client(&ams_shock_client); pmf_unregister_irq_client(&ams_shock_client);
pmf_unregister_irq_client(&ams_freefall_client); pmf_unregister_irq_client(&ams_freefall_client);
}
mutex_unlock(&ams_info.lock);
} }
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)
error = ams_input_enable();
else else
ams_input_disable(); 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;
}
if (!request_region(address + 4, 4, "lm78")) {
pr_debug("lm78: Failed to request high part of region\n");
release_region(address, 4);
return 0; 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);
dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
"COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
address, company, verstep);
if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) { /* All supported chips have the version in common */
err = -ENOMEM; if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) {
goto ERROR0; dev_dbg(&adapter->dev, "Autodetection failed: "
"unsupported version\n");
return -ENODEV;
} }
kind = any_chip;
client = &data->client; /* Now, refine the detection */
i2c_set_clientdata(client, data); if (company == LM85_COMPANY_NATIONAL) {
client->addr = address; switch (verstep) {
client->adapter = adapter; case LM85_VERSTEP_LM85C:
client->driver = &lm85_driver;
/* Now, we do the remaining detection. */
company = lm85_read_value(client, LM85_REG_COMPANY);
verstep = lm85_read_value(client, LM85_REG_VERSTEP);
dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with"
" COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
i2c_adapter_id(client->adapter), client->addr,
company, verstep);
/* If auto-detecting, Determine the chip type. */
if (kind <= 0) {
dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x ...\n",
i2c_adapter_id(adapter), address);
if (company == LM85_COMPANY_NATIONAL
&& verstep == LM85_VERSTEP_LM85C) {
kind = lm85c; kind = lm85c;
} else if (company == LM85_COMPANY_NATIONAL break;
&& verstep == LM85_VERSTEP_LM85B) { case LM85_VERSTEP_LM85B:
kind = lm85b; kind = lm85b;
} 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_ANALOG_DEV) {
" Defaulting to LM85.\n", verstep); switch (verstep) {
kind = any_chip; case LM85_VERSTEP_ADM1027:
} else if (company == LM85_COMPANY_ANALOG_DEV
&& verstep == LM85_VERSTEP_ADM1027) {
kind = adm1027; kind = adm1027;
} else if (company == LM85_COMPANY_ANALOG_DEV break;
&& (verstep == LM85_VERSTEP_ADT7463 case LM85_VERSTEP_ADT7463:
|| verstep == LM85_VERSTEP_ADT7463C)) { case LM85_VERSTEP_ADT7463C:
kind = adt7463; kind = adt7463;
} else if (company == LM85_COMPANY_ANALOG_DEV 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 Generic LM85.\n", verstep); switch (verstep) {
kind = any_chip; case LM85_VERSTEP_EMC6D100_A0:
} else if (company == LM85_COMPANY_SMSC case LM85_VERSTEP_EMC6D100_A1:
&& (verstep == LM85_VERSTEP_EMC6D100_A0 /* Note: we can't tell a '100 from a '101 */
|| 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; kind = emc6d100;
} else if (company == LM85_COMPANY_SMSC break;
&& verstep == LM85_VERSTEP_EMC6D102) { case LM85_VERSTEP_EMC6D102:
kind = emc6d102; kind = emc6d102;
} else if (company == LM85_COMPANY_SMSC break;
&& (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;
}
static int lm85_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lm85_data *data;
int err;
data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
/* Fill in the remaining client fields */ i2c_set_clientdata(client, data);
data->type = kind; 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;
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->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;
} }
......
此差异已折叠。
...@@ -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; \
......
此差异已折叠。
...@@ -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% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册