mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-26 22:24:09 +08:00
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 ...
This commit is contained in:
commit
bdbf0ac7e1
76
Documentation/hwmon/adt7470
Normal file
76
Documentation/hwmon/adt7470
Normal file
@ -0,0 +1,76 @@
|
||||
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.
|
||||
The IT87xx only updates its values each 1.5 seconds; reading it more often
|
||||
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'.
|
||||
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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
the bahaviour of fans. Write 1 to let fans spinning at
|
||||
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,
|
||||
depending on the hardware configuration.
|
||||
|
||||
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
|
||||
the BIOS configured the chip correctly. In that respect, it differs from
|
||||
the original driver (from lm_sensors for Linux 2.4), which would force the
|
||||
LM87 to an arbitrary, compile-time chosen mode, regardless of the actual
|
||||
chipset wiring.
|
||||
time. Which are depends on the hardware setup. This driver normally
|
||||
assumes that firmware configured the chip correctly. Where this is not
|
||||
the case, platform code must set the I2C client's platform_data to point
|
||||
to a u8 value to be written to the channel register.
|
||||
|
||||
For reference, here is the list of exclusive functions:
|
||||
- in0+in5 (default) or temp3
|
||||
|
@ -11,7 +11,7 @@ Supported chips:
|
||||
Prefix: 'lm99'
|
||||
Addresses scanned: I2C 0x4c and 0x4d
|
||||
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
|
||||
Prefix: 'lm99'
|
||||
Addresses scanned: I2C 0x4c and 0x4d
|
||||
@ -21,18 +21,32 @@ Supported chips:
|
||||
Prefix: 'lm86'
|
||||
Addresses scanned: I2C 0x4c
|
||||
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
|
||||
Prefix: 'adm1032'
|
||||
Addresses scanned: I2C 0x4c and 0x4d
|
||||
Datasheet: Publicly available at the Analog Devices website
|
||||
http://www.analog.com/en/prod/0,2877,ADM1032,00.html
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
http://www.onsemi.com/PowerSolutions/product.do?id=ADM1032
|
||||
* Analog Devices ADT7461
|
||||
Prefix: 'adt7461'
|
||||
Addresses scanned: I2C 0x4c and 0x4d
|
||||
Datasheet: Publicly available at the Analog Devices website
|
||||
http://www.analog.com/en/prod/0,2877,ADT7461,00.html
|
||||
Note: Only if in ADM1032 compatibility mode
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461
|
||||
* 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
|
||||
Prefix: 'max6657'
|
||||
Addresses scanned: I2C 0x4c
|
||||
@ -70,25 +84,21 @@ Description
|
||||
|
||||
The LM90 is a digital temperature sensor. It senses its own temperature as
|
||||
well as the temperature of up to one external diode. It is compatible
|
||||
with many other devices such as the LM86, the LM89, the LM99, the ADM1032,
|
||||
the MAX6657, MAX6658, MAX6659, MAX6680 and the MAX6681 all of which are
|
||||
supported by this driver.
|
||||
with many other devices, many of which are supported by this driver.
|
||||
|
||||
Note that there is no easy way to differentiate between the MAX6657,
|
||||
MAX6658 and MAX6659 variants. The extra address and features of the
|
||||
MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only
|
||||
differ in their pinout, therefore they obviously can't (and don't need to)
|
||||
be distinguished. Additionally, the ADT7461 is supported if found in
|
||||
ADM1032 compatibility mode.
|
||||
be distinguished.
|
||||
|
||||
The specificity of this family of chipsets over the ADM1021/LM84
|
||||
family is that it features critical limits with hysteresis, and an
|
||||
increased resolution of the remote temperature measurement.
|
||||
|
||||
The different chipsets of the family are not strictly identical, although
|
||||
very similar. This driver doesn't handle any specific feature for now,
|
||||
with the exception of SMBus PEC. For reference, here comes a non-exhaustive
|
||||
list of specific features:
|
||||
very similar. For reference, here comes a non-exhaustive list of specific
|
||||
features:
|
||||
|
||||
LM90:
|
||||
* Filter and alert configuration register at 0xBF.
|
||||
@ -114,9 +124,11 @@ ADT7461:
|
||||
* Lower resolution for remote temperature
|
||||
|
||||
MAX6657 and MAX6658:
|
||||
* Better local resolution
|
||||
* Remote sensor type selection
|
||||
|
||||
MAX6659:
|
||||
* Better local resolution
|
||||
* Selectable address
|
||||
* Second critical temperature limit
|
||||
* Remote sensor type selection
|
||||
@ -127,7 +139,8 @@ MAX6680 and MAX6681:
|
||||
|
||||
All temperature values are given in degrees Celsius. Resolution
|
||||
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.
|
||||
Additionally, there is a relative hysteresis value common to both critical
|
||||
|
@ -5,12 +5,7 @@ Supported chips:
|
||||
* National Semiconductor PC87360, PC87363, PC87364, PC87365 and PC87366
|
||||
Prefixes: 'pc87360', 'pc87363', 'pc87364', 'pc87365', 'pc87366'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheets:
|
||||
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
|
||||
Datasheets: No longer available
|
||||
|
||||
Authors: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
|
@ -5,7 +5,7 @@ Supported chips:
|
||||
* National Semiconductor PC87427
|
||||
Prefix: 'pc87427'
|
||||
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>
|
||||
|
||||
|
@ -353,7 +353,7 @@ in6=255
|
||||
|
||||
# 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:
|
||||
|
||||
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
|
||||
long as we don't know more, the w83781d driver doesn't handle PWM on
|
||||
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
|
||||
found out the following values do work as a form of coarse pwm:
|
||||
@ -418,3 +418,36 @@ change.
|
||||
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
|
||||
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
|
||||
currently selected). If you know the address of the chip, use a 'force'
|
||||
parameter; this will put it into a more well-behaved state first.
|
||||
|
||||
The driver implements three temperature sensors, five fan rotation speed
|
||||
sensors, and ten voltage sensors.
|
||||
The driver implements three temperature sensors, 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
|
||||
degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
|
||||
the temperature gets higher than the Overtemperature Shutdown value; it stays
|
||||
on until the temperature falls below the Hysteresis value.
|
||||
|
||||
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
|
||||
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,
|
||||
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.
|
||||
An alarm is triggered if the voltage has crossed a programmable minimum
|
||||
or maximum limit.
|
||||
Each fan controlled is controlled by PWM. The PWM duty cycle can be read and
|
||||
set for each fan separately. Valid values range from 0 (stop) to 255 (full).
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
The sysfs interface to the beep bitmask has migrated from the original legacy
|
||||
@ -105,6 +111,27 @@ going forward.
|
||||
The driver reads the hardware chip values at most once every three seconds.
|
||||
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
|
||||
------------------------------------
|
||||
For legacy code using the alarms and beep_mask files:
|
||||
@ -132,7 +159,3 @@ tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch
|
||||
tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch
|
||||
case_open : alarms: 0x001000 beep_mask: 0x001000
|
||||
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
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for National Semiconductor LM90,
|
||||
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657,
|
||||
MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips.
|
||||
|
||||
The Analog Devices ADT7461 sensor chip is also supported, but only
|
||||
if found in ADM1032 compatibility mode.
|
||||
LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim
|
||||
MAX6646, MAX6647, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680 and
|
||||
MAX6681 sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm90.
|
||||
|
@ -279,7 +279,6 @@ struct adm1026_data {
|
||||
u8 fan_min[8]; /* Register value */
|
||||
u8 fan_div[8]; /* Decoded value */
|
||||
struct pwm_data pwm1; /* Pwm control values */
|
||||
int vid; /* Decoded value */
|
||||
u8 vrm; /* VRM version */
|
||||
u8 analog_out; /* Register value (DAC) */
|
||||
long alarms; /* Register encoding, combined */
|
||||
@ -455,7 +454,7 @@ static void adm1026_print_gpio(struct i2c_client *client)
|
||||
struct adm1026_data *data = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
dev_dbg(&client->dev, "GPIO config is:");
|
||||
dev_dbg(&client->dev, "GPIO config is:\n");
|
||||
for (i = 0;i <= 7;++i) {
|
||||
if (data->config2 & (1 << i)) {
|
||||
dev_dbg(&client->dev, "\t%sGP%s%d\n",
|
||||
@ -697,8 +696,6 @@ static struct adm1026_data *adm1026_update_device(struct device *dev)
|
||||
data->last_config = jiffies;
|
||||
}; /* last_config */
|
||||
|
||||
dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n");
|
||||
data->vid = (data->gpio >> 11) & 0x1f;
|
||||
data->valid = 1;
|
||||
mutex_unlock(&data->update_lock);
|
||||
return data;
|
||||
@ -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)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -1681,17 +1681,16 @@ static int adm1026_detect(struct i2c_client *client, int kind,
|
||||
kind = adm1026;
|
||||
} else if (company == ADM1026_COMPANY_ANALOG_DEV
|
||||
&& (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
|
||||
dev_err(&adapter->dev, ": Unrecognized stepping "
|
||||
dev_err(&adapter->dev, "Unrecognized stepping "
|
||||
"0x%02x. Defaulting to ADM1026.\n", verstep);
|
||||
kind = adm1026;
|
||||
} 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",
|
||||
verstep);
|
||||
kind = any_chip;
|
||||
} else {
|
||||
dev_dbg(&adapter->dev, ": Autodetection "
|
||||
"failed\n");
|
||||
dev_dbg(&adapter->dev, "Autodetection failed\n");
|
||||
/* Not an ADM1026 ... */
|
||||
if (kind == 0) { /* User used force=x,y */
|
||||
dev_err(&adapter->dev, "Generic ADM1026 not "
|
||||
|
@ -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 adm1029_data *data = adm1029_update_device(dev);
|
||||
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) {
|
||||
return sprintf(buf, "0\n");
|
||||
}
|
||||
@ -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 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, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
|
||||
}
|
||||
|
@ -99,40 +99,32 @@ static struct pmf_irq_client ams_shock_client = {
|
||||
*/
|
||||
static void ams_worker(struct work_struct *work)
|
||||
{
|
||||
unsigned long flags;
|
||||
u8 irqs_to_clear;
|
||||
|
||||
mutex_lock(&ams_info.lock);
|
||||
|
||||
if (ams_info.has_device) {
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&ams_info.irq_lock, flags);
|
||||
irqs_to_clear = ams_info.worker_irqs;
|
||||
|
||||
spin_lock_irqsave(&ams_info.irq_lock, flags);
|
||||
if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
|
||||
if (verbose)
|
||||
printk(KERN_INFO "ams: freefall detected!\n");
|
||||
|
||||
if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
|
||||
if (verbose)
|
||||
printk(KERN_INFO "ams: freefall detected!\n");
|
||||
|
||||
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 (verbose)
|
||||
printk(KERN_INFO "ams: shock detected!\n");
|
||||
|
||||
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);
|
||||
ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
|
||||
}
|
||||
|
||||
if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
|
||||
if (verbose)
|
||||
printk(KERN_INFO "ams: shock detected!\n");
|
||||
|
||||
ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ams_info.irq_lock, flags);
|
||||
|
||||
ams_info.clear_irq(irqs_to_clear);
|
||||
|
||||
mutex_unlock(&ams_info.lock);
|
||||
}
|
||||
|
||||
@ -223,34 +215,28 @@ int __init ams_init(void)
|
||||
|
||||
void ams_exit(void)
|
||||
{
|
||||
mutex_lock(&ams_info.lock);
|
||||
/* Remove input device */
|
||||
ams_input_exit();
|
||||
|
||||
if (ams_info.has_device) {
|
||||
/* Remove input device */
|
||||
ams_input_exit();
|
||||
/* Remove attributes */
|
||||
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
|
||||
|
||||
/* Shut down implementation */
|
||||
ams_info.exit();
|
||||
/* Shut down implementation */
|
||||
ams_info.exit();
|
||||
|
||||
/* Flush interrupt worker
|
||||
*
|
||||
* We do this after ams_info.exit(), because an interrupt might
|
||||
* have arrived before disabling them.
|
||||
*/
|
||||
flush_scheduled_work();
|
||||
/* Flush interrupt worker
|
||||
*
|
||||
* We do this after ams_info.exit(), because an interrupt might
|
||||
* have arrived before disabling them.
|
||||
*/
|
||||
flush_scheduled_work();
|
||||
|
||||
/* Remove attributes */
|
||||
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
|
||||
/* Remove device */
|
||||
of_device_unregister(ams_info.of_dev);
|
||||
|
||||
/* Remove device */
|
||||
of_device_unregister(ams_info.of_dev);
|
||||
|
||||
/* Remove handler */
|
||||
pmf_unregister_irq_client(&ams_shock_client);
|
||||
pmf_unregister_irq_client(&ams_freefall_client);
|
||||
}
|
||||
|
||||
mutex_unlock(&ams_info.lock);
|
||||
/* Remove handler */
|
||||
pmf_unregister_irq_client(&ams_shock_client);
|
||||
pmf_unregister_irq_client(&ams_freefall_client);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
|
||||
|
@ -60,26 +60,34 @@ enum ams_i2c_cmd {
|
||||
AMS_CMD_START,
|
||||
};
|
||||
|
||||
static int ams_i2c_attach(struct i2c_adapter *adapter);
|
||||
static int ams_i2c_detach(struct i2c_adapter *adapter);
|
||||
static int ams_i2c_probe(struct i2c_client *client,
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "ams",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.attach_adapter = ams_i2c_attach,
|
||||
.detach_adapter = ams_i2c_detach,
|
||||
.probe = ams_i2c_probe,
|
||||
.remove = ams_i2c_remove,
|
||||
.id_table = ams_id,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -152,9 +160,9 @@ static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
|
||||
*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 result;
|
||||
|
||||
@ -162,17 +170,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter)
|
||||
if (unlikely(ams_info.has_device))
|
||||
return -ENODEV;
|
||||
|
||||
if (strncmp(adapter->name, "uni-n", 5))
|
||||
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");
|
||||
ams_info.i2c_client = client;
|
||||
|
||||
if (ams_i2c_cmd(AMS_CMD_RESET)) {
|
||||
printk(KERN_INFO "ams: Failed to reset the device\n");
|
||||
@ -237,7 +235,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter)
|
||||
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) {
|
||||
/* Disable interrupts */
|
||||
@ -261,11 +259,7 @@ static void ams_i2c_exit(void)
|
||||
|
||||
int __init ams_i2c_init(struct device_node *np)
|
||||
{
|
||||
char *tmp_bus;
|
||||
int result;
|
||||
const u32 *prop;
|
||||
|
||||
mutex_lock(&ams_info.lock);
|
||||
|
||||
/* Set implementation stuff */
|
||||
ams_info.of_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.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);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ams_info.lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -20,13 +20,15 @@
|
||||
#include "ams.h"
|
||||
|
||||
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");
|
||||
|
||||
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");
|
||||
|
||||
static DEFINE_MUTEX(ams_input_mutex);
|
||||
|
||||
static void ams_idev_poll(struct input_polled_dev *dev)
|
||||
{
|
||||
struct input_dev *idev = dev->input;
|
||||
@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev)
|
||||
}
|
||||
|
||||
/* Call with ams_info.lock held! */
|
||||
static void ams_input_enable(void)
|
||||
static int ams_input_enable(void)
|
||||
{
|
||||
struct input_dev *input;
|
||||
s8 x, y, z;
|
||||
|
||||
if (ams_info.idev)
|
||||
return;
|
||||
int error;
|
||||
|
||||
ams_sensors(&x, &y, &z);
|
||||
ams_info.xcalib = x;
|
||||
@ -65,7 +65,7 @@ static void ams_input_enable(void)
|
||||
|
||||
ams_info.idev = input_allocate_polled_device();
|
||||
if (!ams_info.idev)
|
||||
return;
|
||||
return -ENOMEM;
|
||||
|
||||
ams_info.idev->poll = ams_idev_poll;
|
||||
ams_info.idev->poll_interval = 25;
|
||||
@ -84,14 +84,18 @@ static void ams_input_enable(void)
|
||||
set_bit(EV_KEY, input->evbit);
|
||||
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);
|
||||
ams_info.idev = NULL;
|
||||
return;
|
||||
return error;
|
||||
}
|
||||
|
||||
joystick = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call with ams_info.lock held! */
|
||||
static void ams_input_disable(void)
|
||||
{
|
||||
if (ams_info.idev) {
|
||||
@ -99,6 +103,8 @@ static void ams_input_disable(void)
|
||||
input_free_polled_device(ams_info.idev);
|
||||
ams_info.idev = NULL;
|
||||
}
|
||||
|
||||
joystick = 0;
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
|
||||
mutex_lock(&ams_info.lock);
|
||||
mutex_lock(&ams_input_mutex);
|
||||
|
||||
if (joystick)
|
||||
ams_input_enable();
|
||||
else
|
||||
ams_input_disable();
|
||||
if (enable != joystick) {
|
||||
if (enable)
|
||||
error = ams_input_enable();
|
||||
else
|
||||
ams_input_disable();
|
||||
}
|
||||
|
||||
mutex_unlock(&ams_info.lock);
|
||||
mutex_unlock(&ams_input_mutex);
|
||||
|
||||
return count;
|
||||
return error ? error : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR,
|
||||
ams_input_show_joystick, ams_input_store_joystick);
|
||||
|
||||
/* Call with ams_info.lock held! */
|
||||
int ams_input_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick);
|
||||
|
||||
if (!result && joystick)
|
||||
if (joystick)
|
||||
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)
|
||||
{
|
||||
ams_input_disable();
|
||||
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)
|
||||
const u32 *prop;
|
||||
int result;
|
||||
|
||||
mutex_lock(&ams_info.lock);
|
||||
|
||||
/* Set implementation stuff */
|
||||
ams_info.of_node = np;
|
||||
ams_info.exit = ams_pmu_exit;
|
||||
@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np)
|
||||
|
||||
/* Get PMU command, should be 0x4e, but we can never know */
|
||||
prop = of_get_property(ams_info.of_node, "reg", NULL);
|
||||
if (!prop) {
|
||||
result = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
if (!prop)
|
||||
return -ENODEV;
|
||||
|
||||
ams_pmu_cmd = ((*prop) >> 8) & 0xff;
|
||||
|
||||
/* Disable interrupts */
|
||||
@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np)
|
||||
|
||||
result = ams_sensor_attach();
|
||||
if (result < 0)
|
||||
goto exit;
|
||||
return result;
|
||||
|
||||
/* Set default values */
|
||||
ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
|
||||
@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np)
|
||||
|
||||
printk(KERN_INFO "ams: Found PMU based motion sensor\n");
|
||||
|
||||
result = 0;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ams_info.lock);
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
@ -46,9 +46,7 @@ struct ams {
|
||||
|
||||
#ifdef CONFIG_SENSORS_AMS_I2C
|
||||
/* I2C properties */
|
||||
int i2c_bus;
|
||||
int i2c_address;
|
||||
struct i2c_client i2c_client;
|
||||
struct i2c_client *i2c_client;
|
||||
#endif
|
||||
|
||||
/* Joystick emulation */
|
||||
|
@ -1118,3 +1118,10 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(aem_init);
|
||||
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");
|
||||
|
||||
module_init(ibmpex_init);
|
||||
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,
|
||||
if (reg & (1 << nr))
|
||||
return sprintf(buf, "3\n"); /* thermal diode */
|
||||
if (reg & (8 << nr))
|
||||
return sprintf(buf, "2\n"); /* thermistor */
|
||||
return sprintf(buf, "4\n"); /* thermistor */
|
||||
return sprintf(buf, "0\n"); /* disabled */
|
||||
}
|
||||
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 &= ~(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)
|
||||
data->sensor |= 1 << nr;
|
||||
else if (val == 2)
|
||||
else if (val == 4)
|
||||
data->sensor |= 8 << nr;
|
||||
else if (val != 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
@ -114,25 +114,16 @@ static inline int TEMP_FROM_REG(s8 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 i2c_client client;
|
||||
struct i2c_client *client;
|
||||
struct device *hwmon_dev;
|
||||
struct mutex lock;
|
||||
enum chips type;
|
||||
|
||||
/* For ISA device only */
|
||||
const char *name;
|
||||
int isa_addr;
|
||||
|
||||
struct mutex update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
@ -151,9 +142,11 @@ struct lm78_data {
|
||||
};
|
||||
|
||||
|
||||
static int lm78_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind);
|
||||
static int lm78_detach_client(struct i2c_client *client);
|
||||
static int lm78_i2c_detect(struct i2c_client *client, int kind,
|
||||
struct i2c_board_info *info);
|
||||
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 __devexit lm78_isa_remove(struct platform_device *pdev);
|
||||
@ -164,12 +157,23 @@ static struct lm78_data *lm78_update_device(struct device *dev);
|
||||
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 = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm78",
|
||||
},
|
||||
.attach_adapter = lm78_attach_adapter,
|
||||
.detach_client = lm78_detach_client,
|
||||
.probe = lm78_i2c_probe,
|
||||
.remove = lm78_i2c_remove,
|
||||
.id_table = lm78_i2c_id,
|
||||
.detect = lm78_i2c_detect,
|
||||
.address_data = &addr_data,
|
||||
};
|
||||
|
||||
static struct platform_driver lm78_isa_driver = {
|
||||
@ -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(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[] = {
|
||||
&sensor_dev_attr_in0_input.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
|
||||
{
|
||||
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);
|
||||
|
||||
/* This function is called by i2c_probe */
|
||||
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
|
||||
/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
|
||||
static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
|
||||
{
|
||||
int i, err;
|
||||
struct i2c_client *new_client;
|
||||
struct lm78_data *data;
|
||||
const char *client_name = "";
|
||||
struct lm78_data *isa;
|
||||
int i;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
err = -ENODEV;
|
||||
goto ERROR1;
|
||||
if (!pdev) /* No ISA chip */
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
/* OK. For now, we presume we have a valid client. We now create the
|
||||
client structure, even though we cannot fill it completely yet.
|
||||
But it allows us to access lm78_{read,write}_value. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
goto ERROR1;
|
||||
}
|
||||
static int lm78_i2c_detect(struct i2c_client *client, int kind,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
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;
|
||||
|
||||
new_client = &data->client;
|
||||
i2c_set_clientdata(new_client, data);
|
||||
new_client->addr = address;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &lm78_driver;
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
/* We block updates of the ISA device to minimize the risk of
|
||||
concurrent access to the same LM78 chip through different
|
||||
interfaces. */
|
||||
if (isa)
|
||||
mutex_lock(&isa->update_lock);
|
||||
|
||||
/* Now, we do the remaining detection. */
|
||||
if (kind < 0) {
|
||||
if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) {
|
||||
err = -ENODEV;
|
||||
goto ERROR2;
|
||||
}
|
||||
if (lm78_read_value(data, LM78_REG_I2C_ADDR) !=
|
||||
address) {
|
||||
err = -ENODEV;
|
||||
goto ERROR2;
|
||||
}
|
||||
if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
|
||||
|| i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR)
|
||||
!= address)
|
||||
goto err_nodev;
|
||||
|
||||
/* Explicitly prevent the misdetection of Winbond chips */
|
||||
i = i2c_smbus_read_byte_data(client, 0x4f);
|
||||
if (i == 0xa3 || i == 0x5c)
|
||||
goto err_nodev;
|
||||
}
|
||||
|
||||
/* Determine the chip type. */
|
||||
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 */
|
||||
|| i == 0x40) /* LM78-J */
|
||||
kind = lm78;
|
||||
@ -586,33 +602,59 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
|
||||
"parameter for unknown chip at "
|
||||
"adapter %d, address 0x%02x\n",
|
||||
i2c_adapter_id(adapter), address);
|
||||
err = -ENODEV;
|
||||
goto ERROR2;
|
||||
goto err_nodev;
|
||||
}
|
||||
|
||||
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) {
|
||||
client_name = "lm78";
|
||||
} else if (kind == lm79) {
|
||||
if (isa)
|
||||
mutex_unlock(&isa->update_lock);
|
||||
|
||||
switch (kind) {
|
||||
case lm79:
|
||||
client_name = "lm79";
|
||||
break;
|
||||
default:
|
||||
client_name = "lm78";
|
||||
}
|
||||
strlcpy(info->type, client_name, I2C_NAME_SIZE);
|
||||
|
||||
/* Fill in the remaining client fields and put into the global list */
|
||||
strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
|
||||
data->type = kind;
|
||||
return 0;
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto ERROR2;
|
||||
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;
|
||||
|
||||
data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
data->client = client;
|
||||
data->type = id->driver_data;
|
||||
|
||||
/* Initialize the LM78 chip */
|
||||
lm78_init_device(data);
|
||||
|
||||
/* 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;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&new_client->dev);
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto ERROR4;
|
||||
@ -621,26 +663,18 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
|
||||
return 0;
|
||||
|
||||
ERROR4:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm78_group);
|
||||
ERROR3:
|
||||
i2c_detach_client(new_client);
|
||||
ERROR2:
|
||||
kfree(data);
|
||||
ERROR1:
|
||||
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);
|
||||
int err;
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm78_group);
|
||||
|
||||
if ((err = i2c_detach_client(client)))
|
||||
return err;
|
||||
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
@ -651,11 +685,10 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
|
||||
int err;
|
||||
struct lm78_data *data;
|
||||
struct resource *res;
|
||||
const char *name;
|
||||
|
||||
/* Reserve the ISA region */
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!request_region(res->start, LM78_EXTENT, "lm78")) {
|
||||
if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) {
|
||||
err = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
@ -665,18 +698,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
|
||||
goto exit_release_region;
|
||||
}
|
||||
mutex_init(&data->lock);
|
||||
data->client.addr = res->start;
|
||||
i2c_set_clientdata(&data->client, data);
|
||||
data->isa_addr = res->start;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
|
||||
data->type = lm79;
|
||||
name = "lm79";
|
||||
data->name = "lm79";
|
||||
} else {
|
||||
data->type = lm78;
|
||||
name = "lm78";
|
||||
data->name = "lm78";
|
||||
}
|
||||
strlcpy(data->client.name, name, I2C_NAME_SIZE);
|
||||
|
||||
/* Initialize the LM78 chip */
|
||||
lm78_init_device(data);
|
||||
@ -699,7 +730,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
|
||||
device_remove_file(&pdev->dev, &dev_attr_name);
|
||||
kfree(data);
|
||||
exit_release_region:
|
||||
release_region(res->start, LM78_EXTENT);
|
||||
release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
@ -707,13 +738,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
|
||||
static int __devexit lm78_isa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lm78_data *data = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
|
||||
device_remove_file(&pdev->dev, &dev_attr_name);
|
||||
release_region(data->client.addr, LM78_EXTENT);
|
||||
kfree(data);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -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. */
|
||||
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;
|
||||
mutex_lock(&data->lock);
|
||||
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
|
||||
res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
|
||||
outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
|
||||
res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
return res;
|
||||
} else
|
||||
@ -746,12 +780,12 @@ static int lm78_read_value(struct lm78_data *data, u8 reg)
|
||||
nowhere else be necessary! */
|
||||
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);
|
||||
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
|
||||
outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
|
||||
outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
|
||||
outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
return 0;
|
||||
} else
|
||||
@ -837,8 +871,17 @@ static int __init lm78_isa_found(unsigned short address)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#define REALLY_SLOW_IO
|
||||
/* We need the timeouts for at least some LM78-like
|
||||
@ -901,7 +944,8 @@ static int __init lm78_isa_found(unsigned short address)
|
||||
val & 0x80 ? "LM79" : "LM78", (int)address);
|
||||
|
||||
release:
|
||||
release_region(address, LM78_EXTENT);
|
||||
release_region(address + 4, 4);
|
||||
release_region(address, 4);
|
||||
return found;
|
||||
}
|
||||
|
||||
@ -949,14 +993,12 @@ static int __init sm_lm78_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = i2c_add_driver(&lm78_driver);
|
||||
if (res)
|
||||
goto exit;
|
||||
|
||||
/* We register the ISA device first, so that we can skip the
|
||||
* registration of an I2C interface to the same device. */
|
||||
if (lm78_isa_found(isa_address)) {
|
||||
res = platform_driver_register(&lm78_isa_driver);
|
||||
if (res)
|
||||
goto exit_unreg_i2c_driver;
|
||||
goto exit;
|
||||
|
||||
/* Sets global pdev as a side effect */
|
||||
res = lm78_isa_device_add(isa_address);
|
||||
@ -964,12 +1006,16 @@ static int __init sm_lm78_init(void)
|
||||
goto exit_unreg_isa_driver;
|
||||
}
|
||||
|
||||
res = i2c_add_driver(&lm78_driver);
|
||||
if (res)
|
||||
goto exit_unreg_isa_device;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_unreg_isa_device:
|
||||
platform_device_unregister(pdev);
|
||||
exit_unreg_isa_driver:
|
||||
platform_driver_unregister(&lm78_isa_driver);
|
||||
exit_unreg_i2c_driver:
|
||||
i2c_del_driver(&lm78_driver);
|
||||
exit:
|
||||
return res;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
|
||||
Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
|
||||
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>
|
||||
|
||||
@ -173,40 +174,39 @@ static int RANGE_TO_REG(int range)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (range >= lm85_range_map[15])
|
||||
return 15;
|
||||
|
||||
/* Find the closest match */
|
||||
for (i = 14; i >= 0; --i) {
|
||||
if (range >= lm85_range_map[i]) {
|
||||
if ((lm85_range_map[i + 1] - range) <
|
||||
(range - lm85_range_map[i]))
|
||||
return i + 1;
|
||||
return i;
|
||||
}
|
||||
for (i = 0; i < 15; ++i) {
|
||||
if (range <= (lm85_range_map[i] + lm85_range_map[i + 1]) / 2)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return i;
|
||||
}
|
||||
#define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]
|
||||
|
||||
/* These are the PWM frequency encodings */
|
||||
static const int lm85_freq_map[] = { /* .1 Hz */
|
||||
100, 150, 230, 300, 380, 470, 620, 940
|
||||
static const int lm85_freq_map[8] = { /* 1 Hz */
|
||||
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;
|
||||
|
||||
if (freq >= lm85_freq_map[7])
|
||||
return 7;
|
||||
/* Find the closest match */
|
||||
for (i = 0; i < 7; ++i)
|
||||
if (freq <= lm85_freq_map[i])
|
||||
if (freq <= (map[i] + map[i + 1]) / 2)
|
||||
break;
|
||||
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
|
||||
* to stand in for the following meanings:
|
||||
@ -275,7 +275,6 @@ struct lm85_zone {
|
||||
|
||||
struct lm85_autofan {
|
||||
u8 config; /* Register value */
|
||||
u8 freq; /* PWM frequency, encoded */
|
||||
u8 min_pwm; /* Minimum PWM value, encoded */
|
||||
u8 min_off; /* Min PWM or OFF below "limit", flag */
|
||||
};
|
||||
@ -283,8 +282,8 @@ struct lm85_autofan {
|
||||
/* For each registered chip, we need to keep some data in memory.
|
||||
The structure is dynamically allocated. */
|
||||
struct lm85_data {
|
||||
struct i2c_client client;
|
||||
struct device *hwmon_dev;
|
||||
const int *freq_map;
|
||||
enum chips type;
|
||||
|
||||
struct mutex update_lock;
|
||||
@ -301,6 +300,7 @@ struct lm85_data {
|
||||
u16 fan[4]; /* Register value */
|
||||
u16 fan_min[4]; /* Register value */
|
||||
u8 pwm[3]; /* Register value */
|
||||
u8 pwm_freq[3]; /* Register encoding */
|
||||
u8 temp_ext[3]; /* Decoded values */
|
||||
u8 in_ext[8]; /* Decoded values */
|
||||
u8 vid; /* Register value */
|
||||
@ -310,22 +310,40 @@ struct lm85_data {
|
||||
struct lm85_zone zone[3];
|
||||
};
|
||||
|
||||
static int lm85_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int lm85_detect(struct i2c_adapter *adapter, int address,
|
||||
int kind);
|
||||
static int lm85_detach_client(struct i2c_client *client);
|
||||
static int lm85_detect(struct i2c_client *client, int kind,
|
||||
struct i2c_board_info *info);
|
||||
static int lm85_probe(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 void lm85_write_value(struct i2c_client *client, u8 reg, int value);
|
||||
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 = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "lm85",
|
||||
},
|
||||
.attach_adapter = lm85_attach_adapter,
|
||||
.detach_client = lm85_detach_client,
|
||||
.probe = lm85_probe,
|
||||
.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
|
||||
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) \
|
||||
static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
|
||||
show_pwm, set_pwm, offset - 1); \
|
||||
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(2);
|
||||
@ -761,31 +807,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev,
|
||||
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) \
|
||||
static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \
|
||||
S_IRUGO | S_IWUSR, show_pwm_auto_channels, \
|
||||
@ -795,10 +816,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \
|
||||
set_pwm_auto_pwm_min, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \
|
||||
S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \
|
||||
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);
|
||||
set_pwm_auto_pwm_minctl, offset - 1)
|
||||
|
||||
pwm_auto(1);
|
||||
pwm_auto(2);
|
||||
@ -867,7 +885,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev,
|
||||
TEMP_FROM_REG(data->zone[nr].limit));
|
||||
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
|
||||
((data->zone[nr].range & 0x0f) << 4)
|
||||
| (data->autofan[nr].freq & 0x07));
|
||||
| (data->pwm_freq[nr] & 0x07));
|
||||
|
||||
/* Update temp_auto_hyst and temp_auto_off */
|
||||
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,
|
||||
val - min);
|
||||
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
|
||||
((data->zone[nr].range & 0x0f) << 4)
|
||||
| (data->autofan[nr].freq & 0x07));
|
||||
| (data->pwm_freq[nr] & 0x07));
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -957,13 +975,6 @@ temp_auto(1);
|
||||
temp_auto(2);
|
||||
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[] = {
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
@ -984,6 +995,9 @@ static struct attribute *lm85_attributes[] = {
|
||||
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_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_in1_input.dev_attr.attr,
|
||||
@ -1026,9 +1040,6 @@ static struct attribute *lm85_attributes[] = {
|
||||
&sensor_dev_attr_pwm1_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_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_temp2_auto_temp_off.dev_attr.attr,
|
||||
@ -1103,109 +1114,74 @@ static void lm85_init_client(struct i2c_client *client)
|
||||
dev_warn(&client->dev, "Device is not ready\n");
|
||||
}
|
||||
|
||||
static int lm85_detect(struct i2c_adapter *adapter, int address,
|
||||
int kind)
|
||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
||||
static int lm85_detect(struct i2c_client *client, int kind,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
int company, verstep;
|
||||
struct i2c_client *client;
|
||||
struct lm85_data *data;
|
||||
int err = 0;
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
int address = client->addr;
|
||||
const char *type_name;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
/* 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
|
||||
client structure, even though we cannot fill it completely yet.
|
||||
But it allows us to access lm85_{read,write}_value. */
|
||||
/* If auto-detecting, determine the chip type */
|
||||
if (kind < 0) {
|
||||
int company = lm85_read_value(client, LM85_REG_COMPANY);
|
||||
int verstep = lm85_read_value(client, LM85_REG_VERSTEP);
|
||||
|
||||
if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
goto ERROR0;
|
||||
}
|
||||
dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
|
||||
"COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
|
||||
address, company, verstep);
|
||||
|
||||
client = &data->client;
|
||||
i2c_set_clientdata(client, data);
|
||||
client->addr = address;
|
||||
client->adapter = adapter;
|
||||
client->driver = &lm85_driver;
|
||||
/* All supported chips have the version in common */
|
||||
if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) {
|
||||
dev_dbg(&adapter->dev, "Autodetection failed: "
|
||||
"unsupported version\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
kind = any_chip;
|
||||
|
||||
/* 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;
|
||||
} else if (company == LM85_COMPANY_NATIONAL
|
||||
&& verstep == LM85_VERSTEP_LM85B) {
|
||||
kind = lm85b;
|
||||
} else if (company == LM85_COMPANY_NATIONAL
|
||||
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
|
||||
dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x"
|
||||
" Defaulting to LM85.\n", verstep);
|
||||
kind = any_chip;
|
||||
} else if (company == LM85_COMPANY_ANALOG_DEV
|
||||
&& verstep == LM85_VERSTEP_ADM1027) {
|
||||
kind = adm1027;
|
||||
} else if (company == LM85_COMPANY_ANALOG_DEV
|
||||
&& (verstep == LM85_VERSTEP_ADT7463
|
||||
|| verstep == LM85_VERSTEP_ADT7463C)) {
|
||||
kind = adt7463;
|
||||
} else if (company == LM85_COMPANY_ANALOG_DEV
|
||||
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
|
||||
dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x"
|
||||
" Defaulting to Generic LM85.\n", verstep);
|
||||
kind = any_chip;
|
||||
} else if (company == LM85_COMPANY_SMSC
|
||||
&& (verstep == LM85_VERSTEP_EMC6D100_A0
|
||||
|| verstep == LM85_VERSTEP_EMC6D100_A1)) {
|
||||
/* Unfortunately, we can't tell a '100 from a '101
|
||||
* from the registers. Since a '101 is a '100
|
||||
* in a package with fewer pins and therefore no
|
||||
* 3.3V, 1.5V or 1.8V inputs, perhaps if those
|
||||
* inputs read 0, then it's a '101.
|
||||
*/
|
||||
kind = emc6d100;
|
||||
} else if (company == LM85_COMPANY_SMSC
|
||||
&& verstep == LM85_VERSTEP_EMC6D102) {
|
||||
kind = emc6d102;
|
||||
} else if (company == LM85_COMPANY_SMSC
|
||||
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
|
||||
dev_err(&adapter->dev, "lm85: Detected SMSC chip\n");
|
||||
dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x"
|
||||
" Defaulting to Generic LM85.\n", verstep);
|
||||
kind = any_chip;
|
||||
} else if (kind == any_chip
|
||||
&& (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
|
||||
dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n");
|
||||
/* Leave kind as "any_chip" */
|
||||
} else {
|
||||
dev_dbg(&adapter->dev, "Autodetection failed\n");
|
||||
/* Not an LM85... */
|
||||
if (kind == any_chip) { /* User used force=x,y */
|
||||
dev_err(&adapter->dev, "Generic LM85 Version 6 not"
|
||||
" found at %d,0x%02x. Try force_lm85c.\n",
|
||||
i2c_adapter_id(adapter), address);
|
||||
/* Now, refine the detection */
|
||||
if (company == LM85_COMPANY_NATIONAL) {
|
||||
switch (verstep) {
|
||||
case LM85_VERSTEP_LM85C:
|
||||
kind = lm85c;
|
||||
break;
|
||||
case LM85_VERSTEP_LM85B:
|
||||
kind = lm85b;
|
||||
break;
|
||||
}
|
||||
err = 0;
|
||||
goto ERROR1;
|
||||
} else if (company == LM85_COMPANY_ANALOG_DEV) {
|
||||
switch (verstep) {
|
||||
case LM85_VERSTEP_ADM1027:
|
||||
kind = adm1027;
|
||||
break;
|
||||
case LM85_VERSTEP_ADT7463:
|
||||
case LM85_VERSTEP_ADT7463C:
|
||||
kind = adt7463;
|
||||
break;
|
||||
}
|
||||
} else if (company == LM85_COMPANY_SMSC) {
|
||||
switch (verstep) {
|
||||
case LM85_VERSTEP_EMC6D100_A0:
|
||||
case LM85_VERSTEP_EMC6D100_A1:
|
||||
/* Note: we can't tell a '100 from a '101 */
|
||||
kind = emc6d100;
|
||||
break;
|
||||
case LM85_VERSTEP_EMC6D102:
|
||||
kind = emc6d102;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
dev_dbg(&adapter->dev, "Autodetection failed: "
|
||||
"unknown vendor\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in the chip specific driver values */
|
||||
switch (kind) {
|
||||
case lm85b:
|
||||
type_name = "lm85b";
|
||||
@ -1228,16 +1204,36 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
|
||||
default:
|
||||
type_name = "lm85";
|
||||
}
|
||||
strlcpy(client->name, type_name, I2C_NAME_SIZE);
|
||||
strlcpy(info->type, type_name, I2C_NAME_SIZE);
|
||||
|
||||
/* Fill in the remaining client fields */
|
||||
data->type = kind;
|
||||
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;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
data->type = id->driver_data;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
err = i2c_attach_client(client);
|
||||
if (err)
|
||||
goto ERROR1;
|
||||
/* Fill in the chip specific driver values */
|
||||
switch (data->type) {
|
||||
case adm1027:
|
||||
case adt7463:
|
||||
case emc6d100:
|
||||
case emc6d102:
|
||||
data->freq_map = adm1027_freq_map;
|
||||
break;
|
||||
default:
|
||||
data->freq_map = lm85_freq_map;
|
||||
}
|
||||
|
||||
/* Set the VRM version */
|
||||
data->vrm = vid_which_vrm();
|
||||
@ -1248,45 +1244,42 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&client->dev.kobj, &lm85_group);
|
||||
if (err)
|
||||
goto ERROR2;
|
||||
goto err_kfree;
|
||||
|
||||
/* The ADT7463 has an optional VRM 10 mode where pin 21 is used
|
||||
as a sixth digital VID input rather than an analog input. */
|
||||
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,
|
||||
&lm85_group_in4)))
|
||||
goto ERROR3;
|
||||
goto err_remove_files;
|
||||
|
||||
/* The EMC6D100 has 3 additional voltage inputs */
|
||||
if (kind == emc6d100)
|
||||
if (data->type == emc6d100)
|
||||
if ((err = sysfs_create_group(&client->dev.kobj,
|
||||
&lm85_group_in567)))
|
||||
goto ERROR3;
|
||||
goto err_remove_files;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto ERROR3;
|
||||
goto err_remove_files;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
/* 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_in4);
|
||||
if (kind == emc6d100)
|
||||
if (data->type == emc6d100)
|
||||
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
|
||||
ERROR2:
|
||||
i2c_detach_client(client);
|
||||
ERROR1:
|
||||
err_kfree:
|
||||
kfree(data);
|
||||
ERROR0:
|
||||
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);
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
@ -1294,7 +1287,6 @@ static int lm85_detach_client(struct i2c_client *client)
|
||||
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
|
||||
if (data->type == emc6d100)
|
||||
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
|
||||
i2c_detach_client(client);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
@ -1481,7 +1473,7 @@ static struct lm85_data *lm85_update_device(struct device *dev)
|
||||
data->autofan[i].config =
|
||||
lm85_read_value(client, LM85_REG_AFAN_CONFIG(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->autofan[i].min_pwm =
|
||||
lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
|
||||
|
@ -21,11 +21,10 @@
|
||||
* http://www.national.com/pf/LM/LM87.html
|
||||
*
|
||||
* 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
|
||||
* the BIOS configured the chip correctly. In that respect, it differs from
|
||||
* the original driver (from lm_sensors for Linux 2.4), which would force the
|
||||
* LM87 to an arbitrary, compile-time chosen mode, regardless of the actual
|
||||
* chipset wiring.
|
||||
* time. Which are depends on the hardware setup. This driver normally
|
||||
* assumes that firmware configured the chip correctly. Where this is not
|
||||
* the case, platform code must set the I2C client's platform_data to point
|
||||
* to a u8 value to be written to the channel register.
|
||||
* For reference, here is the list of exclusive functions:
|
||||
* - in0+in5 (default) or temp3
|
||||
* - fan1 (default) or in6
|
||||
@ -199,6 +198,7 @@ struct lm87_data {
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 channel; /* register value */
|
||||
u8 config; /* original register value */
|
||||
|
||||
u8 in[8]; /* register value */
|
||||
u8 in_max[8]; /* register value */
|
||||
@ -832,6 +832,7 @@ exit_remove:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm87_group);
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt);
|
||||
exit_free:
|
||||
lm87_write_value(new_client, LM87_REG_CONFIG, data->config);
|
||||
kfree(data);
|
||||
exit:
|
||||
return err;
|
||||
@ -840,12 +841,17 @@ exit:
|
||||
static void lm87_init_client(struct i2c_client *client)
|
||||
{
|
||||
struct lm87_data *data = i2c_get_clientdata(client);
|
||||
u8 config;
|
||||
|
||||
data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
|
||||
if (client->dev.platform_data) {
|
||||
data->channel = *(u8 *)client->dev.platform_data;
|
||||
lm87_write_value(client,
|
||||
LM87_REG_CHANNEL_MODE, data->channel);
|
||||
} else {
|
||||
data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
|
||||
}
|
||||
data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F;
|
||||
|
||||
config = lm87_read_value(client, LM87_REG_CONFIG);
|
||||
if (!(config & 0x01)) {
|
||||
if (!(data->config & 0x01)) {
|
||||
int i;
|
||||
|
||||
/* Limits are left uninitialized after power-up */
|
||||
@ -867,11 +873,11 @@ static void lm87_init_client(struct i2c_client *client)
|
||||
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,
|
||||
(config & 0xF7) | 0x01);
|
||||
}
|
||||
(data->config & 0x77) | 0x01);
|
||||
}
|
||||
|
||||
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_opt);
|
||||
|
||||
lm87_write_value(client, LM87_REG_CONFIG, data->config);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,14 +1,12 @@
|
||||
/*
|
||||
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
* monitoring
|
||||
* Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org>
|
||||
* Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org>
|
||||
*
|
||||
* Based on the lm83 driver. The LM90 is a sensor chip made by National
|
||||
* Semiconductor. It reports up to two temperatures (its own plus up to
|
||||
* one external one) with a 0.125 deg resolution (1 deg for local
|
||||
* temperature) and a 3-4 deg accuracy. Complete datasheet can be
|
||||
* obtained from National's website at:
|
||||
* http://www.national.com/pf/LM/LM90.html
|
||||
* temperature) and a 3-4 deg accuracy.
|
||||
*
|
||||
* This driver also supports the LM89 and LM99, two other sensor chips
|
||||
* made by National Semiconductor. Both have an increased remote
|
||||
@ -16,48 +14,38 @@
|
||||
* additionally shifts remote temperatures (measured and limits) by 16
|
||||
* degrees, which allows for higher temperatures measurement. The
|
||||
* driver doesn't handle it since it can be done easily in user-space.
|
||||
* Complete datasheets can be obtained from National's website at:
|
||||
* http://www.national.com/pf/LM/LM89.html
|
||||
* http://www.national.com/pf/LM/LM99.html
|
||||
* Note that there is no way to differentiate between both chips.
|
||||
*
|
||||
* This driver also supports the LM86, another sensor chip made by
|
||||
* National Semiconductor. It is exactly similar to the LM90 except it
|
||||
* has a higher accuracy.
|
||||
* Complete datasheet can be obtained from National's website at:
|
||||
* http://www.national.com/pf/LM/LM86.html
|
||||
*
|
||||
* This driver also supports the ADM1032, a sensor chip made by Analog
|
||||
* Devices. That chip is similar to the LM90, with a few differences
|
||||
* that are not handled by this driver. Complete datasheet can be
|
||||
* obtained from Analog's website at:
|
||||
* http://www.analog.com/en/prod/0,2877,ADM1032,00.html
|
||||
* Among others, it has a higher accuracy than the LM90, much like the
|
||||
* LM86 does.
|
||||
* that are not handled by this driver. Among others, it has a higher
|
||||
* accuracy than the LM90, much like the LM86 does.
|
||||
*
|
||||
* This driver also supports the MAX6657, MAX6658 and MAX6659 sensor
|
||||
* chips made by Maxim. These chips are similar to the LM86. Complete
|
||||
* datasheet can be obtained at Maxim's website at:
|
||||
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
|
||||
* chips made by Maxim. These chips are similar to the LM86.
|
||||
* Note that there is no easy way to differentiate between the three
|
||||
* variants. The extra address and features of the MAX6659 are not
|
||||
* supported by this driver. These chips lack the remote temperature
|
||||
* offset feature.
|
||||
*
|
||||
* This driver also supports the MAX6646, MAX6647 and MAX6649 chips
|
||||
* made by Maxim. These are again similar to the LM86, but they use
|
||||
* unsigned temperature values and can report temperatures from 0 to
|
||||
* 145 degrees.
|
||||
*
|
||||
* This driver also supports the MAX6680 and MAX6681, two other sensor
|
||||
* chips made by Maxim. These are quite similar to the other Maxim
|
||||
* chips. Complete datasheet can be obtained at:
|
||||
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370
|
||||
* The MAX6680 and MAX6681 only differ in the pinout so they can be
|
||||
* treated identically.
|
||||
* chips. The MAX6680 and MAX6681 only differ in the pinout so they can
|
||||
* be treated identically.
|
||||
*
|
||||
* This driver also supports the ADT7461 chip from Analog Devices but
|
||||
* only in its "compatability mode". If an ADT7461 chip is found but
|
||||
* is configured in non-compatible mode (where its temperature
|
||||
* register values are decoded differently) it is ignored by this
|
||||
* driver. Complete datasheet can be obtained from Analog's website
|
||||
* at:
|
||||
* http://www.analog.com/en/prod/0,2877,ADT7461,00.html
|
||||
* This driver also supports the ADT7461 chip from Analog Devices.
|
||||
* It's supported in both compatibility and extended mode. It is mostly
|
||||
* compatible with LM90 except for a data format difference for the
|
||||
* temperature value registers.
|
||||
*
|
||||
* Since the LM90 was the first chipset supported by this driver, most
|
||||
* comments will refer to this chipset, but are actually general and
|
||||
@ -93,9 +81,10 @@
|
||||
* Addresses to scan
|
||||
* Address is fully defined internally and cannot be changed except for
|
||||
* MAX6659, MAX6680 and MAX6681.
|
||||
* LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658
|
||||
* have address 0x4c.
|
||||
* ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d.
|
||||
* LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657
|
||||
* and MAX6658 have address 0x4c.
|
||||
* ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d.
|
||||
* MAX6647 has address 0x4e.
|
||||
* MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported).
|
||||
* MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
|
||||
* 0x4c, 0x4d or 0x4e.
|
||||
@ -108,7 +97,8 @@ static const unsigned short normal_i2c[] = {
|
||||
* Insmod parameters
|
||||
*/
|
||||
|
||||
I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
|
||||
I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680,
|
||||
max6646);
|
||||
|
||||
/*
|
||||
* The LM90 registers
|
||||
@ -149,39 +139,14 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
|
||||
#define LM90_REG_R_TCRIT_HYST 0x21
|
||||
#define LM90_REG_W_TCRIT_HYST 0x21
|
||||
|
||||
/* MAX6646/6647/6649/6657/6658/6659 registers */
|
||||
|
||||
#define MAX6657_REG_R_LOCAL_TEMPL 0x11
|
||||
|
||||
/*
|
||||
* Conversions and various macros
|
||||
* For local temperatures and limits, critical limits and the hysteresis
|
||||
* value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
|
||||
* For remote temperatures and limits, it uses signed 11-bit values with
|
||||
* LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
|
||||
* Device flags
|
||||
*/
|
||||
|
||||
#define TEMP1_FROM_REG(val) ((val) * 1000)
|
||||
#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
|
||||
(val) >= 127000 ? 127 : \
|
||||
(val) < 0 ? ((val) - 500) / 1000 : \
|
||||
((val) + 500) / 1000)
|
||||
#define TEMP2_FROM_REG(val) ((val) / 32 * 125)
|
||||
#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
|
||||
(val) >= 127875 ? 0x7FE0 : \
|
||||
(val) < 0 ? ((val) - 62) / 125 * 32 : \
|
||||
((val) + 62) / 125 * 32)
|
||||
#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \
|
||||
((val) + 500) / 1000)
|
||||
|
||||
/*
|
||||
* ADT7461 is almost identical to LM90 except that attempts to write
|
||||
* values that are outside the range 0 < temp < 127 are treated as
|
||||
* the boundary value.
|
||||
*/
|
||||
|
||||
#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
|
||||
(val) >= 127000 ? 127 : \
|
||||
((val) + 500) / 1000)
|
||||
#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
|
||||
(val) >= 127750 ? 0x7FC0 : \
|
||||
((val) + 125) / 250 * 64)
|
||||
#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */
|
||||
|
||||
/*
|
||||
* Functions declaration
|
||||
@ -206,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = {
|
||||
{ "lm86", lm86 },
|
||||
{ "lm89", lm99 },
|
||||
{ "lm99", lm99 }, /* Missing temperature offset */
|
||||
{ "max6646", max6646 },
|
||||
{ "max6647", max6646 },
|
||||
{ "max6649", max6646 },
|
||||
{ "max6657", max6657 },
|
||||
{ "max6658", max6657 },
|
||||
{ "max6659", max6657 },
|
||||
@ -237,21 +205,149 @@ struct lm90_data {
|
||||
char valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
int kind;
|
||||
int flags;
|
||||
|
||||
/* registers values */
|
||||
s8 temp8[5]; /* 0: local input
|
||||
1: local low limit
|
||||
2: local high limit
|
||||
3: local critical limit
|
||||
4: remote critical limit */
|
||||
s16 temp11[4]; /* 0: remote input
|
||||
s8 temp8[4]; /* 0: local low limit
|
||||
1: local high limit
|
||||
2: local critical limit
|
||||
3: remote critical limit */
|
||||
s16 temp11[5]; /* 0: remote input
|
||||
1: remote low limit
|
||||
2: remote high limit
|
||||
3: remote offset (except max6657) */
|
||||
3: remote offset (except max6646 and max6657)
|
||||
4: local input */
|
||||
u8 temp_hyst;
|
||||
u8 alarms; /* bitvector */
|
||||
};
|
||||
|
||||
/*
|
||||
* Conversions
|
||||
* For local temperatures and limits, critical limits and the hysteresis
|
||||
* value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
|
||||
* For remote temperatures and limits, it uses signed 11-bit values with
|
||||
* LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some
|
||||
* Maxim chips use unsigned values.
|
||||
*/
|
||||
|
||||
static inline int temp_from_s8(s8 val)
|
||||
{
|
||||
return val * 1000;
|
||||
}
|
||||
|
||||
static inline int temp_from_u8(u8 val)
|
||||
{
|
||||
return val * 1000;
|
||||
}
|
||||
|
||||
static inline int temp_from_s16(s16 val)
|
||||
{
|
||||
return val / 32 * 125;
|
||||
}
|
||||
|
||||
static inline int temp_from_u16(u16 val)
|
||||
{
|
||||
return val / 32 * 125;
|
||||
}
|
||||
|
||||
static s8 temp_to_s8(long val)
|
||||
{
|
||||
if (val <= -128000)
|
||||
return -128;
|
||||
if (val >= 127000)
|
||||
return 127;
|
||||
if (val < 0)
|
||||
return (val - 500) / 1000;
|
||||
return (val + 500) / 1000;
|
||||
}
|
||||
|
||||
static u8 temp_to_u8(long val)
|
||||
{
|
||||
if (val <= 0)
|
||||
return 0;
|
||||
if (val >= 255000)
|
||||
return 255;
|
||||
return (val + 500) / 1000;
|
||||
}
|
||||
|
||||
static s16 temp_to_s16(long val)
|
||||
{
|
||||
if (val <= -128000)
|
||||
return 0x8000;
|
||||
if (val >= 127875)
|
||||
return 0x7FE0;
|
||||
if (val < 0)
|
||||
return (val - 62) / 125 * 32;
|
||||
return (val + 62) / 125 * 32;
|
||||
}
|
||||
|
||||
static u8 hyst_to_reg(long val)
|
||||
{
|
||||
if (val <= 0)
|
||||
return 0;
|
||||
if (val >= 30500)
|
||||
return 31;
|
||||
return (val + 500) / 1000;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADT7461 in compatibility mode is almost identical to LM90 except that
|
||||
* attempts to write values that are outside the range 0 < temp < 127 are
|
||||
* treated as the boundary value.
|
||||
*
|
||||
* ADT7461 in "extended mode" operation uses unsigned integers offset by
|
||||
* 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC.
|
||||
*/
|
||||
static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val)
|
||||
{
|
||||
if (data->flags & LM90_FLAG_ADT7461_EXT)
|
||||
return (val - 64) * 1000;
|
||||
else
|
||||
return temp_from_s8(val);
|
||||
}
|
||||
|
||||
static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val)
|
||||
{
|
||||
if (data->flags & LM90_FLAG_ADT7461_EXT)
|
||||
return (val - 0x4000) / 64 * 250;
|
||||
else
|
||||
return temp_from_s16(val);
|
||||
}
|
||||
|
||||
static u8 temp_to_u8_adt7461(struct lm90_data *data, long val)
|
||||
{
|
||||
if (data->flags & LM90_FLAG_ADT7461_EXT) {
|
||||
if (val <= -64000)
|
||||
return 0;
|
||||
if (val >= 191000)
|
||||
return 0xFF;
|
||||
return (val + 500 + 64000) / 1000;
|
||||
} else {
|
||||
if (val <= 0)
|
||||
return 0;
|
||||
if (val >= 127000)
|
||||
return 127;
|
||||
return (val + 500) / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
|
||||
{
|
||||
if (data->flags & LM90_FLAG_ADT7461_EXT) {
|
||||
if (val <= -64000)
|
||||
return 0;
|
||||
if (val >= 191750)
|
||||
return 0xFFC0;
|
||||
return (val + 64000 + 125) / 250 * 64;
|
||||
} else {
|
||||
if (val <= 0)
|
||||
return 0;
|
||||
if (val >= 127750)
|
||||
return 0x7FC0;
|
||||
return (val + 125) / 250 * 64;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs stuff
|
||||
*/
|
||||
@ -261,7 +357,16 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm90_data *data = lm90_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]));
|
||||
int temp;
|
||||
|
||||
if (data->kind == adt7461)
|
||||
temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
|
||||
else if (data->kind == max6646)
|
||||
temp = temp_from_u8(data->temp8[attr->index]);
|
||||
else
|
||||
temp = temp_from_s8(data->temp8[attr->index]);
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
|
||||
@ -282,10 +387,12 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if (data->kind == adt7461)
|
||||
data->temp8[nr] = TEMP1_TO_REG_ADT7461(val);
|
||||
data->temp8[nr] = temp_to_u8_adt7461(data, val);
|
||||
else if (data->kind == max6646)
|
||||
data->temp8[nr] = temp_to_u8(val);
|
||||
else
|
||||
data->temp8[nr] = TEMP1_TO_REG(val);
|
||||
i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]);
|
||||
data->temp8[nr] = temp_to_s8(val);
|
||||
i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -295,7 +402,16 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm90_data *data = lm90_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index]));
|
||||
int temp;
|
||||
|
||||
if (data->kind == adt7461)
|
||||
temp = temp_from_u16_adt7461(data, data->temp11[attr->index]);
|
||||
else if (data->kind == max6646)
|
||||
temp = temp_from_u16(data->temp11[attr->index]);
|
||||
else
|
||||
temp = temp_from_s16(data->temp11[attr->index]);
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
|
||||
@ -318,13 +434,20 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if (data->kind == adt7461)
|
||||
data->temp11[nr] = TEMP2_TO_REG_ADT7461(val);
|
||||
data->temp11[nr] = temp_to_u16_adt7461(data, val);
|
||||
else if (data->kind == max6657 || data->kind == max6680)
|
||||
data->temp11[nr] = temp_to_s8(val) << 8;
|
||||
else if (data->kind == max6646)
|
||||
data->temp11[nr] = temp_to_u8(val) << 8;
|
||||
else
|
||||
data->temp11[nr] = TEMP2_TO_REG(val);
|
||||
data->temp11[nr] = temp_to_s16(val);
|
||||
|
||||
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
|
||||
data->temp11[nr] >> 8);
|
||||
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
|
||||
data->temp11[nr] & 0xff);
|
||||
if (data->kind != max6657 && data->kind != max6680
|
||||
&& data->kind != max6646)
|
||||
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
|
||||
data->temp11[nr] & 0xff);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -334,8 +457,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm90_data *data = lm90_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])
|
||||
- TEMP1_FROM_REG(data->temp_hyst));
|
||||
int temp;
|
||||
|
||||
if (data->kind == adt7461)
|
||||
temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
|
||||
else
|
||||
temp = temp_from_s8(data->temp8[attr->index]);
|
||||
|
||||
return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst));
|
||||
}
|
||||
|
||||
static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
|
||||
@ -347,9 +476,9 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
|
||||
long hyst;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
hyst = TEMP1_FROM_REG(data->temp8[3]) - val;
|
||||
hyst = temp_from_s8(data->temp8[2]) - val;
|
||||
i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
|
||||
HYST_TO_REG(hyst));
|
||||
hyst_to_reg(hyst));
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -371,23 +500,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute
|
||||
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
|
||||
set_temp8, 1);
|
||||
set_temp8, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
|
||||
set_temp11, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
|
||||
set_temp8, 2);
|
||||
set_temp8, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
|
||||
set_temp11, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8,
|
||||
set_temp8, 3);
|
||||
set_temp8, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
|
||||
set_temp8, 4);
|
||||
set_temp8, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
|
||||
set_temphyst, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4);
|
||||
set_temphyst, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
|
||||
set_temp11, 3);
|
||||
|
||||
@ -568,7 +697,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
|
||||
kind = adm1032;
|
||||
} else
|
||||
if (chip_id == 0x51 /* ADT7461 */
|
||||
&& (reg_config1 & 0x1F) == 0x00 /* check compat mode */
|
||||
&& (reg_config1 & 0x1B) == 0x00
|
||||
&& reg_convrate <= 0x0A) {
|
||||
kind = adt7461;
|
||||
}
|
||||
@ -599,13 +728,23 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
|
||||
&& (reg_config1 & 0x03) == 0x00
|
||||
&& reg_convrate <= 0x07) {
|
||||
kind = max6680;
|
||||
} else
|
||||
/* The chip_id register of the MAX6646/6647/6649
|
||||
* holds the revision of the chip.
|
||||
* The lowest 6 bits of the config1 register are
|
||||
* unused and should return zero when read.
|
||||
*/
|
||||
if (chip_id == 0x59
|
||||
&& (reg_config1 & 0x3f) == 0x00
|
||||
&& reg_convrate <= 0x07) {
|
||||
kind = max6646;
|
||||
}
|
||||
}
|
||||
|
||||
if (kind <= 0) { /* identification failed */
|
||||
dev_info(&adapter->dev,
|
||||
"Unsupported chip (man_id=0x%02X, "
|
||||
"chip_id=0x%02X).\n", man_id, chip_id);
|
||||
dev_dbg(&adapter->dev,
|
||||
"Unsupported chip at 0x%02x (man_id=0x%02X, "
|
||||
"chip_id=0x%02X)\n", address, man_id, chip_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
@ -629,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
|
||||
name = "max6680";
|
||||
} else if (kind == adt7461) {
|
||||
name = "adt7461";
|
||||
} else if (kind == max6646) {
|
||||
name = "max6646";
|
||||
}
|
||||
strlcpy(info->type, name, I2C_NAME_SIZE);
|
||||
|
||||
@ -668,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client,
|
||||
&dev_attr_pec)))
|
||||
goto exit_remove_files;
|
||||
}
|
||||
if (data->kind != max6657) {
|
||||
if (data->kind != max6657 && data->kind != max6646) {
|
||||
if ((err = device_create_file(&new_client->dev,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr)))
|
||||
goto exit_remove_files;
|
||||
@ -707,6 +848,12 @@ static void lm90_init_client(struct i2c_client *client)
|
||||
}
|
||||
config_orig = config;
|
||||
|
||||
/* Check Temperature Range Select */
|
||||
if (data->kind == adt7461) {
|
||||
if (config & 0x04)
|
||||
data->flags |= LM90_FLAG_ADT7461_EXT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Put MAX6680/MAX8881 into extended resolution (bit 0x10,
|
||||
* 0.125 degree resolution) and range (0x08, extend range
|
||||
@ -728,7 +875,7 @@ static int lm90_remove(struct i2c_client *client)
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm90_group);
|
||||
device_remove_file(&client->dev, &dev_attr_pec);
|
||||
if (data->kind != max6657)
|
||||
if (data->kind != max6657 && data->kind != max6646)
|
||||
device_remove_file(&client->dev,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr);
|
||||
|
||||
@ -736,6 +883,38 @@ static int lm90_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
|
||||
{
|
||||
int err;
|
||||
u8 oldh, newh, l;
|
||||
|
||||
/*
|
||||
* There is a trick here. We have to read two registers to have the
|
||||
* sensor temperature, but we have to beware a conversion could occur
|
||||
* inbetween the readings. The datasheet says we should either use
|
||||
* the one-shot conversion register, which we don't want to do
|
||||
* (disables hardware monitoring) or monitor the busy bit, which is
|
||||
* impossible (we can't read the values and monitor that bit at the
|
||||
* exact same time). So the solution used here is to read the high
|
||||
* byte once, then the low byte, then the high byte again. If the new
|
||||
* high byte matches the old one, then we have a valid reading. Else
|
||||
* we have to read the low byte again, and now we believe we have a
|
||||
* correct reading.
|
||||
*/
|
||||
if ((err = lm90_read_reg(client, regh, &oldh))
|
||||
|| (err = lm90_read_reg(client, regl, &l))
|
||||
|| (err = lm90_read_reg(client, regh, &newh)))
|
||||
return err;
|
||||
if (oldh != newh) {
|
||||
err = lm90_read_reg(client, regl, &l);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
*value = (newh << 8) | l;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lm90_data *lm90_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
@ -744,49 +923,50 @@ static struct lm90_data *lm90_update_device(struct device *dev)
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
|
||||
u8 oldh, newh, l;
|
||||
u8 h, l;
|
||||
|
||||
dev_dbg(&client->dev, "Updating lm90 data.\n");
|
||||
lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]);
|
||||
lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[1]);
|
||||
lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[2]);
|
||||
lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[3]);
|
||||
lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]);
|
||||
lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]);
|
||||
lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]);
|
||||
lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]);
|
||||
lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);
|
||||
lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
|
||||
|
||||
/*
|
||||
* There is a trick here. We have to read two registers to
|
||||
* have the remote sensor temperature, but we have to beware
|
||||
* a conversion could occur inbetween the readings. The
|
||||
* datasheet says we should either use the one-shot
|
||||
* conversion register, which we don't want to do (disables
|
||||
* hardware monitoring) or monitor the busy bit, which is
|
||||
* impossible (we can't read the values and monitor that bit
|
||||
* at the exact same time). So the solution used here is to
|
||||
* read the high byte once, then the low byte, then the high
|
||||
* byte again. If the new high byte matches the old one,
|
||||
* then we have a valid reading. Else we have to read the low
|
||||
* byte again, and now we believe we have a correct reading.
|
||||
*/
|
||||
if (lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &oldh) == 0
|
||||
&& lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0
|
||||
&& lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &newh) == 0
|
||||
&& (newh == oldh
|
||||
|| lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0))
|
||||
data->temp11[0] = (newh << 8) | l;
|
||||
if (data->kind == max6657 || data->kind == max6646) {
|
||||
lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
|
||||
MAX6657_REG_R_LOCAL_TEMPL,
|
||||
&data->temp11[4]);
|
||||
} else {
|
||||
if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
|
||||
&h) == 0)
|
||||
data->temp11[4] = h << 8;
|
||||
}
|
||||
lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
|
||||
LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]);
|
||||
|
||||
if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &newh) == 0
|
||||
&& lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0)
|
||||
data->temp11[1] = (newh << 8) | l;
|
||||
if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &newh) == 0
|
||||
&& lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0)
|
||||
data->temp11[2] = (newh << 8) | l;
|
||||
if (data->kind != max6657) {
|
||||
if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) {
|
||||
data->temp11[1] = h << 8;
|
||||
if (data->kind != max6657 && data->kind != max6680
|
||||
&& data->kind != max6646
|
||||
&& lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL,
|
||||
&l) == 0)
|
||||
data->temp11[1] |= l;
|
||||
}
|
||||
if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) {
|
||||
data->temp11[2] = h << 8;
|
||||
if (data->kind != max6657 && data->kind != max6680
|
||||
&& data->kind != max6646
|
||||
&& lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL,
|
||||
&l) == 0)
|
||||
data->temp11[2] |= l;
|
||||
}
|
||||
|
||||
if (data->kind != max6657 && data->kind != max6646) {
|
||||
if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH,
|
||||
&newh) == 0
|
||||
&h) == 0
|
||||
&& lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL,
|
||||
&l) == 0)
|
||||
data->temp11[3] = (newh << 8) | l;
|
||||
data->temp11[3] = (h << 8) | l;
|
||||
}
|
||||
lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms);
|
||||
|
||||
|
@ -69,11 +69,18 @@ I2C_CLIENT_INSMOD_1(max1619);
|
||||
#define MAX1619_REG_W_TCRIT_HYST 0x13
|
||||
|
||||
/*
|
||||
* Conversions and various macros
|
||||
* Conversions
|
||||
*/
|
||||
|
||||
#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000)
|
||||
#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000)
|
||||
static int temp_from_reg(int val)
|
||||
{
|
||||
return (val & 0x80 ? val-0x100 : val) * 1000;
|
||||
}
|
||||
|
||||
static int temp_to_reg(int val)
|
||||
{
|
||||
return (val < 0 ? val+0x100*1000 : val) / 1000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions declaration
|
||||
@ -135,7 +142,7 @@ struct max1619_data {
|
||||
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
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_input2);
|
||||
@ -153,7 +160,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co
|
||||
long val = simple_strtol(buf, NULL, 10); \
|
||||
\
|
||||
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); \
|
||||
mutex_unlock(&data->update_lock); \
|
||||
return count; \
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,7 +23,7 @@
|
||||
Supports following chips:
|
||||
|
||||
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
|
||||
83792d. Thus, this file is derived from both the w83792d.c and
|
||||
@ -45,6 +45,7 @@
|
||||
#define NUMBER_OF_VIN 10
|
||||
#define NUMBER_OF_FANIN 5
|
||||
#define NUMBER_OF_TEMPIN 3
|
||||
#define NUMBER_OF_PWM 5
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
@ -116,6 +117,25 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = {
|
||||
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] = {
|
||||
0x84, /* FAN 1/2 configuration */
|
||||
0x95, /* FAN 3 configuration */
|
||||
@ -160,6 +180,7 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = {
|
||||
0xA3, /* BEEP Control Register 3 */
|
||||
};
|
||||
|
||||
#define W83791D_REG_GPIO 0x15
|
||||
#define W83791D_REG_CONFIG 0x40
|
||||
#define W83791D_REG_VID_FANDIV 0x47
|
||||
#define W83791D_REG_DID_VID4 0x49
|
||||
@ -224,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div)
|
||||
(val) < 0 ? ((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_FROM_REG(val) ((val) & 0xffffff)
|
||||
@ -275,6 +305,14 @@ struct w83791d_data {
|
||||
two sensors with three values
|
||||
(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 */
|
||||
u32 alarms; /* realtime status register encoding,combined */
|
||||
u8 beep_enable; /* Global beep enable */
|
||||
@ -652,6 +690,217 @@ static struct sensor_device_attribute sda_fan_alarm[] = {
|
||||
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 */
|
||||
static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
@ -908,8 +1157,6 @@ static struct attribute *w83791d_attributes[] = {
|
||||
FAN_UNIT_ATTRS(0),
|
||||
FAN_UNIT_ATTRS(1),
|
||||
FAN_UNIT_ATTRS(2),
|
||||
FAN_UNIT_ATTRS(3),
|
||||
FAN_UNIT_ATTRS(4),
|
||||
TEMP_UNIT_ATTRS(0),
|
||||
TEMP_UNIT_ATTRS(1),
|
||||
TEMP_UNIT_ATTRS(2),
|
||||
@ -918,6 +1165,18 @@ static struct attribute *w83791d_attributes[] = {
|
||||
&sda_beep_ctrl[1].dev_attr.attr,
|
||||
&dev_attr_cpu0_vid.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
|
||||
};
|
||||
|
||||
@ -925,6 +1184,20 @@ static const struct attribute_group w83791d_group = {
|
||||
.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)
|
||||
{
|
||||
@ -1056,6 +1329,7 @@ static int w83791d_probe(struct i2c_client *client,
|
||||
struct w83791d_data *data;
|
||||
struct device *dev = &client->dev;
|
||||
int i, err;
|
||||
u8 has_fanpwm45;
|
||||
|
||||
#ifdef DEBUG
|
||||
int val1;
|
||||
@ -1090,15 +1364,27 @@ static int w83791d_probe(struct i2c_client *client,
|
||||
if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group)))
|
||||
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 */
|
||||
data->hwmon_dev = hwmon_device_register(dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto error4;
|
||||
goto error5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error5:
|
||||
if (has_fanpwm45)
|
||||
sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45);
|
||||
error4:
|
||||
sysfs_remove_group(&client->dev.kobj, &w83791d_group);
|
||||
error3:
|
||||
@ -1236,6 +1522,36 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
|
||||
for (i = 0; i < 3; i++)
|
||||
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 */
|
||||
for (i = 0; i < 3; i++) {
|
||||
data->temp1[i] = w83791d_read(client,
|
||||
|
@ -259,6 +259,35 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user