GIT ef591d21c2ea9ac939700cea549515202344089b git://lm-sensors.org/kernel/mhoffman/hwmon-2.6.git#testing commit Author: Riku Voipio Date: Fri Aug 24 13:03:09 2007 +0300 hwmon: Add f75375s driver Add support for Fintek F75375S/SP and F75373. Signed-off-by: Riku Voipio Signed-off-by: Mark M. Hoffman commit 8827eb1b3f79a0d61dd4e7dfef060b3636a4ca7a Author: Jean Delvare Date: Mon Aug 20 16:44:44 2007 +0200 hwmon: Update the sysfs interface documentation * Document the name attribute. * Document the *_label attributes. * Drop "typical usage" lists, they no longer match the reality. * Drop non hardware-monitoring related entries. Signed-off-by: Jean Delvare Acked-by: Hans de Goede Signed-off-by: Mark M. Hoffman commit cac7bc592503e38ee7916ac0e8d275d8ba608049 Author: Jean Delvare Date: Thu Aug 16 14:33:37 2007 +0200 hwmon: (smsc47m1) No confusing debugging messages It's confusing to display debugging messages for fan3 and pwm3 for chips which don't have them. Signed-off-by: Jean Delvare Signed-off-by: Mark M. Hoffman commit 350ba3aff1cfd3c6a22949f52548671d243a7d3a Author: Jean Delvare Date: Fri Aug 17 17:31:35 2007 +0200 hwmon: (lm70) Add a name attribute Add a name attribute to the lm70 devices. This is required for libsensors to recognize them. Also drop the "+" before the temperature value, even though it did not cause problems to libsensors, other hardware monitoring drivers don't print it, so it's more consistent that way. Signed-off-by: Jean Delvare Acked-by: Hans de Goede Acked-by: Kaiwan Signed-off-by: Mark M. Hoffman commit 2dfa98719d83a3bc48422e510f65b667af5bd4e5 Author: Jean Delvare Date: Thu Aug 16 14:48:49 2007 +0200 hwmon: (lm93) Documentation fixes * Drop documentation of generic module parameters. * Drop redundant section "Driver Description". * Drop sample configuration section, it belongs to sensors.conf.eg. * Random spelling and punctuation fixes. Signed-off-by: Jean Delvare Acked-by: Hans J. Koch Signed-off-by: Mark M. Hoffman commit 597262e111794b41312f1ba2a296c18c6c6e8c76 Author: Jean Delvare Date: Thu Aug 16 14:30:01 2007 +0200 hwmon: Don't export thermistor beta Deprecate the use of thermistor beta values as thermal sensor types. No driver supports changing the beta value anyway. Signed-off-by: Jean Delvare Acked-by: Hans de Goede Signed-off-by: Mark M. Hoffman commit 9abb0a53d667a7b45d8c252ee4dfc072c02fc3ee Author: Christian Hohnstaedt Date: Thu Aug 16 11:40:10 2007 +0200 hwmon: Allow writing of negative trigger temperatures - replace differing temperature variable types by long - use strtol() instead of strtoul() for conversion Signed-off-by: Christian Hohnstaedt Acked-by: Jean Delvare Signed-off-by: Mark M. Hoffman commit 13454328d91448c7ca79ffd559374f087453bdde Author: Krzysztof Helt Date: Wed Aug 22 14:05:22 2007 -0400 hwmon: (thmc50) add individual alarm & fault files This patch adds individual alarm and fault files to the thmc50 driver. These sysfs entries are required for a new libsensors library. Signed-off-by: Krzysztof Helt Acked-by: Jean Delvare Signed-off-by: Mark M. Hoffman commit 8ecf86217c834fa80cc3abb994b469c8e9fa16ff Author: Darrick J. Wong Date: Tue Jul 31 11:06:52 2007 -0700 hwmon: add support for adt7470 New driver to expose temperature and fan controls attached to Analog Devices ADT7470 hwmon chips. Signed-off-by: Darrick J. Wong Signed-off-by: Mark M. Hoffman commit f93c9bb2f9b83e8631343aad54c015c8239bd496 Author: Mark M. Hoffman Date: Tue Aug 21 23:10:46 2007 -0400 hwmon: (f71882fg) trivial whitespace cleanup Signed-off-by: Mark M. Hoffman commit 0f294bdb95b779d4fc0abb43b4f6aff6af9e1eae Author: Hans de Goede Date: Fri Jul 13 14:34:19 2007 +0200 hwmon: add support for Fintek F71882FG and F71883FG This is the second version of a new driver for the hardware monitoring features of the Fintek F71882FG and F71883FG Super-I/O chips. This version has several small fixes for flaws discovered during the review of the first version. This version of the driver does not support the pwm part of these chips (yet). I'll first design a sysfs api for this and post that for discussion, and then implement pwm support as an incremental patch over this one. This driver supports all sensors of this chip, except for the vid inputs. The vid inputs are somewhat documented in the datasheet, but I know nothing about vid/vrm stuff. Help with this would be much appreciated. Signed-off-by: Hans de Goede Acked-by: Darrick J. Wong Signed-off-by: Mark M. Hoffman commit b2aefff1b4ec2de22bbb4eea3823ca5fbfa0b60a Author: Jean Delvare Date: Sun Jul 15 10:36:06 2007 +0200 hwmon: (f71805f) List the F71806F/FG as supported The Fintek F71806F/FG is compatible with the F71872F/FG, so it is already supported by the f71805f hardware monitoring driver. In fact, both chips have the same chip ID, so the driver can't even differentiate between them. Signed-off-by: Jean Delvare Acked-by: Hans de Goede Signed-off-by: Mark M. Hoffman commit c051c493b9310e50d7184652d8864c1aad7b13f9 Author: Gong Jun Date: Fri Jul 13 18:45:54 2007 +0200 hwmon: (w83792d) Add individual alarm files w83792d: add individual alarm files for the new libsensors. Signed-off-by: Gong Jun Signed-off-by: Jean Delvare Signed-off-by: Mark M. Hoffman commit 1517aa526ad16b6f124f827030e396c480932ced Author: Krzysztof Helt Date: Thu Jul 19 08:00:17 2007 +0200 hwmon: adm1021 clean ups This patch provides some coding standard cleanups and general code improvements (more debug info, signed values for temperatures, changed names of ADM1023 regs, removed read/write_value functions). Signed-off-by: Krzysztof Helt Acked-by: Jean Delvare Signed-off-by: Mark M. Hoffman Documentation/hwmon/f71805f | 7 Documentation/hwmon/lm93 | 126 ---- Documentation/hwmon/sysfs-interface | 79 +-- drivers/hwmon/Kconfig | 36 + drivers/hwmon/Makefile | 3 drivers/hwmon/ad7418.c | 2 drivers/hwmon/adm1021.c | 226 ++++---- drivers/hwmon/adt7470.c | 1050 +++++++++++++++++++++++++++++++++++ drivers/hwmon/asb100.c | 4 drivers/hwmon/ds1621.c | 2 drivers/hwmon/f71805f.c | 5 drivers/hwmon/f71882fg.c | 950 ++++++++++++++++++++++++++++++++ drivers/hwmon/f75375s.c | 691 +++++++++++++++++++++++ drivers/hwmon/lm70.c | 15 - drivers/hwmon/lm75.c | 2 drivers/hwmon/lm75.h | 2 drivers/hwmon/lm77.c | 2 drivers/hwmon/lm93.c | 10 drivers/hwmon/smsc47m1.c | 4 drivers/hwmon/thmc50.c | 28 + drivers/hwmon/w83627ehf.c | 6 drivers/hwmon/w83627hf.c | 25 - drivers/hwmon/w83781d.c | 18 - drivers/hwmon/w83792d.c | 49 ++ 24 files changed, 3040 insertions(+), 302 deletions(-) diff --git a/Documentation/hwmon/f71805f b/Documentation/hwmon/f71805f index 94e0d2c..f0d5597 100644 --- a/Documentation/hwmon/f71805f +++ b/Documentation/hwmon/f71805f @@ -6,6 +6,10 @@ Supported chips: Prefix: 'f71805f' Addresses scanned: none, address read from Super I/O config space Datasheet: Available from the Fintek website + * Fintek F71806F/FG + Prefix: 'f71872f' + Addresses scanned: none, address read from Super I/O config space + Datasheet: Available from the Fintek website * Fintek F71872F/FG Prefix: 'f71872f' Addresses scanned: none, address read from Super I/O config space @@ -38,6 +42,9 @@ The Fintek F71872F/FG Super I/O chip is additional internal voltages monitored (VSB and battery). It also features 6 VID inputs. The VID inputs are not yet supported by this driver. +The Fintek F71806F/FG Super-I/O chip is essentially the same as the +F71872F/FG, and is undistinguishable therefrom. + The driver assumes that no more than one chip is present, which seems reasonable. diff --git a/Documentation/hwmon/lm93 b/Documentation/hwmon/lm93 index 4e4a1dc..ac711f3 100644 --- a/Documentation/hwmon/lm93 +++ b/Documentation/hwmon/lm93 @@ -7,7 +7,7 @@ Supported chips: Addresses scanned: I2C 0x2c-0x2e Datasheet: http://www.national.com/ds.cgi/LM/LM93.pdf -Author: +Authors: Mark M. Hoffman Ported to 2.6 by Eric J. Bowersox Adapted to 2.6.20 by Carsten Emde @@ -16,7 +16,6 @@ Author: Module Parameters ----------------- -(specific to LM93) * init: integer Set to non-zero to force some initializations (default is 0). * disable_block: integer @@ -37,30 +36,13 @@ (specific to LM93) I.e. this parameter controls the VID pin input thresholds; if your VID inputs are not working, try changing this. The default value is "0". -(common among sensor drivers) -* force: short array (min = 1, max = 48) - List of adapter,address pairs to assume to be present. Autodetection - of the target device will still be attempted. Use one of the more - specific force directives below if this doesn't detect the device. -* force_lm93: short array (min = 1, max = 48) - List of adapter,address pairs which are unquestionably assumed to contain - a 'lm93' chip -* ignore: short array (min = 1, max = 48) - List of adapter,address pairs not to scan -* ignore_range: short array (min = 1, max = 48) - List of adapter,start-addr,end-addr triples not to scan -* probe: short array (min = 1, max = 48) - List of adapter,address pairs to scan additionally -* probe_range: short array (min = 1, max = 48) - List of adapter,start-addr,end-addr triples to scan additionally - Hardware Description -------------------- (from the datasheet) -The LM93, hardware monitor, has a two wire digital interface compatible with +The LM93 hardware monitor has a two wire digital interface compatible with SMBus 2.0. Using an 8-bit ADC, the LM93 measures the temperature of two remote diode connected transistors as well as its own die and 16 power supply voltages. To set fan speed, the LM93 has two PWM outputs that are each @@ -69,18 +51,12 @@ table based. The LM93 includes a digital temperature readings for better control of fan speed. The LM93 has four tachometer inputs to measure fan speed. Limit and status registers for all measured values are included. The LM93 builds upon the functionality of -previous motherboard management ASICs and uses some of the LM85 s features +previous motherboard management ASICs and uses some of the LM85's features (i.e. smart tachometer mode). It also adds measurement and control support for dynamic Vccp monitoring and PROCHOT. It is designed to monitor a dual processor Xeon class motherboard with a minimum of external components. -Driver Description ------------------- - -This driver implements support for the National Semiconductor LM93. - - User Interface -------------- @@ -101,7 +77,7 @@ These intervals can be found in the sysf prochot2_interval. The values in these files specify the intervals for #P1_PROCHOT and #P2_PROCHOT, respectively. Selecting a value not in this list will cause the driver to use the next largest interval. The available -intervals are: +intervals are (in seconds): #PROCHOT intervals: 0.73, 1.46, 2.9, 5.8, 11.7, 23.3, 46.6, 93.2, 186, 372 @@ -111,12 +87,12 @@ assert #P2_PROCHOT, and vice-versa. Thi non-zero integer to the sysfs file prochot_short. The LM93 can also override the #PROCHOT pins by driving a PWM signal onto -one or both of them. When overridden, the signal has a period of 3.56 mS, +one or both of them. When overridden, the signal has a period of 3.56 ms, a minimum pulse width of 5 clocks (at 22.5kHz => 6.25% duty cycle), and a maximum pulse width of 80 clocks (at 22.5kHz => 99.88% duty cycle). The sysfs files prochot1_override and prochot2_override contain boolean -intgers which enable or disable the override function for #P1_PROCHOT and +integers which enable or disable the override function for #P1_PROCHOT and #P2_PROCHOT, respectively. The sysfs file prochot_override_duty_cycle contains a value controlling the duty cycle for the PWM signal used when the override function is enabled. This value ranges from 0 to 15, with 0 @@ -166,7 +142,7 @@ frequency values are constrained by the not available will cause the driver to use the next largest value. Also note that this parameter has implications for the Smart Tach Mode (see above). -PWM Output Frequencies: 12, 36, 48, 60, 72, 84, 96, 22500 (h/w default) +PWM Output Frequencies (in Hz): 12, 36, 48, 60, 72, 84, 96, 22500 (default) Automatic PWM: @@ -178,7 +154,7 @@ individual control sources to which the The eight control sources are: temp1-temp4 (aka "zones" in the datasheet), #PROCHOT 1 & 2, and #VRDHOT 1 & 2. The bindings are expressed as a bitmask in the sysfs files pwm_auto_channels, where a "1" enables the binding, and - a "0" disables it. The h/w default is 0x0f (all temperatures bound). +a "0" disables it. The h/w default is 0x0f (all temperatures bound). 0x01 - Temp 1 0x02 - Temp 2 @@ -324,89 +300,3 @@ LM93 Unique sysfs Files gpio input state of 8 GPIO pins; read-only - -Sample Configuration File -------------------------- - -Here is a sample LM93 chip config for sensors.conf: - ----------- cut here ---------- -chip "lm93-*" - -# VOLTAGE INPUTS - - # labels and scaling based on datasheet recommendations - label in1 "+12V1" - compute in1 @ * 12.945, @ / 12.945 - set in1_min 12 * 0.90 - set in1_max 12 * 1.10 - - label in2 "+12V2" - compute in2 @ * 12.945, @ / 12.945 - set in2_min 12 * 0.90 - set in2_max 12 * 1.10 - - label in3 "+12V3" - compute in3 @ * 12.945, @ / 12.945 - set in3_min 12 * 0.90 - set in3_max 12 * 1.10 - - label in4 "FSB_Vtt" - - label in5 "3GIO" - - label in6 "ICH_Core" - - label in7 "Vccp1" - - label in8 "Vccp2" - - label in9 "+3.3V" - set in9_min 3.3 * 0.90 - set in9_max 3.3 * 1.10 - - label in10 "+5V" - set in10_min 5.0 * 0.90 - set in10_max 5.0 * 1.10 - - label in11 "SCSI_Core" - - label in12 "Mem_Core" - - label in13 "Mem_Vtt" - - label in14 "Gbit_Core" - - # Assuming R1/R2 = 4.1143, and 3.3V reference - # -12V = (4.1143 + 1) * (@ - 3.3) + 3.3 - label in15 "-12V" - compute in15 @ * 5.1143 - 13.57719, (@ + 13.57719) / 5.1143 - set in15_min -12 * 0.90 - set in15_max -12 * 1.10 - - label in16 "+3.3VSB" - set in16_min 3.3 * 0.90 - set in16_max 3.3 * 1.10 - -# TEMPERATURE INPUTS - - label temp1 "CPU1" - label temp2 "CPU2" - label temp3 "LM93" - -# TACHOMETER INPUTS - - label fan1 "Fan1" - set fan1_min 3000 - label fan2 "Fan2" - set fan2_min 3000 - label fan3 "Fan3" - set fan3_min 3000 - label fan4 "Fan4" - set fan4_min 3000 - -# PWM OUTPUTS - - label pwm1 "CPU1" - label pwm2 "CPU2" - diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index b3a9e1b..db7bb4a 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -78,8 +78,21 @@ RW read/write value Read/write values may be read-only for some chips, depending on the hardware implementation. -All entries are optional, and should only be created in a given driver -if the chip has the feature. +All entries (except name) are optional, and should only be created in a +given driver if the chip has the feature. + + +******** +* Name * +******** + +name The chip name. + This should be a short, lowercase string, not containing + spaces nor dashes, representing the chip name. This is + the only mandatory attribute. + I2C devices get this attribute created automatically. + RO + ************ * Voltages * @@ -104,18 +117,17 @@ in[0-*]_input Voltage input value. by the chip driver, and must be done by the application. However, some drivers (notably lm87 and via686a) do scale, because of internal resistors built into a chip. - These drivers will output the actual voltage. - - Typical usage: - in0_* CPU #1 voltage (not scaled) - in1_* CPU #2 voltage (not scaled) - in2_* 3.3V nominal (not scaled) - in3_* 5.0V nominal (scaled) - in4_* 12.0V nominal (scaled) - in5_* -12.0V nominal (scaled) - in6_* -5.0V nominal (scaled) - in7_* varies - in8_* varies + These drivers will output the actual voltage. Rule of + thumb: drivers should report the voltage values at the + "pins" of the chip. + +in[0-*]_label Suggested voltage channel label. + Text string + Should only be created if the driver has hints about what + this voltage channel is being used for, and user-space + doesn't. In all other cases, the label is provided by + user-space. + RO cpu[0-*]_vid CPU core reference voltage. Unit: millivolt @@ -159,6 +171,13 @@ fan[1-*]_target Only makes sense if the chip supports closed-loop fan speed control based on the measured fan speed. +fan[1-*]_label Suggested fan channel label. + Text string + Should only be created if the driver has hints about what + this fan channel is being used for, and user-space doesn't. + In all other cases, the label is provided by user-space. + RO + Also see the Alarms section for status flags associated with fans. @@ -219,12 +238,12 @@ temp[1-*]_auto_point[1-*]_temp_hyst **************** temp[1-*]_type Sensor type selection. - Integers 1 to 6 or thermistor Beta value (typically 3435) + Integers 1 to 6 RW 1: PII/Celeron Diode 2: 3904 transistor 3: thermal diode - 4: thermistor (default/unknown Beta) + 4: thermistor 5: AMD AMDSI 6: Intel PECI Not all types are supported by all chips @@ -260,18 +279,19 @@ temp[1-*]_crit_hyst from the critical value. RW -temp[1-4]_offset +temp[1-*]_offset Temperature offset which is added to the temperature reading by the chip. Unit: millidegree Celsius Read/Write value. - If there are multiple temperature sensors, temp1_* is - generally the sensor inside the chip itself, - reported as "motherboard temperature". temp2_* to - temp4_* are generally sensors external to the chip - itself, for example the thermal diode inside the CPU or - a thermistor nearby. +temp[1-*]_label Suggested temperature channel label. + Text string + Should only be created if the driver has hints about what + this temperature channel is being used for, and user-space + doesn't. In all other cases, the label is provided by + user-space. + RO Some chips measure temperature using external thermistors and an ADC, and report the temperature measurement as a voltage. Converting this voltage @@ -391,16 +411,3 @@ beep_mask Bitmask for beep. use discouraged for the same reason. Use individual *_beep files instead. RW - - -********* -* Other * -********* - -eeprom Raw EEPROM data in binary form. - RO - -pec Enable or disable PEC (SMBus only) - 0: disable - 1: enable - RW diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 192953b..a2f6074 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -133,6 +133,16 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_ADT7470 + tristate "Analog Devices ADT7470" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Analog Devices + ADT7470 temperature monitoring chips. + + This driver can also be built as a module. If so, the module + will be called adt7470. + config SENSORS_K8TEMP tristate "AMD Athlon64/FX or Opteron temperature sensor" depends on X86 && PCI && EXPERIMENTAL @@ -206,16 +216,36 @@ config SENSORS_DS1621 will be called ds1621. config SENSORS_F71805F - tristate "Fintek F71805F/FG and F71872F/FG" + tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG" depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring - features of the Fintek F71805F/FG and F71872F/FG Super-I/O - chips. + features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG + Super-I/O chips. This driver can also be built as a module. If so, the module will be called f71805f. +config SENSORS_F71882FG + tristate "Fintek F71882FG and F71883FG" + depends on EXPERIMENTAL + help + If you say yes here you get support for hardware monitoring + features of the Fintek F71882FG and F71883FG Super-I/O chips. + + This driver can also be built as a module. If so, the module + will be called f71882fg. + +config SENSORS_F75375S + tristate "Fintek F75375S/SP and F75373"; + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for hardware monitoring + features of the Fintek F75375S/SP and F75373 + + This driver can also be built as a module. If so, the module + will be called f75375s. + config SENSORS_FSCHER tristate "FSC Hermes" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d04f900..19da12e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SENSORS_ADM1026) += adm1026 obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o @@ -29,6 +30,8 @@ obj-$(CONFIG_SENSORS_CORETEMP) += corete obj-$(CONFIG_SENSORS_DME1737) += dme1737.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o +obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o +obj-$(CONFIG_SENSORS_F75375S) += f75375s.o obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index cc8b624..879ea72 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -172,7 +172,7 @@ static ssize_t set_temp(struct device *d struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct ad7418_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp = simple_strtol(buf, NULL, 10); mutex_lock(&data->lock); data->temp[attr->index] = LM75_TEMP_TO_REG(temp); diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index c466329..6649e06 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -1,6 +1,6 @@ /* adm1021.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring + monitoring Copyright (c) 1998, 1999 Frodo Looijaard and Philip Edelbrock @@ -32,11 +32,12 @@ #include /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, - 0x4c, 0x4d, 0x4e, + 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; /* Insmod parameters */ -I2C_CLIENT_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066); +I2C_CLIENT_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, + mc1066); /* adm1021 constants specified below */ @@ -45,20 +46,21 @@ I2C_CLIENT_INSMOD_8(adm1021, adm1023, ma #define ADM1021_REG_TEMP 0x00 #define ADM1021_REG_REMOTE_TEMP 0x01 #define ADM1021_REG_STATUS 0x02 -#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ -#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ -#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ +/* 0x41 = AD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi */ +#define ADM1021_REG_MAN_ID 0xFE +/* ADM1021 = 0x0X, ADM1023 = 0x3X */ +#define ADM1021_REG_DEV_ID 0xFF /* These use different addresses for reading/writing */ #define ADM1021_REG_CONFIG_R 0x03 #define ADM1021_REG_CONFIG_W 0x09 #define ADM1021_REG_CONV_RATE_R 0x04 #define ADM1021_REG_CONV_RATE_W 0x0A /* These are for the ADM1023's additional precision on the remote temp sensor */ -#define ADM1021_REG_REM_TEMP_PREC 0x010 -#define ADM1021_REG_REM_OFFSET 0x011 -#define ADM1021_REG_REM_OFFSET_PREC 0x012 -#define ADM1021_REG_REM_TOS_PREC 0x013 -#define ADM1021_REG_REM_THYST_PREC 0x014 +#define ADM1023_REG_REM_TEMP_PREC 0x10 +#define ADM1023_REG_REM_OFFSET 0x11 +#define ADM1023_REG_REM_OFFSET_PREC 0x12 +#define ADM1023_REG_REM_TOS_PREC 0x13 +#define ADM1023_REG_REM_THYST_PREC 0x14 /* limits */ #define ADM1021_REG_TOS_R 0x05 #define ADM1021_REG_TOS_W 0x0B @@ -77,14 +79,13 @@ #define ADM1021_REG_ONESHOT 0x0F these macros are called: arguments may be evaluated more than once. Fixing this is just not worth it. */ /* Conversions note: 1021 uses normal integer signed-byte format*/ -#define TEMP_FROM_REG(val) (val > 127 ? (val-256)*1000 : val*1000) -#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? (val/1000)+256 : val/1000),0,255)) +#define TEMP_TO_REG(val) SENSORS_LIMIT((val) / 1000, -128, 127) /* Initial values */ -/* Note: Even though I left the low and high limits named os and hyst, -they don't quite work like a thermostat the way the LM75 does. I.e., -a lower temp than THYST actually triggers an alarm instead of +/* Note: Even though I left the low and high limits named os and hyst, +they don't quite work like a thermostat the way the LM75 does. I.e., +a lower temp than THYST actually triggers an alarm instead of clearing it. Weird, ey? --Phil */ /* Each client has this additional data */ @@ -97,12 +98,12 @@ struct adm1021_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - u8 temp_max; /* Register values */ - u8 temp_hyst; - u8 temp_input; - u8 remote_temp_max; - u8 remote_temp_hyst; - u8 remote_temp_input; + s8 temp_max; /* Register values */ + s8 temp_hyst; + s8 temp_input; + s8 remote_temp_max; + s8 remote_temp_hyst; + s8 remote_temp_input; u8 alarms; /* Special values for ADM1023 only */ u8 remote_temp_prec; @@ -116,9 +117,6 @@ static int adm1021_attach_adapter(struct static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind); static void adm1021_init_client(struct i2c_client *client); static int adm1021_detach_client(struct i2c_client *client); -static int adm1021_read_value(struct i2c_client *client, u8 reg); -static int adm1021_write_value(struct i2c_client *client, u8 reg, - u16 value); static struct adm1021_data *adm1021_update_device(struct device *dev); /* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ @@ -135,11 +133,12 @@ static struct i2c_driver adm1021_driver .detach_client = adm1021_detach_client, }; -#define show(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +#define show(value) \ +static ssize_t show_##value(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ struct adm1021_data *data = adm1021_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ + return sprintf(buf, "%d\n", 1000 * data->value); \ } show(temp_max); show(temp_hyst); @@ -148,26 +147,29 @@ show(remote_temp_max); show(remote_temp_hyst); show(remote_temp_input); -#define show2(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1021_data *data = adm1021_update_device(dev); \ - return sprintf(buf, "%d\n", data->value); \ +static ssize_t show_alarms(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adm1021_data *data = adm1021_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); } -show2(alarms); - -#define set(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct adm1021_data *data = i2c_get_clientdata(client); \ - int temp = simple_strtoul(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock); \ - data->value = TEMP_TO_REG(temp); \ - adm1021_write_value(client, reg, data->value); \ - mutex_unlock(&data->update_lock); \ - return count; \ + +#define set(value, reg) \ +static ssize_t set_##value(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1021_data *data = i2c_get_clientdata(client); \ + long temp = simple_strtol(buf, NULL, 10); \ + \ + mutex_lock(&data->update_lock); \ + data->value = TEMP_TO_REG(temp); \ + if (!read_only) \ + i2c_smbus_write_byte_data(client, reg, data->value); \ + mutex_unlock(&data->update_lock); \ + return count; \ } set(temp_max, ADM1021_REG_TOS_W); set(temp_hyst, ADM1021_REG_THYST_W); @@ -208,35 +210,44 @@ static const struct attribute_group adm1 static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind) { int i; - struct i2c_client *new_client; + struct i2c_client *client; struct adm1021_data *data; int err = 0; const char *type_name = ""; + int conv_rate, status, config; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + pr_debug("adm1021: detect failed, " + "smbus byte data not supported!\n"); goto error0; + } /* OK. For now, we presume we have a valid client. We now create the client structure, even though we cannot fill it completely yet. - But it allows us to access adm1021_{read,write}_value. */ + But it allows us to access adm1021 register values. */ if (!(data = kzalloc(sizeof(struct adm1021_data), GFP_KERNEL))) { + pr_debug("adm1021: detect failed, kzalloc failed!\n"); err = -ENOMEM; goto error0; } - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &adm1021_driver; - new_client->flags = 0; + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; + client->adapter = adapter; + client->driver = &adm1021_driver; + status = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS); + conv_rate = i2c_smbus_read_byte_data(client, + ADM1021_REG_CONV_RATE_R); + config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R); /* Now, we do the remaining detection. */ if (kind < 0) { - if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00 - || (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00 - || (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) { + if ((status & 0x03) != 0x00 || (config & 0x3F) != 0x00 + || (conv_rate & 0xF8) != 0x00) { + pr_debug("adm1021: detect failed, " + "chip not detected!\n"); err = -ENODEV; goto error1; } @@ -244,9 +255,10 @@ static int adm1021_detect(struct i2c_ada /* Determine the chip type. */ if (kind <= 0) { - i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); + i = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID); if (i == 0x41) - if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) + if ((i2c_smbus_read_byte_data(client, + ADM1021_REG_DEV_ID) & 0xF0) == 0x30) kind = adm1023; else kind = adm1021; @@ -255,15 +267,16 @@ static int adm1021_detect(struct i2c_ada else if (i == 0x23) kind = gl523sm; else if ((i == 0x4d) && - (adm1021_read_value(new_client, ADM1021_REG_DEV_ID) == 0x01)) + (i2c_smbus_read_byte_data(client, + ADM1021_REG_DEV_ID) == 0x01)) kind = max1617a; else if (i == 0x54) kind = mc1066; /* LM84 Mfr ID in a different place, and it has more unused bits */ - else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00 - && (kind == 0 /* skip extra detection */ - || ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00 - && (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00))) + else if (conv_rate == 0x00 + && (kind == 0 /* skip extra detection */ + || ((config & 0x7F) == 0x00 + && (status & 0xAB) == 0x00))) kind = lm84; else kind = max1617; @@ -286,26 +299,27 @@ static int adm1021_detect(struct i2c_ada } else if (kind == mc1066) { type_name = "mc1066"; } + pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n", + type_name, i2c_adapter_id(adapter), address); - /* Fill in the remaining client fields and put it into the global list */ - strlcpy(new_client->name, type_name, I2C_NAME_SIZE); + /* Fill in the remaining client fields */ + strlcpy(client->name, type_name, I2C_NAME_SIZE); data->type = kind; - data->valid = 0; mutex_init(&data->update_lock); /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) + if ((err = i2c_attach_client(client))) goto error1; /* Initialize the ADM1021 chip */ - if (kind != lm84) - adm1021_init_client(new_client); + if (kind != lm84 && !read_only) + adm1021_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1021_group))) + if ((err = sysfs_create_group(&client->dev.kobj, &adm1021_group))) goto error2; - data->class_dev = hwmon_device_register(&new_client->dev); + data->class_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); goto error3; @@ -314,9 +328,9 @@ static int adm1021_detect(struct i2c_ada return 0; error3: - sysfs_remove_group(&new_client->dev.kobj, &adm1021_group); + sysfs_remove_group(&client->dev.kobj, &adm1021_group); error2: - i2c_detach_client(new_client); + i2c_detach_client(client); error1: kfree(data); error0: @@ -326,10 +340,10 @@ error0: static void adm1021_init_client(struct i2c_client *client) { /* Enable ADC and disable suspend mode */ - adm1021_write_value(client, ADM1021_REG_CONFIG_W, - adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF); + i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W, + i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R) & 0xBF); /* Set Conversion rate to 1/sec (this can be tinkered with) */ - adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04); + i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04); } static int adm1021_detach_client(struct i2c_client *client) @@ -347,19 +361,6 @@ static int adm1021_detach_client(struct return 0; } -/* All registers are byte-sized */ -static int adm1021_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if (!read_only) - return i2c_smbus_write_byte_data(client, reg, value); - return 0; -} - static struct adm1021_data *adm1021_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -371,19 +372,36 @@ static struct adm1021_data *adm1021_upda || !data->valid) { dev_dbg(&client->dev, "Starting adm1021 update\n"); - data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP); - data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R); - data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R); - data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); - data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); - data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); - data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0x7c; + data->temp_input = i2c_smbus_read_byte_data(client, + ADM1021_REG_TEMP); + data->temp_max = i2c_smbus_read_byte_data(client, + ADM1021_REG_TOS_R); + data->temp_hyst = i2c_smbus_read_byte_data(client, + ADM1021_REG_THYST_R); + data->remote_temp_input = i2c_smbus_read_byte_data(client, + ADM1021_REG_REMOTE_TEMP); + data->remote_temp_max = i2c_smbus_read_byte_data(client, + ADM1021_REG_REMOTE_TOS_R); + data->remote_temp_hyst = i2c_smbus_read_byte_data(client, + ADM1021_REG_REMOTE_THYST_R); + data->alarms = i2c_smbus_read_byte_data(client, + ADM1021_REG_STATUS) & 0x7c; if (data->type == adm1023) { - data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC); - data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); - data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC); - data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET); - data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC); + data->remote_temp_prec = + i2c_smbus_read_byte_data(client, + ADM1023_REG_REM_TEMP_PREC); + data->remote_temp_os_prec = + i2c_smbus_read_byte_data(client, + ADM1023_REG_REM_TOS_PREC); + data->remote_temp_hyst_prec = + i2c_smbus_read_byte_data(client, + ADM1023_REG_REM_THYST_PREC); + data->remote_temp_offset = + i2c_smbus_read_byte_data(client, + ADM1023_REG_REM_OFFSET); + data->remote_temp_offset_prec = + i2c_smbus_read_byte_data(client, + ADM1023_REG_REM_OFFSET_PREC); } data->last_updated = jiffies; data->valid = 1; diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c new file mode 100644 index 0000000..c4dc205 --- /dev/null +++ b/drivers/hwmon/adt7470.c @@ -0,0 +1,1050 @@ +/* + * A hwmon driver for the Analog Devices ADT7470 + * Copyright (C) 2007 IBM + * + * Author: Darrick J. Wong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(adt7470); + +/* ADT7470 registers */ +#define ADT7470_REG_BASE_ADDR 0x20 +#define ADT7470_REG_TEMP_BASE_ADDR 0x20 +#define ADT7470_REG_TEMP_MAX_ADDR 0x29 +#define ADT7470_REG_FAN_BASE_ADDR 0x2A +#define ADT7470_REG_FAN_MAX_ADDR 0x31 +#define ADT7470_REG_PWM_BASE_ADDR 0x32 +#define ADT7470_REG_PWM_MAX_ADDR 0x35 +#define ADT7470_REG_PWM_MAX_BASE_ADDR 0x38 +#define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B +#define ADT7470_REG_CFG 0x40 +#define ADT7470_FSPD_MASK 0x04 +#define ADT7470_REG_ALARM1 0x41 +#define ADT7470_REG_ALARM2 0x42 +#define ADT7470_REG_TEMP_LIMITS_BASE_ADDR 0x44 +#define ADT7470_REG_TEMP_LIMITS_MAX_ADDR 0x57 +#define ADT7470_REG_FAN_MIN_BASE_ADDR 0x58 +#define ADT7470_REG_FAN_MIN_MAX_ADDR 0x5F +#define ADT7470_REG_FAN_MAX_BASE_ADDR 0x60 +#define ADT7470_REG_FAN_MAX_MAX_ADDR 0x67 +#define ADT7470_REG_PWM_CFG_BASE_ADDR 0x68 +#define ADT7470_REG_PWM12_CFG 0x68 +#define ADT7470_PWM2_AUTO_MASK 0x40 +#define ADT7470_PWM1_AUTO_MASK 0x80 +#define ADT7470_REG_PWM34_CFG 0x69 +#define ADT7470_PWM3_AUTO_MASK 0x40 +#define ADT7470_PWM4_AUTO_MASK 0x80 +#define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A +#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D +#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71 +#define ADT7470_REG_ACOUSTICS12 0x75 +#define ADT7470_REG_ACOUSTICS34 0x76 +#define ADT7470_REG_DEVICE 0x3D +#define ADT7470_REG_VENDOR 0x3E +#define ADT7470_REG_REVISION 0x3F +#define ADT7470_REG_ALARM1_MASK 0x72 +#define ADT7470_REG_ALARM2_MASK 0x73 +#define ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR 0x7C +#define ADT7470_REG_PWM_AUTO_TEMP_MAX_ADDR 0x7D +#define ADT7470_REG_MAX_ADDR 0x81 + +#define ADT7470_TEMP_COUNT 10 +#define ADT7470_TEMP_REG(x) (ADT7470_REG_TEMP_BASE_ADDR + (x)) +#define ADT7470_TEMP_MIN_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2)) +#define ADT7470_TEMP_MAX_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + \ + ((x) * 2) + 1) + +#define ADT7470_FAN_COUNT 4 +#define ADT7470_REG_FAN(x) (ADT7470_REG_FAN_BASE_ADDR + ((x) * 2)) +#define ADT7470_REG_FAN_MIN(x) (ADT7470_REG_FAN_MIN_BASE_ADDR + ((x) * 2)) +#define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2)) + +#define ADT7470_PWM_COUNT 4 +#define ADT7470_REG_PWM(x) (ADT7470_REG_PWM_BASE_ADDR + (x)) +#define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x)) +#define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x)) +#define ADT7470_REG_PWM_TMIN(x) (ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR + (x)) +#define ADT7470_REG_PWM_CFG(x) (ADT7470_REG_PWM_CFG_BASE_ADDR + ((x) / 2)) +#define ADT7470_REG_PWM_AUTO_TEMP(x) (ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR + \ + ((x) / 2)) + +#define ADT7470_VENDOR 0x41 +#define ADT7470_DEVICE 0x70 +/* datasheet only mentions a revision 2 */ +#define ADT7470_REVISION 0x02 + +/* "all temps" according to hwmon sysfs interface spec */ +#define ADT7470_PWM_ALL_TEMPS 0x3FF + +/* How often do we reread sensors values? (In jiffies) */ +#define SENSOR_REFRESH_INTERVAL (5 * HZ) + +/* How often do we reread sensor limit values? (In jiffies) */ +#define LIMIT_REFRESH_INTERVAL (60 * HZ) + +/* sleep 1s while gathering temperature data */ +#define TEMP_COLLECTION_TIME 1000 + +#define power_of_2(x) (((x) & ((x) - 1)) == 0) + +/* datasheet says to divide this number by the fan reading to get fan rpm */ +#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) +#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM +#define FAN_PERIOD_INVALID 65535 +#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) + +struct adt7470_data { + struct i2c_client client; + struct class_device *class_dev; + struct attribute_group attrs; + struct mutex lock; + char sensors_valid; + char limits_valid; + unsigned long sensors_last_updated; /* In jiffies */ + unsigned long limits_last_updated; /* In jiffies */ + + s8 temp[ADT7470_TEMP_COUNT]; + s8 temp_min[ADT7470_TEMP_COUNT]; + s8 temp_max[ADT7470_TEMP_COUNT]; + u16 fan[ADT7470_FAN_COUNT]; + u16 fan_min[ADT7470_FAN_COUNT]; + u16 fan_max[ADT7470_FAN_COUNT]; + u16 alarms, alarms_mask; + u8 force_pwm_max; + u8 pwm[ADT7470_PWM_COUNT]; + u8 pwm_max[ADT7470_PWM_COUNT]; + u8 pwm_automatic[ADT7470_PWM_COUNT]; + u8 pwm_min[ADT7470_PWM_COUNT]; + s8 pwm_tmin[ADT7470_PWM_COUNT]; + u8 pwm_auto_temp[ADT7470_PWM_COUNT]; +}; + +static int adt7470_attach_adapter(struct i2c_adapter *adapter); +static int adt7470_detect(struct i2c_adapter *adapter, int address, int kind); +static int adt7470_detach_client(struct i2c_client *client); + +static struct i2c_driver adt7470_driver = { + .driver = { + .name = "adt7470", + }, + .attach_adapter = adt7470_attach_adapter, + .detach_client = adt7470_detach_client, +}; + +/* + * 16-bit registers on the ADT7470 are low-byte first. The data sheet says + * that the low byte must be read before the high byte. + */ +static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg) +{ + u16 foo; + foo = i2c_smbus_read_byte_data(client, reg); + foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); + return foo; +} + +static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg, + u16 value) +{ + return i2c_smbus_write_byte_data(client, reg, value & 0xFF) + && i2c_smbus_write_byte_data(client, reg + 1, value >> 8); +} + +static void adt7470_init_client(struct i2c_client *client) +{ + int reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); + + if (reg < 0) { + dev_err(&client->dev, "cannot read configuration register\n"); + } else { + /* start monitoring (and do a self-test) */ + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3); + } +} + +static struct adt7470_data *adt7470_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + unsigned long local_jiffies = jiffies; + u8 cfg; + int i; + + mutex_lock(&data->lock); + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) + && data->sensors_valid) + goto no_sensor_update; + + /* start reading temperature sensors */ + cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); + cfg |= 0x80; + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); + + /* + * Delay is 200ms * number of tmp05 sensors. Too bad + * there's no way to figure out how many are connected. + * For now, assume 1s will work. + */ + msleep(TEMP_COLLECTION_TIME); + + /* done reading temperature sensors */ + cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); + cfg &= ~0x80; + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); + + for (i = 0; i < ADT7470_TEMP_COUNT; i++) + data->temp[i] = i2c_smbus_read_byte_data(client, + ADT7470_TEMP_REG(i)); + + for (i = 0; i < ADT7470_FAN_COUNT; i++) + data->fan[i] = adt7470_read_word_data(client, + ADT7470_REG_FAN(i)); + + for (i = 0; i < ADT7470_PWM_COUNT; i++) { + int reg; + int reg_mask; + + data->pwm[i] = i2c_smbus_read_byte_data(client, + ADT7470_REG_PWM(i)); + + if (i % 2) + reg_mask = ADT7470_PWM2_AUTO_MASK; + else + reg_mask = ADT7470_PWM1_AUTO_MASK; + + reg = ADT7470_REG_PWM_CFG(i); + if (i2c_smbus_read_byte_data(client, reg) & reg_mask) + data->pwm_automatic[i] = 1; + else + data->pwm_automatic[i] = 0; + + reg = ADT7470_REG_PWM_AUTO_TEMP(i); + cfg = i2c_smbus_read_byte_data(client, reg); + if (!(i % 2)) + data->pwm_auto_temp[i] = cfg >> 4; + else + data->pwm_auto_temp[i] = cfg & 0xF; + } + + if (i2c_smbus_read_byte_data(client, ADT7470_REG_CFG) & + ADT7470_FSPD_MASK) + data->force_pwm_max = 1; + else + data->force_pwm_max = 0; + + data->alarms = adt7470_read_word_data(client, ADT7470_REG_ALARM1); + data->alarms_mask = adt7470_read_word_data(client, + ADT7470_REG_ALARM1_MASK); + + data->sensors_last_updated = local_jiffies; + data->sensors_valid = 1; + +no_sensor_update: + if (time_before(local_jiffies, data->limits_last_updated + + LIMIT_REFRESH_INTERVAL) + && data->limits_valid) + goto out; + + for (i = 0; i < ADT7470_TEMP_COUNT; i++) { + data->temp_min[i] = i2c_smbus_read_byte_data(client, + ADT7470_TEMP_MIN_REG(i)); + data->temp_max[i] = i2c_smbus_read_byte_data(client, + ADT7470_TEMP_MAX_REG(i)); + } + + for (i = 0; i < ADT7470_FAN_COUNT; i++) { + data->fan_min[i] = adt7470_read_word_data(client, + ADT7470_REG_FAN_MIN(i)); + data->fan_max[i] = adt7470_read_word_data(client, + ADT7470_REG_FAN_MAX(i)); + } + + for (i = 0; i < ADT7470_PWM_COUNT; i++) { + data->pwm_max[i] = i2c_smbus_read_byte_data(client, + ADT7470_REG_PWM_MAX(i)); + data->pwm_min[i] = i2c_smbus_read_byte_data(client, + ADT7470_REG_PWM_MIN(i)); + data->pwm_tmin[i] = i2c_smbus_read_byte_data(client, + ADT7470_REG_PWM_TMIN(i)); + } + + data->limits_last_updated = local_jiffies; + data->limits_valid = 1; + +out: + mutex_unlock(&data->lock); + return data; +} + +static ssize_t show_temp_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]); +} + +static ssize_t set_temp_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10) / 1000; + + mutex_lock(&data->lock); + data->temp_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7470_TEMP_MIN_REG(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]); +} + +static ssize_t set_temp_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10) / 1000; + + mutex_lock(&data->lock); + data->temp_max[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7470_TEMP_MAX_REG(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]); +} + +static ssize_t show_alarms(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + + if (attr->index) + return sprintf(buf, "%x\n", data->alarms); + else + return sprintf(buf, "%x\n", data->alarms_mask); +} + +static ssize_t show_fan_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + + if (FAN_DATA_VALID(data->fan_max[attr->index])) + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan_max[attr->index])); + else + return sprintf(buf, "0\n"); +} + +static ssize_t set_fan_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + + if (!temp) + return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + + mutex_lock(&data->lock); + data->fan_max[attr->index] = temp; + adt7470_write_word_data(client, ADT7470_REG_FAN_MAX(attr->index), temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_fan_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + + if (FAN_DATA_VALID(data->fan_min[attr->index])) + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan_min[attr->index])); + else + return sprintf(buf, "0\n"); +} + +static ssize_t set_fan_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + + if (!temp) + return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + + mutex_lock(&data->lock); + data->fan_min[attr->index] = temp; + adt7470_write_word_data(client, ADT7470_REG_FAN_MIN(attr->index), temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + + if (FAN_DATA_VALID(data->fan[attr->index])) + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan[attr->index])); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_force_pwm_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->force_pwm_max); +} + +static ssize_t set_force_pwm_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + u8 reg; + + mutex_lock(&data->lock); + data->force_pwm_max = temp; + reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); + if (temp) + reg |= ADT7470_FSPD_MASK; + else + reg &= ~ADT7470_FSPD_MASK; + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->pwm[attr->index]); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->lock); + data->pwm[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(attr->index), temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_max[attr->index]); +} + +static ssize_t set_pwm_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->lock); + data->pwm_max[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MAX(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_min[attr->index]); +} + +static ssize_t set_pwm_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->lock); + data->pwm_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_tmax(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + /* the datasheet says that tmax = tmin + 20C */ + return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index])); +} + +static ssize_t show_pwm_tmin(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]); +} + +static ssize_t set_pwm_tmin(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10) / 1000; + + mutex_lock(&data->lock); + data->pwm_tmin[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_TMIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_auto(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]); +} + +static ssize_t set_pwm_auto(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index); + int pwm_auto_reg_mask; + u8 reg; + + if (attr->index % 2) + pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; + else + pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; + + if (temp != 2 && temp != 1) + return -EINVAL; + temp--; + + mutex_lock(&data->lock); + data->pwm_automatic[attr->index] = temp; + reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); + if (temp) + reg |= pwm_auto_reg_mask; + else + reg &= ~pwm_auto_reg_mask; + i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_auto_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + u8 ctrl = data->pwm_auto_temp[attr->index]; + + if (ctrl) + return sprintf(buf, "%d\n", 1 << (ctrl - 1)); + else + return sprintf(buf, "%d\n", ADT7470_PWM_ALL_TEMPS); +} + +static int cvt_auto_temp(int input) +{ + if (input == ADT7470_PWM_ALL_TEMPS) + return 0; + if (input < 1 || !power_of_2(input)) + return -EINVAL; + return ilog2(input) + 1; +} + +static ssize_t set_pwm_auto_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + int temp = cvt_auto_temp(simple_strtol(buf, NULL, 10)); + int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); + u8 reg; + + if (temp < 0) + return temp; + + mutex_lock(&data->lock); + data->pwm_automatic[attr->index] = temp; + reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); + + if (!(attr->index % 2)) { + reg &= 0xF; + reg |= (temp << 4) & 0xF0; + } else { + reg &= 0xF0; + reg |= temp & 0xF; + } + + i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); + mutex_unlock(&data->lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL, 0); +static SENSOR_DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarms, NULL, 1); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 3); +static SENSOR_DEVICE_ATTR(temp5_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 4); +static SENSOR_DEVICE_ATTR(temp6_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 5); +static SENSOR_DEVICE_ATTR(temp7_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 6); +static SENSOR_DEVICE_ATTR(temp8_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 7); +static SENSOR_DEVICE_ATTR(temp9_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 8); +static SENSOR_DEVICE_ATTR(temp10_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 9); + +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 0); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 3); +static SENSOR_DEVICE_ATTR(temp5_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 4); +static SENSOR_DEVICE_ATTR(temp6_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 5); +static SENSOR_DEVICE_ATTR(temp7_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 6); +static SENSOR_DEVICE_ATTR(temp8_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 7); +static SENSOR_DEVICE_ATTR(temp9_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 8); +static SENSOR_DEVICE_ATTR(temp10_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 9); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, show_temp, NULL, 9); + +static SENSOR_DEVICE_ATTR(fan1_max, S_IWUSR | S_IRUGO, show_fan_max, + set_fan_max, 0); +static SENSOR_DEVICE_ATTR(fan2_max, S_IWUSR | S_IRUGO, show_fan_max, + set_fan_max, 1); +static SENSOR_DEVICE_ATTR(fan3_max, S_IWUSR | S_IRUGO, show_fan_max, + set_fan_max, 2); +static SENSOR_DEVICE_ATTR(fan4_max, S_IWUSR | S_IRUGO, show_fan_max, + set_fan_max, 3); + +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 0); +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 2); +static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 3); + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); + +static SENSOR_DEVICE_ATTR(force_pwm_max, S_IWUSR | S_IRUGO, + show_force_pwm_max, set_force_pwm_max, 0); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO, show_pwm_tmax, + NULL, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_temp, S_IRUGO, show_pwm_tmax, + NULL, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_temp, S_IRUGO, show_pwm_tmax, + NULL, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point2_temp, S_IRUGO, show_pwm_tmax, + NULL, 3); + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 2); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 3); + +static struct attribute *adt7470_attr[] = +{ + &sensor_dev_attr_alarms.dev_attr.attr, + &sensor_dev_attr_alarm_mask.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp7_max.dev_attr.attr, + &sensor_dev_attr_temp8_max.dev_attr.attr, + &sensor_dev_attr_temp9_max.dev_attr.attr, + &sensor_dev_attr_temp10_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp6_min.dev_attr.attr, + &sensor_dev_attr_temp7_min.dev_attr.attr, + &sensor_dev_attr_temp8_min.dev_attr.attr, + &sensor_dev_attr_temp9_min.dev_attr.attr, + &sensor_dev_attr_temp10_min.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + &sensor_dev_attr_temp10_input.dev_attr.attr, + &sensor_dev_attr_fan1_max.dev_attr.attr, + &sensor_dev_attr_fan2_max.dev_attr.attr, + &sensor_dev_attr_fan3_max.dev_attr.attr, + &sensor_dev_attr_fan4_max.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_force_pwm_max.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr, + &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_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, + NULL +}; + +static int adt7470_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_probe(adapter, &addr_data, adt7470_detect); +} + +static int adt7470_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct adt7470_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kzalloc(sizeof(struct adt7470_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + client = &data->client; + client->addr = address; + client->adapter = adapter; + client->driver = &adt7470_driver; + + i2c_set_clientdata(client, data); + + mutex_init(&data->lock); + + if (kind <= 0) { + int vendor, device, revision; + + vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR); + if (vendor != ADT7470_VENDOR) { + err = -ENODEV; + goto exit_free; + } + + device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE); + if (device != ADT7470_DEVICE) { + err = -ENODEV; + goto exit_free; + } + + revision = i2c_smbus_read_byte_data(client, + ADT7470_REG_REVISION); + if (revision != ADT7470_REVISION) { + err = -ENODEV; + goto exit_free; + } + } else + dev_dbg(&adapter->dev, "detection forced\n"); + + strlcpy(client->name, "adt7470", I2C_NAME_SIZE); + + if ((err = i2c_attach_client(client))) + goto exit_free; + + dev_info(&client->dev, "%s chip found\n", client->name); + + /* Initialize the ADT7470 chip */ + adt7470_init_client(client); + + /* Register sysfs hooks */ + data->attrs.attrs = adt7470_attr; + if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs))) + goto exit_detach; + + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &data->attrs); +exit_detach: + i2c_detach_client(client); +exit_free: + kfree(data); +exit: + return err; +} + +static int adt7470_detach_client(struct i2c_client *client) +{ + struct adt7470_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &data->attrs); + i2c_detach_client(client); + kfree(data); + return 0; +} + +static int __init adt7470_init(void) +{ + return i2c_add_driver(&adt7470_driver); +} + +static void __exit adt7470_exit(void) +{ + i2c_del_driver(&adt7470_driver); +} + +MODULE_AUTHOR("Darrick J. Wong "); +MODULE_DESCRIPTION("ADT7470 driver"); +MODULE_LICENSE("GPL"); + +module_init(adt7470_init); +module_exit(adt7470_exit); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 57b1c7b..55d92e1 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -143,7 +143,7 @@ #define ASB100_TEMP_MAX ( 127000) /* TEMP: 0.001C/bit (-128C to +127C) REG: 1C/bit, two's complement */ -static u8 TEMP_TO_REG(int temp) +static u8 TEMP_TO_REG(long temp) { int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); ntemp += (ntemp<0 ? -500 : 500); @@ -448,7 +448,7 @@ static ssize_t set_##reg(struct device * { \ struct i2c_client *client = to_i2c_client(dev); \ struct asb100_data *data = i2c_get_clientdata(client); \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ + long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ switch (nr) { \ diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 1212d6b..5a861f9 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -151,7 +151,7 @@ static ssize_t set_temp(struct device *d struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct ds1621_data *data = ds1621_update_client(dev); - u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); + u16 val = LM75_TEMP_TO_REG(simple_strtol(buf, NULL, 10)); mutex_lock(&data->update_lock); data->temp[attr->index] = val; diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 6f60715..1cd71a5 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -10,6 +10,9 @@ * The F71872F/FG is almost the same, with two more voltages monitored, * and 6 VID inputs. * + * The F71806F/FG is essentially the same as the F71872F/FG. It even has + * the same chip ID, so the driver can't differentiate between. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -1485,7 +1488,7 @@ static int __init f71805f_find(int sioad static const char *names[] = { "F71805F/FG", - "F71872F/FG", + "F71872F/FG or F71806F/FG", }; superio_enter(sioaddr); diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c new file mode 100644 index 0000000..b4ff0f9 --- /dev/null +++ b/drivers/hwmon/f71882fg.c @@ -0,0 +1,950 @@ +/*************************************************************************** + * Copyright (C) 2006 by Hans Edgington * + * Copyright (C) 2007 by Hans de Goede * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "f71882fg" + +#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device*/ +#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_DEVREV 0x22 /* Device revision */ +#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ +#define SIO_F71882_ID 0x0541 /* Chipset ID */ + +#define REGION_LENGTH 8 +#define ADDR_REG_OFFSET 5 +#define DATA_REG_OFFSET 6 + +#define F71882FG_REG_PECI 0x0A + +#define F71882FG_REG_IN_STATUS 0x12 +#define F71882FG_REG_IN_BEEP 0x13 +#define F71882FG_REG_IN(nr) (0x20 + (nr)) +#define F71882FG_REG_IN1_HIGH 0x32 + +#define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr))) +#define F71882FG_REG_FAN_STATUS 0x92 +#define F71882FG_REG_FAN_BEEP 0x93 + +#define F71882FG_REG_TEMP(nr) (0x72 + 2 * (nr)) +#define F71882FG_REG_TEMP_OVT(nr) (0x82 + 2 * (nr)) +#define F71882FG_REG_TEMP_HIGH(nr) (0x83 + 2 * (nr)) +#define F71882FG_REG_TEMP_STATUS 0x62 +#define F71882FG_REG_TEMP_BEEP 0x63 +#define F71882FG_REG_TEMP_HYST1 0x6C +#define F71882FG_REG_TEMP_HYST23 0x6D +#define F71882FG_REG_TEMP_TYPE 0x6B +#define F71882FG_REG_TEMP_DIODE_OPEN 0x6F + +#define F71882FG_REG_START 0x01 + +#define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */ + +static struct platform_device *f71882fg_pdev = NULL; + +/* Super-I/O Function prototypes */ +static inline int superio_inb(int base, int reg); +static inline int superio_inw(int base, int reg); +static inline void superio_enter(int base); +static inline void superio_select(int base, int ld); +static inline void superio_exit(int base); + +static inline u16 fan_from_reg ( u16 reg ); + +struct f71882fg_data { + unsigned short addr; + struct class_device *class_dev; + + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long last_limits; /* In jiffies */ + + /* Register Values */ + u8 in[9]; + u8 in1_max; + u8 in_status; + u8 in_beep; + u16 fan[4]; + u8 fan_status; + u8 fan_beep; + u8 temp[3]; + u8 temp_ovt[3]; + u8 temp_high[3]; + u8 temp_hyst[3]; + u8 temp_type[3]; + u8 temp_status; + u8 temp_beep; + u8 temp_diode_open; +}; + +static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg); +static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg); +static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val); + +/* Sysfs in*/ +static ssize_t show_in(struct device *dev, struct device_attribute *devattr, + char *buf); +static ssize_t show_in_max(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t store_in_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count); +static ssize_t show_in_beep(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t store_in_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count); +static ssize_t show_in_alarm(struct device *dev, struct device_attribute + *devattr, char *buf); +/* Sysfs Fan */ +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf); +static ssize_t show_fan_beep(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t store_fan_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count); +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute + *devattr, char *buf); +/* Sysfs Temp */ +static ssize_t show_temp(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t show_temp_max(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t store_temp_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count); +static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count); +static ssize_t show_temp_crit(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t store_temp_crit(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count); +static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t show_temp_type(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t show_temp_beep(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t store_temp_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count); +static ssize_t show_temp_alarm(struct device *dev, struct device_attribute + *devattr, char *buf); +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf); +/* Sysfs misc */ +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf); + +static int __devinit f71882fg_probe(struct platform_device * pdev); +static int __devexit f71882fg_remove(struct platform_device *pdev); +static int __init f71882fg_init(void); +static int __init f71882fg_find(int sioaddr, unsigned short *address); +static int __init f71882fg_device_add(unsigned short address); +static void __exit f71882fg_exit(void); + +static struct platform_driver f71882fg_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = f71882fg_probe, + .remove = __devexit_p(f71882fg_remove), +}; + +static struct device_attribute f71882fg_dev_attr[] = +{ + __ATTR( name, S_IRUGO, show_name, NULL ), +}; + +static struct sensor_device_attribute f71882fg_in_temp_attr[] = +{ + SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), + SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), + SENSOR_ATTR(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 1), + SENSOR_ATTR(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, 1), + SENSOR_ATTR(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1), + SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), + SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), + SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), + SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), + SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), + SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), + SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), + SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0), + SENSOR_ATTR(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0), + SENSOR_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0), + SENSOR_ATTR(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0), + SENSOR_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0), + SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0), + SENSOR_ATTR(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0), + SENSOR_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0), + SENSOR_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0), + SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), + SENSOR_ATTR(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 1), + SENSOR_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 1), + SENSOR_ATTR(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 1), + SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1), + SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1), + SENSOR_ATTR(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 1), + SENSOR_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1), + SENSOR_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1), + SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), + SENSOR_ATTR(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 2), + SENSOR_ATTR(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 2), + SENSOR_ATTR(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 2), + SENSOR_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 2), + SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), + SENSOR_ATTR(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 2), + SENSOR_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2), + SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2) +}; + +static struct sensor_device_attribute f71882fg_fan_attr[] = +{ + SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), + SENSOR_ATTR(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0), + SENSOR_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0), + SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), + SENSOR_ATTR(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 1), + SENSOR_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1), + SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), + SENSOR_ATTR(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 2), + SENSOR_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2), + SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), + SENSOR_ATTR(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 3), + SENSOR_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3) +}; + + +/* Super I/O functions */ +static inline int superio_inb(int base, int reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static int superio_inw(int base, int reg) +{ + int val; + outb(reg++, base); + val = inb(base + 1) << 8; + outb(reg, base); + val |= inb(base + 1); + return val; +} + +static inline void superio_enter(int base) +{ + /* according to the datasheet the key must be send twice! */ + outb( SIO_UNLOCK_KEY, base); + outb( SIO_UNLOCK_KEY, base); +} + +static inline void superio_select( int base, int ld) +{ + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(int base) +{ + outb(SIO_LOCK_KEY, base); +} + +static inline u16 fan_from_reg(u16 reg) +{ + return reg ? (1500000 / reg) : 0; +} + +static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg) +{ + u8 val; + + outb(reg, data->addr + ADDR_REG_OFFSET); + val = inb(data->addr + DATA_REG_OFFSET); + + return val; +} + +static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg) +{ + u16 val; + + outb(reg++, data->addr + ADDR_REG_OFFSET); + val = inb(data->addr + DATA_REG_OFFSET) << 8; + outb(reg, data->addr + ADDR_REG_OFFSET); + val |= inb(data->addr + DATA_REG_OFFSET); + + return val; +} + +static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) +{ + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val, data->addr + DATA_REG_OFFSET); +} + +static struct f71882fg_data *f71882fg_update_device(struct device * dev) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr, reg, reg2; + + mutex_lock(&data->update_lock); + + /* Update once every 60 seconds */ + if ( time_after(jiffies, data->last_limits + 60 * HZ ) || + !data->valid) { + data->in1_max = f71882fg_read8(data, F71882FG_REG_IN1_HIGH); + data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); + + /* Get High & boundary temps*/ + for (nr = 0; nr < 3; nr++) { + data->temp_ovt[nr] = f71882fg_read8(data, + F71882FG_REG_TEMP_OVT(nr)); + data->temp_high[nr] = f71882fg_read8(data, + F71882FG_REG_TEMP_HIGH(nr)); + } + + /* Have to hardcode hyst*/ + data->temp_hyst[0] = f71882fg_read8(data, + F71882FG_REG_TEMP_HYST1) >> 4; + /* Hyst temps 2 & 3 stored in same register */ + reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23); + data->temp_hyst[1] = reg & 0x0F; + data->temp_hyst[2] = reg >> 4; + + /* Have to hardcode type, because temp1 is special */ + reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); + reg2 = f71882fg_read8(data, F71882FG_REG_PECI); + if ((reg2 & 0x03) == 0x01) + data->temp_type[0] = 6 /* PECI */; + else if ((reg2 & 0x03) == 0x02) + data->temp_type[0] = 5 /* AMDSI */; + else + data->temp_type[0] = (reg & 0x02) ? 2 : 4; + + data->temp_type[1] = (reg & 0x04) ? 2 : 4; + data->temp_type[2] = (reg & 0x08) ? 2 : 4; + + data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); + + data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); + + data->last_limits = jiffies; + } + + /* Update every second */ + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->temp_status = f71882fg_read8(data, + F71882FG_REG_TEMP_STATUS); + data->temp_diode_open = f71882fg_read8(data, + F71882FG_REG_TEMP_DIODE_OPEN); + for (nr = 0; nr < 3; nr++) + data->temp[nr] = f71882fg_read8(data, + F71882FG_REG_TEMP(nr)); + + data->fan_status = f71882fg_read8(data, + F71882FG_REG_FAN_STATUS); + for (nr = 0; nr < 4; nr++) + data->fan[nr] = f71882fg_read16(data, + F71882FG_REG_FAN(nr)); + + data->in_status = f71882fg_read8(data, + F71882FG_REG_IN_STATUS); + for (nr = 0; nr < 9; nr++) + data->in[nr] = f71882fg_read8(data, + F71882FG_REG_IN(nr)); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* Sysfs Interface */ +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int speed = fan_from_reg(data->fan[nr]); + + if (speed == FAN_MIN_DETECT) + speed = 0; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t show_fan_beep(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + if (data->fan_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_fan_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + if (val) + data->fan_beep |= 1 << nr; + else + data->fan_beep &= ~(1 << nr); + + f71882fg_write8(data, F71882FG_REG_FAN_BEEP, data->fan_beep); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + if (data->fan_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_in(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", data->in[nr] * 8); +} + +static ssize_t show_in_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + + return sprintf(buf, "%d\n", data->in1_max * 8); +} + +static ssize_t store_in_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int val = simple_strtoul(buf, NULL, 10) / 8; + + if (val > 255) + val = 255; + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); + data->in1_max = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_in_beep(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + if (data->in_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_in_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + if (val) + data->in_beep |= 1 << nr; + else + data->in_beep &= ~(1 << nr); + + f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_in_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + if (data->in_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", data->temp[nr] * 1000); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", data->temp_high[nr] * 1000); +} + +static ssize_t store_temp_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int val = simple_strtoul(buf, NULL, 10) / 1000; + + if (val > 255) + val = 255; + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_TEMP_HIGH(nr), val); + data->temp_high[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", + (data->temp_high[nr] - data->temp_hyst[nr]) * 1000); +} + +static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int val = simple_strtoul(buf, NULL, 10) / 1000; + ssize_t ret = count; + + mutex_lock(&data->update_lock); + + /* convert abs to relative and check */ + val = data->temp_high[nr] - val; + if (val < 0 || val > 15) { + ret = -EINVAL; + goto store_temp_max_hyst_exit; + } + + data->temp_hyst[nr] = val; + + /* convert value to register contents */ + switch (nr) { + case 0: + val = val << 4; + break; + case 1: + val = val | (data->temp_hyst[2] << 4); + break; + case 2: + val = data->temp_hyst[1] | (val << 4); + break; + } + + f71882fg_write8(data, nr ? F71882FG_REG_TEMP_HYST23 : + F71882FG_REG_TEMP_HYST1, val); + +store_temp_max_hyst_exit: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_temp_crit(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", data->temp_ovt[nr] * 1000); +} + +static ssize_t store_temp_crit(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int val = simple_strtoul(buf, NULL, 10) / 1000; + + if (val > 255) + val = 255; + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_TEMP_OVT(nr), val); + data->temp_ovt[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", + (data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000); +} + +static ssize_t show_temp_type(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", data->temp_type[nr]); +} + +static ssize_t show_temp_beep(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + if (data->temp_beep & (1 << (nr + 1))) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_temp_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + if (val) + data->temp_beep |= 1 << (nr + 1); + else + data->temp_beep &= ~(1 << (nr + 1)); + + f71882fg_write8(data, F71882FG_REG_TEMP_BEEP, data->temp_beep); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_temp_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + if (data->temp_status & (1 << (nr + 1))) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + if (data->temp_diode_open & (1 << (nr + 1))) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, DRVNAME "\n"); +} + + +static int __devinit f71882fg_probe(struct platform_device * pdev) +{ + struct f71882fg_data *data; + int err, i; + u8 start_reg; + + if (!(data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL))) + return -ENOMEM; + + data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + /* Register sysfs interface files */ + for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) { + err = device_create_file(&pdev->dev, &f71882fg_dev_attr[i]); + if (err) + goto exit_unregister_sysfs; + } + + start_reg = f71882fg_read8(data, F71882FG_REG_START); + if (start_reg & 0x01) { + for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) { + err = device_create_file(&pdev->dev, + &f71882fg_in_temp_attr[i].dev_attr); + if (err) + goto exit_unregister_sysfs; + } + } + + if (start_reg & 0x02) { + for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) { + err = device_create_file(&pdev->dev, + &f71882fg_fan_attr[i].dev_attr); + if (err) + goto exit_unregister_sysfs; + } + } + + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_unregister_sysfs; + } + + return 0; + +exit_unregister_sysfs: + for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) + device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]); + + for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) + device_remove_file(&pdev->dev, + &f71882fg_in_temp_attr[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) + device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); + + kfree(data); + + return err; +} + +static int __devexit f71882fg_remove(struct platform_device *pdev) +{ + int i; + struct f71882fg_data *data = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + hwmon_device_unregister(data->class_dev); + + for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) + device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]); + + for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) + device_remove_file(&pdev->dev, + &f71882fg_in_temp_attr[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) + device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); + + kfree(data); + + return 0; +} + +static int __init f71882fg_find(int sioaddr, unsigned short *address) +{ + int err = -ENODEV; + u16 devid; + u8 start_reg; + struct f71882fg_data data; + + superio_enter(sioaddr); + + devid = superio_inw(sioaddr, SIO_REG_MANID); + if (devid != SIO_FINTEK_ID) { + printk(KERN_INFO DRVNAME ": Not a Fintek device\n"); + goto exit; + } + + devid = superio_inw(sioaddr, SIO_REG_DEVID); + if (devid != SIO_F71882_ID) { + printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n"); + goto exit; + } + + superio_select(sioaddr, SIO_F71882FG_LD_HWM); + if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { + printk(KERN_WARNING DRVNAME ": Device not activated\n"); + goto exit; + } + + *address = superio_inw(sioaddr, SIO_REG_ADDR); + if (*address == 0) + { + printk(KERN_WARNING DRVNAME ": Base address not set\n"); + goto exit; + } + *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ + + data.addr = *address; + start_reg = f71882fg_read8(&data, F71882FG_REG_START); + if (!(start_reg & 0x03)) { + printk(KERN_WARNING DRVNAME + ": Hardware monitoring not activated\n"); + goto exit; + } + + err = 0; + printk(KERN_INFO DRVNAME ": Found F71882FG chip at %#x, revision %d\n", + (unsigned int)*address, + (int)superio_inb(sioaddr, SIO_REG_DEVREV)); +exit: + superio_exit(sioaddr); + return err; +} + +static int __init f71882fg_device_add(unsigned short address) +{ + struct resource res = { + .start = address, + .end = address + REGION_LENGTH - 1, + .flags = IORESOURCE_IO, + }; + int err; + + f71882fg_pdev = platform_device_alloc(DRVNAME, address); + if (!f71882fg_pdev) + return -ENOMEM; + + res.name = f71882fg_pdev->name; + err = platform_device_add_resources(f71882fg_pdev, &res, 1); + if (err) { + printk(KERN_ERR DRVNAME ": Device resource addition failed\n"); + goto exit_device_put; + } + + err = platform_device_add(f71882fg_pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed\n"); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(f71882fg_pdev); + + return err; +} + +static int __init f71882fg_init(void) +{ + int err = -ENODEV; + unsigned short address; + + if (f71882fg_find(0x2e, &address) && f71882fg_find(0x4e, &address)) + goto exit; + + if ((err = platform_driver_register(&f71882fg_driver))) + goto exit; + + if ((err = f71882fg_device_add(address))) + goto exit_driver; + + return 0; + +exit_driver: + platform_driver_unregister(&f71882fg_driver); +exit: + return err; +} + +static void __exit f71882fg_exit(void) +{ + platform_device_unregister(f71882fg_pdev); + platform_driver_unregister(&f71882fg_driver); +} + +MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver"); +MODULE_AUTHOR("Hans Edgington (hans@edgington.nl)"); +MODULE_LICENSE("GPL"); + +module_init(f71882fg_init); +module_exit(f71882fg_exit); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c new file mode 100644 index 0000000..6425184 --- /dev/null +++ b/drivers/hwmon/f75375s.c @@ -0,0 +1,691 @@ +/* + * f75375s.c - driver for the Fintek F75375/SP and F75373 + * hardware monitoring features + * Copyright (C) 2006-2007 Riku Voipio + * + * Datasheets available at: + * + * f75375: + * http://www.fintek.com.tw/files/productfiles/2005111152950.pdf + * + * f75373: + * http://www.fintek.com.tw/files/productfiles/2005111153128.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_2(f75373, f75375); + +/* Fintek F75375 registers */ +#define F75375_REG_CONFIG0 0x0 +#define F75375_REG_CONFIG1 0x1 +#define F75375_REG_CONFIG2 0x2 +#define F75375_REG_CONFIG3 0x3 +#define F75375_REG_ADDR 0x4 +#define F75375_REG_INTR 0x31 +#define F75375_CHIP_ID 0x5A +#define F75375_REG_VERSION 0x5C +#define F75375_REG_VENDOR 0x5D +#define F75375_REG_FAN_TIMER 0x60 + +#define F75375_REG_VOLT(nr) (0x10 + (nr)) +#define F75375_REG_VOLT_HIGH(nr) (0x20 + (nr) * 2) +#define F75375_REG_VOLT_LOW(nr) (0x21 + (nr) * 2) + +#define F75375_REG_TEMP(nr) (0x14 + (nr)) +#define F75375_REG_TEMP_HIGH(nr) (0x28 + (nr) * 2) +#define F75375_REG_TEMP_HYST(nr) (0x29 + (nr) * 2) + +#define F75375_REG_FAN(nr) (0x16 + (nr) * 2) +#define F75375_REG_FAN_MIN(nr) (0x2C + (nr) * 2) +#define F75375_REG_FAN_FULL(nr) (0x70 + (nr) * 0x10) +#define F75375_REG_FAN_PWM_DUTY(nr) (0x76 + (nr) * 0x10) +#define F75375_REG_FAN_PWM_CLOCK(nr) (0x7D + (nr) * 0x10) + +#define F75375_REG_FAN_EXP(nr) (0x74 + (nr) * 0x10) +#define F75375_REG_FAN_B_TEMP(nr, step) ((0xA0 + (nr) * 0x10) + (step)) +#define F75375_REG_FAN_B_SPEED(nr, step) \ + ((0xA5 + (nr) * 0x10) + (step) * 2) + +#define F75375_REG_PWM1_RAISE_DUTY 0x69 +#define F75375_REG_PWM2_RAISE_DUTY 0x6A +#define F75375_REG_PWM1_DROP_DUTY 0x6B +#define F75375_REG_PWM2_DROP_DUTY 0x6C + +#define FAN_CTRL_LINEAR(nr) (4 + nr) +#define FAN_CTRL_MODE(nr) (5 + ((nr) * 2)) + +/* + * Data structures and manipulation thereof + */ + +struct f75375_data { + unsigned short addr; + struct i2c_client client; + struct class_device *class_dev; + + const char *name; + int kind; + struct mutex update_lock; /* protect register access */ + char valid; + unsigned long last_updated; /* In jiffies */ + unsigned long last_limits; /* In jiffies */ + + /* Register values */ + u8 in[4]; + u8 in_max[4]; + u8 in_min[4]; + u16 fan[2]; + u16 fan_min[2]; + u16 fan_full[2]; + u16 fan_exp[2]; + u8 fan_timer; + u8 pwm[2]; + u8 pwm_mode[2]; + u8 pwm_enable[2]; + s8 temp[2]; + s8 temp_high[2]; + s8 temp_max_hyst[2]; +}; + +static int f75375_attach_adapter(struct i2c_adapter *adapter); +static int f75375_detect(struct i2c_adapter *adapter, int address, int kind); +static int f75375_detach_client(struct i2c_client *client); + +static struct i2c_driver f75375_driver = { + .driver = { + .name = "f75375", + }, + .attach_adapter = f75375_attach_adapter, + .detach_client = f75375_detach_client, +}; + +static inline int f75375_read8(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* in most cases, should be called while holding update_lock */ +static inline u16 f75375_read16(struct i2c_client *client, u8 reg) +{ + return ((i2c_smbus_read_byte_data(client, reg) << 8) + | i2c_smbus_read_byte_data(client, reg + 1)); +} + +static inline void f75375_write8(struct i2c_client *client, u8 reg, + u8 value) +{ + i2c_smbus_write_byte_data(client, reg, value); +} + +static inline void f75375_write16(struct i2c_client *client, u8 reg, + u16 value) +{ + int err = i2c_smbus_write_byte_data(client, reg, (value << 8)); + if (err) + return; + i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF)); +} + +static struct f75375_data *f75375_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct f75375_data *data = i2c_get_clientdata(client); + int nr; + + mutex_lock(&data->update_lock); + + /* Limit registers cache is refreshed after 60 seconds */ + if (time_after(jiffies, data->last_limits + 60 * HZ) + || !data->valid) { + for (nr = 0; nr < 2; nr++) { + data->temp_high[nr] = + f75375_read8(client, F75375_REG_TEMP_HIGH(nr)); + data->temp_max_hyst[nr] = + f75375_read8(client, F75375_REG_TEMP_HYST(nr)); + data->fan_full[nr] = + f75375_read16(client, F75375_REG_FAN_FULL(nr)); + data->fan_min[nr] = + f75375_read16(client, F75375_REG_FAN_MIN(nr)); + data->fan_exp[nr] = + f75375_read16(client, F75375_REG_FAN_EXP(nr)); + data->pwm[nr] = f75375_read8(client, + F75375_REG_FAN_PWM_DUTY(nr)); + + } + for (nr = 0; nr < 4; nr++) { + data->in_max[nr] = + f75375_read8(client, F75375_REG_VOLT_HIGH(nr)); + data->in_min[nr] = + f75375_read8(client, F75375_REG_VOLT_LOW(nr)); + } + data->fan_timer = f75375_read8(client, F75375_REG_FAN_TIMER); + data->last_limits = jiffies; + } + + /* Measurement registers cache is refreshed after 2 second */ + if (time_after(jiffies, data->last_updated + 2 * HZ) + || !data->valid) { + for (nr = 0; nr < 2; nr++) { + data->temp[nr] = + f75375_read8(client, F75375_REG_TEMP(nr)); + data->fan[nr] = + f75375_read16(client, F75375_REG_FAN(nr)); + } + for (nr = 0; nr < 4; nr++) + data->in[nr] = + f75375_read8(client, F75375_REG_VOLT(nr)); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + return data; +} + +static inline u16 rpm_from_reg(u16 reg) +{ + if (reg == 0 || reg == 0xffff) + return 0; + return (1500000 / reg); +} + +static inline u16 rpm_to_reg(int rpm) +{ + if (rpm < 367 || rpm > 0xffff) + return 0xffff; + return (1500000 / rpm); +} + +static ssize_t set_fan_min(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->fan_min[nr] = rpm_to_reg(val); + f75375_write16(client, F75375_REG_FAN_MIN(nr), data->fan_min[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t set_fan_exp(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->fan_exp[nr] = rpm_to_reg(val); + f75375_write16(client, F75375_REG_FAN_EXP(nr), data->fan_exp[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t set_pwm(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); + f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), data->pwm[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_pwm_enable(struct device *dev, struct device_attribute + *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_enable[nr]); +} + +static ssize_t set_pwm_enable(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + u8 fanmode; + + if (val < 0 || val > 4) + return -EINVAL; + + mutex_lock(&data->update_lock); + fanmode = f75375_read8(client, F75375_REG_FAN_TIMER); + fanmode = ~(3 << FAN_CTRL_MODE(nr)); + + switch (val) { + case 0: /* Full speed */ + fanmode |= (3 << FAN_CTRL_MODE(nr)); + data->pwm[nr] = 255; + f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), + data->pwm[nr]); + break; + case 1: /* PWM */ + fanmode |= (3 << FAN_CTRL_MODE(nr)); + break; + case 2: /* AUTOMATIC*/ + fanmode |= (2 << FAN_CTRL_MODE(nr)); + break; + case 3: /* fan speed */ + break; + } + f75375_write8(client, F75375_REG_FAN_TIMER, fanmode); + data->pwm_enable[nr] = val; + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t set_pwm_mode(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + u8 conf = 0; + + if (val != 0 || val != 1 || data->kind == f75373) + return -EINVAL; + + mutex_lock(&data->update_lock); + conf = f75375_read8(client, F75375_REG_CONFIG1); + conf = ~(1 << FAN_CTRL_LINEAR(nr)); + + if (val == 0) + conf |= (1 << FAN_CTRL_LINEAR(nr)) ; + + f75375_write8(client, F75375_REG_CONFIG1, conf); + data->pwm_mode[nr] = val; + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute + *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", data->pwm[nr]); +} + +static ssize_t show_pwm_mode(struct device *dev, struct device_attribute + *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_mode[nr]); +} + +#define VOLT_FROM_REG(val) ((val) * 8) +#define VOLT_TO_REG(val) ((val) / 8) + +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in[nr])); +} + +static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in_max[nr])); +} + +static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in_min[nr])); +} + +static ssize_t set_in_max(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + val = SENSORS_LIMIT(VOLT_TO_REG(val), 0, 0xff); + mutex_lock(&data->update_lock); + data->in_max[nr] = val; + f75375_write8(client, F75375_REG_VOLT_HIGH(nr), data->in_max[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t set_in_min(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + val = SENSORS_LIMIT(VOLT_TO_REG(val), 0, 0xff); + mutex_lock(&data->update_lock); + data->in_min[nr] = val; + f75375_write8(client, F75375_REG_VOLT_LOW(nr), data->in_min[nr]); + mutex_unlock(&data->update_lock); + return count; +} +#define TEMP_FROM_REG(val) ((val) * 1000) +#define TEMP_TO_REG(val) ((val) / 1000) + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])); +} + +static ssize_t show_temp_max_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct f75375_data *data = f75375_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[nr])); +} + +static ssize_t set_temp_max(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(TEMP_TO_REG(val), 0, 127); + mutex_lock(&data->update_lock); + data->temp_high[nr] = val; + f75375_write8(client, F75375_REG_TEMP_HIGH(nr), data->temp_high[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t set_temp_max_hyst(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 f75375_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(TEMP_TO_REG(val), 0, 127); + mutex_lock(&data->update_lock); + data->temp_max_hyst[nr] = val; + f75375_write8(client, F75375_REG_TEMP_HYST(nr), + data->temp_max_hyst[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +#define show_fan(thing) \ +static ssize_t show_##thing(struct device *dev, struct device_attribute *attr, \ + char *buf)\ +{\ + int nr = to_sensor_dev_attr(attr)->index;\ + struct f75375_data *data = f75375_update_device(dev); \ + return sprintf(buf, "%d\n", rpm_from_reg(data->thing[nr])); \ +} + +show_fan(fan); +show_fan(fan_min); +show_fan(fan_full); +show_fan(fan_exp); + +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); +static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO|S_IWUSR, + show_in_max, set_in_max, 0); +static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO|S_IWUSR, + show_in_min, set_in_min, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO|S_IWUSR, + show_in_max, set_in_max, 1); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO|S_IWUSR, + show_in_min, set_in_min, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO|S_IWUSR, + show_in_max, set_in_max, 2); +static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO|S_IWUSR, + show_in_min, set_in_min, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3); +static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO|S_IWUSR, + show_in_max, set_in_max, 3); +static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO|S_IWUSR, + show_in_min, set_in_min, 3); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR, + show_temp_max_hyst, set_temp_max_hyst, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO|S_IWUSR, + show_temp_max, set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR, + show_temp_max_hyst, set_temp_max_hyst, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO|S_IWUSR, + show_temp_max, set_temp_max, 1); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan1_full, S_IRUGO, show_fan_full, NULL, 0); +static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO|S_IWUSR, + show_fan_min, set_fan_min, 0); +static SENSOR_DEVICE_ATTR(fan1_exp, S_IRUGO|S_IWUSR, + show_fan_exp, set_fan_exp, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan2_full, S_IRUGO, show_fan_full, NULL, 1); +static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO|S_IWUSR, + show_fan_min, set_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan2_exp, S_IRUGO|S_IWUSR, + show_fan_exp, set_fan_exp, 1); +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO|S_IWUSR, + show_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO|S_IWUSR, + show_pwm_enable, set_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO|S_IWUSR, + show_pwm_mode, set_pwm_mode, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, + show_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO|S_IWUSR, + show_pwm_enable, set_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm2_mode, S_IRUGO|S_IWUSR, + show_pwm_mode, set_pwm_mode, 1); + +static struct attribute *f75375_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_full.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_exp.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_full.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_exp.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_mode.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_mode.dev_attr.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + NULL +}; + +static const struct attribute_group f75375_group = { + .attrs = f75375_attributes, +}; + +static int f75375_detach_client(struct i2c_client *client) +{ + struct f75375_data *data = i2c_get_clientdata(client); + int err; + + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &f75375_group); + + err = i2c_detach_client(client); + if (err) { + dev_err(&client->dev, + "Client deregistration failed, " + "client not detached.\n"); + return err; + } + kfree(data); + return 0; +} + +static int f75375_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_probe(adapter, &addr_data, f75375_detect); +} + +/* This function is called by i2c_probe */ +static int f75375_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct f75375_data *data; + u8 version = 0; + int err = 0; + const char *name = ""; + + if (!(data = kzalloc(sizeof(struct f75375_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; + client->adapter = adapter; + client->driver = &f75375_driver; + + if (kind < 0) { + u16 vendid = f75375_read16(client, F75375_REG_VENDOR); + u16 chipid = f75375_read16(client, F75375_CHIP_ID); + version = f75375_read8(client, F75375_REG_VERSION); + if (chipid == 0x0306 && vendid == 0x1934) { + kind = f75375; + } else if (chipid == 0x0204 && vendid == 0x1934) { + kind = f75373; + } else { + dev_err(&adapter->dev, + "failed,%02X,%02X,%02X\n", + chipid, version, vendid); + goto exit_free; + } + } + + if (kind == f75375) { + name = "f75375"; + } else if (kind == f75373) { + name = "f75373"; + } + + dev_info(&adapter->dev, "found %s version: %02X\n", name, version); + strlcpy(client->name, name, I2C_NAME_SIZE); + data->kind = kind; + mutex_init(&data->update_lock); + if ((err = i2c_attach_client(client))) + goto exit_free; + + if ((err = sysfs_create_group(&client->dev.kobj, &f75375_group))) + goto exit_detach; + + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &f75375_group); +exit_detach: + i2c_detach_client(client); +exit_free: + kfree(data); +exit: + return err; +} + +static int __init sensors_f75375_init(void) +{ + return i2c_add_driver(&f75375_driver); +} + +static void __exit sensors_f75375_exit(void) +{ + i2c_del_driver(&f75375_driver); +} + +MODULE_AUTHOR("Riku Voipio "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("F75373/F75375 hardware monitoring driver"); + +module_init(sensors_f75375_init); +module_exit(sensors_f75375_exit); diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 275d392..30b9b2b 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -81,7 +81,7 @@ static ssize_t lm70_sense_temp(struct de * So it's equivalent to multiplying by 0.25 * 1000 = 250. */ val = ((int)raw/32) * 250; - status = sprintf(buf, "%+d\n", val); /* millidegrees Celsius */ + status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ out: up(&p_lm70->sem); return status; @@ -89,6 +89,14 @@ out: static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL); +static ssize_t lm70_show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + return sprintf(buf, "lm70\n"); +} + +static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); + /*----------------------------------------------------------------------*/ static int __devinit lm70_probe(struct spi_device *spi) @@ -115,7 +123,8 @@ static int __devinit lm70_probe(struct s } dev_set_drvdata(&spi->dev, p_lm70); - if ((status = device_create_file(&spi->dev, &dev_attr_temp1_input))) { + if ((status = device_create_file(&spi->dev, &dev_attr_temp1_input)) + || (status = device_create_file(&spi->dev, &dev_attr_name))) { dev_dbg(&spi->dev, "device_create_file failure.\n"); goto out_dev_create_file_failed; } @@ -123,6 +132,7 @@ static int __devinit lm70_probe(struct s return 0; out_dev_create_file_failed: + device_remove_file(&spi->dev, &dev_attr_temp1_input); hwmon_device_unregister(p_lm70->cdev); out_dev_reg_failed: dev_set_drvdata(&spi->dev, NULL); @@ -135,6 +145,7 @@ static int __devexit lm70_remove(struct struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); device_remove_file(&spi->dev, &dev_attr_temp1_input); + device_remove_file(&spi->dev, &dev_attr_name); hwmon_device_unregister(p_lm70->cdev); dev_set_drvdata(&spi->dev, NULL); kfree(p_lm70); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index a40166f..4fa3220 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -95,7 +95,7 @@ static ssize_t set_temp(struct device *d struct i2c_client *client = to_i2c_client(dev); struct lm75_data *data = i2c_get_clientdata(client); int nr = attr->index; - unsigned long temp = simple_strtoul(buf, NULL, 10); + long temp = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->temp[nr] = LM75_TEMP_TO_REG(temp); diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h index af7dc65..7c93454 100644 --- a/drivers/hwmon/lm75.h +++ b/drivers/hwmon/lm75.h @@ -33,7 +33,7 @@ #define LM75_TEMP_MAX 125000 /* TEMP: 0.001C/bit (-55C to +125C) REG: (0.5C/bit, two's complement) << 7 */ -static inline u16 LM75_TEMP_TO_REG(int temp) +static inline u16 LM75_TEMP_TO_REG(long temp) { int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); ntemp += (ntemp<0 ? -250 : 250); diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index dd969f1..46e3aba 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -138,7 +138,7 @@ static ssize_t set_##value(struct device { \ struct i2c_client *client = to_i2c_client(dev); \ struct lm77_data *data = i2c_get_clientdata(client); \ - long val = simple_strtoul(buf, NULL, 10); \ + long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ data->value = val; \ diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index d84f8bf..86c6c6e 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -413,7 +413,7 @@ #define LM93_TEMP_MAX ( 127000) /* TEMP: 1/1000 degrees C (-128C to +127C) REG: 1C/bit, two's complement */ -static u8 LM93_TEMP_TO_REG(int temp) +static u8 LM93_TEMP_TO_REG(long temp) { int ntemp = SENSORS_LIMIT(temp, LM93_TEMP_MIN, LM93_TEMP_MAX); ntemp += (ntemp<0 ? -500 : 500); @@ -1268,7 +1268,7 @@ static ssize_t store_temp_min(struct dev int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->temp_lim[nr].min = LM93_TEMP_TO_REG(val); @@ -1298,7 +1298,7 @@ static ssize_t store_temp_max(struct dev int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->temp_lim[nr].max = LM93_TEMP_TO_REG(val); @@ -1329,7 +1329,7 @@ static ssize_t store_temp_auto_base(stru int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->block10.base[nr] = LM93_TEMP_TO_REG(val); @@ -1360,7 +1360,7 @@ static ssize_t store_temp_auto_boost(str int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->boost[nr] = LM93_TEMP_TO_REG(val); diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index d318196..7179277 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -553,7 +553,7 @@ static int __devinit smsc47m1_probe(stru || (err = device_create_file(dev, &sensor_dev_attr_fan3_div.dev_attr))) goto error_remove_files; - } else + } else if (data->type == smsc47m2) dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n"); if (pwm1) { @@ -580,7 +580,7 @@ static int __devinit smsc47m1_probe(stru || (err = device_create_file(dev, &sensor_dev_attr_pwm3_enable.dev_attr))) goto error_remove_files; - } else + } else if (data->type == smsc47m2) dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n"); if ((err = device_create_file(dev, &dev_attr_alarms))) diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 9395b52..c920f39 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -46,6 +46,11 @@ #define THMC50_REG_CONF 0x40 #define THMC50_REG_COMPANY_ID 0x3E #define THMC50_REG_DIE_CODE 0x3F #define THMC50_REG_ANALOG_OUT 0x19 +/* + * We use mirror status register for reading alarms + * so ACPI can use the primary status register. + */ +#define THMC50_REG_INTR_MIRROR 0x4C const static u8 THMC50_REG_TEMP[] = { 0x27, 0x26, 0x20 }; const static u8 THMC50_REG_TEMP_MIN[] = { 0x3A, 0x38, 0x2C }; @@ -69,6 +74,7 @@ struct thmc50_data { s8 temp_max[3]; s8 temp_min[3]; u8 analog_out; + u8 alarms; }; static int thmc50_attach_adapter(struct i2c_adapter *adapter); @@ -180,6 +186,15 @@ static ssize_t set_temp_max(struct devic return count; } +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + struct thmc50_data *data = thmc50_update_device(dev); + + return sprintf(buf, "%u\n", (data->alarms >> index) & 1); +} + #define temp_reg(offset) \ static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp, \ NULL, offset - 1); \ @@ -192,6 +207,12 @@ temp_reg(1); temp_reg(2); temp_reg(3); +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2); + static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_analog_out, set_analog_out, 0); static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL, 0); @@ -200,9 +221,12 @@ static struct attribute *thmc50_attribut &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_mode.dev_attr.attr, NULL @@ -217,6 +241,8 @@ static struct attribute *adm1022_attribu &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, NULL }; @@ -414,6 +440,8 @@ static struct thmc50_data *thmc50_update } data->analog_out = i2c_smbus_read_byte_data(client, THMC50_REG_ANALOG_OUT); + data->alarms = + i2c_smbus_read_byte_data(client, THMC50_REG_INTR_MIRROR); data->last_updated = jiffies; data->valid = 1; } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index d9a9ec7..e3dfc52 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -223,7 +223,7 @@ temp1_from_reg(s8 reg) } static inline s8 -temp1_to_reg(int temp, int min, int max) +temp1_to_reg(long temp, int min, int max) { if (temp <= min) return min / 1000; @@ -805,7 +805,7 @@ store_temp1_##reg(struct device *dev, st const char *buf, size_t count) \ { \ struct w83627ehf_data *data = dev_get_drvdata(dev); \ - u32 val = simple_strtoul(buf, NULL, 10); \ + long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \ @@ -840,7 +840,7 @@ store_##reg(struct device *dev, struct d struct w83627ehf_data *data = dev_get_drvdata(dev); \ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - u32 val = simple_strtoul(buf, NULL, 10); \ + long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ data->reg[nr] = LM75_TEMP_TO_REG(val); \ diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 7a4a15f..f0e0e20 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -263,7 +263,7 @@ #define TEMP_MAX ( 127000) /* TEMP: 0.001C/bit (-128C to +127C) REG: 1C/bit, two's complement */ -static u8 TEMP_TO_REG(int temp) +static u8 TEMP_TO_REG(long temp) { int ntemp = SENSORS_LIMIT(temp, TEMP_MIN, TEMP_MAX); ntemp += (ntemp<0 ? -500 : 500); @@ -372,11 +372,8 @@ struct w83627hf_data { u8 beep_enable; /* Boolean */ u8 pwm[3]; /* Register value */ u8 pwm_freq[3]; /* Register value */ - u16 sens[3]; /* 782D/783S only. - 1 = pentium diode; 2 = 3904 diode; - 3000-5000 = thermistor beta. - Default = 3435. - Other Betas unimplemented */ + u16 sens[3]; /* 1 = pentium diode; 2 = 3904 diode; + 4 = thermistor */ u8 vrm; u8 vrm_ovt; /* Register value, 627THF/637HF/687THF only */ }; @@ -642,9 +639,9 @@ static ssize_t \ store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ { \ struct w83627hf_data *data = dev_get_drvdata(dev); \ - u32 val; \ + long val; \ \ - val = simple_strtoul(buf, NULL, 10); \ + val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ \ @@ -1001,7 +998,11 @@ store_sensor_reg(struct device *dev, con tmp & ~BIT_SCFG2[nr - 1]); data->sens[nr - 1] = val; break; - case W83781D_DEFAULT_BETA: /* thermistor */ + case W83781D_DEFAULT_BETA: + dev_warn(dev, "Sensor type %d is deprecated, please use 4 " + "instead\n", W83781D_DEFAULT_BETA); + /* fall through */ + case 4: /* thermistor */ tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); w83627hf_write_value(data, W83781D_REG_SCFG1, tmp & ~BIT_SCFG1[nr - 1]); @@ -1009,8 +1010,8 @@ store_sensor_reg(struct device *dev, con break; default: dev_err(dev, - "Invalid sensor type %ld; must be 1, 2, or %d\n", - (long) val, W83781D_DEFAULT_BETA); + "Invalid sensor type %ld; must be 1, 2, or 4\n", + (long) val); break; } @@ -1513,7 +1514,7 @@ static void __devinit w83627hf_init_devi tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); for (i = 1; i <= 3; i++) { if (!(tmp & BIT_SCFG1[i - 1])) { - data->sens[i - 1] = W83781D_DEFAULT_BETA; + data->sens[i - 1] = 4; } else { if (w83627hf_read_value (data, diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index c95909c..fe2a791 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -251,9 +251,7 @@ struct w83781d_data { u8 pwm2_enable; /* Boolean */ u16 sens[3]; /* 782D/783S only. 1 = pentium diode; 2 = 3904 diode; - 3000-5000 = thermistor beta. - Default = 3435. - Other Betas unimplemented */ + 4 = thermistor */ u8 vrm; }; @@ -410,7 +408,7 @@ static ssize_t store_temp_##reg (struct struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ struct w83781d_data *data = dev_get_drvdata(dev); \ int nr = attr->index; \ - s32 val; \ + long val; \ \ val = simple_strtol(buf, NULL, 10); \ \ @@ -721,15 +719,19 @@ store_sensor(struct device *dev, struct tmp & ~BIT_SCFG2[nr]); data->sens[nr] = val; break; - case W83781D_DEFAULT_BETA: /* thermistor */ + case W83781D_DEFAULT_BETA: + dev_warn(dev, "Sensor type %d is deprecated, please use 4 " + "instead\n", W83781D_DEFAULT_BETA); + /* fall through */ + case 4: /* thermistor */ tmp = w83781d_read_value(data, W83781D_REG_SCFG1); w83781d_write_value(data, W83781D_REG_SCFG1, tmp & ~BIT_SCFG1[nr]); data->sens[nr] = val; break; default: - dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or %d\n", - (long) val, W83781D_DEFAULT_BETA); + dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or 4\n", + (long) val); break; } @@ -1485,7 +1487,7 @@ w83781d_init_device(struct device *dev) tmp = w83781d_read_value(data, W83781D_REG_SCFG1); for (i = 1; i <= 3; i++) { if (!(tmp & BIT_SCFG1[i - 1])) { - data->sens[i - 1] = W83781D_DEFAULT_BETA; + data->sens[i - 1] = 4; } else { if (w83781d_read_value (data, diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index b0fa296..f6dee38 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -540,6 +540,15 @@ show_alarms_reg(struct device *dev, stru return sprintf(buf, "%d\n", data->alarms); } +static ssize_t show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83792d_data *data = w83792d_update_device(dev); + return sprintf(buf, "%d\n", (data->alarms >> nr) & 1); +} + static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) @@ -1015,6 +1024,25 @@ static SENSOR_DEVICE_ATTR_2(temp2_max_hy static SENSOR_DEVICE_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 1, 4); static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_alarm, NULL, 15); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 19); +static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 20); +static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 21); +static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 23); static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL); static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR, show_chassis_clear, store_chassis_clear); @@ -1123,26 +1151,30 @@ static SENSOR_DEVICE_ATTR(fan6_div, S_IW static SENSOR_DEVICE_ATTR(fan7_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 7); -static struct attribute *w83792d_attributes_fan[4][4] = { +static struct attribute *w83792d_attributes_fan[4][5] = { { &sensor_dev_attr_fan4_input.dev_attr.attr, &sensor_dev_attr_fan4_min.dev_attr.attr, &sensor_dev_attr_fan4_div.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, NULL }, { &sensor_dev_attr_fan5_input.dev_attr.attr, &sensor_dev_attr_fan5_min.dev_attr.attr, &sensor_dev_attr_fan5_div.dev_attr.attr, + &sensor_dev_attr_fan5_alarm.dev_attr.attr, NULL }, { &sensor_dev_attr_fan6_input.dev_attr.attr, &sensor_dev_attr_fan6_min.dev_attr.attr, &sensor_dev_attr_fan6_div.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, NULL }, { &sensor_dev_attr_fan7_input.dev_attr.attr, &sensor_dev_attr_fan7_min.dev_attr.attr, &sensor_dev_attr_fan7_div.dev_attr.attr, + &sensor_dev_attr_fan7_alarm.dev_attr.attr, NULL } }; @@ -1182,6 +1214,15 @@ static struct attribute *w83792d_attribu &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_in8_max.dev_attr.attr, &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, @@ -1191,6 +1232,9 @@ static struct attribute *w83792d_attribu &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_mode.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, @@ -1233,12 +1277,15 @@ static struct attribute *w83792d_attribu &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, &sensor_dev_attr_fan3_div.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, NULL };