From 9cab0217f3f35bd618363842576867badb72ca4b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 15 Jul 2007 10:36:06 +0200 Subject: 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 --- Documentation/hwmon/f71805f | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/f71805f b/Documentation/hwmon/f71805f index 94e0d2cbd3d2..f0d55976740a 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 almost the same, with two 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. -- cgit v1.2.3 From b26f93309282bdfebb3edb8939e022a4bbe56dfe Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 16 Aug 2007 14:30:01 +0200 Subject: 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 --- Documentation/hwmon/sysfs-interface | 4 ++-- drivers/hwmon/w83627hf.c | 19 ++++++++++--------- drivers/hwmon/w83781d.c | 16 +++++++++------- 3 files changed, 21 insertions(+), 18 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index b3a9e1b9dbda..331fa7a1d7d5 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -219,12 +219,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 diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 0866739a227c..f0e0e208be9a 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -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 */ }; @@ -1001,7 +998,11 @@ store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) 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, const char *buf, size_t count, int nr) 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_device(struct platform_device *pdev) 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 8ed88f3d5767..c12bdf9d9e1d 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; }; @@ -721,15 +719,19 @@ store_sensor(struct device *dev, struct device_attribute *da, 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, -- cgit v1.2.3 From 471c606827394f59117cf45c808afd8fe638f902 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 16 Aug 2007 14:48:49 +0200 Subject: 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 --- Documentation/hwmon/lm93 | 126 +++-------------------------------------------- 1 file changed, 8 insertions(+), 118 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm93 b/Documentation/hwmon/lm93 index 4e4a1dc1d2da..ac711f357faf 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 @@ Module Parameters 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 filter that can be invoked to smooth temperature readings for better control of fan speed. The LM93 has four tachometer inputs to measure fan speed. Limit and status registers for all measured values are included. The LM93 builds upon the functionality of -previous motherboard management ASICs and uses some of the LM85 s features +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 sysfs files prochot1_interval and prochot2_interval. The values in these files specify the intervals for #P1_PROCHOT and #P2_PROCHOT, respectively. Selecting a value not in this list will cause the driver to use the next largest interval. The available -intervals are: +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. This mode is enabled by writing a non-zero integer to the sysfs file prochot_short. The LM93 can also override the #PROCHOT pins by driving a PWM signal onto -one or both of them. When overridden, the signal has a period of 3.56 mS, +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 hardware. Selecting a value which is not available will cause the driver to use the next largest value. Also note that this parameter has implications for the Smart Tach Mode (see above). -PWM Output Frequencies: 12, 36, 48, 60, 72, 84, 96, 22500 (h/w default) +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 PWM output is bound. The eight control sources are: temp1-temp4 (aka "zones" in the datasheet), #PROCHOT 1 & 2, and #VRDHOT 1 & 2. The bindings are expressed as a bitmask in the sysfs files pwm_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" - -- cgit v1.2.3 From 176544dc55b0a912a2e1bacb9f48ccbd4aabd55f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 20 Aug 2007 16:44:44 +0200 Subject: 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 --- Documentation/hwmon/sysfs-interface | 75 ++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 34 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 331fa7a1d7d5..db7bb4a659c4 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. @@ -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 -- cgit v1.2.3 From 6438312367523b26bd628b60cfd16f25a7a6f7ae Mon Sep 17 00:00:00 2001 From: Charles Spirakis Date: Tue, 4 Sep 2007 13:31:56 -0700 Subject: hwmon: (w83791d) new sysfs beep/alarm methodology Add new sysfs alarm methodology to w83791d driver Signed-off-by: Charles Spirakis Acked-by: Jean Delvare Signed-off-by: Mark M. Hoffman --- Documentation/hwmon/w83791d | 96 ++++++++++++++++++++-------------- MAINTAINERS | 2 +- drivers/hwmon/w83791d.c | 122 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 176 insertions(+), 44 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index db9881df88a5..f153b2f6d62c 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -75,46 +75,64 @@ Voltage sensors (also known as IN sensors) report their values in millivolts. An alarm is triggered if the voltage has crossed a programmable minimum or maximum limit. -The bit ordering for the alarm "realtime status register" and the -"beep enable registers" are different. - -in0 (VCORE) : alarms: 0x000001 beep_enable: 0x000001 -in1 (VINR0) : alarms: 0x000002 beep_enable: 0x002000 <== mismatch -in2 (+3.3VIN): alarms: 0x000004 beep_enable: 0x000004 -in3 (5VDD) : alarms: 0x000008 beep_enable: 0x000008 -in4 (+12VIN) : alarms: 0x000100 beep_enable: 0x000100 -in5 (-12VIN) : alarms: 0x000200 beep_enable: 0x000200 -in6 (-5VIN) : alarms: 0x000400 beep_enable: 0x000400 -in7 (VSB) : alarms: 0x080000 beep_enable: 0x010000 <== mismatch -in8 (VBAT) : alarms: 0x100000 beep_enable: 0x020000 <== mismatch -in9 (VINR1) : alarms: 0x004000 beep_enable: 0x004000 -temp1 : alarms: 0x000010 beep_enable: 0x000010 -temp2 : alarms: 0x000020 beep_enable: 0x000020 -temp3 : alarms: 0x002000 beep_enable: 0x000002 <== mismatch -fan1 : alarms: 0x000040 beep_enable: 0x000040 -fan2 : alarms: 0x000080 beep_enable: 0x000080 -fan3 : alarms: 0x000800 beep_enable: 0x000800 -fan4 : alarms: 0x200000 beep_enable: 0x200000 -fan5 : alarms: 0x400000 beep_enable: 0x400000 -tart1 : alarms: 0x010000 beep_enable: 0x040000 <== mismatch -tart2 : alarms: 0x020000 beep_enable: 0x080000 <== mismatch -tart3 : alarms: 0x040000 beep_enable: 0x100000 <== mismatch -case_open : alarms: 0x001000 beep_enable: 0x001000 -user_enable : alarms: -------- beep_enable: 0x800000 - -*** NOTE: It is the responsibility of user-space code to handle the fact -that the beep enable and alarm bits are in different positions when using that -feature of the chip. - -When an alarm goes off, you can be warned by a beeping signal through your -computer speaker. It is possible to enable all beeping globally, or only -the beeping for some alarms. - -The driver only reads the chip values each 3 seconds; reading them more -often will do no harm, but will return 'old' values. +The w83791d has a global bit used to enable beeping from the speaker when an +alarm is triggered as well as a bitmask to enable or disable the beep for +specific alarms. You need both the global beep enable bit and the +corresponding beep bit to be on for a triggered alarm to sound a beep. + +The sysfs interface to the gloabal enable is via the sysfs beep_enable file. +This file is used for both legacy and new code. + +The sysfs interface to the beep bitmask has migrated from the original legacy +method of a single sysfs beep_mask file to a newer method using multiple +*_beep files as described in .../Documentation/hwmon/sysfs-interface. + +A similar change has occured for the bitmap corresponding to the alarms. The +original legacy method used a single sysfs alarms file containing a bitmap +of triggered alarms. The newer method uses multiple sysfs *_alarm files +(again following the pattern described in sysfs-interface). + +Since both methods read and write the underlying hardware, they can be used +interchangeably and changes in one will automatically be reflected by +the other. If you use the legacy bitmask method, your user-space code is +responsible for handling the fact that the alarms and beep_mask bitmaps +are not the same (see the table below). + +NOTE: All new code should be written to use the newer sysfs-interface +specification as that avoids bitmap problems and is the preferred interface +going forward. + +The driver reads the hardware chip values at most once every three seconds. +User mode code requesting values more often will receive cached values. + +Alarms bitmap vs. beep_mask bitmask +------------------------------------ +For legacy code using the alarms and beep_mask files: + +in0 (VCORE) : alarms: 0x000001 beep_mask: 0x000001 +in1 (VINR0) : alarms: 0x000002 beep_mask: 0x002000 <== mismatch +in2 (+3.3VIN): alarms: 0x000004 beep_mask: 0x000004 +in3 (5VDD) : alarms: 0x000008 beep_mask: 0x000008 +in4 (+12VIN) : alarms: 0x000100 beep_mask: 0x000100 +in5 (-12VIN) : alarms: 0x000200 beep_mask: 0x000200 +in6 (-5VIN) : alarms: 0x000400 beep_mask: 0x000400 +in7 (VSB) : alarms: 0x080000 beep_mask: 0x010000 <== mismatch +in8 (VBAT) : alarms: 0x100000 beep_mask: 0x020000 <== mismatch +in9 (VINR1) : alarms: 0x004000 beep_mask: 0x004000 +temp1 : alarms: 0x000010 beep_mask: 0x000010 +temp2 : alarms: 0x000020 beep_mask: 0x000020 +temp3 : alarms: 0x002000 beep_mask: 0x000002 <== mismatch +fan1 : alarms: 0x000040 beep_mask: 0x000040 +fan2 : alarms: 0x000080 beep_mask: 0x000080 +fan3 : alarms: 0x000800 beep_mask: 0x000800 +fan4 : alarms: 0x200000 beep_mask: 0x200000 +fan5 : alarms: 0x400000 beep_mask: 0x400000 +tart1 : alarms: 0x010000 beep_mask: 0x040000 <== mismatch +tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch +tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch +case_open : alarms: 0x001000 beep_mask: 0x001000 +global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) W83791D TODO: --------------- -Provide a patch for per-file alarms and beep enables as defined in the hwmon - documentation (Documentation/hwmon/sysfs-interface) Provide a patch for smart-fan control (still need appropriate motherboard/fans) diff --git a/MAINTAINERS b/MAINTAINERS index 9a91d9e3f1f2..62931dbb4a75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4106,7 +4106,7 @@ W83791D HARDWARE MONITORING DRIVER P: Charles Spirakis M: bezaur@gmail.com L: lm-sensors@lm-sensors.org -S: Maintained +S: Odd Fixes W83793 HARDWARE MONITORING DRIVER P: Rudolf Marek diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a3cb89455ea7..de4eb3e7b271 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -2,7 +2,7 @@ w83791d.c - Part of lm_sensors, Linux kernel modules for hardware monitoring - Copyright (C) 2006 Charles Spirakis + Copyright (C) 2006-2007 Charles Spirakis 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 @@ -384,6 +384,85 @@ static struct sensor_device_attribute sda_in_max[] = { SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), }; + +static ssize_t show_beep(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = + to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int bitnr = sensor_attr->index; + + return sprintf(buf, "%d\n", (data->beep_mask >> bitnr) & 1); +} + +static ssize_t store_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = + to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int bitnr = sensor_attr->index; + int bytenr = bitnr / 8; + long val = simple_strtol(buf, NULL, 10) ? 1 : 0; + + mutex_lock(&data->update_lock); + + data->beep_mask &= ~(0xff << (bytenr * 8)); + data->beep_mask |= w83791d_read(client, W83791D_REG_BEEP_CTRL[bytenr]) + << (bytenr * 8); + + data->beep_mask &= ~(1 << bitnr); + data->beep_mask |= val << bitnr; + + w83791d_write(client, W83791D_REG_BEEP_CTRL[bytenr], + (data->beep_mask >> (bytenr * 8)) & 0xff); + + mutex_unlock(&data->update_lock); + + return count; +} + +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); + struct w83791d_data *data = w83791d_update_device(dev); + int bitnr = sensor_attr->index; + + return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); +} + +/* Note: The bitmask for the beep enable/disable is different than + the bitmask for the alarm. */ +static struct sensor_device_attribute sda_in_beep[] = { + SENSOR_ATTR(in0_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 0), + SENSOR_ATTR(in1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 13), + SENSOR_ATTR(in2_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 2), + SENSOR_ATTR(in3_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 3), + SENSOR_ATTR(in4_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 8), + SENSOR_ATTR(in5_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 9), + SENSOR_ATTR(in6_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 10), + SENSOR_ATTR(in7_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 16), + SENSOR_ATTR(in8_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 17), + SENSOR_ATTR(in9_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 14), +}; + +static struct sensor_device_attribute sda_in_alarm[] = { + SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0), + SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1), + SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2), + SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3), + SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8), + SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9), + SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10), + SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 19), + SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 20), + SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 14), +}; + #define show_fan_reg(reg) \ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ @@ -536,6 +615,22 @@ static struct sensor_device_attribute sda_fan_div[] = { show_fan_div, store_fan_div, 4), }; +static struct sensor_device_attribute sda_fan_beep[] = { + SENSOR_ATTR(fan1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 6), + SENSOR_ATTR(fan2_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 7), + SENSOR_ATTR(fan3_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 11), + SENSOR_ATTR(fan4_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 21), + SENSOR_ATTR(fan5_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 22), +}; + +static struct sensor_device_attribute sda_fan_alarm[] = { + SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6), + SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7), + SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11), + SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 21), + SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -618,6 +713,19 @@ static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { show_temp23, store_temp23, 1, 2), }; +/* Note: The bitmask for the beep enable/disable is different than + the bitmask for the alarm. */ +static struct sensor_device_attribute sda_temp_beep[] = { + SENSOR_ATTR(temp1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 4), + SENSOR_ATTR(temp2_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 5), + SENSOR_ATTR(temp3_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 1), +}; + +static struct sensor_device_attribute sda_temp_alarm[] = { + SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4), + SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5), + SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), +}; /* get reatime status of all sensors items: voltage, temp, fan */ static ssize_t show_alarms_reg(struct device *dev, @@ -749,17 +857,23 @@ static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); #define IN_UNIT_ATTRS(X) \ &sda_in_input[X].dev_attr.attr, \ &sda_in_min[X].dev_attr.attr, \ - &sda_in_max[X].dev_attr.attr + &sda_in_max[X].dev_attr.attr, \ + &sda_in_beep[X].dev_attr.attr, \ + &sda_in_alarm[X].dev_attr.attr #define FAN_UNIT_ATTRS(X) \ &sda_fan_input[X].dev_attr.attr, \ &sda_fan_min[X].dev_attr.attr, \ - &sda_fan_div[X].dev_attr.attr + &sda_fan_div[X].dev_attr.attr, \ + &sda_fan_beep[X].dev_attr.attr, \ + &sda_fan_alarm[X].dev_attr.attr #define TEMP_UNIT_ATTRS(X) \ &sda_temp_input[X].dev_attr.attr, \ &sda_temp_max[X].dev_attr.attr, \ - &sda_temp_max_hyst[X].dev_attr.attr + &sda_temp_max_hyst[X].dev_attr.attr, \ + &sda_temp_beep[X].dev_attr.attr, \ + &sda_temp_alarm[X].dev_attr.attr static struct attribute *w83791d_attributes[] = { IN_UNIT_ATTRS(0), -- cgit v1.2.3 From c7f1f7166a83f8f5607cc03c7a6c5f936cde6b0d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 3 Sep 2007 17:11:46 +0200 Subject: hwmon: (it87) Add support for fan4 and fan5 Add support for the IT8716F and IT8718F fan4 and fan5. The late revisions of the IT8712F have these too but support is harder to add and nobody asked for it yet, so I didn't include it. Signed-off-by: Jean Delvare Signed-off-by: Mark M. Hoffman --- Documentation/hwmon/it87 | 3 ++- drivers/hwmon/it87.c | 54 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 81ecc7e41c50..5b704a40256b 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -90,7 +90,8 @@ upper VID bits share their pins with voltage inputs (in5 and in6) so you can't have both on a given board. The IT8716F, IT8718F and later IT8712F revisions have support for -2 additional fans. They are not yet supported by the driver. +2 additional fans. They are supported by the driver for the IT8716F and +IT8718F but not for the IT8712F The IT8716F and IT8718F, and late IT8712F and IT8705F also have optional 16-bit tachometer counters for fans 1 to 3. This is better (no more fan diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 41a90e5613bc..cd741b79c178 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -141,10 +141,10 @@ static int fix_pwm_polarity; /* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ -#define IT87_REG_FAN(nr) (0x0d + (nr)) -#define IT87_REG_FAN_MIN(nr) (0x10 + (nr)) -#define IT87_REG_FANX(nr) (0x18 + (nr)) -#define IT87_REG_FANX_MIN(nr) (0x1b + (nr)) +static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82 }; +static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86 }; +static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83 }; +static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; #define IT87_REG_FAN_MAIN_CTRL 0x13 #define IT87_REG_FAN_CTL 0x14 #define IT87_REG_PWM(nr) (0x15 + (nr)) @@ -235,8 +235,8 @@ struct it87_data { u8 in_max[8]; /* Register value */ u8 in_min[8]; /* Register value */ u8 has_fan; /* Bitfield, fans enabled */ - u16 fan[3]; /* Register values, possibly combined */ - u16 fan_min[3]; /* Register values, possibly combined */ + u16 fan[5]; /* Register values, possibly combined */ + u16 fan_min[5]; /* Register values, possibly combined */ u8 temp[3]; /* Register value */ u8 temp_high[3]; /* Register value */ u8 temp_low[3]; /* Register value */ @@ -555,7 +555,7 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, } data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - it87_write_value(data, IT87_REG_FAN_MIN(nr), data->fan_min[nr]); + it87_write_value(data, IT87_REG_FAN_MIN[nr], data->fan_min[nr]); mutex_unlock(&data->update_lock); return count; } @@ -596,7 +596,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, /* Restore fan min limit */ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - it87_write_value(data, IT87_REG_FAN_MIN(nr), data->fan_min[nr]); + it87_write_value(data, IT87_REG_FAN_MIN[nr], data->fan_min[nr]); mutex_unlock(&data->update_lock); return count; @@ -729,9 +729,9 @@ static ssize_t set_fan16_min(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->fan_min[nr] = FAN16_TO_REG(val); - it87_write_value(data, IT87_REG_FAN_MIN(nr), + it87_write_value(data, IT87_REG_FAN_MIN[nr], data->fan_min[nr] & 0xff); - it87_write_value(data, IT87_REG_FANX_MIN(nr), + it87_write_value(data, IT87_REG_FANX_MIN[nr], data->fan_min[nr] >> 8); mutex_unlock(&data->update_lock); return count; @@ -751,6 +751,8 @@ static struct sensor_device_attribute sensor_dev_attr_fan##offset##_min16 \ show_fan16_offset(1); show_fan16_offset(2); show_fan16_offset(3); +show_fan16_offset(4); +show_fan16_offset(5); /* Alarms */ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) @@ -851,6 +853,10 @@ static struct attribute *it87_attributes_opt[] = { &sensor_dev_attr_fan2_min16.dev_attr.attr, &sensor_dev_attr_fan3_input16.dev_attr.attr, &sensor_dev_attr_fan3_min16.dev_attr.attr, + &sensor_dev_attr_fan4_input16.dev_attr.attr, + &sensor_dev_attr_fan4_min16.dev_attr.attr, + &sensor_dev_attr_fan5_input16.dev_attr.attr, + &sensor_dev_attr_fan5_min16.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, @@ -1024,6 +1030,20 @@ static int __devinit it87_probe(struct platform_device *pdev) &sensor_dev_attr_fan3_min16.dev_attr))) goto ERROR4; } + if (data->has_fan & (1 << 3)) { + if ((err = device_create_file(dev, + &sensor_dev_attr_fan4_input16.dev_attr)) + || (err = device_create_file(dev, + &sensor_dev_attr_fan4_min16.dev_attr))) + goto ERROR4; + } + if (data->has_fan & (1 << 4)) { + if ((err = device_create_file(dev, + &sensor_dev_attr_fan5_input16.dev_attr)) + || (err = device_create_file(dev, + &sensor_dev_attr_fan5_min16.dev_attr))) + goto ERROR4; + } } else { /* 8-bit tachometers with clock divider */ if (data->has_fan & (1 << 0)) { @@ -1260,6 +1280,10 @@ static void __devinit it87_init_device(struct platform_device *pdev) it87_write_value(data, IT87_REG_FAN_16BIT, tmp | 0x07); } + if (tmp & (1 << 4)) + data->has_fan |= (1 << 3); /* fan4 enabled */ + if (tmp & (1 << 5)) + data->has_fan |= (1 << 4); /* fan5 enabled */ } /* Set current fan mode registers and the default settings for the @@ -1314,21 +1338,21 @@ static struct it87_data *it87_update_device(struct device *dev) data->in[8] = it87_read_value(data, IT87_REG_VIN(8)); - for (i = 0; i < 3; i++) { + for (i = 0; i < 5; i++) { /* Skip disabled fans */ if (!(data->has_fan & (1 << i))) continue; data->fan_min[i] = - it87_read_value(data, IT87_REG_FAN_MIN(i)); + it87_read_value(data, IT87_REG_FAN_MIN[i]); data->fan[i] = it87_read_value(data, - IT87_REG_FAN(i)); + IT87_REG_FAN[i]); /* Add high byte if in 16-bit mode */ if (data->type == it8716 || data->type == it8718) { data->fan[i] |= it87_read_value(data, - IT87_REG_FANX(i)) << 8; + IT87_REG_FANX[i]) << 8; data->fan_min[i] |= it87_read_value(data, - IT87_REG_FANX_MIN(i)) << 8; + IT87_REG_FANX_MIN[i]) << 8; } } for (i = 0; i < 3; i++) { -- cgit v1.2.3 From 428a7039c5717695935b946af9413e59f68928a4 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 4 Sep 2007 23:25:33 +0200 Subject: hwmon: (lm78) Add individual alarm files Add individual alarm files to the lm78 driver, these are needed by the next version of libsensors. Signed-off-by: Jean Delvare Acked-by: Hans de Goede Signed-off-by: Mark M. Hoffman --- Documentation/hwmon/lm78 | 10 ---------- drivers/hwmon/lm78.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm78 b/Documentation/hwmon/lm78 index fd5dc7a19f0e..dfc318a60fd4 100644 --- a/Documentation/hwmon/lm78 +++ b/Documentation/hwmon/lm78 @@ -56,16 +56,6 @@ should work with. This is hardcoded by the mainboard and/or processor itself. It is a value in volts. When it is unconnected, you will often find the value 3.50 V here. -In addition to the alarms described above, there are a couple of additional -ones. There is a BTI alarm, which gets triggered when an external chip has -crossed its limits. Usually, this is connected to all LM75 chips; if at -least one crosses its limits, this bit gets set. The CHAS alarm triggers -if your computer case is open. The FIFO alarms should never trigger; it -indicates an internal error. The SMI_IN alarm indicates some other chip -has triggered an SMI interrupt. As we do not use SMI interrupts at all, -this condition usually indicates there is a problem with some other -device. - If an alarm triggers, it will remain triggered until the hardware register is read at least once. This means that the cause for the alarm may already have disappeared! Note that in the current implementation, all diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index f5278fda8268..3f7055ee679f 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -438,6 +438,25 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *da, } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static ssize_t show_alarm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct lm78_data *data = lm78_update_device(dev); + int nr = to_sensor_dev_attr(da)->index; + return sprintf(buf, "%u\n", (data->alarms >> nr) & 1); +} +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); + /* This function is called when: * lm78_driver is inserted (when this module is loaded), for each available adapter @@ -453,36 +472,47 @@ static struct attribute *lm78_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_min.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in2_min.dev_attr.attr, &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, &sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_in3_min.dev_attr.attr, &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, &sensor_dev_attr_in4_input.dev_attr.attr, &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in5_min.dev_attr.attr, &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, &sensor_dev_attr_in6_input.dev_attr.attr, &sensor_dev_attr_in6_min.dev_attr.attr, &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, &dev_attr_temp1_input.attr, &dev_attr_temp1_max.attr, &dev_attr_temp1_max_hyst.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_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, &dev_attr_alarms.attr, &dev_attr_cpu0_vid.attr, -- cgit v1.2.3 From 2ed4263308971af3a7a0f20bef6b2257ea230a71 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Sep 2007 17:03:32 +0200 Subject: hwmon: update sysfs interface document - error handling Here is a patch adding some text to the sysfs interface documentation on how settings written to sysfs attributes should be handled, focussing mainly on error handling. This version incorperates Jean's latest comments. Signed-off-by: Hans de Goede Acked-by: Jean Delvare Signed-off-by: Mark M. Hoffman --- Documentation/hwmon/sysfs-interface | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index db7bb4a659c4..a2153c4616ad 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -67,6 +67,10 @@ between readings to be caught and alarmed. The exact definition of an alarm (for example, whether a threshold must be met or must be exceeded to cause an alarm) is chip-dependent. +When setting values of hwmon sysfs attributes, the string representation of +the desired value must be written, note that strings which are not a number +are interpreted as 0! For more on how written strings are interpreted see the +"sysfs attribute writes interpretation" section at the end of this file. ------------------------------------------------------------------------- @@ -411,3 +415,57 @@ beep_mask Bitmask for beep. use discouraged for the same reason. Use individual *_beep files instead. RW + + +sysfs attribute writes interpretation +------------------------------------- + +hwmon sysfs attributes always contain numbers, so the first thing to do is to +convert the input to a number, there are 2 ways todo this depending whether +the number can be negative or not: +unsigned long u = simple_strtoul(buf, NULL, 10); +long s = simple_strtol(buf, NULL, 10); + +With buf being the buffer with the user input being passed by the kernel. +Notice that we do not use the second argument of strto[u]l, and thus cannot +tell when 0 is returned, if this was really 0 or is caused by invalid input. +This is done deliberately as checking this everywhere would add a lot of +code to the kernel. + +Notice that it is important to always store the converted value in an +unsigned long or long, so that no wrap around can happen before any further +checking. + +After the input string is converted to an (unsigned) long, the value should be +checked if its acceptable. Be careful with further conversions on the value +before checking it for validity, as these conversions could still cause a wrap +around before the check. For example do not multiply the result, and only +add/subtract if it has been divided before the add/subtract. + +What to do if a value is found to be invalid, depends on the type of the +sysfs attribute that is being set. If it is a continuous setting like a +tempX_max or inX_max attribute, then the value should be clamped to its +limits using SENSORS_LIMIT(value, min_limit, max_limit). If it is not +continuous like for example a tempX_type, then when an invalid value is +written, -EINVAL should be returned. + +Example1, temp1_max, register is a signed 8 bit value (-128 - 127 degrees): +--- begin code --- +long v = simple_strtol(buf, NULL, 10) / 1000; +SENSORS_LIMIT(v, -128, 127); +/* write v to register */ +--- end code --- + +Example2, fan divider setting, valid values 2, 4 and 8: +--- begin code --- +unsigned long v = simple_strtoul(buf, NULL, 10); + +switch (v) { + case 2: v = 1; break; + case 4: v = 2; break; + case 8: v = 3; break; + default: + return -EINVAL; +} +/* write v to register */ +--- end code --- -- cgit v1.2.3 From 5fbea518dab712af63076aed897fce89cd7faec3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 7 Oct 2007 22:44:33 +0200 Subject: hwmon: Fix the code examples in documentation Fix a bug in the code examples, make them comply with CodingStyle, and indent them for a better redability. Signed-off-by: Jean Delvare Acked-by: Hans de Goede Signed-off-by: Mark M. Hoffman --- Documentation/hwmon/sysfs-interface | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index a2153c4616ad..a17b692d2679 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -450,22 +450,20 @@ continuous like for example a tempX_type, then when an invalid value is written, -EINVAL should be returned. Example1, temp1_max, register is a signed 8 bit value (-128 - 127 degrees): ---- begin code --- -long v = simple_strtol(buf, NULL, 10) / 1000; -SENSORS_LIMIT(v, -128, 127); -/* write v to register */ ---- end code --- + + long v = simple_strtol(buf, NULL, 10) / 1000; + v = SENSORS_LIMIT(v, -128, 127); + /* write v to register */ Example2, fan divider setting, valid values 2, 4 and 8: ---- begin code --- -unsigned long v = simple_strtoul(buf, NULL, 10); - -switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; - default: - return -EINVAL; -} -/* write v to register */ ---- end code --- + + unsigned long v = simple_strtoul(buf, NULL, 10); + + switch (v) { + case 2: v = 1; break; + case 4: v = 2; break; + case 8: v = 3; break; + default: + return -EINVAL; + } + /* write v to register */ -- cgit v1.2.3 From c940336b4403540c498fceb102c7142799252129 Mon Sep 17 00:00:00 2001 From: Rudolf Marek Date: Sun, 7 Oct 2007 13:42:09 +0200 Subject: hwmon: (coretemp) Add support for Celeron 4xx This patch adds support for the Celeron 4xx based on Core 2 core. Signed-off-by: Rudolf Marek Signed-off-by: Mark M. Hoffman --- Documentation/hwmon/coretemp | 2 +- drivers/hwmon/coretemp.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index 870cda9416e9..170bf862437b 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp @@ -4,7 +4,7 @@ Kernel driver coretemp Supported chips: * All Intel Core family Prefix: 'coretemp' - CPUID: family 0x6, models 0xe, 0xf + CPUID: family 0x6, models 0xe, 0xf, 0x16 Datasheet: Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3A: System Programming Guide diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 1a0ff8d42bb2..6f66551d9e51 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -369,9 +369,10 @@ static int __init coretemp_init(void) for_each_online_cpu(i) { struct cpuinfo_x86 *c = &(cpu_data)[i]; - /* check if family 6, models e, f */ + /* check if family 6, models e, f, 16 */ if ((c->cpuid_level < 0) || (c->x86 != 0x6) || - !((c->x86_model == 0xe) || (c->x86_model == 0xf))) { + !((c->x86_model == 0xe) || (c->x86_model == 0xf) || + (c->x86_model == 0x16))) { /* supported CPU not found, but report the unknown family 6 CPU */ -- cgit v1.2.3 From e95c237d78c0dc8fc0ae1207cec87af7a37dd366 Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Sun, 7 Oct 2007 21:27:35 -0700 Subject: hwmon: (dme1737) Add sch311x support This patch adds support for the SMSC SCH3112, SCH3114, and SCH3116 Super-I/O chips. These chips feature identical hardware monitoring capabilites with the expection that some of the fan inputs and pmw outputs don't exist. The hardware monitoring features of the SCH311x chips can only be accessed via the ISA bus. The driver therefore registers as a platform driver, if such a chip is detected. Signed-off-by: Juerg Haefliger Acked-by: Jean Delvare Signed-off-by: Mark M. Hoffman --- Documentation/hwmon/dme1737 | 33 ++-- drivers/hwmon/Kconfig | 4 +- drivers/hwmon/dme1737.c | 375 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 359 insertions(+), 53 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/dme1737 b/Documentation/hwmon/dme1737 index 1a0f3d64ab80..8f446070e64a 100644 --- a/Documentation/hwmon/dme1737 +++ b/Documentation/hwmon/dme1737 @@ -6,6 +6,10 @@ Supported chips: Prefix: 'dme1737' Addresses scanned: I2C 0x2c, 0x2d, 0x2e Datasheet: Provided by SMSC upon request and under NDA + * SMSC SCH3112, SCH3114, SCH3116 + Prefix: 'sch311x' + Addresses scanned: none, address read from Super-I/O config space + Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf Authors: Juerg Haefliger @@ -27,16 +31,25 @@ Description ----------- This driver implements support for the hardware monitoring capabilities of the -SMSC DME1737 and Asus A8000 (which are the same) Super-I/O chips. This chip -features monitoring of 3 temp sensors temp[1-3] (2 remote diodes and 1 -internal), 7 voltages in[0-6] (6 external and 1 internal) and 6 fan speeds -fan[1-6]. Additionally, the chip implements 5 PWM outputs pwm[1-3,5-6] for -controlling fan speeds both manually and automatically. - -Fan[3-6] and pwm[3,5-6] are optional features and their availability is -dependent on the configuration of the chip. The driver will detect which -features are present during initialization and create the sysfs attributes -accordingly. +SMSC DME1737 and Asus A8000 (which are the same) and SMSC SCH311x Super-I/O +chips. These chips feature monitoring of 3 temp sensors temp[1-3] (2 remote +diodes and 1 internal), 7 voltages in[0-6] (6 external and 1 internal) and up +to 6 fan speeds fan[1-6]. Additionally, the chips implement up to 5 PWM +outputs pwm[1-3,5-6] for controlling fan speeds both manually and +automatically. + +For the DME1737 and A8000, fan[1-2] and pwm[1-2] are always present. Fan[3-6] +and pwm[3,5-6] are optional features and their availability depends on the +configuration of the chip. The driver will detect which features are present +during initialization and create the sysfs attributes accordingly. + +For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and +pwm[5-6] don't exist. + +The hardware monitoring features of the DME1737 and A8000 are only accessible +via SMBus, while the SCH311x only provides access via the ISA bus. The driver +will therefore register itself as an I2C client driver if it detects a DME1737 +or A8000 and as a platform driver if it detects a SCH311x chip. Voltage Monitoring diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b8854b94807f..c69de6c56d97 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -509,13 +509,13 @@ config SENSORS_SIS5595 will be called sis5595. config SENSORS_DME1737 - tristate "SMSC DME1737 and compatibles" + tristate "SMSC DME1737, SCH311x and compatibles" depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for the hardware monitoring and fan control features of the SMSC DME1737 (and compatibles - like the Asus A8000) Super-I/O chip. + like the Asus A8000) and SCH311x Super-I/O chips. This driver can also be built as a module. If so, the module will be called dme1737. diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index ec8edd2a8cbd..a878c98e252e 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -1,12 +1,12 @@ /* - * dme1737.c - driver for the SMSC DME1737 and Asus A8000 Super-I/O chips - * integrated hardware monitoring features. + * dme1737.c - Driver for the SMSC DME1737, Asus A8000, and SMSC SCH311x + * Super-I/O chips integrated hardware monitoring features. * Copyright (c) 2007 Juerg Haefliger * - * This driver is based on the LM85 driver. The hardware monitoring - * capabilities of the DME1737 are very similar to the LM85 with some - * additional features. Even though the DME1737 is a Super-I/O chip, the - * hardware monitoring registers are only accessible via SMBus. + * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access + * the chip registers if a DME1737 (or A8000) is found and the ISA bus if a + * SCH311x chip is found. Both types of chips have very similar hardware + * monitoring capabilities but differ in the way they can be accessed. * * 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 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,9 @@ #include #include +/* ISA device, if found */ +static struct platform_device *pdev; + /* Module load parameters */ static int force_start; module_param(force_start, bool, 0); @@ -133,6 +137,7 @@ static const u8 DME1737_BIT_ALARM_TEMP[] = {4, 5, 6}; static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23}; /* Miscellaneous registers */ +#define DME1737_REG_DEVICE 0x3d #define DME1737_REG_COMPANY 0x3e #define DME1737_REG_VERSTEP 0x3f #define DME1737_REG_CONFIG 0x40 @@ -148,11 +153,17 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23}; #define DME1737_COMPANY_SMSC 0x5c #define DME1737_VERSTEP 0x88 #define DME1737_VERSTEP_MASK 0xf8 +#define SCH311X_DEVICE 0x8c + +/* Length of ISA address segment */ +#define DME1737_EXTENT 2 /* --------------------------------------------------------------------- * Data structures and manipulation thereof * --------------------------------------------------------------------- */ +/* For ISA chips, we abuse the i2c_client addr and name fields. We also use + the driver field to differentiate between I2C and ISA chips. */ struct dme1737_data { struct i2c_client client; struct device *hwmon_dev; @@ -465,27 +476,48 @@ static inline int PWM_OFF_TO_REG(int val, int ix, int reg) /* --------------------------------------------------------------------- * Device I/O access + * + * ISA access is performed through an index/data register pair and needs to + * be protected by a mutex during runtime (not required for initialization). + * We use data->update_lock for this and need to ensure that we acquire it + * before calling dme1737_read or dme1737_write. * --------------------------------------------------------------------- */ static u8 dme1737_read(struct i2c_client *client, u8 reg) { - s32 val = i2c_smbus_read_byte_data(client, reg); + s32 val; - if (val < 0) { - dev_warn(&client->dev, "Read from register 0x%02x failed! " - "Please report to the driver maintainer.\n", reg); + if (client->driver) { /* I2C device */ + val = i2c_smbus_read_byte_data(client, reg); + + if (val < 0) { + dev_warn(&client->dev, "Read from register " + "0x%02x failed! Please report to the driver " + "maintainer.\n", reg); + } + } else { /* ISA device */ + outb(reg, client->addr); + val = inb(client->addr + 1); } return val; } -static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 value) +static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val) { - s32 res = i2c_smbus_write_byte_data(client, reg, value); + s32 res = 0; + + if (client->driver) { /* I2C device */ + res = i2c_smbus_write_byte_data(client, reg, val); - if (res < 0) { - dev_warn(&client->dev, "Write to register 0x%02x failed! " - "Please report to the driver maintainer.\n", reg); + if (res < 0) { + dev_warn(&client->dev, "Write to register " + "0x%02x failed! Please report to the driver " + "maintainer.\n", reg); + } + } else { /* ISA device */ + outb(reg, client->addr); + outb(val, client->addr + 1); } return res; @@ -630,6 +662,24 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) DME1737_REG_ALARM3) << 16; } + /* The ISA chips require explicit clearing of alarm bits. + * Don't worry, an alarm will come back if the condition + * that causes it still exists */ + if (!client->driver) { + if (data->alarms & 0xff0000) { + dme1737_write(client, DME1737_REG_ALARM3, + 0xff); + } + if (data->alarms & 0xff00) { + dme1737_write(client, DME1737_REG_ALARM2, + 0xff); + } + if (data->alarms & 0xff) { + dme1737_write(client, DME1737_REG_ALARM1, + 0xff); + } + } + data->last_update = jiffies; data->valid = 1; } @@ -995,7 +1045,7 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, /* Only valid for fan[1-4] */ if (!(val == 1 || val == 2 || val == 4)) { count = -EINVAL; - dev_warn(&client->dev, "Fan type value %ld not " + dev_warn(dev, "Fan type value %ld not " "supported. Choose one of 1, 2, or 4.\n", val); goto exit; @@ -1122,7 +1172,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, /* Only valid for pwm[1-3] */ if (val < 0 || val > 2) { count = -EINVAL; - dev_warn(&client->dev, "PWM enable %ld not " + dev_warn(dev, "PWM enable %ld not " "supported. Choose one of 0, 1, or 2.\n", val); goto exit; @@ -1223,7 +1273,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, if (!(val == 1 || val == 2 || val == 4 || val == 6 || val == 7)) { count = -EINVAL; - dev_warn(&client->dev, "PWM auto channels zone %ld " + dev_warn(dev, "PWM auto channels zone %ld " "not supported. Choose one of 1, 2, 4, 6, " "or 7.\n", val); goto exit; @@ -1311,6 +1361,14 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } +static ssize_t show_name(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dme1737_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->client.name); +} + /* --------------------------------------------------------------------- * Sysfs device attribute defines and structs * --------------------------------------------------------------------- */ @@ -1446,6 +1504,7 @@ SENSOR_DEVICE_ATTR_PWM_5TO6(6); static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* for ISA devices */ #define SENSOR_DEV_ATTR_IN(ix) \ &sensor_dev_attr_in##ix##_input.dev_attr.attr, \ @@ -1698,7 +1757,7 @@ static inline void dme1737_sio_outb(int sio_cip, int reg, int val) } /* --------------------------------------------------------------------- - * Device detection, registration and initialization + * Device initialization * --------------------------------------------------------------------- */ static int dme1737_i2c_get_features(int, struct dme1737_data*); @@ -1743,6 +1802,10 @@ static void dme1737_remove_files(struct device *dev) } sysfs_remove_group(&dev->kobj, &dme1737_group); + + if (!data->client.driver) { + sysfs_remove_file(&dev->kobj, &dev_attr_name.attr); + } } static int dme1737_create_files(struct device *dev) @@ -1750,9 +1813,15 @@ static int dme1737_create_files(struct device *dev) struct dme1737_data *data = dev_get_drvdata(dev); int err, ix; + /* Create a name attribute for ISA devices */ + if (!data->client.driver && + (err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr))) { + goto exit; + } + /* Create standard sysfs attributes */ if ((err = sysfs_create_group(&dev->kobj, &dme1737_group))) { - goto exit; + goto exit_remove; } /* Create fan sysfs attributes */ @@ -1840,27 +1909,36 @@ static int dme1737_init_device(struct device *dev) return -EFAULT; } - data->config2 = dme1737_read(client, DME1737_REG_CONFIG2); - /* Check if optional fan3 input is enabled */ - if (data->config2 & 0x04) { - data->has_fan |= (1 << 2); - } + /* Determine which optional fan and pwm features are enabled/present */ + if (client->driver) { /* I2C chip */ + data->config2 = dme1737_read(client, DME1737_REG_CONFIG2); + /* Check if optional fan3 input is enabled */ + if (data->config2 & 0x04) { + data->has_fan |= (1 << 2); + } - /* Fan4 and pwm3 are only available if the client's I2C address - * is the default 0x2e. Otherwise the I/Os associated with these - * functions are used for addr enable/select. */ - if (client->addr == 0x2e) { - data->has_fan |= (1 << 3); - data->has_pwm |= (1 << 2); - } + /* Fan4 and pwm3 are only available if the client's I2C address + * is the default 0x2e. Otherwise the I/Os associated with + * these functions are used for addr enable/select. */ + if (data->client.addr == 0x2e) { + data->has_fan |= (1 << 3); + data->has_pwm |= (1 << 2); + } - /* Determine if the optional fan[5-6] and/or pwm[5-6] are enabled. - * For this, we need to query the runtime registers through the - * Super-IO LPC interface. Try both config ports 0x2e and 0x4e. */ - if (dme1737_i2c_get_features(0x2e, data) && - dme1737_i2c_get_features(0x4e, data)) { - dev_warn(dev, "Failed to query Super-IO for optional " - "features.\n"); + /* Determine which of the optional fan[5-6] and pwm[5-6] + * features are enabled. For this, we need to query the runtime + * registers through the Super-IO LPC interface. Try both + * config ports 0x2e and 0x4e. */ + if (dme1737_i2c_get_features(0x2e, data) && + dme1737_i2c_get_features(0x4e, data)) { + dev_warn(dev, "Failed to query Super-IO for optional " + "features.\n"); + } + } else { /* ISA chip */ + /* Fan3 and pwm3 are always available. Fan[4-5] and pwm[5-6] + * don't exist in the ISA chip. */ + data->has_fan |= (1 << 2); + data->has_pwm |= (1 << 2); } /* Fan1, fan2, pwm1, and pwm2 are always present */ @@ -1879,13 +1957,19 @@ static int dme1737_init_device(struct device *dev) reg = dme1737_read(client, DME1737_REG_TACH_PWM); /* Inform if fan-to-pwm mapping differs from the default */ - if (reg != 0xa4) { + if (client->driver && reg != 0xa4) { /* I2C chip */ dev_warn(dev, "Non-standard fan to pwm mapping: " "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, " "fan4->pwm%d. Please report to the driver " "maintainer.\n", (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1, ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1); + } else if (!client->driver && reg != 0x24) { /* ISA chip */ + dev_warn(dev, "Non-standard fan to pwm mapping: " + "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. " + "Please report to the driver maintainer.\n", + (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1, + ((reg >> 4) & 0x03) + 1); } /* Switch pwm[1-3] to manual mode if they are currently disabled and @@ -2093,17 +2177,226 @@ static struct i2c_driver dme1737_i2c_driver = { .detach_client = dme1737_i2c_detach_client, }; +/* --------------------------------------------------------------------- + * ISA device detection and registration + * --------------------------------------------------------------------- */ + +static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr) +{ + int err = 0, reg; + unsigned short base_addr; + + dme1737_sio_enter(sio_cip); + + /* Check device ID + * We currently know about SCH3112 (0x7c), SCH3114 (0x7d), and + * SCH3116 (0x7f). */ + reg = dme1737_sio_inb(sio_cip, 0x20); + if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) { + err = -ENODEV; + goto exit; + } + + /* Select logical device A (runtime registers) */ + dme1737_sio_outb(sio_cip, 0x07, 0x0a); + + /* Get the base address of the runtime registers */ + if (!(base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) | + dme1737_sio_inb(sio_cip, 0x61))) { + printk(KERN_ERR "dme1737: Base address not set.\n"); + err = -ENODEV; + goto exit; + } + + /* Access to the hwmon registers is through an index/data register + * pair located at offset 0x70/0x71. */ + *addr = base_addr + 0x70; + +exit: + dme1737_sio_exit(sio_cip); + return err; +} + +static int __init dme1737_isa_device_add(unsigned short addr) +{ + struct resource res = { + .start = addr, + .end = addr + DME1737_EXTENT - 1, + .name = "dme1737", + .flags = IORESOURCE_IO, + }; + int err; + + if (!(pdev = platform_device_alloc("dme1737", addr))) { + printk(KERN_ERR "dme1737: Failed to allocate device.\n"); + err = -ENOMEM; + goto exit; + } + + if ((err = platform_device_add_resources(pdev, &res, 1))) { + printk(KERN_ERR "dme1737: Failed to add device resource " + "(err = %d).\n", err); + goto exit_device_put; + } + + if ((err = platform_device_add(pdev))) { + printk(KERN_ERR "dme1737: Failed to add device (err = %d).\n", + err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); + pdev = NULL; +exit: + return err; +} + +static int __devinit dme1737_isa_probe(struct platform_device *pdev) +{ + u8 company, device; + struct resource *res; + struct i2c_client *client; + struct dme1737_data *data; + struct device *dev = &pdev->dev; + int err; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start, DME1737_EXTENT, "dme1737")) { + dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n", + (unsigned short)res->start, + (unsigned short)res->start + DME1737_EXTENT - 1); + err = -EBUSY; + goto exit; + } + + if (!(data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit_release_region; + } + + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = res->start; + platform_set_drvdata(pdev, data); + + company = dme1737_read(client, DME1737_REG_COMPANY); + device = dme1737_read(client, DME1737_REG_DEVICE); + + if (!((company == DME1737_COMPANY_SMSC) && + (device == SCH311X_DEVICE))) { + err = -ENODEV; + goto exit_kfree; + } + + /* Fill in the remaining client fields and initialize the mutex */ + strlcpy(client->name, "sch311x", I2C_NAME_SIZE); + mutex_init(&data->update_lock); + + dev_info(dev, "Found a SCH311x chip at 0x%04x\n", client->addr); + + /* Initialize the chip */ + if ((err = dme1737_init_device(dev))) { + dev_err(dev, "Failed to initialize device.\n"); + goto exit_kfree; + } + + /* Create sysfs files */ + if ((err = dme1737_create_files(dev))) { + dev_err(dev, "Failed to create sysfs files.\n"); + goto exit_kfree; + } + + /* Register device */ + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + dev_err(dev, "Failed to register device.\n"); + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: + dme1737_remove_files(dev); +exit_kfree: + platform_set_drvdata(pdev, NULL); + kfree(data); +exit_release_region: + release_region(res->start, DME1737_EXTENT); +exit: + return err; +} + +static int __devexit dme1737_isa_remove(struct platform_device *pdev) +{ + struct dme1737_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + dme1737_remove_files(&pdev->dev); + release_region(data->client.addr, DME1737_EXTENT); + platform_set_drvdata(pdev, NULL); + kfree(data); + + return 0; +} + +static struct platform_driver dme1737_isa_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "dme1737", + }, + .probe = dme1737_isa_probe, + .remove = __devexit_p(dme1737_isa_remove), +}; + /* --------------------------------------------------------------------- * Module initialization and cleanup * --------------------------------------------------------------------- */ static int __init dme1737_init(void) { - return i2c_add_driver(&dme1737_i2c_driver); + int err; + unsigned short addr; + + if ((err = i2c_add_driver(&dme1737_i2c_driver))) { + goto exit; + } + + if (dme1737_isa_detect(0x2e, &addr) && + dme1737_isa_detect(0x4e, &addr)) { + /* Return 0 if we didn't find an ISA device */ + return 0; + } + + if ((err = platform_driver_register(&dme1737_isa_driver))) { + goto exit_del_i2c_driver; + } + + /* Sets global pdev as a side effect */ + if ((err = dme1737_isa_device_add(addr))) { + goto exit_del_isa_driver; + } + + return 0; + +exit_del_isa_driver: + platform_driver_unregister(&dme1737_isa_driver); +exit_del_i2c_driver: + i2c_del_driver(&dme1737_i2c_driver); +exit: + return err; } static void __exit dme1737_exit(void) { + if (pdev) { + platform_device_unregister(pdev); + platform_driver_unregister(&dme1737_isa_driver); + } + i2c_del_driver(&dme1737_i2c_driver); } -- cgit v1.2.3