diff options
author | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-13 12:13:19 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-13 12:13:19 -0500 |
commit | bbc7610c062074468f362b37d33603536e87fb96 (patch) | |
tree | 71fe8b24a4f7925167407adda1396fbfda700e5b | |
parent | 5cbded585d129d0226cb48ac4202b253c781be26 (diff) | |
parent | 284f42b627c070a2dd07b5c072cbd75d7fbb7c96 (diff) |
Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6
* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6:
hwmon: Add MAINTAINERS entry for new ams driver
hwmon: New AMS hardware monitoring driver
hwmon/w83793: Add documentation and maintainer
hwmon: New Winbond W83793 hardware monitoring driver
hwmon: Update Rudolf Marek's e-mail address
hwmon/f71805f: Fix the device address decoding
hwmon/f71805f: Always create all fan inputs
hwmon/f71805f: Add support for the Fintek F71872F/FG chip
hwmon: New PC87427 hardware monitoring driver
hwmon/it87: Remove the SMBus interface support
hwmon/hdaps: Update the list of supported devices
hwmon/hdaps: Move the DMI detection data to .data
hwmon/pc87360: Autodetect the VRM version
hwmon/f71805f: Document the fan control features
hwmon/f71805f: Add support for "speed mode" fan speed control
hwmon/f71805f: Support DC fan speed control mode
hwmon/f71805f: Let the user adjust the PWM base frequency
hwmon/f71805f: Add manual fan speed control
hwmon/f71805f: Store the fan control registers
30 files changed, 4115 insertions, 300 deletions
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 64ce44da5936..040f437c421b 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt | |||
@@ -151,15 +151,6 @@ Who: Thomas Gleixner <tglx@linutronix.de> | |||
151 | 151 | ||
152 | --------------------------- | 152 | --------------------------- |
153 | 153 | ||
154 | What: I2C interface of the it87 driver | ||
155 | When: January 2007 | ||
156 | Why: The ISA interface is faster and should be always available. The I2C | ||
157 | probing is also known to cause trouble in at least one case (see | ||
158 | bug #5889.) | ||
159 | Who: Jean Delvare <khali@linux-fr.org> | ||
160 | |||
161 | --------------------------- | ||
162 | |||
163 | What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports | 154 | What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports |
164 | (temporary transition config option provided until then) | 155 | (temporary transition config option provided until then) |
165 | The transition config option will also be removed at the same time. | 156 | The transition config option will also be removed at the same time. |
diff --git a/Documentation/hwmon/f71805f b/Documentation/hwmon/f71805f index 2ca69df669c3..bfd0f154959c 100644 --- a/Documentation/hwmon/f71805f +++ b/Documentation/hwmon/f71805f | |||
@@ -6,6 +6,10 @@ Supported chips: | |||
6 | Prefix: 'f71805f' | 6 | Prefix: 'f71805f' |
7 | Addresses scanned: none, address read from Super I/O config space | 7 | Addresses scanned: none, address read from Super I/O config space |
8 | Datasheet: Provided by Fintek on request | 8 | Datasheet: Provided by Fintek on request |
9 | * Fintek F71872F/FG | ||
10 | Prefix: 'f71872f' | ||
11 | Addresses scanned: none, address read from Super I/O config space | ||
12 | Datasheet: Provided by Fintek on request | ||
9 | 13 | ||
10 | Author: Jean Delvare <khali@linux-fr.org> | 14 | Author: Jean Delvare <khali@linux-fr.org> |
11 | 15 | ||
@@ -13,8 +17,8 @@ Thanks to Denis Kieft from Barracuda Networks for the donation of a | |||
13 | test system (custom Jetway K8M8MS motherboard, with CPU and RAM) and | 17 | test system (custom Jetway K8M8MS motherboard, with CPU and RAM) and |
14 | for providing initial documentation. | 18 | for providing initial documentation. |
15 | 19 | ||
16 | Thanks to Kris Chen from Fintek for answering technical questions and | 20 | Thanks to Kris Chen and Aaron Huang from Fintek for answering technical |
17 | providing additional documentation. | 21 | questions and providing additional documentation. |
18 | 22 | ||
19 | Thanks to Chris Lin from Jetway for providing wiring schematics and | 23 | Thanks to Chris Lin from Jetway for providing wiring schematics and |
20 | answering technical questions. | 24 | answering technical questions. |
@@ -28,8 +32,11 @@ capabilities. It can monitor up to 9 voltages (counting its own power | |||
28 | source), 3 fans and 3 temperature sensors. | 32 | source), 3 fans and 3 temperature sensors. |
29 | 33 | ||
30 | This chip also has fan controlling features, using either DC or PWM, in | 34 | This chip also has fan controlling features, using either DC or PWM, in |
31 | three different modes (one manual, two automatic). The driver doesn't | 35 | three different modes (one manual, two automatic). |
32 | support these features yet. | 36 | |
37 | The Fintek F71872F/FG Super I/O chip is almost the same, with two | ||
38 | additional internal voltages monitored (VSB and battery). It also features | ||
39 | 6 VID inputs. The VID inputs are not yet supported by this driver. | ||
33 | 40 | ||
34 | The driver assumes that no more than one chip is present, which seems | 41 | The driver assumes that no more than one chip is present, which seems |
35 | reasonable. | 42 | reasonable. |
@@ -42,7 +49,8 @@ Voltages are sampled by an 8-bit ADC with a LSB of 8 mV. The supported | |||
42 | range is thus from 0 to 2.040 V. Voltage values outside of this range | 49 | range is thus from 0 to 2.040 V. Voltage values outside of this range |
43 | need external resistors. An exception is in0, which is used to monitor | 50 | need external resistors. An exception is in0, which is used to monitor |
44 | the chip's own power source (+3.3V), and is divided internally by a | 51 | the chip's own power source (+3.3V), and is divided internally by a |
45 | factor 2. | 52 | factor 2. For the F71872F/FG, in9 (VSB) and in10 (battery) are also |
53 | divided internally by a factor 2. | ||
46 | 54 | ||
47 | The two LSB of the voltage limit registers are not used (always 0), so | 55 | The two LSB of the voltage limit registers are not used (always 0), so |
48 | you can only set the limits in steps of 32 mV (before scaling). | 56 | you can only set the limits in steps of 32 mV (before scaling). |
@@ -61,9 +69,12 @@ in5 VIN5 +12V 200K 20K 11.00 1.05 V | |||
61 | in6 VIN6 VCC1.5V 10K - 1.00 1.50 V | 69 | in6 VIN6 VCC1.5V 10K - 1.00 1.50 V |
62 | in7 VIN7 VCORE 10K - 1.00 ~1.40 V (1) | 70 | in7 VIN7 VCORE 10K - 1.00 ~1.40 V (1) |
63 | in8 VIN8 VSB5V 200K 47K 1.00 0.95 V | 71 | in8 VIN8 VSB5V 200K 47K 1.00 0.95 V |
72 | in10 VSB VSB3.3V int. int. 2.00 1.65 V (3) | ||
73 | in9 VBAT VBATTERY int. int. 2.00 1.50 V (3) | ||
64 | 74 | ||
65 | (1) Depends on your hardware setup. | 75 | (1) Depends on your hardware setup. |
66 | (2) Obviously not correct, swapping R1 and R2 would make more sense. | 76 | (2) Obviously not correct, swapping R1 and R2 would make more sense. |
77 | (3) F71872F/FG only. | ||
67 | 78 | ||
68 | These values can be used as hints at best, as motherboard manufacturers | 79 | These values can be used as hints at best, as motherboard manufacturers |
69 | are free to use a completely different setup. As a matter of fact, the | 80 | are free to use a completely different setup. As a matter of fact, the |
@@ -103,3 +114,38 @@ sensor. Each channel can be used for connecting either a thermal diode | |||
103 | or a thermistor. The driver reports the currently selected mode, but | 114 | or a thermistor. The driver reports the currently selected mode, but |
104 | doesn't allow changing it. In theory, the BIOS should have configured | 115 | doesn't allow changing it. In theory, the BIOS should have configured |
105 | everything properly. | 116 | everything properly. |
117 | |||
118 | |||
119 | Fan Control | ||
120 | ----------- | ||
121 | |||
122 | Both PWM (pulse-width modulation) and DC fan speed control methods are | ||
123 | supported. The right one to use depends on external circuitry on the | ||
124 | motherboard, so the driver assumes that the BIOS set the method | ||
125 | properly. The driver will report the method, but won't let you change | ||
126 | it. | ||
127 | |||
128 | When the PWM method is used, you can select the operating frequency, | ||
129 | from 187.5 kHz (default) to 31 Hz. The best frequency depends on the | ||
130 | fan model. As a rule of thumb, lower frequencies seem to give better | ||
131 | control, but may generate annoying high-pitch noise. Fintek recommends | ||
132 | not going below 1 kHz, as the fan tachometers get confused by lower | ||
133 | frequencies as well. | ||
134 | |||
135 | When the DC method is used, Fintek recommends not going below 5 V, which | ||
136 | corresponds to a pwm value of 106 for the driver. The driver doesn't | ||
137 | enforce this limit though. | ||
138 | |||
139 | Three different fan control modes are supported: | ||
140 | |||
141 | * Manual mode | ||
142 | You ask for a specific PWM duty cycle or DC voltage. | ||
143 | |||
144 | * Fan speed mode | ||
145 | You ask for a specific fan speed. This mode assumes that pwm1 | ||
146 | corresponds to fan1, pwm2 to fan2 and pwm3 to fan3. | ||
147 | |||
148 | * Temperature mode | ||
149 | You define 3 temperature/fan speed trip points, and the fan speed is | ||
150 | adjusted depending on the measured temperature, using interpolation. | ||
151 | This mode is not yet supported by the driver. | ||
diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index e783fd62e308..74a80992d237 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 | |||
@@ -9,8 +9,7 @@ Supported chips: | |||
9 | http://www.ite.com.tw/ | 9 | http://www.ite.com.tw/ |
10 | * IT8712F | 10 | * IT8712F |
11 | Prefix: 'it8712' | 11 | Prefix: 'it8712' |
12 | Addresses scanned: I2C 0x2d | 12 | Addresses scanned: from Super I/O config space (8 I/O ports) |
13 | from Super I/O config space (8 I/O ports) | ||
14 | Datasheet: Publicly available at the ITE website | 13 | Datasheet: Publicly available at the ITE website |
15 | http://www.ite.com.tw/ | 14 | http://www.ite.com.tw/ |
16 | * IT8716F | 15 | * IT8716F |
@@ -53,6 +52,18 @@ Module Parameters | |||
53 | misconfigured by BIOS - PWM values would be inverted. This option tries | 52 | misconfigured by BIOS - PWM values would be inverted. This option tries |
54 | to fix this. Please contact your BIOS manufacturer and ask him for fix. | 53 | to fix this. Please contact your BIOS manufacturer and ask him for fix. |
55 | 54 | ||
55 | |||
56 | Hardware Interfaces | ||
57 | ------------------- | ||
58 | |||
59 | All the chips suported by this driver are LPC Super-I/O chips, accessed | ||
60 | through the LPC bus (ISA-like I/O ports). The IT8712F additionally has an | ||
61 | SMBus interface to the hardware monitoring functions. This driver no | ||
62 | longer supports this interface though, as it is slower and less reliable | ||
63 | than the ISA access, and was only available on a small number of | ||
64 | motherboard models. | ||
65 | |||
66 | |||
56 | Description | 67 | Description |
57 | ----------- | 68 | ----------- |
58 | 69 | ||
diff --git a/Documentation/hwmon/k8temp b/Documentation/hwmon/k8temp index 30d123b8d920..0005c7166146 100644 --- a/Documentation/hwmon/k8temp +++ b/Documentation/hwmon/k8temp | |||
@@ -8,7 +8,7 @@ Supported chips: | |||
8 | Datasheet: http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf | 8 | Datasheet: http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf |
9 | 9 | ||
10 | Author: Rudolf Marek | 10 | Author: Rudolf Marek |
11 | Contact: Rudolf Marek <r.marek@sh.cvut.cz> | 11 | Contact: Rudolf Marek <r.marek@assembler.cz> |
12 | 12 | ||
13 | Description | 13 | Description |
14 | ----------- | 14 | ----------- |
diff --git a/Documentation/hwmon/pc87427 b/Documentation/hwmon/pc87427 new file mode 100644 index 000000000000..9a0708f9f49e --- /dev/null +++ b/Documentation/hwmon/pc87427 | |||
@@ -0,0 +1,38 @@ | |||
1 | Kernel driver pc87427 | ||
2 | ===================== | ||
3 | |||
4 | Supported chips: | ||
5 | * National Semiconductor PC87427 | ||
6 | Prefix: 'pc87427' | ||
7 | Addresses scanned: none, address read from Super I/O config space | ||
8 | Datasheet: http://www.winbond.com.tw/E-WINBONDHTM/partner/apc_007.html | ||
9 | |||
10 | Author: Jean Delvare <khali@linux-fr.org> | ||
11 | |||
12 | Thanks to Amir Habibi at Candelis for setting up a test system, and to | ||
13 | Michael Kress for testing several iterations of this driver. | ||
14 | |||
15 | |||
16 | Description | ||
17 | ----------- | ||
18 | |||
19 | The National Semiconductor Super I/O chip includes complete hardware | ||
20 | monitoring capabilities. It can monitor up to 18 voltages, 8 fans and | ||
21 | 6 temperature sensors. Only the fans are supported at the moment. | ||
22 | |||
23 | This chip also has fan controlling features, which are not yet supported | ||
24 | by this driver either. | ||
25 | |||
26 | The driver assumes that no more than one chip is present, which seems | ||
27 | reasonable. | ||
28 | |||
29 | |||
30 | Fan Monitoring | ||
31 | -------------- | ||
32 | |||
33 | Fan rotation speeds are reported as 14-bit values from a gated clock | ||
34 | signal. Speeds down to 83 RPM can be measured. | ||
35 | |||
36 | An alarm is triggered if the rotation speed drops below a programmable | ||
37 | limit. Another alarm is triggered if the speed is too low to to be measured | ||
38 | (including stalled or missing fan). | ||
diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index d1d390aaf620..efef3b962cd3 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface | |||
@@ -208,12 +208,14 @@ temp[1-*]_auto_point[1-*]_temp_hyst | |||
208 | **************** | 208 | **************** |
209 | 209 | ||
210 | temp[1-*]_type Sensor type selection. | 210 | temp[1-*]_type Sensor type selection. |
211 | Integers 1 to 4 or thermistor Beta value (typically 3435) | 211 | Integers 1 to 6 or thermistor Beta value (typically 3435) |
212 | RW | 212 | RW |
213 | 1: PII/Celeron Diode | 213 | 1: PII/Celeron Diode |
214 | 2: 3904 transistor | 214 | 2: 3904 transistor |
215 | 3: thermal diode | 215 | 3: thermal diode |
216 | 4: thermistor (default/unknown Beta) | 216 | 4: thermistor (default/unknown Beta) |
217 | 5: AMD AMDSI | ||
218 | 6: Intel PECI | ||
217 | Not all types are supported by all chips | 219 | Not all types are supported by all chips |
218 | 220 | ||
219 | temp[1-*]_max Temperature max value. | 221 | temp[1-*]_max Temperature max value. |
diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf index caa610a297e8..8a15a7408753 100644 --- a/Documentation/hwmon/w83627ehf +++ b/Documentation/hwmon/w83627ehf | |||
@@ -10,7 +10,7 @@ Supported chips: | |||
10 | Authors: | 10 | Authors: |
11 | Jean Delvare <khali@linux-fr.org> | 11 | Jean Delvare <khali@linux-fr.org> |
12 | Yuan Mu (Winbond) | 12 | Yuan Mu (Winbond) |
13 | Rudolf Marek <r.marek@sh.cvut.cz> | 13 | Rudolf Marek <r.marek@assembler.cz> |
14 | 14 | ||
15 | Description | 15 | Description |
16 | ----------- | 16 | ----------- |
diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index 19b2ed739fa1..db9881df88a5 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d | |||
@@ -18,7 +18,7 @@ Credits: | |||
18 | and Mark Studebaker <mdsxyz123@yahoo.com> | 18 | and Mark Studebaker <mdsxyz123@yahoo.com> |
19 | w83792d.c: | 19 | w83792d.c: |
20 | Chunhao Huang <DZShen@Winbond.com.tw>, | 20 | Chunhao Huang <DZShen@Winbond.com.tw>, |
21 | Rudolf Marek <r.marek@sh.cvut.cz> | 21 | Rudolf Marek <r.marek@assembler.cz> |
22 | 22 | ||
23 | Additional contributors: | 23 | Additional contributors: |
24 | Sven Anders <anders@anduras.de> | 24 | Sven Anders <anders@anduras.de> |
diff --git a/Documentation/hwmon/w83793 b/Documentation/hwmon/w83793 new file mode 100644 index 000000000000..45e5408340e0 --- /dev/null +++ b/Documentation/hwmon/w83793 | |||
@@ -0,0 +1,110 @@ | |||
1 | Kernel driver w83793 | ||
2 | ==================== | ||
3 | |||
4 | Supported chips: | ||
5 | * Winbond W83793G/W83793R | ||
6 | Prefix: 'w83793' | ||
7 | Addresses scanned: I2C 0x2c - 0x2f | ||
8 | Datasheet: Still not published | ||
9 | |||
10 | Authors: | ||
11 | Yuan Mu (Winbond Electronics) | ||
12 | Rudolf Marek <r.marek@assembler.cz> | ||
13 | |||
14 | |||
15 | Module parameters | ||
16 | ----------------- | ||
17 | |||
18 | * reset int | ||
19 | (default 0) | ||
20 | This parameter is not recommended, it will lose motherboard specific | ||
21 | settings. Use 'reset=1' to reset the chip when loading this module. | ||
22 | |||
23 | * force_subclients=bus,caddr,saddr1,saddr2 | ||
24 | This is used to force the i2c addresses for subclients of | ||
25 | a certain chip. Typical usage is `force_subclients=0,0x2f,0x4a,0x4b' | ||
26 | to force the subclients of chip 0x2f on bus 0 to i2c addresses | ||
27 | 0x4a and 0x4b. | ||
28 | |||
29 | |||
30 | Description | ||
31 | ----------- | ||
32 | |||
33 | This driver implements support for Winbond W83793G/W83793R chips. | ||
34 | |||
35 | * Exported features | ||
36 | This driver exports 10 voltage sensors, up to 12 fan tachometer inputs, | ||
37 | 6 remote temperatures, up to 8 sets of PWM fan controls, SmartFan | ||
38 | (automatic fan speed control) on all temperature/PWM combinations, 2 | ||
39 | sets of 6-pin CPU VID input. | ||
40 | |||
41 | * Sensor resolutions | ||
42 | If your motherboard maker used the reference design, the resolution of | ||
43 | voltage0-2 is 2mV, resolution of voltage3/4/5 is 16mV, 8mV for voltage6, | ||
44 | 24mV for voltage7/8. Temp1-4 have a 0.25 degree Celsius resolution, | ||
45 | temp5-6 have a 1 degree Celsiis resolution. | ||
46 | |||
47 | * Temperature sensor types | ||
48 | Temp1-4 have 3 possible types. It can be read from (and written to) | ||
49 | temp[1-4]_type. | ||
50 | - If the value of 0, the related temperature channel stops | ||
51 | monitoring. | ||
52 | - If the value is 3, it starts monitoring using a remote termal diode | ||
53 | (default). | ||
54 | - If the value is 5, it starts monitoring using the temperature sensor | ||
55 | in AMD CPU and get result by AMDSI. | ||
56 | - If the value is 6, it starts monitoring using the temperature sensor | ||
57 | in Intel CPU and get result by PECI. | ||
58 | Temp5-6 can be connected to external thermistors (value of | ||
59 | temp[5-6]_type is 4). They can also be disabled (value is 0). | ||
60 | |||
61 | * Alarm mechanism | ||
62 | For voltage sensors, an alarm triggers if the measured value is below | ||
63 | the low voltage limit or over the high voltage limit. | ||
64 | For temperature sensors, an alarm triggers if the measured value goes | ||
65 | above the high temperature limit, and wears off only after the measured | ||
66 | value drops below the hysteresis value. | ||
67 | For fan sensors, an alarm triggers if the measured value is below the | ||
68 | low speed limit. | ||
69 | |||
70 | * SmartFan/PWM control | ||
71 | If you want to set a pwm fan to manual mode, you just need to make sure it | ||
72 | is not controlled by any temp channel, for example, you want to set fan1 | ||
73 | to manual mode, you need to check the value of temp[1-6]_fan_map, make | ||
74 | sure bit 0 is cleared in the 6 values. And then set the pwm1 value to | ||
75 | control the fan. | ||
76 | |||
77 | Each temperature channel can control all the 8 PWM outputs (by setting the | ||
78 | corresponding bit in tempX_fan_map), you can set the temperature channel | ||
79 | mode using temp[1-6]_pwm_enable, 2 is Thermal Cruise mode and 3 | ||
80 | is the SmartFanII mode. Temperature channels will try to speed up or | ||
81 | slow down all controlled fans, this means one fan can receive different | ||
82 | PWM value requests from different temperature channels, but the chip | ||
83 | will always pick the safest (max) PWM value for each fan. | ||
84 | |||
85 | In Thermal Cruise mode, the chip attempts to keep the temperature at a | ||
86 | predefined value, within a tolerance margin. So if tempX_input > | ||
87 | thermal_cruiseX + toleranceX, the chip will increase the PWM value, | ||
88 | if tempX_input < thermal_cruiseX - toleranceX, the chip will decrease | ||
89 | the PWM value. If the temperature is within the tolerance range, the PWM | ||
90 | value is left unchanged. | ||
91 | |||
92 | SmartFanII works differently, you have to define up to 7 PWM, temperature | ||
93 | trip points, defining a PWM/temperature curve which the chip will follow. | ||
94 | While not fundamentally different from the Thermal Cruise mode, the | ||
95 | implementation is quite different, giving you a finer-grained control. | ||
96 | |||
97 | * Chassis | ||
98 | If the case open alarm triggers, it will stay in this state unless cleared | ||
99 | by any write to the sysfs file "chassis". | ||
100 | |||
101 | * VID and VRM | ||
102 | The VRM version is detected automatically, don't modify the it unless you | ||
103 | *do* know the cpu VRM version and it's not properly detected. | ||
104 | |||
105 | |||
106 | Notes | ||
107 | ----- | ||
108 | |||
109 | Only Fan1-5 and PWM1-3 are guaranteed to always exist, other fan inputs and | ||
110 | PWM outputs may or may not exist depending on the chip pin configuration. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 38df7256d287..dea5b2a6de0a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -277,7 +277,7 @@ S: Maintained | |||
277 | 277 | ||
278 | ALI1563 I2C DRIVER | 278 | ALI1563 I2C DRIVER |
279 | P: Rudolf Marek | 279 | P: Rudolf Marek |
280 | M: r.marek@sh.cvut.cz | 280 | M: r.marek@assembler.cz |
281 | L: i2c@lm-sensors.org | 281 | L: i2c@lm-sensors.org |
282 | S: Maintained | 282 | S: Maintained |
283 | 283 | ||
@@ -296,6 +296,13 @@ L: info-linux@geode.amd.com | |||
296 | W: http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html | 296 | W: http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html |
297 | S: Supported | 297 | S: Supported |
298 | 298 | ||
299 | AMS (Apple Motion Sensor) DRIVER | ||
300 | P: Stelian Pop | ||
301 | M: stelian@popies.net | ||
302 | P: Michael Hanselmann | ||
303 | M: linux-kernel@hansmi.ch | ||
304 | S: Supported | ||
305 | |||
299 | AMSO1100 RNIC DRIVER | 306 | AMSO1100 RNIC DRIVER |
300 | P: Tom Tucker | 307 | P: Tom Tucker |
301 | M: tom@opengridcomputing.com | 308 | M: tom@opengridcomputing.com |
@@ -3436,6 +3443,12 @@ M: bezaur@gmail.com | |||
3436 | L: lm-sensors@lm-sensors.org | 3443 | L: lm-sensors@lm-sensors.org |
3437 | S: Maintained | 3444 | S: Maintained |
3438 | 3445 | ||
3446 | W83793 HARDWARE MONITORING DRIVER | ||
3447 | P: Rudolf Marek | ||
3448 | M: r.marek@assembler.cz | ||
3449 | L: lm-sensors@lm-sensors.org | ||
3450 | S: Maintained | ||
3451 | |||
3439 | W83L51xD SD/MMC CARD INTERFACE DRIVER | 3452 | W83L51xD SD/MMC CARD INTERFACE DRIVER |
3440 | P: Pierre Ossman | 3453 | P: Pierre Ossman |
3441 | M: drzeus-wbsd@drzeus.cx | 3454 | M: drzeus-wbsd@drzeus.cx |
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e76d91906c99..891ef6d0b1bf 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -106,6 +106,31 @@ config SENSORS_K8TEMP | |||
106 | This driver can also be built as a module. If so, the module | 106 | This driver can also be built as a module. If so, the module |
107 | will be called k8temp. | 107 | will be called k8temp. |
108 | 108 | ||
109 | config SENSORS_AMS | ||
110 | tristate "Apple Motion Sensor driver" | ||
111 | depends on HWMON && PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL | ||
112 | help | ||
113 | Support for the motion sensor included in PowerBooks. Includes | ||
114 | implementations for PMU and I2C. | ||
115 | |||
116 | This driver can also be built as a module. If so, the module | ||
117 | will be called ams. | ||
118 | |||
119 | config SENSORS_AMS_PMU | ||
120 | bool "PMU variant" | ||
121 | depends on SENSORS_AMS && ADB_PMU | ||
122 | default y | ||
123 | help | ||
124 | PMU variant of motion sensor, found in late 2005 PowerBooks. | ||
125 | |||
126 | config SENSORS_AMS_I2C | ||
127 | bool "I2C variant" | ||
128 | depends on SENSORS_AMS && I2C | ||
129 | default y | ||
130 | help | ||
131 | I2C variant of motion sensor, found in early 2005 PowerBooks and | ||
132 | iBooks. | ||
133 | |||
109 | config SENSORS_ASB100 | 134 | config SENSORS_ASB100 |
110 | tristate "Asus ASB100 Bach" | 135 | tristate "Asus ASB100 Bach" |
111 | depends on HWMON && I2C && EXPERIMENTAL | 136 | depends on HWMON && I2C && EXPERIMENTAL |
@@ -142,11 +167,12 @@ config SENSORS_DS1621 | |||
142 | will be called ds1621. | 167 | will be called ds1621. |
143 | 168 | ||
144 | config SENSORS_F71805F | 169 | config SENSORS_F71805F |
145 | tristate "Fintek F71805F/FG" | 170 | tristate "Fintek F71805F/FG and F71872F/FG" |
146 | depends on HWMON && EXPERIMENTAL | 171 | depends on HWMON && EXPERIMENTAL |
147 | help | 172 | help |
148 | If you say yes here you get support for hardware monitoring | 173 | If you say yes here you get support for hardware monitoring |
149 | features of the Fintek F71805F/FG chips. | 174 | features of the Fintek F71805F/FG and F71872F/FG Super-I/O |
175 | chips. | ||
150 | 176 | ||
151 | This driver can also be built as a module. If so, the module | 177 | This driver can also be built as a module. If so, the module |
152 | will be called f71805f. | 178 | will be called f71805f. |
@@ -353,6 +379,19 @@ config SENSORS_PC87360 | |||
353 | This driver can also be built as a module. If so, the module | 379 | This driver can also be built as a module. If so, the module |
354 | will be called pc87360. | 380 | will be called pc87360. |
355 | 381 | ||
382 | config SENSORS_PC87427 | ||
383 | tristate "National Semiconductor PC87427" | ||
384 | depends on HWMON && EXPERIMENTAL | ||
385 | help | ||
386 | If you say yes here you get access to the hardware monitoring | ||
387 | functions of the National Semiconductor PC87427 Super-I/O chip. | ||
388 | The chip has two distinct logical devices, one for fan speed | ||
389 | monitoring and control, and one for voltage and temperature | ||
390 | monitoring. Only fan speed monitoring is supported right now. | ||
391 | |||
392 | This driver can also be built as a module. If so, the module | ||
393 | will be called pc87427. | ||
394 | |||
356 | config SENSORS_SIS5595 | 395 | config SENSORS_SIS5595 |
357 | tristate "Silicon Integrated Systems Corp. SiS5595" | 396 | tristate "Silicon Integrated Systems Corp. SiS5595" |
358 | depends on HWMON && I2C && PCI && EXPERIMENTAL | 397 | depends on HWMON && I2C && PCI && EXPERIMENTAL |
@@ -474,6 +513,16 @@ config SENSORS_W83792D | |||
474 | This driver can also be built as a module. If so, the module | 513 | This driver can also be built as a module. If so, the module |
475 | will be called w83792d. | 514 | will be called w83792d. |
476 | 515 | ||
516 | config SENSORS_W83793 | ||
517 | tristate "Winbond W83793" | ||
518 | depends on HWMON && I2C && EXPERIMENTAL | ||
519 | help | ||
520 | If you say yes here you get support for the Winbond W83793 | ||
521 | hardware monitoring chip. | ||
522 | |||
523 | This driver can also be built as a module. If so, the module | ||
524 | will be called w83793. | ||
525 | |||
477 | config SENSORS_W83L785TS | 526 | config SENSORS_W83L785TS |
478 | tristate "Winbond W83L785TS-S" | 527 | tristate "Winbond W83L785TS-S" |
479 | depends on HWMON && I2C && EXPERIMENTAL | 528 | depends on HWMON && I2C && EXPERIMENTAL |
@@ -527,6 +576,9 @@ config SENSORS_HDAPS | |||
527 | This driver also provides an absolute input class device, allowing | 576 | This driver also provides an absolute input class device, allowing |
528 | the laptop to act as a pinball machine-esque joystick. | 577 | the laptop to act as a pinball machine-esque joystick. |
529 | 578 | ||
579 | If your ThinkPad is not recognized by the driver, please update to latest | ||
580 | BIOS. This is especially the case for some R52 ThinkPads. | ||
581 | |||
530 | Say Y here if you have an applicable laptop and want to experience | 582 | Say Y here if you have an applicable laptop and want to experience |
531 | the awesome power of hdaps. | 583 | the awesome power of hdaps. |
532 | 584 | ||
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index af01cc64f7d2..31661124271e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_HWMON_VID) += hwmon-vid.o | |||
9 | obj-$(CONFIG_SENSORS_ASB100) += asb100.o | 9 | obj-$(CONFIG_SENSORS_ASB100) += asb100.o |
10 | obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o | 10 | obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o |
11 | obj-$(CONFIG_SENSORS_W83792D) += w83792d.o | 11 | obj-$(CONFIG_SENSORS_W83792D) += w83792d.o |
12 | obj-$(CONFIG_SENSORS_W83793) += w83793.o | ||
12 | obj-$(CONFIG_SENSORS_W83781D) += w83781d.o | 13 | obj-$(CONFIG_SENSORS_W83781D) += w83781d.o |
13 | obj-$(CONFIG_SENSORS_W83791D) += w83791d.o | 14 | obj-$(CONFIG_SENSORS_W83791D) += w83791d.o |
14 | 15 | ||
@@ -18,6 +19,7 @@ obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o | |||
18 | obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o | 19 | obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o |
19 | obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o | 20 | obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o |
20 | obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o | 21 | obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o |
22 | obj-$(CONFIG_SENSORS_AMS) += ams/ | ||
21 | obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o | 23 | obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o |
22 | obj-$(CONFIG_SENSORS_DS1621) += ds1621.o | 24 | obj-$(CONFIG_SENSORS_DS1621) += ds1621.o |
23 | obj-$(CONFIG_SENSORS_F71805F) += f71805f.o | 25 | obj-$(CONFIG_SENSORS_F71805F) += f71805f.o |
@@ -41,6 +43,7 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o | |||
41 | obj-$(CONFIG_SENSORS_LM92) += lm92.o | 43 | obj-$(CONFIG_SENSORS_LM92) += lm92.o |
42 | obj-$(CONFIG_SENSORS_MAX1619) += max1619.o | 44 | obj-$(CONFIG_SENSORS_MAX1619) += max1619.o |
43 | obj-$(CONFIG_SENSORS_PC87360) += pc87360.o | 45 | obj-$(CONFIG_SENSORS_PC87360) += pc87360.o |
46 | obj-$(CONFIG_SENSORS_PC87427) += pc87427.o | ||
44 | obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o | 47 | obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o |
45 | obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o | 48 | obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o |
46 | obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o | 49 | obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o |
diff --git a/drivers/hwmon/ams/Makefile b/drivers/hwmon/ams/Makefile new file mode 100644 index 000000000000..41c95b2089dc --- /dev/null +++ b/drivers/hwmon/ams/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Makefile for Apple Motion Sensor driver | ||
3 | # | ||
4 | |||
5 | ams-y := ams-core.o ams-input.o | ||
6 | ams-$(CONFIG_SENSORS_AMS_PMU) += ams-pmu.o | ||
7 | ams-$(CONFIG_SENSORS_AMS_I2C) += ams-i2c.o | ||
8 | obj-$(CONFIG_SENSORS_AMS) += ams.o | ||
diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c new file mode 100644 index 000000000000..f1f0f5d0442c --- /dev/null +++ b/drivers/hwmon/ams/ams-core.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* | ||
2 | * Apple Motion Sensor driver | ||
3 | * | ||
4 | * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | ||
5 | * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <asm/pmac_pfunc.h> | ||
28 | #include <asm/of_platform.h> | ||
29 | |||
30 | #include "ams.h" | ||
31 | |||
32 | /* There is only one motion sensor per machine */ | ||
33 | struct ams ams_info; | ||
34 | |||
35 | static unsigned int verbose; | ||
36 | module_param(verbose, bool, 0644); | ||
37 | MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output"); | ||
38 | |||
39 | /* Call with ams_info.lock held! */ | ||
40 | void ams_sensors(s8 *x, s8 *y, s8 *z) | ||
41 | { | ||
42 | u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2; | ||
43 | |||
44 | if (orient & 0x80) | ||
45 | /* X and Y swapped */ | ||
46 | ams_info.get_xyz(y, x, z); | ||
47 | else | ||
48 | ams_info.get_xyz(x, y, z); | ||
49 | |||
50 | if (orient & 0x04) | ||
51 | *z = ~(*z); | ||
52 | if (orient & 0x02) | ||
53 | *y = ~(*y); | ||
54 | if (orient & 0x01) | ||
55 | *x = ~(*x); | ||
56 | } | ||
57 | |||
58 | static ssize_t ams_show_current(struct device *dev, | ||
59 | struct device_attribute *attr, char *buf) | ||
60 | { | ||
61 | s8 x, y, z; | ||
62 | |||
63 | mutex_lock(&ams_info.lock); | ||
64 | ams_sensors(&x, &y, &z); | ||
65 | mutex_unlock(&ams_info.lock); | ||
66 | |||
67 | return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z); | ||
68 | } | ||
69 | |||
70 | static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL); | ||
71 | |||
72 | static void ams_handle_irq(void *data) | ||
73 | { | ||
74 | enum ams_irq irq = *((enum ams_irq *)data); | ||
75 | |||
76 | spin_lock(&ams_info.irq_lock); | ||
77 | |||
78 | ams_info.worker_irqs |= irq; | ||
79 | schedule_work(&ams_info.worker); | ||
80 | |||
81 | spin_unlock(&ams_info.irq_lock); | ||
82 | } | ||
83 | |||
84 | static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL; | ||
85 | static struct pmf_irq_client ams_freefall_client = { | ||
86 | .owner = THIS_MODULE, | ||
87 | .handler = ams_handle_irq, | ||
88 | .data = &ams_freefall_irq_data, | ||
89 | }; | ||
90 | |||
91 | static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK; | ||
92 | static struct pmf_irq_client ams_shock_client = { | ||
93 | .owner = THIS_MODULE, | ||
94 | .handler = ams_handle_irq, | ||
95 | .data = &ams_shock_irq_data, | ||
96 | }; | ||
97 | |||
98 | /* Once hard disk parking is implemented in the kernel, this function can | ||
99 | * trigger it. | ||
100 | */ | ||
101 | static void ams_worker(struct work_struct *work) | ||
102 | { | ||
103 | mutex_lock(&ams_info.lock); | ||
104 | |||
105 | if (ams_info.has_device) { | ||
106 | unsigned long flags; | ||
107 | |||
108 | spin_lock_irqsave(&ams_info.irq_lock, flags); | ||
109 | |||
110 | if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { | ||
111 | if (verbose) | ||
112 | printk(KERN_INFO "ams: freefall detected!\n"); | ||
113 | |||
114 | ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; | ||
115 | |||
116 | /* we must call this with interrupts enabled */ | ||
117 | spin_unlock_irqrestore(&ams_info.irq_lock, flags); | ||
118 | ams_info.clear_irq(AMS_IRQ_FREEFALL); | ||
119 | spin_lock_irqsave(&ams_info.irq_lock, flags); | ||
120 | } | ||
121 | |||
122 | if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { | ||
123 | if (verbose) | ||
124 | printk(KERN_INFO "ams: shock detected!\n"); | ||
125 | |||
126 | ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; | ||
127 | |||
128 | /* we must call this with interrupts enabled */ | ||
129 | spin_unlock_irqrestore(&ams_info.irq_lock, flags); | ||
130 | ams_info.clear_irq(AMS_IRQ_SHOCK); | ||
131 | spin_lock_irqsave(&ams_info.irq_lock, flags); | ||
132 | } | ||
133 | |||
134 | spin_unlock_irqrestore(&ams_info.irq_lock, flags); | ||
135 | } | ||
136 | |||
137 | mutex_unlock(&ams_info.lock); | ||
138 | } | ||
139 | |||
140 | /* Call with ams_info.lock held! */ | ||
141 | int ams_sensor_attach(void) | ||
142 | { | ||
143 | int result; | ||
144 | u32 *prop; | ||
145 | |||
146 | /* Get orientation */ | ||
147 | prop = (u32*)get_property(ams_info.of_node, "orientation", NULL); | ||
148 | if (!prop) | ||
149 | return -ENODEV; | ||
150 | ams_info.orient1 = *prop; | ||
151 | ams_info.orient2 = *(prop + 1); | ||
152 | |||
153 | /* Register freefall interrupt handler */ | ||
154 | result = pmf_register_irq_client(ams_info.of_node, | ||
155 | "accel-int-1", | ||
156 | &ams_freefall_client); | ||
157 | if (result < 0) | ||
158 | return -ENODEV; | ||
159 | |||
160 | /* Reset saved irqs */ | ||
161 | ams_info.worker_irqs = 0; | ||
162 | |||
163 | /* Register shock interrupt handler */ | ||
164 | result = pmf_register_irq_client(ams_info.of_node, | ||
165 | "accel-int-2", | ||
166 | &ams_shock_client); | ||
167 | if (result < 0) | ||
168 | goto release_freefall; | ||
169 | |||
170 | /* Create device */ | ||
171 | ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL); | ||
172 | if (!ams_info.of_dev) { | ||
173 | result = -ENODEV; | ||
174 | goto release_shock; | ||
175 | } | ||
176 | |||
177 | /* Create attributes */ | ||
178 | result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current); | ||
179 | if (result) | ||
180 | goto release_of; | ||
181 | |||
182 | ams_info.vflag = !!(ams_info.get_vendor() & 0x10); | ||
183 | |||
184 | /* Init input device */ | ||
185 | result = ams_input_init(); | ||
186 | if (result) | ||
187 | goto release_device_file; | ||
188 | |||
189 | return result; | ||
190 | release_device_file: | ||
191 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); | ||
192 | release_of: | ||
193 | of_device_unregister(ams_info.of_dev); | ||
194 | release_shock: | ||
195 | pmf_unregister_irq_client(&ams_shock_client); | ||
196 | release_freefall: | ||
197 | pmf_unregister_irq_client(&ams_freefall_client); | ||
198 | return result; | ||
199 | } | ||
200 | |||
201 | int __init ams_init(void) | ||
202 | { | ||
203 | struct device_node *np; | ||
204 | |||
205 | spin_lock_init(&ams_info.irq_lock); | ||
206 | mutex_init(&ams_info.lock); | ||
207 | INIT_WORK(&ams_info.worker, ams_worker); | ||
208 | |||
209 | #ifdef CONFIG_SENSORS_AMS_I2C | ||
210 | np = of_find_node_by_name(NULL, "accelerometer"); | ||
211 | if (np && device_is_compatible(np, "AAPL,accelerometer_1")) | ||
212 | /* Found I2C motion sensor */ | ||
213 | return ams_i2c_init(np); | ||
214 | #endif | ||
215 | |||
216 | #ifdef CONFIG_SENSORS_AMS_PMU | ||
217 | np = of_find_node_by_name(NULL, "sms"); | ||
218 | if (np && device_is_compatible(np, "sms")) | ||
219 | /* Found PMU motion sensor */ | ||
220 | return ams_pmu_init(np); | ||
221 | #endif | ||
222 | |||
223 | printk(KERN_ERR "ams: No motion sensor found.\n"); | ||
224 | |||
225 | return -ENODEV; | ||
226 | } | ||
227 | |||
228 | void ams_exit(void) | ||
229 | { | ||
230 | mutex_lock(&ams_info.lock); | ||
231 | |||
232 | if (ams_info.has_device) { | ||
233 | /* Remove input device */ | ||
234 | ams_input_exit(); | ||
235 | |||
236 | /* Shut down implementation */ | ||
237 | ams_info.exit(); | ||
238 | |||
239 | /* Flush interrupt worker | ||
240 | * | ||
241 | * We do this after ams_info.exit(), because an interrupt might | ||
242 | * have arrived before disabling them. | ||
243 | */ | ||
244 | flush_scheduled_work(); | ||
245 | |||
246 | /* Remove attributes */ | ||
247 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); | ||
248 | |||
249 | /* Remove device */ | ||
250 | of_device_unregister(ams_info.of_dev); | ||
251 | |||
252 | /* Remove handler */ | ||
253 | pmf_unregister_irq_client(&ams_shock_client); | ||
254 | pmf_unregister_irq_client(&ams_freefall_client); | ||
255 | } | ||
256 | |||
257 | mutex_unlock(&ams_info.lock); | ||
258 | } | ||
259 | |||
260 | MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); | ||
261 | MODULE_DESCRIPTION("Apple Motion Sensor driver"); | ||
262 | MODULE_LICENSE("GPL"); | ||
263 | |||
264 | module_init(ams_init); | ||
265 | module_exit(ams_exit); | ||
diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c new file mode 100644 index 000000000000..0d24bdfea53e --- /dev/null +++ b/drivers/hwmon/ams/ams-i2c.c | |||
@@ -0,0 +1,299 @@ | |||
1 | /* | ||
2 | * Apple Motion Sensor driver (I2C variant) | ||
3 | * | ||
4 | * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | ||
5 | * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | ||
6 | * | ||
7 | * Clean room implementation based on the reverse engineered Mac OS X driver by | ||
8 | * Johannes Berg <johannes@sipsolutions.net>, documentation available at | ||
9 | * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/delay.h> | ||
22 | |||
23 | #include "ams.h" | ||
24 | |||
25 | /* AMS registers */ | ||
26 | #define AMS_COMMAND 0x00 /* command register */ | ||
27 | #define AMS_STATUS 0x01 /* status register */ | ||
28 | #define AMS_CTRL1 0x02 /* read control 1 (number of values) */ | ||
29 | #define AMS_CTRL2 0x03 /* read control 2 (offset?) */ | ||
30 | #define AMS_CTRL3 0x04 /* read control 3 (size of each value?) */ | ||
31 | #define AMS_DATA1 0x05 /* read data 1 */ | ||
32 | #define AMS_DATA2 0x06 /* read data 2 */ | ||
33 | #define AMS_DATA3 0x07 /* read data 3 */ | ||
34 | #define AMS_DATA4 0x08 /* read data 4 */ | ||
35 | #define AMS_DATAX 0x20 /* data X */ | ||
36 | #define AMS_DATAY 0x21 /* data Y */ | ||
37 | #define AMS_DATAZ 0x22 /* data Z */ | ||
38 | #define AMS_FREEFALL 0x24 /* freefall int control */ | ||
39 | #define AMS_SHOCK 0x25 /* shock int control */ | ||
40 | #define AMS_SENSLOW 0x26 /* sensitivity low limit */ | ||
41 | #define AMS_SENSHIGH 0x27 /* sensitivity high limit */ | ||
42 | #define AMS_CTRLX 0x28 /* control X */ | ||
43 | #define AMS_CTRLY 0x29 /* control Y */ | ||
44 | #define AMS_CTRLZ 0x2A /* control Z */ | ||
45 | #define AMS_UNKNOWN1 0x2B /* unknown 1 */ | ||
46 | #define AMS_UNKNOWN2 0x2C /* unknown 2 */ | ||
47 | #define AMS_UNKNOWN3 0x2D /* unknown 3 */ | ||
48 | #define AMS_VENDOR 0x2E /* vendor */ | ||
49 | |||
50 | /* AMS commands - use with the AMS_COMMAND register */ | ||
51 | enum ams_i2c_cmd { | ||
52 | AMS_CMD_NOOP = 0, | ||
53 | AMS_CMD_VERSION, | ||
54 | AMS_CMD_READMEM, | ||
55 | AMS_CMD_WRITEMEM, | ||
56 | AMS_CMD_ERASEMEM, | ||
57 | AMS_CMD_READEE, | ||
58 | AMS_CMD_WRITEEE, | ||
59 | AMS_CMD_RESET, | ||
60 | AMS_CMD_START, | ||
61 | }; | ||
62 | |||
63 | static int ams_i2c_attach(struct i2c_adapter *adapter); | ||
64 | static int ams_i2c_detach(struct i2c_adapter *adapter); | ||
65 | |||
66 | static struct i2c_driver ams_i2c_driver = { | ||
67 | .driver = { | ||
68 | .name = "ams", | ||
69 | .owner = THIS_MODULE, | ||
70 | }, | ||
71 | .attach_adapter = ams_i2c_attach, | ||
72 | .detach_adapter = ams_i2c_detach, | ||
73 | }; | ||
74 | |||
75 | static s32 ams_i2c_read(u8 reg) | ||
76 | { | ||
77 | return i2c_smbus_read_byte_data(&ams_info.i2c_client, reg); | ||
78 | } | ||
79 | |||
80 | static int ams_i2c_write(u8 reg, u8 value) | ||
81 | { | ||
82 | return i2c_smbus_write_byte_data(&ams_info.i2c_client, reg, value); | ||
83 | } | ||
84 | |||
85 | static int ams_i2c_cmd(enum ams_i2c_cmd cmd) | ||
86 | { | ||
87 | s32 result; | ||
88 | int remaining = HZ / 20; | ||
89 | |||
90 | ams_i2c_write(AMS_COMMAND, cmd); | ||
91 | mdelay(5); | ||
92 | |||
93 | while (remaining) { | ||
94 | result = ams_i2c_read(AMS_COMMAND); | ||
95 | if (result == 0 || result & 0x80) | ||
96 | return 0; | ||
97 | |||
98 | remaining = schedule_timeout(remaining); | ||
99 | } | ||
100 | |||
101 | return -1; | ||
102 | } | ||
103 | |||
104 | static void ams_i2c_set_irq(enum ams_irq reg, char enable) | ||
105 | { | ||
106 | if (reg & AMS_IRQ_FREEFALL) { | ||
107 | u8 val = ams_i2c_read(AMS_CTRLX); | ||
108 | if (enable) | ||
109 | val |= 0x80; | ||
110 | else | ||
111 | val &= ~0x80; | ||
112 | ams_i2c_write(AMS_CTRLX, val); | ||
113 | } | ||
114 | |||
115 | if (reg & AMS_IRQ_SHOCK) { | ||
116 | u8 val = ams_i2c_read(AMS_CTRLY); | ||
117 | if (enable) | ||
118 | val |= 0x80; | ||
119 | else | ||
120 | val &= ~0x80; | ||
121 | ams_i2c_write(AMS_CTRLY, val); | ||
122 | } | ||
123 | |||
124 | if (reg & AMS_IRQ_GLOBAL) { | ||
125 | u8 val = ams_i2c_read(AMS_CTRLZ); | ||
126 | if (enable) | ||
127 | val |= 0x80; | ||
128 | else | ||
129 | val &= ~0x80; | ||
130 | ams_i2c_write(AMS_CTRLZ, val); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | static void ams_i2c_clear_irq(enum ams_irq reg) | ||
135 | { | ||
136 | if (reg & AMS_IRQ_FREEFALL) | ||
137 | ams_i2c_write(AMS_FREEFALL, 0); | ||
138 | |||
139 | if (reg & AMS_IRQ_SHOCK) | ||
140 | ams_i2c_write(AMS_SHOCK, 0); | ||
141 | } | ||
142 | |||
143 | static u8 ams_i2c_get_vendor(void) | ||
144 | { | ||
145 | return ams_i2c_read(AMS_VENDOR); | ||
146 | } | ||
147 | |||
148 | static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z) | ||
149 | { | ||
150 | *x = ams_i2c_read(AMS_DATAX); | ||
151 | *y = ams_i2c_read(AMS_DATAY); | ||
152 | *z = ams_i2c_read(AMS_DATAZ); | ||
153 | } | ||
154 | |||
155 | static int ams_i2c_attach(struct i2c_adapter *adapter) | ||
156 | { | ||
157 | unsigned long bus; | ||
158 | int vmaj, vmin; | ||
159 | int result; | ||
160 | |||
161 | /* There can be only one */ | ||
162 | if (unlikely(ams_info.has_device)) | ||
163 | return -ENODEV; | ||
164 | |||
165 | if (strncmp(adapter->name, "uni-n", 5)) | ||
166 | return -ENODEV; | ||
167 | |||
168 | bus = simple_strtoul(adapter->name + 6, NULL, 10); | ||
169 | if (bus != ams_info.i2c_bus) | ||
170 | return -ENODEV; | ||
171 | |||
172 | ams_info.i2c_client.addr = ams_info.i2c_address; | ||
173 | ams_info.i2c_client.adapter = adapter; | ||
174 | ams_info.i2c_client.driver = &ams_i2c_driver; | ||
175 | strcpy(ams_info.i2c_client.name, "Apple Motion Sensor"); | ||
176 | |||
177 | if (ams_i2c_cmd(AMS_CMD_RESET)) { | ||
178 | printk(KERN_INFO "ams: Failed to reset the device\n"); | ||
179 | return -ENODEV; | ||
180 | } | ||
181 | |||
182 | if (ams_i2c_cmd(AMS_CMD_START)) { | ||
183 | printk(KERN_INFO "ams: Failed to start the device\n"); | ||
184 | return -ENODEV; | ||
185 | } | ||
186 | |||
187 | /* get version/vendor information */ | ||
188 | ams_i2c_write(AMS_CTRL1, 0x02); | ||
189 | ams_i2c_write(AMS_CTRL2, 0x85); | ||
190 | ams_i2c_write(AMS_CTRL3, 0x01); | ||
191 | |||
192 | ams_i2c_cmd(AMS_CMD_READMEM); | ||
193 | |||
194 | vmaj = ams_i2c_read(AMS_DATA1); | ||
195 | vmin = ams_i2c_read(AMS_DATA2); | ||
196 | if (vmaj != 1 || vmin != 52) { | ||
197 | printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n", | ||
198 | vmaj, vmin); | ||
199 | return -ENODEV; | ||
200 | } | ||
201 | |||
202 | ams_i2c_cmd(AMS_CMD_VERSION); | ||
203 | |||
204 | vmaj = ams_i2c_read(AMS_DATA1); | ||
205 | vmin = ams_i2c_read(AMS_DATA2); | ||
206 | if (vmaj != 0 || vmin != 1) { | ||
207 | printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n", | ||
208 | vmaj, vmin); | ||
209 | return -ENODEV; | ||
210 | } | ||
211 | |||
212 | /* Disable interrupts */ | ||
213 | ams_i2c_set_irq(AMS_IRQ_ALL, 0); | ||
214 | |||
215 | result = ams_sensor_attach(); | ||
216 | if (result < 0) | ||
217 | return result; | ||
218 | |||
219 | /* Set default values */ | ||
220 | ams_i2c_write(AMS_SENSLOW, 0x15); | ||
221 | ams_i2c_write(AMS_SENSHIGH, 0x60); | ||
222 | ams_i2c_write(AMS_CTRLX, 0x08); | ||
223 | ams_i2c_write(AMS_CTRLY, 0x0F); | ||
224 | ams_i2c_write(AMS_CTRLZ, 0x4F); | ||
225 | ams_i2c_write(AMS_UNKNOWN1, 0x14); | ||
226 | |||
227 | /* Clear interrupts */ | ||
228 | ams_i2c_clear_irq(AMS_IRQ_ALL); | ||
229 | |||
230 | ams_info.has_device = 1; | ||
231 | |||
232 | /* Enable interrupts */ | ||
233 | ams_i2c_set_irq(AMS_IRQ_ALL, 1); | ||
234 | |||
235 | printk(KERN_INFO "ams: Found I2C based motion sensor\n"); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int ams_i2c_detach(struct i2c_adapter *adapter) | ||
241 | { | ||
242 | if (ams_info.has_device) { | ||
243 | /* Disable interrupts */ | ||
244 | ams_i2c_set_irq(AMS_IRQ_ALL, 0); | ||
245 | |||
246 | /* Clear interrupts */ | ||
247 | ams_i2c_clear_irq(AMS_IRQ_ALL); | ||
248 | |||
249 | printk(KERN_INFO "ams: Unloading\n"); | ||
250 | |||
251 | ams_info.has_device = 0; | ||
252 | } | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static void ams_i2c_exit(void) | ||
258 | { | ||
259 | i2c_del_driver(&ams_i2c_driver); | ||
260 | } | ||
261 | |||
262 | int __init ams_i2c_init(struct device_node *np) | ||
263 | { | ||
264 | char *tmp_bus; | ||
265 | int result; | ||
266 | u32 *prop; | ||
267 | |||
268 | mutex_lock(&ams_info.lock); | ||
269 | |||
270 | /* Set implementation stuff */ | ||
271 | ams_info.of_node = np; | ||
272 | ams_info.exit = ams_i2c_exit; | ||
273 | ams_info.get_vendor = ams_i2c_get_vendor; | ||
274 | ams_info.get_xyz = ams_i2c_get_xyz; | ||
275 | ams_info.clear_irq = ams_i2c_clear_irq; | ||
276 | ams_info.bustype = BUS_I2C; | ||
277 | |||
278 | /* look for bus either using "reg" or by path */ | ||
279 | prop = (u32*)get_property(ams_info.of_node, "reg", NULL); | ||
280 | if (!prop) { | ||
281 | result = -ENODEV; | ||
282 | |||
283 | goto exit; | ||
284 | } | ||
285 | |||
286 | tmp_bus = strstr(ams_info.of_node->full_name, "/i2c-bus@"); | ||
287 | if (tmp_bus) | ||
288 | ams_info.i2c_bus = *(tmp_bus + 9) - '0'; | ||
289 | else | ||
290 | ams_info.i2c_bus = ((*prop) >> 8) & 0x0f; | ||
291 | ams_info.i2c_address = ((*prop) & 0xff) >> 1; | ||
292 | |||
293 | result = i2c_add_driver(&ams_i2c_driver); | ||
294 | |||
295 | exit: | ||
296 | mutex_unlock(&ams_info.lock); | ||
297 | |||
298 | return result; | ||
299 | } | ||
diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c new file mode 100644 index 000000000000..f126aa485134 --- /dev/null +++ b/drivers/hwmon/ams/ams-input.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * Apple Motion Sensor driver (joystick emulation) | ||
3 | * | ||
4 | * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | ||
5 | * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | |||
15 | #include <linux/types.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/delay.h> | ||
19 | |||
20 | #include "ams.h" | ||
21 | |||
22 | static unsigned int joystick; | ||
23 | module_param(joystick, bool, 0644); | ||
24 | MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); | ||
25 | |||
26 | static unsigned int invert; | ||
27 | module_param(invert, bool, 0644); | ||
28 | MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); | ||
29 | |||
30 | static int ams_input_kthread(void *data) | ||
31 | { | ||
32 | s8 x, y, z; | ||
33 | |||
34 | while (!kthread_should_stop()) { | ||
35 | mutex_lock(&ams_info.lock); | ||
36 | |||
37 | ams_sensors(&x, &y, &z); | ||
38 | |||
39 | x -= ams_info.xcalib; | ||
40 | y -= ams_info.ycalib; | ||
41 | z -= ams_info.zcalib; | ||
42 | |||
43 | input_report_abs(ams_info.idev, ABS_X, invert ? -x : x); | ||
44 | input_report_abs(ams_info.idev, ABS_Y, invert ? -y : y); | ||
45 | input_report_abs(ams_info.idev, ABS_Z, z); | ||
46 | |||
47 | input_sync(ams_info.idev); | ||
48 | |||
49 | mutex_unlock(&ams_info.lock); | ||
50 | |||
51 | msleep(25); | ||
52 | } | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static int ams_input_open(struct input_dev *dev) | ||
58 | { | ||
59 | ams_info.kthread = kthread_run(ams_input_kthread, NULL, "kams"); | ||
60 | return IS_ERR(ams_info.kthread) ? PTR_ERR(ams_info.kthread) : 0; | ||
61 | } | ||
62 | |||
63 | static void ams_input_close(struct input_dev *dev) | ||
64 | { | ||
65 | kthread_stop(ams_info.kthread); | ||
66 | } | ||
67 | |||
68 | /* Call with ams_info.lock held! */ | ||
69 | static void ams_input_enable(void) | ||
70 | { | ||
71 | s8 x, y, z; | ||
72 | |||
73 | if (ams_info.idev) | ||
74 | return; | ||
75 | |||
76 | ams_sensors(&x, &y, &z); | ||
77 | ams_info.xcalib = x; | ||
78 | ams_info.ycalib = y; | ||
79 | ams_info.zcalib = z; | ||
80 | |||
81 | ams_info.idev = input_allocate_device(); | ||
82 | if (!ams_info.idev) | ||
83 | return; | ||
84 | |||
85 | ams_info.idev->name = "Apple Motion Sensor"; | ||
86 | ams_info.idev->id.bustype = ams_info.bustype; | ||
87 | ams_info.idev->id.vendor = 0; | ||
88 | ams_info.idev->open = ams_input_open; | ||
89 | ams_info.idev->close = ams_input_close; | ||
90 | ams_info.idev->cdev.dev = &ams_info.of_dev->dev; | ||
91 | |||
92 | input_set_abs_params(ams_info.idev, ABS_X, -50, 50, 3, 0); | ||
93 | input_set_abs_params(ams_info.idev, ABS_Y, -50, 50, 3, 0); | ||
94 | input_set_abs_params(ams_info.idev, ABS_Z, -50, 50, 3, 0); | ||
95 | |||
96 | set_bit(EV_ABS, ams_info.idev->evbit); | ||
97 | set_bit(EV_KEY, ams_info.idev->evbit); | ||
98 | set_bit(BTN_TOUCH, ams_info.idev->keybit); | ||
99 | |||
100 | if (input_register_device(ams_info.idev)) { | ||
101 | input_free_device(ams_info.idev); | ||
102 | ams_info.idev = NULL; | ||
103 | return; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | /* Call with ams_info.lock held! */ | ||
108 | static void ams_input_disable(void) | ||
109 | { | ||
110 | if (ams_info.idev) { | ||
111 | input_unregister_device(ams_info.idev); | ||
112 | ams_info.idev = NULL; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | static ssize_t ams_input_show_joystick(struct device *dev, | ||
117 | struct device_attribute *attr, char *buf) | ||
118 | { | ||
119 | return sprintf(buf, "%d\n", joystick); | ||
120 | } | ||
121 | |||
122 | static ssize_t ams_input_store_joystick(struct device *dev, | ||
123 | struct device_attribute *attr, const char *buf, size_t count) | ||
124 | { | ||
125 | if (sscanf(buf, "%d\n", &joystick) != 1) | ||
126 | return -EINVAL; | ||
127 | |||
128 | mutex_lock(&ams_info.lock); | ||
129 | |||
130 | if (joystick) | ||
131 | ams_input_enable(); | ||
132 | else | ||
133 | ams_input_disable(); | ||
134 | |||
135 | mutex_unlock(&ams_info.lock); | ||
136 | |||
137 | return count; | ||
138 | } | ||
139 | |||
140 | static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, | ||
141 | ams_input_show_joystick, ams_input_store_joystick); | ||
142 | |||
143 | /* Call with ams_info.lock held! */ | ||
144 | int ams_input_init(void) | ||
145 | { | ||
146 | int result; | ||
147 | |||
148 | result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); | ||
149 | |||
150 | if (!result && joystick) | ||
151 | ams_input_enable(); | ||
152 | return result; | ||
153 | } | ||
154 | |||
155 | /* Call with ams_info.lock held! */ | ||
156 | void ams_input_exit() | ||
157 | { | ||
158 | ams_input_disable(); | ||
159 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); | ||
160 | } | ||
diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c new file mode 100644 index 000000000000..4636ae031a53 --- /dev/null +++ b/drivers/hwmon/ams/ams-pmu.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * Apple Motion Sensor driver (PMU variant) | ||
3 | * | ||
4 | * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/adb.h> | ||
17 | #include <linux/pmu.h> | ||
18 | |||
19 | #include "ams.h" | ||
20 | |||
21 | /* Attitude */ | ||
22 | #define AMS_X 0x00 | ||
23 | #define AMS_Y 0x01 | ||
24 | #define AMS_Z 0x02 | ||
25 | |||
26 | /* Not exactly known, maybe chip vendor */ | ||
27 | #define AMS_VENDOR 0x03 | ||
28 | |||
29 | /* Freefall registers */ | ||
30 | #define AMS_FF_CLEAR 0x04 | ||
31 | #define AMS_FF_ENABLE 0x05 | ||
32 | #define AMS_FF_LOW_LIMIT 0x06 | ||
33 | #define AMS_FF_DEBOUNCE 0x07 | ||
34 | |||
35 | /* Shock registers */ | ||
36 | #define AMS_SHOCK_CLEAR 0x08 | ||
37 | #define AMS_SHOCK_ENABLE 0x09 | ||
38 | #define AMS_SHOCK_HIGH_LIMIT 0x0a | ||
39 | #define AMS_SHOCK_DEBOUNCE 0x0b | ||
40 | |||
41 | /* Global interrupt and power control register */ | ||
42 | #define AMS_CONTROL 0x0c | ||
43 | |||
44 | static u8 ams_pmu_cmd; | ||
45 | |||
46 | static void ams_pmu_req_complete(struct adb_request *req) | ||
47 | { | ||
48 | complete((struct completion *)req->arg); | ||
49 | } | ||
50 | |||
51 | /* Only call this function from task context */ | ||
52 | static void ams_pmu_set_register(u8 reg, u8 value) | ||
53 | { | ||
54 | static struct adb_request req; | ||
55 | DECLARE_COMPLETION(req_complete); | ||
56 | |||
57 | req.arg = &req_complete; | ||
58 | if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value)) | ||
59 | return; | ||
60 | |||
61 | wait_for_completion(&req_complete); | ||
62 | } | ||
63 | |||
64 | /* Only call this function from task context */ | ||
65 | static u8 ams_pmu_get_register(u8 reg) | ||
66 | { | ||
67 | static struct adb_request req; | ||
68 | DECLARE_COMPLETION(req_complete); | ||
69 | |||
70 | req.arg = &req_complete; | ||
71 | if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg)) | ||
72 | return 0; | ||
73 | |||
74 | wait_for_completion(&req_complete); | ||
75 | |||
76 | if (req.reply_len > 0) | ||
77 | return req.reply[0]; | ||
78 | else | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /* Enables or disables the specified interrupts */ | ||
83 | static void ams_pmu_set_irq(enum ams_irq reg, char enable) | ||
84 | { | ||
85 | if (reg & AMS_IRQ_FREEFALL) { | ||
86 | u8 val = ams_pmu_get_register(AMS_FF_ENABLE); | ||
87 | if (enable) | ||
88 | val |= 0x80; | ||
89 | else | ||
90 | val &= ~0x80; | ||
91 | ams_pmu_set_register(AMS_FF_ENABLE, val); | ||
92 | } | ||
93 | |||
94 | if (reg & AMS_IRQ_SHOCK) { | ||
95 | u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE); | ||
96 | if (enable) | ||
97 | val |= 0x80; | ||
98 | else | ||
99 | val &= ~0x80; | ||
100 | ams_pmu_set_register(AMS_SHOCK_ENABLE, val); | ||
101 | } | ||
102 | |||
103 | if (reg & AMS_IRQ_GLOBAL) { | ||
104 | u8 val = ams_pmu_get_register(AMS_CONTROL); | ||
105 | if (enable) | ||
106 | val |= 0x80; | ||
107 | else | ||
108 | val &= ~0x80; | ||
109 | ams_pmu_set_register(AMS_CONTROL, val); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static void ams_pmu_clear_irq(enum ams_irq reg) | ||
114 | { | ||
115 | if (reg & AMS_IRQ_FREEFALL) | ||
116 | ams_pmu_set_register(AMS_FF_CLEAR, 0x00); | ||
117 | |||
118 | if (reg & AMS_IRQ_SHOCK) | ||
119 | ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00); | ||
120 | } | ||
121 | |||
122 | static u8 ams_pmu_get_vendor(void) | ||
123 | { | ||
124 | return ams_pmu_get_register(AMS_VENDOR); | ||
125 | } | ||
126 | |||
127 | static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z) | ||
128 | { | ||
129 | *x = ams_pmu_get_register(AMS_X); | ||
130 | *y = ams_pmu_get_register(AMS_Y); | ||
131 | *z = ams_pmu_get_register(AMS_Z); | ||
132 | } | ||
133 | |||
134 | static void ams_pmu_exit(void) | ||
135 | { | ||
136 | /* Disable interrupts */ | ||
137 | ams_pmu_set_irq(AMS_IRQ_ALL, 0); | ||
138 | |||
139 | /* Clear interrupts */ | ||
140 | ams_pmu_clear_irq(AMS_IRQ_ALL); | ||
141 | |||
142 | ams_info.has_device = 0; | ||
143 | |||
144 | printk(KERN_INFO "ams: Unloading\n"); | ||
145 | } | ||
146 | |||
147 | int __init ams_pmu_init(struct device_node *np) | ||
148 | { | ||
149 | u32 *prop; | ||
150 | int result; | ||
151 | |||
152 | mutex_lock(&ams_info.lock); | ||
153 | |||
154 | /* Set implementation stuff */ | ||
155 | ams_info.of_node = np; | ||
156 | ams_info.exit = ams_pmu_exit; | ||
157 | ams_info.get_vendor = ams_pmu_get_vendor; | ||
158 | ams_info.get_xyz = ams_pmu_get_xyz; | ||
159 | ams_info.clear_irq = ams_pmu_clear_irq; | ||
160 | ams_info.bustype = BUS_HOST; | ||
161 | |||
162 | /* Get PMU command, should be 0x4e, but we can never know */ | ||
163 | prop = (u32*)get_property(ams_info.of_node, "reg", NULL); | ||
164 | if (!prop) { | ||
165 | result = -ENODEV; | ||
166 | goto exit; | ||
167 | } | ||
168 | ams_pmu_cmd = ((*prop) >> 8) & 0xff; | ||
169 | |||
170 | /* Disable interrupts */ | ||
171 | ams_pmu_set_irq(AMS_IRQ_ALL, 0); | ||
172 | |||
173 | /* Clear interrupts */ | ||
174 | ams_pmu_clear_irq(AMS_IRQ_ALL); | ||
175 | |||
176 | result = ams_sensor_attach(); | ||
177 | if (result < 0) | ||
178 | goto exit; | ||
179 | |||
180 | /* Set default values */ | ||
181 | ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); | ||
182 | ams_pmu_set_register(AMS_FF_ENABLE, 0x08); | ||
183 | ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14); | ||
184 | |||
185 | ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60); | ||
186 | ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f); | ||
187 | ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14); | ||
188 | |||
189 | ams_pmu_set_register(AMS_CONTROL, 0x4f); | ||
190 | |||
191 | /* Clear interrupts */ | ||
192 | ams_pmu_clear_irq(AMS_IRQ_ALL); | ||
193 | |||
194 | ams_info.has_device = 1; | ||
195 | |||
196 | /* Enable interrupts */ | ||
197 | ams_pmu_set_irq(AMS_IRQ_ALL, 1); | ||
198 | |||
199 | printk(KERN_INFO "ams: Found PMU based motion sensor\n"); | ||
200 | |||
201 | result = 0; | ||
202 | |||
203 | exit: | ||
204 | mutex_unlock(&ams_info.lock); | ||
205 | |||
206 | return result; | ||
207 | } | ||
diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h new file mode 100644 index 000000000000..240730e6bcde --- /dev/null +++ b/drivers/hwmon/ams/ams.h | |||
@@ -0,0 +1,72 @@ | |||
1 | #include <linux/i2c.h> | ||
2 | #include <linux/input.h> | ||
3 | #include <linux/kthread.h> | ||
4 | #include <linux/mutex.h> | ||
5 | #include <linux/spinlock.h> | ||
6 | #include <linux/types.h> | ||
7 | #include <asm/of_device.h> | ||
8 | |||
9 | enum ams_irq { | ||
10 | AMS_IRQ_FREEFALL = 0x01, | ||
11 | AMS_IRQ_SHOCK = 0x02, | ||
12 | AMS_IRQ_GLOBAL = 0x04, | ||
13 | AMS_IRQ_ALL = | ||
14 | AMS_IRQ_FREEFALL | | ||
15 | AMS_IRQ_SHOCK | | ||
16 | AMS_IRQ_GLOBAL, | ||
17 | }; | ||
18 | |||
19 | struct ams { | ||
20 | /* Locks */ | ||
21 | spinlock_t irq_lock; | ||
22 | struct mutex lock; | ||
23 | |||
24 | /* General properties */ | ||
25 | struct device_node *of_node; | ||
26 | struct of_device *of_dev; | ||
27 | char has_device; | ||
28 | char vflag; | ||
29 | u32 orient1; | ||
30 | u32 orient2; | ||
31 | |||
32 | /* Interrupt worker */ | ||
33 | struct work_struct worker; | ||
34 | u8 worker_irqs; | ||
35 | |||
36 | /* Implementation | ||
37 | * | ||
38 | * Only call these functions with the main lock held. | ||
39 | */ | ||
40 | void (*exit)(void); | ||
41 | |||
42 | void (*get_xyz)(s8 *x, s8 *y, s8 *z); | ||
43 | u8 (*get_vendor)(void); | ||
44 | |||
45 | void (*clear_irq)(enum ams_irq reg); | ||
46 | |||
47 | #ifdef CONFIG_SENSORS_AMS_I2C | ||
48 | /* I2C properties */ | ||
49 | int i2c_bus; | ||
50 | int i2c_address; | ||
51 | struct i2c_client i2c_client; | ||
52 | #endif | ||
53 | |||
54 | /* Joystick emulation */ | ||
55 | struct task_struct *kthread; | ||
56 | struct input_dev *idev; | ||
57 | __u16 bustype; | ||
58 | |||
59 | /* calibrated null values */ | ||
60 | int xcalib, ycalib, zcalib; | ||
61 | }; | ||
62 | |||
63 | extern struct ams ams_info; | ||
64 | |||
65 | extern void ams_sensors(s8 *x, s8 *y, s8 *z); | ||
66 | extern int ams_sensor_attach(void); | ||
67 | |||
68 | extern int ams_pmu_init(struct device_node *np); | ||
69 | extern int ams_i2c_init(struct device_node *np); | ||
70 | |||
71 | extern int ams_input_init(void); | ||
72 | extern void ams_input_exit(void); | ||
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index de17a72149d9..a272cae8f60e 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c | |||
@@ -1,12 +1,15 @@ | |||
1 | /* | 1 | /* |
2 | * f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated | 2 | * f71805f.c - driver for the Fintek F71805F/FG and F71872F/FG Super-I/O |
3 | * hardware monitoring features | 3 | * chips integrated hardware monitoring features |
4 | * Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org> | 4 | * Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org> |
5 | * | 5 | * |
6 | * The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates | 6 | * The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates |
7 | * complete hardware monitoring features: voltage, fan and temperature | 7 | * complete hardware monitoring features: voltage, fan and temperature |
8 | * sensors, and manual and automatic fan speed control. | 8 | * sensors, and manual and automatic fan speed control. |
9 | * | 9 | * |
10 | * The F71872F/FG is almost the same, with two more voltages monitored, | ||
11 | * and 6 VID inputs. | ||
12 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | 13 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by | 14 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; either version 2 of the License, or | 15 | * the Free Software Foundation; either version 2 of the License, or |
@@ -37,6 +40,7 @@ | |||
37 | static struct platform_device *pdev; | 40 | static struct platform_device *pdev; |
38 | 41 | ||
39 | #define DRVNAME "f71805f" | 42 | #define DRVNAME "f71805f" |
43 | enum kinds { f71805f, f71872f }; | ||
40 | 44 | ||
41 | /* | 45 | /* |
42 | * Super-I/O constants and functions | 46 | * Super-I/O constants and functions |
@@ -48,11 +52,13 @@ static struct platform_device *pdev; | |||
48 | #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ | 52 | #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ |
49 | #define SIO_REG_DEVREV 0x22 /* Device revision */ | 53 | #define SIO_REG_DEVREV 0x22 /* Device revision */ |
50 | #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ | 54 | #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ |
55 | #define SIO_REG_FNSEL1 0x29 /* Multi Function Select 1 (F71872F) */ | ||
51 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ | 56 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ |
52 | #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ | 57 | #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ |
53 | 58 | ||
54 | #define SIO_FINTEK_ID 0x1934 | 59 | #define SIO_FINTEK_ID 0x1934 |
55 | #define SIO_F71805F_ID 0x0406 | 60 | #define SIO_F71805F_ID 0x0406 |
61 | #define SIO_F71872F_ID 0x0341 | ||
56 | 62 | ||
57 | static inline int | 63 | static inline int |
58 | superio_inb(int base, int reg) | 64 | superio_inb(int base, int reg) |
@@ -96,22 +102,25 @@ superio_exit(int base) | |||
96 | * ISA constants | 102 | * ISA constants |
97 | */ | 103 | */ |
98 | 104 | ||
99 | #define REGION_LENGTH 2 | 105 | #define REGION_LENGTH 8 |
100 | #define ADDR_REG_OFFSET 0 | 106 | #define ADDR_REG_OFFSET 5 |
101 | #define DATA_REG_OFFSET 1 | 107 | #define DATA_REG_OFFSET 6 |
102 | 108 | ||
103 | /* | 109 | /* |
104 | * Registers | 110 | * Registers |
105 | */ | 111 | */ |
106 | 112 | ||
107 | /* in nr from 0 to 8 (8-bit values) */ | 113 | /* in nr from 0 to 10 (8-bit values) */ |
108 | #define F71805F_REG_IN(nr) (0x10 + (nr)) | 114 | #define F71805F_REG_IN(nr) (0x10 + (nr)) |
109 | #define F71805F_REG_IN_HIGH(nr) (0x40 + 2 * (nr)) | 115 | #define F71805F_REG_IN_HIGH(nr) ((nr) < 10 ? 0x40 + 2 * (nr) : 0x2E) |
110 | #define F71805F_REG_IN_LOW(nr) (0x41 + 2 * (nr)) | 116 | #define F71805F_REG_IN_LOW(nr) ((nr) < 10 ? 0x41 + 2 * (nr) : 0x2F) |
111 | /* fan nr from 0 to 2 (12-bit values, two registers) */ | 117 | /* fan nr from 0 to 2 (12-bit values, two registers) */ |
112 | #define F71805F_REG_FAN(nr) (0x20 + 2 * (nr)) | 118 | #define F71805F_REG_FAN(nr) (0x20 + 2 * (nr)) |
113 | #define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr)) | 119 | #define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr)) |
120 | #define F71805F_REG_FAN_TARGET(nr) (0x69 + 16 * (nr)) | ||
114 | #define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr)) | 121 | #define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr)) |
122 | #define F71805F_REG_PWM_FREQ(nr) (0x63 + 16 * (nr)) | ||
123 | #define F71805F_REG_PWM_DUTY(nr) (0x6B + 16 * (nr)) | ||
115 | /* temp nr from 0 to 2 (8-bit values) */ | 124 | /* temp nr from 0 to 2 (8-bit values) */ |
116 | #define F71805F_REG_TEMP(nr) (0x1B + (nr)) | 125 | #define F71805F_REG_TEMP(nr) (0x1B + (nr)) |
117 | #define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr)) | 126 | #define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr)) |
@@ -122,6 +131,14 @@ superio_exit(int base) | |||
122 | /* status nr from 0 to 2 */ | 131 | /* status nr from 0 to 2 */ |
123 | #define F71805F_REG_STATUS(nr) (0x36 + (nr)) | 132 | #define F71805F_REG_STATUS(nr) (0x36 + (nr)) |
124 | 133 | ||
134 | /* individual register bits */ | ||
135 | #define FAN_CTRL_DC_MODE 0x10 | ||
136 | #define FAN_CTRL_LATCH_FULL 0x08 | ||
137 | #define FAN_CTRL_MODE_MASK 0x03 | ||
138 | #define FAN_CTRL_MODE_SPEED 0x00 | ||
139 | #define FAN_CTRL_MODE_TEMPERATURE 0x01 | ||
140 | #define FAN_CTRL_MODE_MANUAL 0x02 | ||
141 | |||
125 | /* | 142 | /* |
126 | * Data structures and manipulation thereof | 143 | * Data structures and manipulation thereof |
127 | */ | 144 | */ |
@@ -138,12 +155,16 @@ struct f71805f_data { | |||
138 | unsigned long last_limits; /* In jiffies */ | 155 | unsigned long last_limits; /* In jiffies */ |
139 | 156 | ||
140 | /* Register values */ | 157 | /* Register values */ |
141 | u8 in[9]; | 158 | u8 in[11]; |
142 | u8 in_high[9]; | 159 | u8 in_high[11]; |
143 | u8 in_low[9]; | 160 | u8 in_low[11]; |
161 | u16 has_in; | ||
144 | u16 fan[3]; | 162 | u16 fan[3]; |
145 | u16 fan_low[3]; | 163 | u16 fan_low[3]; |
146 | u8 fan_enabled; /* Read once at init time */ | 164 | u16 fan_target[3]; |
165 | u8 fan_ctrl[3]; | ||
166 | u8 pwm[3]; | ||
167 | u8 pwm_freq[3]; | ||
147 | u8 temp[3]; | 168 | u8 temp[3]; |
148 | u8 temp_high[3]; | 169 | u8 temp_high[3]; |
149 | u8 temp_hyst[3]; | 170 | u8 temp_hyst[3]; |
@@ -151,6 +172,11 @@ struct f71805f_data { | |||
151 | unsigned long alarms; | 172 | unsigned long alarms; |
152 | }; | 173 | }; |
153 | 174 | ||
175 | struct f71805f_sio_data { | ||
176 | enum kinds kind; | ||
177 | u8 fnsel1; | ||
178 | }; | ||
179 | |||
154 | static inline long in_from_reg(u8 reg) | 180 | static inline long in_from_reg(u8 reg) |
155 | { | 181 | { |
156 | return (reg * 8); | 182 | return (reg * 8); |
@@ -200,6 +226,33 @@ static inline u16 fan_to_reg(long rpm) | |||
200 | return (1500000 / rpm); | 226 | return (1500000 / rpm); |
201 | } | 227 | } |
202 | 228 | ||
229 | static inline unsigned long pwm_freq_from_reg(u8 reg) | ||
230 | { | ||
231 | unsigned long clock = (reg & 0x80) ? 48000000UL : 1000000UL; | ||
232 | |||
233 | reg &= 0x7f; | ||
234 | if (reg == 0) | ||
235 | reg++; | ||
236 | return clock / (reg << 8); | ||
237 | } | ||
238 | |||
239 | static inline u8 pwm_freq_to_reg(unsigned long val) | ||
240 | { | ||
241 | if (val >= 187500) /* The highest we can do */ | ||
242 | return 0x80; | ||
243 | if (val >= 1475) /* Use 48 MHz clock */ | ||
244 | return 0x80 | (48000000UL / (val << 8)); | ||
245 | if (val < 31) /* The lowest we can do */ | ||
246 | return 0x7f; | ||
247 | else /* Use 1 MHz clock */ | ||
248 | return 1000000UL / (val << 8); | ||
249 | } | ||
250 | |||
251 | static inline int pwm_mode_from_reg(u8 reg) | ||
252 | { | ||
253 | return !(reg & FAN_CTRL_DC_MODE); | ||
254 | } | ||
255 | |||
203 | static inline long temp_from_reg(u8 reg) | 256 | static inline long temp_from_reg(u8 reg) |
204 | { | 257 | { |
205 | return (reg * 1000); | 258 | return (reg * 1000); |
@@ -274,16 +327,21 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) | |||
274 | /* Limit registers cache is refreshed after 60 seconds */ | 327 | /* Limit registers cache is refreshed after 60 seconds */ |
275 | if (time_after(jiffies, data->last_updated + 60 * HZ) | 328 | if (time_after(jiffies, data->last_updated + 60 * HZ) |
276 | || !data->valid) { | 329 | || !data->valid) { |
277 | for (nr = 0; nr < 9; nr++) { | 330 | for (nr = 0; nr < 11; nr++) { |
331 | if (!(data->has_in & (1 << nr))) | ||
332 | continue; | ||
278 | data->in_high[nr] = f71805f_read8(data, | 333 | data->in_high[nr] = f71805f_read8(data, |
279 | F71805F_REG_IN_HIGH(nr)); | 334 | F71805F_REG_IN_HIGH(nr)); |
280 | data->in_low[nr] = f71805f_read8(data, | 335 | data->in_low[nr] = f71805f_read8(data, |
281 | F71805F_REG_IN_LOW(nr)); | 336 | F71805F_REG_IN_LOW(nr)); |
282 | } | 337 | } |
283 | for (nr = 0; nr < 3; nr++) { | 338 | for (nr = 0; nr < 3; nr++) { |
284 | if (data->fan_enabled & (1 << nr)) | 339 | data->fan_low[nr] = f71805f_read16(data, |
285 | data->fan_low[nr] = f71805f_read16(data, | 340 | F71805F_REG_FAN_LOW(nr)); |
286 | F71805F_REG_FAN_LOW(nr)); | 341 | data->fan_target[nr] = f71805f_read16(data, |
342 | F71805F_REG_FAN_TARGET(nr)); | ||
343 | data->pwm_freq[nr] = f71805f_read8(data, | ||
344 | F71805F_REG_PWM_FREQ(nr)); | ||
287 | } | 345 | } |
288 | for (nr = 0; nr < 3; nr++) { | 346 | for (nr = 0; nr < 3; nr++) { |
289 | data->temp_high[nr] = f71805f_read8(data, | 347 | data->temp_high[nr] = f71805f_read8(data, |
@@ -299,14 +357,19 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) | |||
299 | /* Measurement registers cache is refreshed after 1 second */ | 357 | /* Measurement registers cache is refreshed after 1 second */ |
300 | if (time_after(jiffies, data->last_updated + HZ) | 358 | if (time_after(jiffies, data->last_updated + HZ) |
301 | || !data->valid) { | 359 | || !data->valid) { |
302 | for (nr = 0; nr < 9; nr++) { | 360 | for (nr = 0; nr < 11; nr++) { |
361 | if (!(data->has_in & (1 << nr))) | ||
362 | continue; | ||
303 | data->in[nr] = f71805f_read8(data, | 363 | data->in[nr] = f71805f_read8(data, |
304 | F71805F_REG_IN(nr)); | 364 | F71805F_REG_IN(nr)); |
305 | } | 365 | } |
306 | for (nr = 0; nr < 3; nr++) { | 366 | for (nr = 0; nr < 3; nr++) { |
307 | if (data->fan_enabled & (1 << nr)) | 367 | data->fan[nr] = f71805f_read16(data, |
308 | data->fan[nr] = f71805f_read16(data, | 368 | F71805F_REG_FAN(nr)); |
309 | F71805F_REG_FAN(nr)); | 369 | data->fan_ctrl[nr] = f71805f_read8(data, |
370 | F71805F_REG_FAN_CTRL(nr)); | ||
371 | data->pwm[nr] = f71805f_read8(data, | ||
372 | F71805F_REG_PWM_DUTY(nr)); | ||
310 | } | 373 | } |
311 | for (nr = 0; nr < 3; nr++) { | 374 | for (nr = 0; nr < 3; nr++) { |
312 | data->temp[nr] = f71805f_read8(data, | 375 | data->temp[nr] = f71805f_read8(data, |
@@ -333,35 +396,43 @@ static ssize_t show_in0(struct device *dev, struct device_attribute *devattr, | |||
333 | char *buf) | 396 | char *buf) |
334 | { | 397 | { |
335 | struct f71805f_data *data = f71805f_update_device(dev); | 398 | struct f71805f_data *data = f71805f_update_device(dev); |
399 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
400 | int nr = attr->index; | ||
336 | 401 | ||
337 | return sprintf(buf, "%ld\n", in0_from_reg(data->in[0])); | 402 | return sprintf(buf, "%ld\n", in0_from_reg(data->in[nr])); |
338 | } | 403 | } |
339 | 404 | ||
340 | static ssize_t show_in0_max(struct device *dev, struct device_attribute | 405 | static ssize_t show_in0_max(struct device *dev, struct device_attribute |
341 | *devattr, char *buf) | 406 | *devattr, char *buf) |
342 | { | 407 | { |
343 | struct f71805f_data *data = f71805f_update_device(dev); | 408 | struct f71805f_data *data = f71805f_update_device(dev); |
409 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
410 | int nr = attr->index; | ||
344 | 411 | ||
345 | return sprintf(buf, "%ld\n", in0_from_reg(data->in_high[0])); | 412 | return sprintf(buf, "%ld\n", in0_from_reg(data->in_high[nr])); |
346 | } | 413 | } |
347 | 414 | ||
348 | static ssize_t show_in0_min(struct device *dev, struct device_attribute | 415 | static ssize_t show_in0_min(struct device *dev, struct device_attribute |
349 | *devattr, char *buf) | 416 | *devattr, char *buf) |
350 | { | 417 | { |
351 | struct f71805f_data *data = f71805f_update_device(dev); | 418 | struct f71805f_data *data = f71805f_update_device(dev); |
419 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
420 | int nr = attr->index; | ||
352 | 421 | ||
353 | return sprintf(buf, "%ld\n", in0_from_reg(data->in_low[0])); | 422 | return sprintf(buf, "%ld\n", in0_from_reg(data->in_low[nr])); |
354 | } | 423 | } |
355 | 424 | ||
356 | static ssize_t set_in0_max(struct device *dev, struct device_attribute | 425 | static ssize_t set_in0_max(struct device *dev, struct device_attribute |
357 | *devattr, const char *buf, size_t count) | 426 | *devattr, const char *buf, size_t count) |
358 | { | 427 | { |
359 | struct f71805f_data *data = dev_get_drvdata(dev); | 428 | struct f71805f_data *data = dev_get_drvdata(dev); |
429 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
430 | int nr = attr->index; | ||
360 | long val = simple_strtol(buf, NULL, 10); | 431 | long val = simple_strtol(buf, NULL, 10); |
361 | 432 | ||
362 | mutex_lock(&data->update_lock); | 433 | mutex_lock(&data->update_lock); |
363 | data->in_high[0] = in0_to_reg(val); | 434 | data->in_high[nr] = in0_to_reg(val); |
364 | f71805f_write8(data, F71805F_REG_IN_HIGH(0), data->in_high[0]); | 435 | f71805f_write8(data, F71805F_REG_IN_HIGH(nr), data->in_high[nr]); |
365 | mutex_unlock(&data->update_lock); | 436 | mutex_unlock(&data->update_lock); |
366 | 437 | ||
367 | return count; | 438 | return count; |
@@ -371,11 +442,13 @@ static ssize_t set_in0_min(struct device *dev, struct device_attribute | |||
371 | *devattr, const char *buf, size_t count) | 442 | *devattr, const char *buf, size_t count) |
372 | { | 443 | { |
373 | struct f71805f_data *data = dev_get_drvdata(dev); | 444 | struct f71805f_data *data = dev_get_drvdata(dev); |
445 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
446 | int nr = attr->index; | ||
374 | long val = simple_strtol(buf, NULL, 10); | 447 | long val = simple_strtol(buf, NULL, 10); |
375 | 448 | ||
376 | mutex_lock(&data->update_lock); | 449 | mutex_lock(&data->update_lock); |
377 | data->in_low[0] = in0_to_reg(val); | 450 | data->in_low[nr] = in0_to_reg(val); |
378 | f71805f_write8(data, F71805F_REG_IN_LOW(0), data->in_low[0]); | 451 | f71805f_write8(data, F71805F_REG_IN_LOW(nr), data->in_low[nr]); |
379 | mutex_unlock(&data->update_lock); | 452 | mutex_unlock(&data->update_lock); |
380 | 453 | ||
381 | return count; | 454 | return count; |
@@ -463,6 +536,16 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute | |||
463 | return sprintf(buf, "%ld\n", fan_from_reg(data->fan_low[nr])); | 536 | return sprintf(buf, "%ld\n", fan_from_reg(data->fan_low[nr])); |
464 | } | 537 | } |
465 | 538 | ||
539 | static ssize_t show_fan_target(struct device *dev, struct device_attribute | ||
540 | *devattr, char *buf) | ||
541 | { | ||
542 | struct f71805f_data *data = f71805f_update_device(dev); | ||
543 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
544 | int nr = attr->index; | ||
545 | |||
546 | return sprintf(buf, "%ld\n", fan_from_reg(data->fan_target[nr])); | ||
547 | } | ||
548 | |||
466 | static ssize_t set_fan_min(struct device *dev, struct device_attribute | 549 | static ssize_t set_fan_min(struct device *dev, struct device_attribute |
467 | *devattr, const char *buf, size_t count) | 550 | *devattr, const char *buf, size_t count) |
468 | { | 551 | { |
@@ -479,6 +562,157 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute | |||
479 | return count; | 562 | return count; |
480 | } | 563 | } |
481 | 564 | ||
565 | static ssize_t set_fan_target(struct device *dev, struct device_attribute | ||
566 | *devattr, const char *buf, size_t count) | ||
567 | { | ||
568 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
569 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
570 | int nr = attr->index; | ||
571 | long val = simple_strtol(buf, NULL, 10); | ||
572 | |||
573 | mutex_lock(&data->update_lock); | ||
574 | data->fan_target[nr] = fan_to_reg(val); | ||
575 | f71805f_write16(data, F71805F_REG_FAN_TARGET(nr), | ||
576 | data->fan_target[nr]); | ||
577 | mutex_unlock(&data->update_lock); | ||
578 | |||
579 | return count; | ||
580 | } | ||
581 | |||
582 | static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, | ||
583 | char *buf) | ||
584 | { | ||
585 | struct f71805f_data *data = f71805f_update_device(dev); | ||
586 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
587 | int nr = attr->index; | ||
588 | |||
589 | return sprintf(buf, "%d\n", (int)data->pwm[nr]); | ||
590 | } | ||
591 | |||
592 | static ssize_t show_pwm_enable(struct device *dev, struct device_attribute | ||
593 | *devattr, char *buf) | ||
594 | { | ||
595 | struct f71805f_data *data = f71805f_update_device(dev); | ||
596 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
597 | int nr = attr->index; | ||
598 | int mode; | ||
599 | |||
600 | switch (data->fan_ctrl[nr] & FAN_CTRL_MODE_MASK) { | ||
601 | case FAN_CTRL_MODE_SPEED: | ||
602 | mode = 3; | ||
603 | break; | ||
604 | case FAN_CTRL_MODE_TEMPERATURE: | ||
605 | mode = 2; | ||
606 | break; | ||
607 | default: /* MANUAL */ | ||
608 | mode = 1; | ||
609 | } | ||
610 | |||
611 | return sprintf(buf, "%d\n", mode); | ||
612 | } | ||
613 | |||
614 | static ssize_t show_pwm_freq(struct device *dev, struct device_attribute | ||
615 | *devattr, char *buf) | ||
616 | { | ||
617 | struct f71805f_data *data = f71805f_update_device(dev); | ||
618 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
619 | int nr = attr->index; | ||
620 | |||
621 | return sprintf(buf, "%lu\n", pwm_freq_from_reg(data->pwm_freq[nr])); | ||
622 | } | ||
623 | |||
624 | static ssize_t show_pwm_mode(struct device *dev, struct device_attribute | ||
625 | *devattr, char *buf) | ||
626 | { | ||
627 | struct f71805f_data *data = f71805f_update_device(dev); | ||
628 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
629 | int nr = attr->index; | ||
630 | |||
631 | return sprintf(buf, "%d\n", pwm_mode_from_reg(data->fan_ctrl[nr])); | ||
632 | } | ||
633 | |||
634 | static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, | ||
635 | const char *buf, size_t count) | ||
636 | { | ||
637 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
638 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
639 | int nr = attr->index; | ||
640 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
641 | |||
642 | if (val > 255) | ||
643 | return -EINVAL; | ||
644 | |||
645 | mutex_lock(&data->update_lock); | ||
646 | data->pwm[nr] = val; | ||
647 | f71805f_write8(data, F71805F_REG_PWM_DUTY(nr), data->pwm[nr]); | ||
648 | mutex_unlock(&data->update_lock); | ||
649 | |||
650 | return count; | ||
651 | } | ||
652 | |||
653 | static struct attribute *f71805f_attr_pwm[]; | ||
654 | |||
655 | static ssize_t set_pwm_enable(struct device *dev, struct device_attribute | ||
656 | *devattr, const char *buf, size_t count) | ||
657 | { | ||
658 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
659 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
660 | int nr = attr->index; | ||
661 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
662 | u8 reg; | ||
663 | |||
664 | if (val < 1 || val > 3) | ||
665 | return -EINVAL; | ||
666 | |||
667 | if (val > 1) { /* Automatic mode, user can't set PWM value */ | ||
668 | if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr], | ||
669 | S_IRUGO)) | ||
670 | dev_dbg(dev, "chmod -w pwm%d failed\n", nr + 1); | ||
671 | } | ||
672 | |||
673 | mutex_lock(&data->update_lock); | ||
674 | reg = f71805f_read8(data, F71805F_REG_FAN_CTRL(nr)) | ||
675 | & ~FAN_CTRL_MODE_MASK; | ||
676 | switch (val) { | ||
677 | case 1: | ||
678 | reg |= FAN_CTRL_MODE_MANUAL; | ||
679 | break; | ||
680 | case 2: | ||
681 | reg |= FAN_CTRL_MODE_TEMPERATURE; | ||
682 | break; | ||
683 | case 3: | ||
684 | reg |= FAN_CTRL_MODE_SPEED; | ||
685 | break; | ||
686 | } | ||
687 | data->fan_ctrl[nr] = reg; | ||
688 | f71805f_write8(data, F71805F_REG_FAN_CTRL(nr), reg); | ||
689 | mutex_unlock(&data->update_lock); | ||
690 | |||
691 | if (val == 1) { /* Manual mode, user can set PWM value */ | ||
692 | if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr], | ||
693 | S_IRUGO | S_IWUSR)) | ||
694 | dev_dbg(dev, "chmod +w pwm%d failed\n", nr + 1); | ||
695 | } | ||
696 | |||
697 | return count; | ||
698 | } | ||
699 | |||
700 | static ssize_t set_pwm_freq(struct device *dev, struct device_attribute | ||
701 | *devattr, const char *buf, size_t count) | ||
702 | { | ||
703 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
704 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
705 | int nr = attr->index; | ||
706 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
707 | |||
708 | mutex_lock(&data->update_lock); | ||
709 | data->pwm_freq[nr] = pwm_freq_to_reg(val); | ||
710 | f71805f_write8(data, F71805F_REG_PWM_FREQ(nr), data->pwm_freq[nr]); | ||
711 | mutex_unlock(&data->update_lock); | ||
712 | |||
713 | return count; | ||
714 | } | ||
715 | |||
482 | static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, | 716 | static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, |
483 | char *buf) | 717 | char *buf) |
484 | { | 718 | { |
@@ -557,7 +791,7 @@ static ssize_t show_alarms_in(struct device *dev, struct device_attribute | |||
557 | { | 791 | { |
558 | struct f71805f_data *data = f71805f_update_device(dev); | 792 | struct f71805f_data *data = f71805f_update_device(dev); |
559 | 793 | ||
560 | return sprintf(buf, "%lu\n", data->alarms & 0x1ff); | 794 | return sprintf(buf, "%lu\n", data->alarms & 0x7ff); |
561 | } | 795 | } |
562 | 796 | ||
563 | static ssize_t show_alarms_fan(struct device *dev, struct device_attribute | 797 | static ssize_t show_alarms_fan(struct device *dev, struct device_attribute |
@@ -594,9 +828,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute | |||
594 | return sprintf(buf, "%s\n", data->name); | 828 | return sprintf(buf, "%s\n", data->name); |
595 | } | 829 | } |
596 | 830 | ||
597 | static DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL); | 831 | static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL, 0); |
598 | static DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max); | 832 | static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, |
599 | static DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min); | 833 | show_in0_max, set_in0_max, 0); |
834 | static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, | ||
835 | show_in0_min, set_in0_min, 0); | ||
600 | static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); | 836 | static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); |
601 | static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, | 837 | static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, |
602 | show_in_max, set_in_max, 1); | 838 | show_in_max, set_in_max, 1); |
@@ -637,16 +873,32 @@ static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR, | |||
637 | show_in_max, set_in_max, 8); | 873 | show_in_max, set_in_max, 8); |
638 | static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR, | 874 | static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR, |
639 | show_in_min, set_in_min, 8); | 875 | show_in_min, set_in_min, 8); |
876 | static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in0, NULL, 9); | ||
877 | static SENSOR_DEVICE_ATTR(in9_max, S_IRUGO | S_IWUSR, | ||
878 | show_in0_max, set_in0_max, 9); | ||
879 | static SENSOR_DEVICE_ATTR(in9_min, S_IRUGO | S_IWUSR, | ||
880 | show_in0_min, set_in0_min, 9); | ||
881 | static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in0, NULL, 10); | ||
882 | static SENSOR_DEVICE_ATTR(in10_max, S_IRUGO | S_IWUSR, | ||
883 | show_in0_max, set_in0_max, 10); | ||
884 | static SENSOR_DEVICE_ATTR(in10_min, S_IRUGO | S_IWUSR, | ||
885 | show_in0_min, set_in0_min, 10); | ||
640 | 886 | ||
641 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); | 887 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); |
642 | static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, | 888 | static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, |
643 | show_fan_min, set_fan_min, 0); | 889 | show_fan_min, set_fan_min, 0); |
890 | static SENSOR_DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, | ||
891 | show_fan_target, set_fan_target, 0); | ||
644 | static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); | 892 | static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); |
645 | static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, | 893 | static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, |
646 | show_fan_min, set_fan_min, 1); | 894 | show_fan_min, set_fan_min, 1); |
895 | static SENSOR_DEVICE_ATTR(fan2_target, S_IRUGO | S_IWUSR, | ||
896 | show_fan_target, set_fan_target, 1); | ||
647 | static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); | 897 | static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); |
648 | static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR, | 898 | static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR, |
649 | show_fan_min, set_fan_min, 2); | 899 | show_fan_min, set_fan_min, 2); |
900 | static SENSOR_DEVICE_ATTR(fan3_target, S_IRUGO | S_IWUSR, | ||
901 | show_fan_target, set_fan_target, 2); | ||
650 | 902 | ||
651 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); | 903 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); |
652 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, | 904 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, |
@@ -667,6 +919,27 @@ static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, | |||
667 | show_temp_hyst, set_temp_hyst, 2); | 919 | show_temp_hyst, set_temp_hyst, 2); |
668 | static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2); | 920 | static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2); |
669 | 921 | ||
922 | /* pwm (value) files are created read-only, write permission is | ||
923 | then added or removed dynamically as needed */ | ||
924 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, show_pwm, set_pwm, 0); | ||
925 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, | ||
926 | show_pwm_enable, set_pwm_enable, 0); | ||
927 | static SENSOR_DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR, | ||
928 | show_pwm_freq, set_pwm_freq, 0); | ||
929 | static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL, 0); | ||
930 | static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO, show_pwm, set_pwm, 1); | ||
931 | static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, | ||
932 | show_pwm_enable, set_pwm_enable, 1); | ||
933 | static SENSOR_DEVICE_ATTR(pwm2_freq, S_IRUGO | S_IWUSR, | ||
934 | show_pwm_freq, set_pwm_freq, 1); | ||
935 | static SENSOR_DEVICE_ATTR(pwm2_mode, S_IRUGO, show_pwm_mode, NULL, 1); | ||
936 | static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO, show_pwm, set_pwm, 2); | ||
937 | static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, | ||
938 | show_pwm_enable, set_pwm_enable, 2); | ||
939 | static SENSOR_DEVICE_ATTR(pwm3_freq, S_IRUGO | S_IWUSR, | ||
940 | show_pwm_freq, set_pwm_freq, 2); | ||
941 | static SENSOR_DEVICE_ATTR(pwm3_mode, S_IRUGO, show_pwm_mode, NULL, 2); | ||
942 | |||
670 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); | 943 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); |
671 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); | 944 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); |
672 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); | 945 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); |
@@ -676,6 +949,8 @@ static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5); | |||
676 | static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); | 949 | static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); |
677 | static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); | 950 | static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); |
678 | static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); | 951 | static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); |
952 | static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9); | ||
953 | static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10); | ||
679 | static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11); | 954 | static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11); |
680 | static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12); | 955 | static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12); |
681 | static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13); | 956 | static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13); |
@@ -689,9 +964,9 @@ static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL); | |||
689 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); | 964 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); |
690 | 965 | ||
691 | static struct attribute *f71805f_attributes[] = { | 966 | static struct attribute *f71805f_attributes[] = { |
692 | &dev_attr_in0_input.attr, | 967 | &sensor_dev_attr_in0_input.dev_attr.attr, |
693 | &dev_attr_in0_max.attr, | 968 | &sensor_dev_attr_in0_max.dev_attr.attr, |
694 | &dev_attr_in0_min.attr, | 969 | &sensor_dev_attr_in0_min.dev_attr.attr, |
695 | &sensor_dev_attr_in1_input.dev_attr.attr, | 970 | &sensor_dev_attr_in1_input.dev_attr.attr, |
696 | &sensor_dev_attr_in1_max.dev_attr.attr, | 971 | &sensor_dev_attr_in1_max.dev_attr.attr, |
697 | &sensor_dev_attr_in1_min.dev_attr.attr, | 972 | &sensor_dev_attr_in1_min.dev_attr.attr, |
@@ -701,9 +976,6 @@ static struct attribute *f71805f_attributes[] = { | |||
701 | &sensor_dev_attr_in3_input.dev_attr.attr, | 976 | &sensor_dev_attr_in3_input.dev_attr.attr, |
702 | &sensor_dev_attr_in3_max.dev_attr.attr, | 977 | &sensor_dev_attr_in3_max.dev_attr.attr, |
703 | &sensor_dev_attr_in3_min.dev_attr.attr, | 978 | &sensor_dev_attr_in3_min.dev_attr.attr, |
704 | &sensor_dev_attr_in4_input.dev_attr.attr, | ||
705 | &sensor_dev_attr_in4_max.dev_attr.attr, | ||
706 | &sensor_dev_attr_in4_min.dev_attr.attr, | ||
707 | &sensor_dev_attr_in5_input.dev_attr.attr, | 979 | &sensor_dev_attr_in5_input.dev_attr.attr, |
708 | &sensor_dev_attr_in5_max.dev_attr.attr, | 980 | &sensor_dev_attr_in5_max.dev_attr.attr, |
709 | &sensor_dev_attr_in5_min.dev_attr.attr, | 981 | &sensor_dev_attr_in5_min.dev_attr.attr, |
@@ -713,9 +985,29 @@ static struct attribute *f71805f_attributes[] = { | |||
713 | &sensor_dev_attr_in7_input.dev_attr.attr, | 985 | &sensor_dev_attr_in7_input.dev_attr.attr, |
714 | &sensor_dev_attr_in7_max.dev_attr.attr, | 986 | &sensor_dev_attr_in7_max.dev_attr.attr, |
715 | &sensor_dev_attr_in7_min.dev_attr.attr, | 987 | &sensor_dev_attr_in7_min.dev_attr.attr, |
716 | &sensor_dev_attr_in8_input.dev_attr.attr, | 988 | |
717 | &sensor_dev_attr_in8_max.dev_attr.attr, | 989 | &sensor_dev_attr_fan1_input.dev_attr.attr, |
718 | &sensor_dev_attr_in8_min.dev_attr.attr, | 990 | &sensor_dev_attr_fan1_min.dev_attr.attr, |
991 | &sensor_dev_attr_fan1_alarm.dev_attr.attr, | ||
992 | &sensor_dev_attr_fan1_target.dev_attr.attr, | ||
993 | &sensor_dev_attr_fan2_input.dev_attr.attr, | ||
994 | &sensor_dev_attr_fan2_min.dev_attr.attr, | ||
995 | &sensor_dev_attr_fan2_alarm.dev_attr.attr, | ||
996 | &sensor_dev_attr_fan2_target.dev_attr.attr, | ||
997 | &sensor_dev_attr_fan3_input.dev_attr.attr, | ||
998 | &sensor_dev_attr_fan3_min.dev_attr.attr, | ||
999 | &sensor_dev_attr_fan3_alarm.dev_attr.attr, | ||
1000 | &sensor_dev_attr_fan3_target.dev_attr.attr, | ||
1001 | |||
1002 | &sensor_dev_attr_pwm1.dev_attr.attr, | ||
1003 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, | ||
1004 | &sensor_dev_attr_pwm1_mode.dev_attr.attr, | ||
1005 | &sensor_dev_attr_pwm2.dev_attr.attr, | ||
1006 | &sensor_dev_attr_pwm2_enable.dev_attr.attr, | ||
1007 | &sensor_dev_attr_pwm2_mode.dev_attr.attr, | ||
1008 | &sensor_dev_attr_pwm3.dev_attr.attr, | ||
1009 | &sensor_dev_attr_pwm3_enable.dev_attr.attr, | ||
1010 | &sensor_dev_attr_pwm3_mode.dev_attr.attr, | ||
719 | 1011 | ||
720 | &sensor_dev_attr_temp1_input.dev_attr.attr, | 1012 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
721 | &sensor_dev_attr_temp1_max.dev_attr.attr, | 1013 | &sensor_dev_attr_temp1_max.dev_attr.attr, |
@@ -734,11 +1026,9 @@ static struct attribute *f71805f_attributes[] = { | |||
734 | &sensor_dev_attr_in1_alarm.dev_attr.attr, | 1026 | &sensor_dev_attr_in1_alarm.dev_attr.attr, |
735 | &sensor_dev_attr_in2_alarm.dev_attr.attr, | 1027 | &sensor_dev_attr_in2_alarm.dev_attr.attr, |
736 | &sensor_dev_attr_in3_alarm.dev_attr.attr, | 1028 | &sensor_dev_attr_in3_alarm.dev_attr.attr, |
737 | &sensor_dev_attr_in4_alarm.dev_attr.attr, | ||
738 | &sensor_dev_attr_in5_alarm.dev_attr.attr, | 1029 | &sensor_dev_attr_in5_alarm.dev_attr.attr, |
739 | &sensor_dev_attr_in6_alarm.dev_attr.attr, | 1030 | &sensor_dev_attr_in6_alarm.dev_attr.attr, |
740 | &sensor_dev_attr_in7_alarm.dev_attr.attr, | 1031 | &sensor_dev_attr_in7_alarm.dev_attr.attr, |
741 | &sensor_dev_attr_in8_alarm.dev_attr.attr, | ||
742 | &dev_attr_alarms_in.attr, | 1032 | &dev_attr_alarms_in.attr, |
743 | &sensor_dev_attr_temp1_alarm.dev_attr.attr, | 1033 | &sensor_dev_attr_temp1_alarm.dev_attr.attr, |
744 | &sensor_dev_attr_temp2_alarm.dev_attr.attr, | 1034 | &sensor_dev_attr_temp2_alarm.dev_attr.attr, |
@@ -754,29 +1044,59 @@ static const struct attribute_group f71805f_group = { | |||
754 | .attrs = f71805f_attributes, | 1044 | .attrs = f71805f_attributes, |
755 | }; | 1045 | }; |
756 | 1046 | ||
757 | static struct attribute *f71805f_attributes_fan[3][4] = { | 1047 | static struct attribute *f71805f_attributes_optin[4][5] = { |
758 | { | 1048 | { |
759 | &sensor_dev_attr_fan1_input.dev_attr.attr, | 1049 | &sensor_dev_attr_in4_input.dev_attr.attr, |
760 | &sensor_dev_attr_fan1_min.dev_attr.attr, | 1050 | &sensor_dev_attr_in4_max.dev_attr.attr, |
761 | &sensor_dev_attr_fan1_alarm.dev_attr.attr, | 1051 | &sensor_dev_attr_in4_min.dev_attr.attr, |
1052 | &sensor_dev_attr_in4_alarm.dev_attr.attr, | ||
1053 | NULL | ||
1054 | }, { | ||
1055 | &sensor_dev_attr_in8_input.dev_attr.attr, | ||
1056 | &sensor_dev_attr_in8_max.dev_attr.attr, | ||
1057 | &sensor_dev_attr_in8_min.dev_attr.attr, | ||
1058 | &sensor_dev_attr_in8_alarm.dev_attr.attr, | ||
762 | NULL | 1059 | NULL |
763 | }, { | 1060 | }, { |
764 | &sensor_dev_attr_fan2_input.dev_attr.attr, | 1061 | &sensor_dev_attr_in9_input.dev_attr.attr, |
765 | &sensor_dev_attr_fan2_min.dev_attr.attr, | 1062 | &sensor_dev_attr_in9_max.dev_attr.attr, |
766 | &sensor_dev_attr_fan2_alarm.dev_attr.attr, | 1063 | &sensor_dev_attr_in9_min.dev_attr.attr, |
1064 | &sensor_dev_attr_in9_alarm.dev_attr.attr, | ||
767 | NULL | 1065 | NULL |
768 | }, { | 1066 | }, { |
769 | &sensor_dev_attr_fan3_input.dev_attr.attr, | 1067 | &sensor_dev_attr_in10_input.dev_attr.attr, |
770 | &sensor_dev_attr_fan3_min.dev_attr.attr, | 1068 | &sensor_dev_attr_in10_max.dev_attr.attr, |
771 | &sensor_dev_attr_fan3_alarm.dev_attr.attr, | 1069 | &sensor_dev_attr_in10_min.dev_attr.attr, |
1070 | &sensor_dev_attr_in10_alarm.dev_attr.attr, | ||
772 | NULL | 1071 | NULL |
773 | } | 1072 | } |
774 | }; | 1073 | }; |
775 | 1074 | ||
776 | static const struct attribute_group f71805f_group_fan[3] = { | 1075 | static const struct attribute_group f71805f_group_optin[4] = { |
777 | { .attrs = f71805f_attributes_fan[0] }, | 1076 | { .attrs = f71805f_attributes_optin[0] }, |
778 | { .attrs = f71805f_attributes_fan[1] }, | 1077 | { .attrs = f71805f_attributes_optin[1] }, |
779 | { .attrs = f71805f_attributes_fan[2] }, | 1078 | { .attrs = f71805f_attributes_optin[2] }, |
1079 | { .attrs = f71805f_attributes_optin[3] }, | ||
1080 | }; | ||
1081 | |||
1082 | /* We don't include pwm_freq files in the arrays above, because they must be | ||
1083 | created conditionally (only if pwm_mode is 1 == PWM) */ | ||
1084 | static struct attribute *f71805f_attributes_pwm_freq[] = { | ||
1085 | &sensor_dev_attr_pwm1_freq.dev_attr.attr, | ||
1086 | &sensor_dev_attr_pwm2_freq.dev_attr.attr, | ||
1087 | &sensor_dev_attr_pwm3_freq.dev_attr.attr, | ||
1088 | NULL | ||
1089 | }; | ||
1090 | |||
1091 | static const struct attribute_group f71805f_group_pwm_freq = { | ||
1092 | .attrs = f71805f_attributes_pwm_freq, | ||
1093 | }; | ||
1094 | |||
1095 | /* We also need an indexed access to pwmN files to toggle writability */ | ||
1096 | static struct attribute *f71805f_attr_pwm[] = { | ||
1097 | &sensor_dev_attr_pwm1.dev_attr.attr, | ||
1098 | &sensor_dev_attr_pwm2.dev_attr.attr, | ||
1099 | &sensor_dev_attr_pwm3.dev_attr.attr, | ||
780 | }; | 1100 | }; |
781 | 1101 | ||
782 | /* | 1102 | /* |
@@ -798,18 +1118,30 @@ static void __devinit f71805f_init_device(struct f71805f_data *data) | |||
798 | /* Fan monitoring can be disabled. If it is, we won't be polling | 1118 | /* Fan monitoring can be disabled. If it is, we won't be polling |
799 | the register values, and won't create the related sysfs files. */ | 1119 | the register values, and won't create the related sysfs files. */ |
800 | for (i = 0; i < 3; i++) { | 1120 | for (i = 0; i < 3; i++) { |
801 | reg = f71805f_read8(data, F71805F_REG_FAN_CTRL(i)); | 1121 | data->fan_ctrl[i] = f71805f_read8(data, |
802 | if (!(reg & 0x80)) | 1122 | F71805F_REG_FAN_CTRL(i)); |
803 | data->fan_enabled |= (1 << i); | 1123 | /* Clear latch full bit, else "speed mode" fan speed control |
1124 | doesn't work */ | ||
1125 | if (data->fan_ctrl[i] & FAN_CTRL_LATCH_FULL) { | ||
1126 | data->fan_ctrl[i] &= ~FAN_CTRL_LATCH_FULL; | ||
1127 | f71805f_write8(data, F71805F_REG_FAN_CTRL(i), | ||
1128 | data->fan_ctrl[i]); | ||
1129 | } | ||
804 | } | 1130 | } |
805 | } | 1131 | } |
806 | 1132 | ||
807 | static int __devinit f71805f_probe(struct platform_device *pdev) | 1133 | static int __devinit f71805f_probe(struct platform_device *pdev) |
808 | { | 1134 | { |
1135 | struct f71805f_sio_data *sio_data = pdev->dev.platform_data; | ||
809 | struct f71805f_data *data; | 1136 | struct f71805f_data *data; |
810 | struct resource *res; | 1137 | struct resource *res; |
811 | int i, err; | 1138 | int i, err; |
812 | 1139 | ||
1140 | static const char *names[] = { | ||
1141 | "f71805f", | ||
1142 | "f71872f", | ||
1143 | }; | ||
1144 | |||
813 | if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) { | 1145 | if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) { |
814 | err = -ENOMEM; | 1146 | err = -ENOMEM; |
815 | printk(KERN_ERR DRVNAME ": Out of memory\n"); | 1147 | printk(KERN_ERR DRVNAME ": Out of memory\n"); |
@@ -819,24 +1151,69 @@ static int __devinit f71805f_probe(struct platform_device *pdev) | |||
819 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 1151 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
820 | data->addr = res->start; | 1152 | data->addr = res->start; |
821 | mutex_init(&data->lock); | 1153 | mutex_init(&data->lock); |
822 | data->name = "f71805f"; | 1154 | data->name = names[sio_data->kind]; |
823 | mutex_init(&data->update_lock); | 1155 | mutex_init(&data->update_lock); |
824 | 1156 | ||
825 | platform_set_drvdata(pdev, data); | 1157 | platform_set_drvdata(pdev, data); |
826 | 1158 | ||
1159 | /* Some voltage inputs depend on chip model and configuration */ | ||
1160 | switch (sio_data->kind) { | ||
1161 | case f71805f: | ||
1162 | data->has_in = 0x1ff; | ||
1163 | break; | ||
1164 | case f71872f: | ||
1165 | data->has_in = 0x6ef; | ||
1166 | if (sio_data->fnsel1 & 0x01) | ||
1167 | data->has_in |= (1 << 4); /* in4 */ | ||
1168 | if (sio_data->fnsel1 & 0x02) | ||
1169 | data->has_in |= (1 << 8); /* in8 */ | ||
1170 | break; | ||
1171 | } | ||
1172 | |||
827 | /* Initialize the F71805F chip */ | 1173 | /* Initialize the F71805F chip */ |
828 | f71805f_init_device(data); | 1174 | f71805f_init_device(data); |
829 | 1175 | ||
830 | /* Register sysfs interface files */ | 1176 | /* Register sysfs interface files */ |
831 | if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) | 1177 | if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) |
832 | goto exit_free; | 1178 | goto exit_free; |
833 | for (i = 0; i < 3; i++) { | 1179 | if (data->has_in & (1 << 4)) { /* in4 */ |
834 | if (!(data->fan_enabled & (1 << i))) | 1180 | if ((err = sysfs_create_group(&pdev->dev.kobj, |
835 | continue; | 1181 | &f71805f_group_optin[0]))) |
1182 | goto exit_remove_files; | ||
1183 | } | ||
1184 | if (data->has_in & (1 << 8)) { /* in8 */ | ||
1185 | if ((err = sysfs_create_group(&pdev->dev.kobj, | ||
1186 | &f71805f_group_optin[1]))) | ||
1187 | goto exit_remove_files; | ||
1188 | } | ||
1189 | if (data->has_in & (1 << 9)) { /* in9 (F71872F/FG only) */ | ||
836 | if ((err = sysfs_create_group(&pdev->dev.kobj, | 1190 | if ((err = sysfs_create_group(&pdev->dev.kobj, |
837 | &f71805f_group_fan[i]))) | 1191 | &f71805f_group_optin[2]))) |
838 | goto exit_remove_files; | 1192 | goto exit_remove_files; |
839 | } | 1193 | } |
1194 | if (data->has_in & (1 << 10)) { /* in9 (F71872F/FG only) */ | ||
1195 | if ((err = sysfs_create_group(&pdev->dev.kobj, | ||
1196 | &f71805f_group_optin[3]))) | ||
1197 | goto exit_remove_files; | ||
1198 | } | ||
1199 | for (i = 0; i < 3; i++) { | ||
1200 | /* If control mode is PWM, create pwm_freq file */ | ||
1201 | if (!(data->fan_ctrl[i] & FAN_CTRL_DC_MODE)) { | ||
1202 | if ((err = sysfs_create_file(&pdev->dev.kobj, | ||
1203 | f71805f_attributes_pwm_freq[i]))) | ||
1204 | goto exit_remove_files; | ||
1205 | } | ||
1206 | /* If PWM is in manual mode, add write permission */ | ||
1207 | if (data->fan_ctrl[i] & FAN_CTRL_MODE_MANUAL) { | ||
1208 | if ((err = sysfs_chmod_file(&pdev->dev.kobj, | ||
1209 | f71805f_attr_pwm[i], | ||
1210 | S_IRUGO | S_IWUSR))) { | ||
1211 | dev_err(&pdev->dev, "chmod +w pwm%d failed\n", | ||
1212 | i + 1); | ||
1213 | goto exit_remove_files; | ||
1214 | } | ||
1215 | } | ||
1216 | } | ||
840 | 1217 | ||
841 | data->class_dev = hwmon_device_register(&pdev->dev); | 1218 | data->class_dev = hwmon_device_register(&pdev->dev); |
842 | if (IS_ERR(data->class_dev)) { | 1219 | if (IS_ERR(data->class_dev)) { |
@@ -849,8 +1226,9 @@ static int __devinit f71805f_probe(struct platform_device *pdev) | |||
849 | 1226 | ||
850 | exit_remove_files: | 1227 | exit_remove_files: |
851 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); | 1228 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); |
852 | for (i = 0; i < 3; i++) | 1229 | for (i = 0; i < 4; i++) |
853 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); | 1230 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); |
1231 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); | ||
854 | exit_free: | 1232 | exit_free: |
855 | platform_set_drvdata(pdev, NULL); | 1233 | platform_set_drvdata(pdev, NULL); |
856 | kfree(data); | 1234 | kfree(data); |
@@ -866,8 +1244,9 @@ static int __devexit f71805f_remove(struct platform_device *pdev) | |||
866 | platform_set_drvdata(pdev, NULL); | 1244 | platform_set_drvdata(pdev, NULL); |
867 | hwmon_device_unregister(data->class_dev); | 1245 | hwmon_device_unregister(data->class_dev); |
868 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); | 1246 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); |
869 | for (i = 0; i < 3; i++) | 1247 | for (i = 0; i < 4; i++) |
870 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); | 1248 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); |
1249 | sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); | ||
871 | kfree(data); | 1250 | kfree(data); |
872 | 1251 | ||
873 | return 0; | 1252 | return 0; |
@@ -882,7 +1261,8 @@ static struct platform_driver f71805f_driver = { | |||
882 | .remove = __devexit_p(f71805f_remove), | 1261 | .remove = __devexit_p(f71805f_remove), |
883 | }; | 1262 | }; |
884 | 1263 | ||
885 | static int __init f71805f_device_add(unsigned short address) | 1264 | static int __init f71805f_device_add(unsigned short address, |
1265 | const struct f71805f_sio_data *sio_data) | ||
886 | { | 1266 | { |
887 | struct resource res = { | 1267 | struct resource res = { |
888 | .start = address, | 1268 | .start = address, |
@@ -906,26 +1286,45 @@ static int __init f71805f_device_add(unsigned short address) | |||
906 | goto exit_device_put; | 1286 | goto exit_device_put; |
907 | } | 1287 | } |
908 | 1288 | ||
1289 | pdev->dev.platform_data = kmalloc(sizeof(struct f71805f_sio_data), | ||
1290 | GFP_KERNEL); | ||
1291 | if (!pdev->dev.platform_data) { | ||
1292 | err = -ENOMEM; | ||
1293 | printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); | ||
1294 | goto exit_device_put; | ||
1295 | } | ||
1296 | memcpy(pdev->dev.platform_data, sio_data, | ||
1297 | sizeof(struct f71805f_sio_data)); | ||
1298 | |||
909 | err = platform_device_add(pdev); | 1299 | err = platform_device_add(pdev); |
910 | if (err) { | 1300 | if (err) { |
911 | printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", | 1301 | printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", |
912 | err); | 1302 | err); |
913 | goto exit_device_put; | 1303 | goto exit_kfree_data; |
914 | } | 1304 | } |
915 | 1305 | ||
916 | return 0; | 1306 | return 0; |
917 | 1307 | ||
1308 | exit_kfree_data: | ||
1309 | kfree(pdev->dev.platform_data); | ||
1310 | pdev->dev.platform_data = NULL; | ||
918 | exit_device_put: | 1311 | exit_device_put: |
919 | platform_device_put(pdev); | 1312 | platform_device_put(pdev); |
920 | exit: | 1313 | exit: |
921 | return err; | 1314 | return err; |
922 | } | 1315 | } |
923 | 1316 | ||
924 | static int __init f71805f_find(int sioaddr, unsigned short *address) | 1317 | static int __init f71805f_find(int sioaddr, unsigned short *address, |
1318 | struct f71805f_sio_data *sio_data) | ||
925 | { | 1319 | { |
926 | int err = -ENODEV; | 1320 | int err = -ENODEV; |
927 | u16 devid; | 1321 | u16 devid; |
928 | 1322 | ||
1323 | static const char *names[] = { | ||
1324 | "F71805F/FG", | ||
1325 | "F71872F/FG", | ||
1326 | }; | ||
1327 | |||
929 | superio_enter(sioaddr); | 1328 | superio_enter(sioaddr); |
930 | 1329 | ||
931 | devid = superio_inw(sioaddr, SIO_REG_MANID); | 1330 | devid = superio_inw(sioaddr, SIO_REG_MANID); |
@@ -933,7 +1332,15 @@ static int __init f71805f_find(int sioaddr, unsigned short *address) | |||
933 | goto exit; | 1332 | goto exit; |
934 | 1333 | ||
935 | devid = superio_inw(sioaddr, SIO_REG_DEVID); | 1334 | devid = superio_inw(sioaddr, SIO_REG_DEVID); |
936 | if (devid != SIO_F71805F_ID) { | 1335 | switch (devid) { |
1336 | case SIO_F71805F_ID: | ||
1337 | sio_data->kind = f71805f; | ||
1338 | break; | ||
1339 | case SIO_F71872F_ID: | ||
1340 | sio_data->kind = f71872f; | ||
1341 | sio_data->fnsel1 = superio_inb(sioaddr, SIO_REG_FNSEL1); | ||
1342 | break; | ||
1343 | default: | ||
937 | printk(KERN_INFO DRVNAME ": Unsupported Fintek device, " | 1344 | printk(KERN_INFO DRVNAME ": Unsupported Fintek device, " |
938 | "skipping\n"); | 1345 | "skipping\n"); |
939 | goto exit; | 1346 | goto exit; |
@@ -952,10 +1359,12 @@ static int __init f71805f_find(int sioaddr, unsigned short *address) | |||
952 | "skipping\n"); | 1359 | "skipping\n"); |
953 | goto exit; | 1360 | goto exit; |
954 | } | 1361 | } |
1362 | *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ | ||
955 | 1363 | ||
956 | err = 0; | 1364 | err = 0; |
957 | printk(KERN_INFO DRVNAME ": Found F71805F chip at %#x, revision %u\n", | 1365 | printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %u\n", |
958 | *address, superio_inb(sioaddr, SIO_REG_DEVREV)); | 1366 | names[sio_data->kind], *address, |
1367 | superio_inb(sioaddr, SIO_REG_DEVREV)); | ||
959 | 1368 | ||
960 | exit: | 1369 | exit: |
961 | superio_exit(sioaddr); | 1370 | superio_exit(sioaddr); |
@@ -966,9 +1375,10 @@ static int __init f71805f_init(void) | |||
966 | { | 1375 | { |
967 | int err; | 1376 | int err; |
968 | unsigned short address; | 1377 | unsigned short address; |
1378 | struct f71805f_sio_data sio_data; | ||
969 | 1379 | ||
970 | if (f71805f_find(0x2e, &address) | 1380 | if (f71805f_find(0x2e, &address, &sio_data) |
971 | && f71805f_find(0x4e, &address)) | 1381 | && f71805f_find(0x4e, &address, &sio_data)) |
972 | return -ENODEV; | 1382 | return -ENODEV; |
973 | 1383 | ||
974 | err = platform_driver_register(&f71805f_driver); | 1384 | err = platform_driver_register(&f71805f_driver); |
@@ -976,7 +1386,7 @@ static int __init f71805f_init(void) | |||
976 | goto exit; | 1386 | goto exit; |
977 | 1387 | ||
978 | /* Sets global pdev as a side effect */ | 1388 | /* Sets global pdev as a side effect */ |
979 | err = f71805f_device_add(address); | 1389 | err = f71805f_device_add(address, &sio_data); |
980 | if (err) | 1390 | if (err) |
981 | goto exit_driver; | 1391 | goto exit_driver; |
982 | 1392 | ||
@@ -990,13 +1400,16 @@ exit: | |||
990 | 1400 | ||
991 | static void __exit f71805f_exit(void) | 1401 | static void __exit f71805f_exit(void) |
992 | { | 1402 | { |
1403 | kfree(pdev->dev.platform_data); | ||
1404 | pdev->dev.platform_data = NULL; | ||
993 | platform_device_unregister(pdev); | 1405 | platform_device_unregister(pdev); |
1406 | |||
994 | platform_driver_unregister(&f71805f_driver); | 1407 | platform_driver_unregister(&f71805f_driver); |
995 | } | 1408 | } |
996 | 1409 | ||
997 | MODULE_AUTHOR("Jean Delvare <khali@linux-fr>"); | 1410 | MODULE_AUTHOR("Jean Delvare <khali@linux-fr>"); |
998 | MODULE_LICENSE("GPL"); | 1411 | MODULE_LICENSE("GPL"); |
999 | MODULE_DESCRIPTION("F71805F hardware monitoring driver"); | 1412 | MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver"); |
1000 | 1413 | ||
1001 | module_init(f71805f_init); | 1414 | module_init(f71805f_init); |
1002 | module_exit(f71805f_exit); | 1415 | module_exit(f71805f_exit); |
diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index e8ef62b83d6b..bf759ea545ac 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c | |||
@@ -478,74 +478,64 @@ static struct attribute_group hdaps_attribute_group = { | |||
478 | /* Module stuff */ | 478 | /* Module stuff */ |
479 | 479 | ||
480 | /* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ | 480 | /* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ |
481 | static int hdaps_dmi_match(struct dmi_system_id *id) | 481 | static int __init hdaps_dmi_match(struct dmi_system_id *id) |
482 | { | 482 | { |
483 | printk(KERN_INFO "hdaps: %s detected.\n", id->ident); | 483 | printk(KERN_INFO "hdaps: %s detected.\n", id->ident); |
484 | return 1; | 484 | return 1; |
485 | } | 485 | } |
486 | 486 | ||
487 | /* hdaps_dmi_match_invert - found an inverted match. */ | 487 | /* hdaps_dmi_match_invert - found an inverted match. */ |
488 | static int hdaps_dmi_match_invert(struct dmi_system_id *id) | 488 | static int __init hdaps_dmi_match_invert(struct dmi_system_id *id) |
489 | { | 489 | { |
490 | hdaps_invert = 1; | 490 | hdaps_invert = 1; |
491 | printk(KERN_INFO "hdaps: inverting axis readings.\n"); | 491 | printk(KERN_INFO "hdaps: inverting axis readings.\n"); |
492 | return hdaps_dmi_match(id); | 492 | return hdaps_dmi_match(id); |
493 | } | 493 | } |
494 | 494 | ||
495 | #define HDAPS_DMI_MATCH_NORMAL(model) { \ | 495 | #define HDAPS_DMI_MATCH_NORMAL(vendor, model) { \ |
496 | .ident = "IBM " model, \ | 496 | .ident = vendor " " model, \ |
497 | .callback = hdaps_dmi_match, \ | 497 | .callback = hdaps_dmi_match, \ |
498 | .matches = { \ | 498 | .matches = { \ |
499 | DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ | 499 | DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ |
500 | DMI_MATCH(DMI_PRODUCT_VERSION, model) \ | 500 | DMI_MATCH(DMI_PRODUCT_VERSION, model) \ |
501 | } \ | 501 | } \ |
502 | } | 502 | } |
503 | 503 | ||
504 | #define HDAPS_DMI_MATCH_INVERT(model) { \ | 504 | #define HDAPS_DMI_MATCH_INVERT(vendor, model) { \ |
505 | .ident = "IBM " model, \ | 505 | .ident = vendor " " model, \ |
506 | .callback = hdaps_dmi_match_invert, \ | 506 | .callback = hdaps_dmi_match_invert, \ |
507 | .matches = { \ | 507 | .matches = { \ |
508 | DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ | 508 | DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ |
509 | DMI_MATCH(DMI_PRODUCT_VERSION, model) \ | 509 | DMI_MATCH(DMI_PRODUCT_VERSION, model) \ |
510 | } \ | 510 | } \ |
511 | } | 511 | } |
512 | 512 | ||
513 | #define HDAPS_DMI_MATCH_LENOVO(model) { \ | 513 | /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match |
514 | .ident = "Lenovo " model, \ | 514 | "ThinkPad T42p", so the order of the entries matters. |
515 | .callback = hdaps_dmi_match_invert, \ | 515 | If your ThinkPad is not recognized, please update to latest |
516 | .matches = { \ | 516 | BIOS. This is especially the case for some R52 ThinkPads. */ |
517 | DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), \ | 517 | static struct dmi_system_id __initdata hdaps_whitelist[] = { |
518 | DMI_MATCH(DMI_PRODUCT_VERSION, model) \ | 518 | HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p"), |
519 | } \ | 519 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), |
520 | } | 520 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), |
521 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), | ||
522 | HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"), | ||
523 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), | ||
524 | HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"), | ||
525 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), | ||
526 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), | ||
527 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"), | ||
528 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), | ||
529 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"), | ||
530 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"), | ||
531 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), | ||
532 | { .ident = NULL } | ||
533 | }; | ||
521 | 534 | ||
522 | static int __init hdaps_init(void) | 535 | static int __init hdaps_init(void) |
523 | { | 536 | { |
524 | int ret; | 537 | int ret; |
525 | 538 | ||
526 | /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match | ||
527 | "ThinkPad T42p", so the order of the entries matters */ | ||
528 | struct dmi_system_id hdaps_whitelist[] = { | ||
529 | HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), | ||
530 | HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), | ||
531 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), | ||
532 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), | ||
533 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"), | ||
534 | HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), /* R52 (1846AQG) */ | ||
535 | HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"), | ||
536 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"), | ||
537 | HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), | ||
538 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"), | ||
539 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), | ||
540 | HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"), | ||
541 | HDAPS_DMI_MATCH_LENOVO("ThinkPad T60"), | ||
542 | HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), | ||
543 | HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"), | ||
544 | HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"), | ||
545 | HDAPS_DMI_MATCH_NORMAL("ThinkPad Z60m"), | ||
546 | { .ident = NULL } | ||
547 | }; | ||
548 | |||
549 | if (!dmi_check_system(hdaps_whitelist)) { | 539 | if (!dmi_check_system(hdaps_whitelist)) { |
550 | printk(KERN_WARNING "hdaps: supported laptop not found!\n"); | 540 | printk(KERN_WARNING "hdaps: supported laptop not found!\n"); |
551 | ret = -ENODEV; | 541 | ret = -ENODEV; |
diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index 9d67320e6840..31c42002708f 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | hwmon-vid.c - VID/VRM/VRD voltage conversions | 2 | hwmon-vid.c - VID/VRM/VRD voltage conversions |
3 | 3 | ||
4 | Copyright (c) 2004 Rudolf Marek <r.marek@sh.cvut.cz> | 4 | Copyright (c) 2004 Rudolf Marek <r.marek@assembler.cz> |
5 | 5 | ||
6 | Partly imported from i2c-vid.h of the lm_sensors project | 6 | Partly imported from i2c-vid.h of the lm_sensors project |
7 | Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> | 7 | Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> |
@@ -232,7 +232,7 @@ u8 vid_which_vrm(void) | |||
232 | EXPORT_SYMBOL(vid_from_reg); | 232 | EXPORT_SYMBOL(vid_from_reg); |
233 | EXPORT_SYMBOL(vid_which_vrm); | 233 | EXPORT_SYMBOL(vid_which_vrm); |
234 | 234 | ||
235 | MODULE_AUTHOR("Rudolf Marek <r.marek@sh.cvut.cz>"); | 235 | MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); |
236 | 236 | ||
237 | MODULE_DESCRIPTION("hwmon-vid driver"); | 237 | MODULE_DESCRIPTION("hwmon-vid driver"); |
238 | MODULE_LICENSE("GPL"); | 238 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 323ef06719c1..1ed8b7e2c35d 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c | |||
@@ -3,7 +3,7 @@ | |||
3 | monitoring. | 3 | monitoring. |
4 | 4 | ||
5 | Supports: IT8705F Super I/O chip w/LPC interface | 5 | Supports: IT8705F Super I/O chip w/LPC interface |
6 | IT8712F Super I/O chip w/LPC interface & SMBus | 6 | IT8712F Super I/O chip w/LPC interface |
7 | IT8716F Super I/O chip w/LPC interface | 7 | IT8716F Super I/O chip w/LPC interface |
8 | IT8718F Super I/O chip w/LPC interface | 8 | IT8718F Super I/O chip w/LPC interface |
9 | Sis950 A clone of the IT8705F | 9 | Sis950 A clone of the IT8705F |
@@ -41,12 +41,8 @@ | |||
41 | #include <asm/io.h> | 41 | #include <asm/io.h> |
42 | 42 | ||
43 | 43 | ||
44 | /* Addresses to scan */ | ||
45 | static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; | ||
46 | static unsigned short isa_address; | 44 | static unsigned short isa_address; |
47 | 45 | enum chips { it87, it8712, it8716, it8718 }; | |
48 | /* Insmod parameters */ | ||
49 | I2C_CLIENT_INSMOD_4(it87, it8712, it8716, it8718); | ||
50 | 46 | ||
51 | #define REG 0x2e /* The register to read/write */ | 47 | #define REG 0x2e /* The register to read/write */ |
52 | #define DEV 0x07 /* Register: Logical device select */ | 48 | #define DEV 0x07 /* Register: Logical device select */ |
@@ -162,8 +158,6 @@ static u8 vid_value; | |||
162 | #define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2) | 158 | #define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2) |
163 | #define IT87_REG_TEMP_LOW(nr) (0x41 + (nr) * 2) | 159 | #define IT87_REG_TEMP_LOW(nr) (0x41 + (nr) * 2) |
164 | 160 | ||
165 | #define IT87_REG_I2C_ADDR 0x48 | ||
166 | |||
167 | #define IT87_REG_VIN_ENABLE 0x50 | 161 | #define IT87_REG_VIN_ENABLE 0x50 |
168 | #define IT87_REG_TEMP_ENABLE 0x51 | 162 | #define IT87_REG_TEMP_ENABLE 0x51 |
169 | 163 | ||
@@ -242,33 +236,22 @@ struct it87_data { | |||
242 | }; | 236 | }; |
243 | 237 | ||
244 | 238 | ||
245 | static int it87_attach_adapter(struct i2c_adapter *adapter); | 239 | static int it87_detect(struct i2c_adapter *adapter); |
246 | static int it87_isa_attach_adapter(struct i2c_adapter *adapter); | ||
247 | static int it87_detect(struct i2c_adapter *adapter, int address, int kind); | ||
248 | static int it87_detach_client(struct i2c_client *client); | 240 | static int it87_detach_client(struct i2c_client *client); |
249 | 241 | ||
250 | static int it87_read_value(struct i2c_client *client, u8 reg); | 242 | static int it87_read_value(struct i2c_client *client, u8 reg); |
251 | static int it87_write_value(struct i2c_client *client, u8 reg, u8 value); | 243 | static void it87_write_value(struct i2c_client *client, u8 reg, u8 value); |
252 | static struct it87_data *it87_update_device(struct device *dev); | 244 | static struct it87_data *it87_update_device(struct device *dev); |
253 | static int it87_check_pwm(struct i2c_client *client); | 245 | static int it87_check_pwm(struct i2c_client *client); |
254 | static void it87_init_client(struct i2c_client *client, struct it87_data *data); | 246 | static void it87_init_client(struct i2c_client *client, struct it87_data *data); |
255 | 247 | ||
256 | 248 | ||
257 | static struct i2c_driver it87_driver = { | ||
258 | .driver = { | ||
259 | .name = "it87", | ||
260 | }, | ||
261 | .id = I2C_DRIVERID_IT87, | ||
262 | .attach_adapter = it87_attach_adapter, | ||
263 | .detach_client = it87_detach_client, | ||
264 | }; | ||
265 | |||
266 | static struct i2c_driver it87_isa_driver = { | 249 | static struct i2c_driver it87_isa_driver = { |
267 | .driver = { | 250 | .driver = { |
268 | .owner = THIS_MODULE, | 251 | .owner = THIS_MODULE, |
269 | .name = "it87-isa", | 252 | .name = "it87-isa", |
270 | }, | 253 | }, |
271 | .attach_adapter = it87_isa_attach_adapter, | 254 | .attach_adapter = it87_detect, |
272 | .detach_client = it87_detach_client, | 255 | .detach_client = it87_detach_client, |
273 | }; | 256 | }; |
274 | 257 | ||
@@ -850,22 +833,6 @@ static const struct attribute_group it87_group_opt = { | |||
850 | .attrs = it87_attributes_opt, | 833 | .attrs = it87_attributes_opt, |
851 | }; | 834 | }; |
852 | 835 | ||
853 | /* This function is called when: | ||
854 | * it87_driver is inserted (when this module is loaded), for each | ||
855 | available adapter | ||
856 | * when a new adapter is inserted (and it87_driver is still present) */ | ||
857 | static int it87_attach_adapter(struct i2c_adapter *adapter) | ||
858 | { | ||
859 | if (!(adapter->class & I2C_CLASS_HWMON)) | ||
860 | return 0; | ||
861 | return i2c_probe(adapter, &addr_data, it87_detect); | ||
862 | } | ||
863 | |||
864 | static int it87_isa_attach_adapter(struct i2c_adapter *adapter) | ||
865 | { | ||
866 | return it87_detect(adapter, isa_address, -1); | ||
867 | } | ||
868 | |||
869 | /* SuperIO detection - will change isa_address if a chip is found */ | 836 | /* SuperIO detection - will change isa_address if a chip is found */ |
870 | static int __init it87_find(unsigned short *address) | 837 | static int __init it87_find(unsigned short *address) |
871 | { | 838 | { |
@@ -916,29 +883,20 @@ exit: | |||
916 | } | 883 | } |
917 | 884 | ||
918 | /* This function is called by i2c_probe */ | 885 | /* This function is called by i2c_probe */ |
919 | static int it87_detect(struct i2c_adapter *adapter, int address, int kind) | 886 | static int it87_detect(struct i2c_adapter *adapter) |
920 | { | 887 | { |
921 | int i; | ||
922 | struct i2c_client *new_client; | 888 | struct i2c_client *new_client; |
923 | struct it87_data *data; | 889 | struct it87_data *data; |
924 | int err = 0; | 890 | int err = 0; |
925 | const char *name = ""; | 891 | const char *name; |
926 | int is_isa = i2c_is_isa_adapter(adapter); | ||
927 | int enable_pwm_interface; | 892 | int enable_pwm_interface; |
928 | 893 | ||
929 | if (!is_isa && | ||
930 | !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
931 | goto ERROR0; | ||
932 | |||
933 | /* Reserve the ISA region */ | 894 | /* Reserve the ISA region */ |
934 | if (is_isa) | 895 | if (!request_region(isa_address, IT87_EXTENT, |
935 | if (!request_region(address, IT87_EXTENT, | 896 | it87_isa_driver.driver.name)){ |
936 | it87_isa_driver.driver.name)) | 897 | err = -EBUSY; |
937 | goto ERROR0; | 898 | goto ERROR0; |
938 | 899 | } | |
939 | /* For now, we presume we have a valid client. We create the | ||
940 | client structure, even though we cannot fill it completely yet. | ||
941 | But it allows us to access it87_{read,write}_value. */ | ||
942 | 900 | ||
943 | if (!(data = kzalloc(sizeof(struct it87_data), GFP_KERNEL))) { | 901 | if (!(data = kzalloc(sizeof(struct it87_data), GFP_KERNEL))) { |
944 | err = -ENOMEM; | 902 | err = -ENOMEM; |
@@ -946,80 +904,46 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind) | |||
946 | } | 904 | } |
947 | 905 | ||
948 | new_client = &data->client; | 906 | new_client = &data->client; |
949 | if (is_isa) | 907 | mutex_init(&data->lock); |
950 | mutex_init(&data->lock); | ||
951 | i2c_set_clientdata(new_client, data); | 908 | i2c_set_clientdata(new_client, data); |
952 | new_client->addr = address; | 909 | new_client->addr = isa_address; |
953 | new_client->adapter = adapter; | 910 | new_client->adapter = adapter; |
954 | new_client->driver = is_isa ? &it87_isa_driver : &it87_driver; | 911 | new_client->driver = &it87_isa_driver; |
955 | new_client->flags = 0; | ||
956 | 912 | ||
957 | /* Now, we do the remaining detection. */ | 913 | /* Now, we do the remaining detection. */ |
958 | 914 | if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) | |
959 | if (kind < 0) { | 915 | || it87_read_value(new_client, IT87_REG_CHIPID) != 0x90) { |
960 | if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) | 916 | err = -ENODEV; |
961 | || (!is_isa | 917 | goto ERROR2; |
962 | && it87_read_value(new_client, IT87_REG_I2C_ADDR) != address)) { | ||
963 | err = -ENODEV; | ||
964 | goto ERROR2; | ||
965 | } | ||
966 | } | 918 | } |
967 | 919 | ||
968 | /* Determine the chip type. */ | 920 | /* Determine the chip type. */ |
969 | if (kind <= 0) { | 921 | switch (chip_type) { |
970 | i = it87_read_value(new_client, IT87_REG_CHIPID); | 922 | case IT8712F_DEVID: |
971 | if (i == 0x90) { | 923 | data->type = it8712; |
972 | kind = it87; | ||
973 | if (is_isa) { | ||
974 | switch (chip_type) { | ||
975 | case IT8712F_DEVID: | ||
976 | kind = it8712; | ||
977 | break; | ||
978 | case IT8716F_DEVID: | ||
979 | kind = it8716; | ||
980 | break; | ||
981 | case IT8718F_DEVID: | ||
982 | kind = it8718; | ||
983 | break; | ||
984 | } | ||
985 | } | ||
986 | } | ||
987 | else { | ||
988 | if (kind == 0) | ||
989 | dev_info(&adapter->dev, | ||
990 | "Ignoring 'force' parameter for unknown chip at " | ||
991 | "adapter %d, address 0x%02x\n", | ||
992 | i2c_adapter_id(adapter), address); | ||
993 | err = -ENODEV; | ||
994 | goto ERROR2; | ||
995 | } | ||
996 | } | ||
997 | |||
998 | if (kind == it87) { | ||
999 | name = "it87"; | ||
1000 | } else if (kind == it8712) { | ||
1001 | name = "it8712"; | 924 | name = "it8712"; |
1002 | } else if (kind == it8716) { | 925 | break; |
926 | case IT8716F_DEVID: | ||
927 | data->type = it8716; | ||
1003 | name = "it8716"; | 928 | name = "it8716"; |
1004 | } else if (kind == it8718) { | 929 | break; |
930 | case IT8718F_DEVID: | ||
931 | data->type = it8718; | ||
1005 | name = "it8718"; | 932 | name = "it8718"; |
933 | break; | ||
934 | default: | ||
935 | data->type = it87; | ||
936 | name = "it87"; | ||
1006 | } | 937 | } |
1007 | 938 | ||
1008 | /* Fill in the remaining client fields and put it into the global list */ | 939 | /* Fill in the remaining client fields and put it into the global list */ |
1009 | strlcpy(new_client->name, name, I2C_NAME_SIZE); | 940 | strlcpy(new_client->name, name, I2C_NAME_SIZE); |
1010 | data->type = kind; | ||
1011 | data->valid = 0; | ||
1012 | mutex_init(&data->update_lock); | 941 | mutex_init(&data->update_lock); |
1013 | 942 | ||
1014 | /* Tell the I2C layer a new client has arrived */ | 943 | /* Tell the I2C layer a new client has arrived */ |
1015 | if ((err = i2c_attach_client(new_client))) | 944 | if ((err = i2c_attach_client(new_client))) |
1016 | goto ERROR2; | 945 | goto ERROR2; |
1017 | 946 | ||
1018 | if (!is_isa) | ||
1019 | dev_info(&new_client->dev, "The I2C interface to IT87xxF " | ||
1020 | "hardware monitoring chips is deprecated. Please " | ||
1021 | "report if you still rely on it.\n"); | ||
1022 | |||
1023 | /* Check PWM configuration */ | 947 | /* Check PWM configuration */ |
1024 | enable_pwm_interface = it87_check_pwm(new_client); | 948 | enable_pwm_interface = it87_check_pwm(new_client); |
1025 | 949 | ||
@@ -1129,8 +1053,7 @@ ERROR3: | |||
1129 | ERROR2: | 1053 | ERROR2: |
1130 | kfree(data); | 1054 | kfree(data); |
1131 | ERROR1: | 1055 | ERROR1: |
1132 | if (is_isa) | 1056 | release_region(isa_address, IT87_EXTENT); |
1133 | release_region(address, IT87_EXTENT); | ||
1134 | ERROR0: | 1057 | ERROR0: |
1135 | return err; | 1058 | return err; |
1136 | } | 1059 | } |
@@ -1147,50 +1070,39 @@ static int it87_detach_client(struct i2c_client *client) | |||
1147 | if ((err = i2c_detach_client(client))) | 1070 | if ((err = i2c_detach_client(client))) |
1148 | return err; | 1071 | return err; |
1149 | 1072 | ||
1150 | if(i2c_is_isa_client(client)) | 1073 | release_region(client->addr, IT87_EXTENT); |
1151 | release_region(client->addr, IT87_EXTENT); | ||
1152 | kfree(data); | 1074 | kfree(data); |
1153 | 1075 | ||
1154 | return 0; | 1076 | return 0; |
1155 | } | 1077 | } |
1156 | 1078 | ||
1157 | /* The SMBus locks itself, but ISA access must be locked explicitly! | 1079 | /* ISA access must be locked explicitly! |
1158 | We don't want to lock the whole ISA bus, so we lock each client | ||
1159 | separately. | ||
1160 | We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, | 1080 | We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, |
1161 | would slow down the IT87 access and should not be necessary. */ | 1081 | would slow down the IT87 access and should not be necessary. */ |
1162 | static int it87_read_value(struct i2c_client *client, u8 reg) | 1082 | static int it87_read_value(struct i2c_client *client, u8 reg) |
1163 | { | 1083 | { |
1164 | struct it87_data *data = i2c_get_clientdata(client); | 1084 | struct it87_data *data = i2c_get_clientdata(client); |
1165 | |||
1166 | int res; | 1085 | int res; |
1167 | if (i2c_is_isa_client(client)) { | 1086 | |
1168 | mutex_lock(&data->lock); | 1087 | mutex_lock(&data->lock); |
1169 | outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); | 1088 | outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); |
1170 | res = inb_p(client->addr + IT87_DATA_REG_OFFSET); | 1089 | res = inb_p(client->addr + IT87_DATA_REG_OFFSET); |
1171 | mutex_unlock(&data->lock); | 1090 | mutex_unlock(&data->lock); |
1172 | return res; | 1091 | |
1173 | } else | 1092 | return res; |
1174 | return i2c_smbus_read_byte_data(client, reg); | ||
1175 | } | 1093 | } |
1176 | 1094 | ||
1177 | /* The SMBus locks itself, but ISA access muse be locked explicitly! | 1095 | /* ISA access must be locked explicitly! |
1178 | We don't want to lock the whole ISA bus, so we lock each client | ||
1179 | separately. | ||
1180 | We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, | 1096 | We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, |
1181 | would slow down the IT87 access and should not be necessary. */ | 1097 | would slow down the IT87 access and should not be necessary. */ |
1182 | static int it87_write_value(struct i2c_client *client, u8 reg, u8 value) | 1098 | static void it87_write_value(struct i2c_client *client, u8 reg, u8 value) |
1183 | { | 1099 | { |
1184 | struct it87_data *data = i2c_get_clientdata(client); | 1100 | struct it87_data *data = i2c_get_clientdata(client); |
1185 | 1101 | ||
1186 | if (i2c_is_isa_client(client)) { | 1102 | mutex_lock(&data->lock); |
1187 | mutex_lock(&data->lock); | 1103 | outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); |
1188 | outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); | 1104 | outb_p(value, client->addr + IT87_DATA_REG_OFFSET); |
1189 | outb_p(value, client->addr + IT87_DATA_REG_OFFSET); | 1105 | mutex_unlock(&data->lock); |
1190 | mutex_unlock(&data->lock); | ||
1191 | return 0; | ||
1192 | } else | ||
1193 | return i2c_smbus_write_byte_data(client, reg, value); | ||
1194 | } | 1106 | } |
1195 | 1107 | ||
1196 | /* Return 1 if and only if the PWM interface is safe to use */ | 1108 | /* Return 1 if and only if the PWM interface is safe to use */ |
@@ -1426,26 +1338,14 @@ static int __init sm_it87_init(void) | |||
1426 | { | 1338 | { |
1427 | int res; | 1339 | int res; |
1428 | 1340 | ||
1429 | res = i2c_add_driver(&it87_driver); | 1341 | if ((res = it87_find(&isa_address))) |
1430 | if (res) | ||
1431 | return res; | 1342 | return res; |
1432 | 1343 | return i2c_isa_add_driver(&it87_isa_driver); | |
1433 | if (!it87_find(&isa_address)) { | ||
1434 | res = i2c_isa_add_driver(&it87_isa_driver); | ||
1435 | if (res) { | ||
1436 | i2c_del_driver(&it87_driver); | ||
1437 | return res; | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | return 0; | ||
1442 | } | 1344 | } |
1443 | 1345 | ||
1444 | static void __exit sm_it87_exit(void) | 1346 | static void __exit sm_it87_exit(void) |
1445 | { | 1347 | { |
1446 | if (isa_address) | 1348 | i2c_isa_del_driver(&it87_isa_driver); |
1447 | i2c_isa_del_driver(&it87_isa_driver); | ||
1448 | i2c_del_driver(&it87_driver); | ||
1449 | } | 1349 | } |
1450 | 1350 | ||
1451 | 1351 | ||
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index f58b64ed09e3..5d8d0ca08fa9 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * k8temp.c - Linux kernel module for hardware monitoring | 2 | * k8temp.c - Linux kernel module for hardware monitoring |
3 | * | 3 | * |
4 | * Copyright (C) 2006 Rudolf Marek <r.marek@sh.cvut.cz> | 4 | * Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz> |
5 | * | 5 | * |
6 | * Inspired from the w83785 and amd756 drivers. | 6 | * Inspired from the w83785 and amd756 drivers. |
7 | * | 7 | * |
@@ -286,7 +286,7 @@ static void __exit k8temp_exit(void) | |||
286 | pci_unregister_driver(&k8temp_driver); | 286 | pci_unregister_driver(&k8temp_driver); |
287 | } | 287 | } |
288 | 288 | ||
289 | MODULE_AUTHOR("Rudolf Marek <r.marek@sh.cvut.cz>"); | 289 | MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); |
290 | MODULE_DESCRIPTION("AMD K8 core temperature monitor"); | 290 | MODULE_DESCRIPTION("AMD K8 core temperature monitor"); |
291 | MODULE_LICENSE("GPL"); | 291 | MODULE_LICENSE("GPL"); |
292 | 292 | ||
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 3b8b81984ad4..c8a21be09d87 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c | |||
@@ -1000,7 +1000,7 @@ static int pc87360_detect(struct i2c_adapter *adapter) | |||
1000 | (i&0x02) ? "external" : "internal"); | 1000 | (i&0x02) ? "external" : "internal"); |
1001 | 1001 | ||
1002 | data->vid_conf = confreg[3]; | 1002 | data->vid_conf = confreg[3]; |
1003 | data->vrm = 90; | 1003 | data->vrm = vid_which_vrm(); |
1004 | } | 1004 | } |
1005 | 1005 | ||
1006 | /* Fan clock dividers may be needed before any data is read */ | 1006 | /* Fan clock dividers may be needed before any data is read */ |
diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c new file mode 100644 index 000000000000..affa21a5ccfd --- /dev/null +++ b/drivers/hwmon/pc87427.c | |||
@@ -0,0 +1,627 @@ | |||
1 | /* | ||
2 | * pc87427.c - hardware monitoring driver for the | ||
3 | * National Semiconductor PC87427 Super-I/O chip | ||
4 | * Copyright (C) 2006 Jean Delvare <khali@linux-fr.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Supports the following chips: | ||
16 | * | ||
17 | * Chip #vin #fan #pwm #temp devid | ||
18 | * PC87427 - 8 - - 0xF2 | ||
19 | * | ||
20 | * This driver assumes that no more than one chip is present. | ||
21 | * Only fan inputs are supported so far, although the chip can do much more. | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/jiffies.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/hwmon.h> | ||
30 | #include <linux/hwmon-sysfs.h> | ||
31 | #include <linux/err.h> | ||
32 | #include <linux/mutex.h> | ||
33 | #include <linux/sysfs.h> | ||
34 | #include <asm/io.h> | ||
35 | |||
36 | static struct platform_device *pdev; | ||
37 | |||
38 | #define DRVNAME "pc87427" | ||
39 | |||
40 | /* The lock mutex protects both the I/O accesses (needed because the | ||
41 | device is using banked registers) and the register cache (needed to keep | ||
42 | the data in the registers and the cache in sync at any time). */ | ||
43 | struct pc87427_data { | ||
44 | struct class_device *class_dev; | ||
45 | struct mutex lock; | ||
46 | int address[2]; | ||
47 | const char *name; | ||
48 | |||
49 | unsigned long last_updated; /* in jiffies */ | ||
50 | u8 fan_enabled; /* bit vector */ | ||
51 | u16 fan[8]; /* register values */ | ||
52 | u16 fan_min[8]; /* register values */ | ||
53 | u8 fan_status[8]; /* register values */ | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * Super-I/O registers and operations | ||
58 | */ | ||
59 | |||
60 | #define SIOREG_LDSEL 0x07 /* Logical device select */ | ||
61 | #define SIOREG_DEVID 0x20 /* Device ID */ | ||
62 | #define SIOREG_ACT 0x30 /* Device activation */ | ||
63 | #define SIOREG_MAP 0x50 /* I/O or memory mapping */ | ||
64 | #define SIOREG_IOBASE 0x60 /* I/O base address */ | ||
65 | |||
66 | static const u8 logdev[2] = { 0x09, 0x14 }; | ||
67 | static const char *logdev_str[2] = { DRVNAME " FMC", DRVNAME " HMC" }; | ||
68 | #define LD_FAN 0 | ||
69 | #define LD_IN 1 | ||
70 | #define LD_TEMP 1 | ||
71 | |||
72 | static inline void superio_outb(int sioaddr, int reg, int val) | ||
73 | { | ||
74 | outb(reg, sioaddr); | ||
75 | outb(val, sioaddr + 1); | ||
76 | } | ||
77 | |||
78 | static inline int superio_inb(int sioaddr, int reg) | ||
79 | { | ||
80 | outb(reg, sioaddr); | ||
81 | return inb(sioaddr + 1); | ||
82 | } | ||
83 | |||
84 | static inline void superio_exit(int sioaddr) | ||
85 | { | ||
86 | outb(0x02, sioaddr); | ||
87 | outb(0x02, sioaddr + 1); | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Logical devices | ||
92 | */ | ||
93 | |||
94 | #define REGION_LENGTH 32 | ||
95 | #define PC87427_REG_BANK 0x0f | ||
96 | #define BANK_FM(nr) (nr) | ||
97 | #define BANK_FT(nr) (0x08 + (nr)) | ||
98 | #define BANK_FC(nr) (0x10 + (nr) * 2) | ||
99 | |||
100 | /* | ||
101 | * I/O access functions | ||
102 | */ | ||
103 | |||
104 | /* ldi is the logical device index */ | ||
105 | static inline int pc87427_read8(struct pc87427_data *data, u8 ldi, u8 reg) | ||
106 | { | ||
107 | return inb(data->address[ldi] + reg); | ||
108 | } | ||
109 | |||
110 | /* Must be called with data->lock held, except during init */ | ||
111 | static inline int pc87427_read8_bank(struct pc87427_data *data, u8 ldi, | ||
112 | u8 bank, u8 reg) | ||
113 | { | ||
114 | outb(bank, data->address[ldi] + PC87427_REG_BANK); | ||
115 | return inb(data->address[ldi] + reg); | ||
116 | } | ||
117 | |||
118 | /* Must be called with data->lock held, except during init */ | ||
119 | static inline void pc87427_write8_bank(struct pc87427_data *data, u8 ldi, | ||
120 | u8 bank, u8 reg, u8 value) | ||
121 | { | ||
122 | outb(bank, data->address[ldi] + PC87427_REG_BANK); | ||
123 | outb(value, data->address[ldi] + reg); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Fan registers and conversions | ||
128 | */ | ||
129 | |||
130 | /* fan data registers are 16-bit wide */ | ||
131 | #define PC87427_REG_FAN 0x12 | ||
132 | #define PC87427_REG_FAN_MIN 0x14 | ||
133 | #define PC87427_REG_FAN_STATUS 0x10 | ||
134 | |||
135 | #define FAN_STATUS_STALL (1 << 3) | ||
136 | #define FAN_STATUS_LOSPD (1 << 1) | ||
137 | #define FAN_STATUS_MONEN (1 << 0) | ||
138 | |||
139 | /* Dedicated function to read all registers related to a given fan input. | ||
140 | This saves us quite a few locks and bank selections. | ||
141 | Must be called with data->lock held. | ||
142 | nr is from 0 to 7 */ | ||
143 | static void pc87427_readall_fan(struct pc87427_data *data, u8 nr) | ||
144 | { | ||
145 | int iobase = data->address[LD_FAN]; | ||
146 | |||
147 | outb(BANK_FM(nr), iobase + PC87427_REG_BANK); | ||
148 | data->fan[nr] = inw(iobase + PC87427_REG_FAN); | ||
149 | data->fan_min[nr] = inw(iobase + PC87427_REG_FAN_MIN); | ||
150 | data->fan_status[nr] = inb(iobase + PC87427_REG_FAN_STATUS); | ||
151 | /* Clear fan alarm bits */ | ||
152 | outb(data->fan_status[nr], iobase + PC87427_REG_FAN_STATUS); | ||
153 | } | ||
154 | |||
155 | /* The 2 LSB of fan speed registers are used for something different. | ||
156 | The actual 2 LSB of the measurements are not available. */ | ||
157 | static inline unsigned long fan_from_reg(u16 reg) | ||
158 | { | ||
159 | reg &= 0xfffc; | ||
160 | if (reg == 0x0000 || reg == 0xfffc) | ||
161 | return 0; | ||
162 | return 5400000UL / reg; | ||
163 | } | ||
164 | |||
165 | /* The 2 LSB of the fan speed limit registers are not significant. */ | ||
166 | static inline u16 fan_to_reg(unsigned long val) | ||
167 | { | ||
168 | if (val < 83UL) | ||
169 | return 0xffff; | ||
170 | if (val >= 1350000UL) | ||
171 | return 0x0004; | ||
172 | return ((1350000UL + val / 2) / val) << 2; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * Data interface | ||
177 | */ | ||
178 | |||
179 | static struct pc87427_data *pc87427_update_device(struct device *dev) | ||
180 | { | ||
181 | struct pc87427_data *data = dev_get_drvdata(dev); | ||
182 | int i; | ||
183 | |||
184 | mutex_lock(&data->lock); | ||
185 | if (!time_after(jiffies, data->last_updated + HZ) | ||
186 | && data->last_updated) | ||
187 | goto done; | ||
188 | |||
189 | /* Fans */ | ||
190 | for (i = 0; i < 8; i++) { | ||
191 | if (!(data->fan_enabled & (1 << i))) | ||
192 | continue; | ||
193 | pc87427_readall_fan(data, i); | ||
194 | } | ||
195 | data->last_updated = jiffies; | ||
196 | |||
197 | done: | ||
198 | mutex_unlock(&data->lock); | ||
199 | return data; | ||
200 | } | ||
201 | |||
202 | static ssize_t show_fan_input(struct device *dev, struct device_attribute | ||
203 | *devattr, char *buf) | ||
204 | { | ||
205 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
206 | struct pc87427_data *data = pc87427_update_device(dev); | ||
207 | int nr = attr->index; | ||
208 | |||
209 | return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr])); | ||
210 | } | ||
211 | |||
212 | static ssize_t show_fan_min(struct device *dev, struct device_attribute | ||
213 | *devattr, char *buf) | ||
214 | { | ||
215 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
216 | struct pc87427_data *data = pc87427_update_device(dev); | ||
217 | int nr = attr->index; | ||
218 | |||
219 | return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr])); | ||
220 | } | ||
221 | |||
222 | static ssize_t show_fan_alarm(struct device *dev, struct device_attribute | ||
223 | *devattr, char *buf) | ||
224 | { | ||
225 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
226 | struct pc87427_data *data = pc87427_update_device(dev); | ||
227 | int nr = attr->index; | ||
228 | |||
229 | return sprintf(buf, "%d\n", !!(data->fan_status[nr] | ||
230 | & FAN_STATUS_LOSPD)); | ||
231 | } | ||
232 | |||
233 | static ssize_t show_fan_fault(struct device *dev, struct device_attribute | ||
234 | *devattr, char *buf) | ||
235 | { | ||
236 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
237 | struct pc87427_data *data = pc87427_update_device(dev); | ||
238 | int nr = attr->index; | ||
239 | |||
240 | return sprintf(buf, "%d\n", !!(data->fan_status[nr] | ||
241 | & FAN_STATUS_STALL)); | ||
242 | } | ||
243 | |||
244 | static ssize_t set_fan_min(struct device *dev, struct device_attribute | ||
245 | *devattr, const char *buf, size_t count) | ||
246 | { | ||
247 | struct pc87427_data *data = dev_get_drvdata(dev); | ||
248 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
249 | int nr = attr->index; | ||
250 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
251 | int iobase = data->address[LD_FAN]; | ||
252 | |||
253 | mutex_lock(&data->lock); | ||
254 | outb(BANK_FM(nr), iobase + PC87427_REG_BANK); | ||
255 | /* The low speed limit registers are read-only while monitoring | ||
256 | is enabled, so we have to disable monitoring, then change the | ||
257 | limit, and finally enable monitoring again. */ | ||
258 | outb(0, iobase + PC87427_REG_FAN_STATUS); | ||
259 | data->fan_min[nr] = fan_to_reg(val); | ||
260 | outw(data->fan_min[nr], iobase + PC87427_REG_FAN_MIN); | ||
261 | outb(FAN_STATUS_MONEN, iobase + PC87427_REG_FAN_STATUS); | ||
262 | mutex_unlock(&data->lock); | ||
263 | |||
264 | return count; | ||
265 | } | ||
266 | |||
267 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0); | ||
268 | static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1); | ||
269 | static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_input, NULL, 2); | ||
270 | static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan_input, NULL, 3); | ||
271 | static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan_input, NULL, 4); | ||
272 | static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan_input, NULL, 5); | ||
273 | static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan_input, NULL, 6); | ||
274 | static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan_input, NULL, 7); | ||
275 | |||
276 | static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, | ||
277 | show_fan_min, set_fan_min, 0); | ||
278 | static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, | ||
279 | show_fan_min, set_fan_min, 1); | ||
280 | static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, | ||
281 | show_fan_min, set_fan_min, 2); | ||
282 | static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, | ||
283 | show_fan_min, set_fan_min, 3); | ||
284 | static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO, | ||
285 | show_fan_min, set_fan_min, 4); | ||
286 | static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO, | ||
287 | show_fan_min, set_fan_min, 5); | ||
288 | static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO, | ||
289 | show_fan_min, set_fan_min, 6); | ||
290 | static SENSOR_DEVICE_ATTR(fan8_min, S_IWUSR | S_IRUGO, | ||
291 | show_fan_min, set_fan_min, 7); | ||
292 | |||
293 | static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0); | ||
294 | static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1); | ||
295 | static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2); | ||
296 | static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3); | ||
297 | static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_fan_alarm, NULL, 4); | ||
298 | static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_fan_alarm, NULL, 5); | ||
299 | static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_fan_alarm, NULL, 6); | ||
300 | static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_fan_alarm, NULL, 7); | ||
301 | |||
302 | static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0); | ||
303 | static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1); | ||
304 | static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2); | ||
305 | static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3); | ||
306 | static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, show_fan_fault, NULL, 4); | ||
307 | static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, show_fan_fault, NULL, 5); | ||
308 | static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, show_fan_fault, NULL, 6); | ||
309 | static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, show_fan_fault, NULL, 7); | ||
310 | |||
311 | static struct attribute *pc87427_attributes_fan[8][5] = { | ||
312 | { | ||
313 | &sensor_dev_attr_fan1_input.dev_attr.attr, | ||
314 | &sensor_dev_attr_fan1_min.dev_attr.attr, | ||
315 | &sensor_dev_attr_fan1_alarm.dev_attr.attr, | ||
316 | &sensor_dev_attr_fan1_fault.dev_attr.attr, | ||
317 | NULL | ||
318 | }, { | ||
319 | &sensor_dev_attr_fan2_input.dev_attr.attr, | ||
320 | &sensor_dev_attr_fan2_min.dev_attr.attr, | ||
321 | &sensor_dev_attr_fan2_alarm.dev_attr.attr, | ||
322 | &sensor_dev_attr_fan2_fault.dev_attr.attr, | ||
323 | NULL | ||
324 | }, { | ||
325 | &sensor_dev_attr_fan3_input.dev_attr.attr, | ||
326 | &sensor_dev_attr_fan3_min.dev_attr.attr, | ||
327 | &sensor_dev_attr_fan3_alarm.dev_attr.attr, | ||
328 | &sensor_dev_attr_fan3_fault.dev_attr.attr, | ||
329 | NULL | ||
330 | }, { | ||
331 | &sensor_dev_attr_fan4_input.dev_attr.attr, | ||
332 | &sensor_dev_attr_fan4_min.dev_attr.attr, | ||
333 | &sensor_dev_attr_fan4_alarm.dev_attr.attr, | ||
334 | &sensor_dev_attr_fan4_fault.dev_attr.attr, | ||
335 | NULL | ||
336 | }, { | ||
337 | &sensor_dev_attr_fan5_input.dev_attr.attr, | ||
338 | &sensor_dev_attr_fan5_min.dev_attr.attr, | ||
339 | &sensor_dev_attr_fan5_alarm.dev_attr.attr, | ||
340 | &sensor_dev_attr_fan5_fault.dev_attr.attr, | ||
341 | NULL | ||
342 | }, { | ||
343 | &sensor_dev_attr_fan6_input.dev_attr.attr, | ||
344 | &sensor_dev_attr_fan6_min.dev_attr.attr, | ||
345 | &sensor_dev_attr_fan6_alarm.dev_attr.attr, | ||
346 | &sensor_dev_attr_fan6_fault.dev_attr.attr, | ||
347 | NULL | ||
348 | }, { | ||
349 | &sensor_dev_attr_fan7_input.dev_attr.attr, | ||
350 | &sensor_dev_attr_fan7_min.dev_attr.attr, | ||
351 | &sensor_dev_attr_fan7_alarm.dev_attr.attr, | ||
352 | &sensor_dev_attr_fan7_fault.dev_attr.attr, | ||
353 | NULL | ||
354 | }, { | ||
355 | &sensor_dev_attr_fan8_input.dev_attr.attr, | ||
356 | &sensor_dev_attr_fan8_min.dev_attr.attr, | ||
357 | &sensor_dev_attr_fan8_alarm.dev_attr.attr, | ||
358 | &sensor_dev_attr_fan8_fault.dev_attr.attr, | ||
359 | NULL | ||
360 | } | ||
361 | }; | ||
362 | |||
363 | static const struct attribute_group pc87427_group_fan[8] = { | ||
364 | { .attrs = pc87427_attributes_fan[0] }, | ||
365 | { .attrs = pc87427_attributes_fan[1] }, | ||
366 | { .attrs = pc87427_attributes_fan[2] }, | ||
367 | { .attrs = pc87427_attributes_fan[3] }, | ||
368 | { .attrs = pc87427_attributes_fan[4] }, | ||
369 | { .attrs = pc87427_attributes_fan[5] }, | ||
370 | { .attrs = pc87427_attributes_fan[6] }, | ||
371 | { .attrs = pc87427_attributes_fan[7] }, | ||
372 | }; | ||
373 | |||
374 | static ssize_t show_name(struct device *dev, struct device_attribute | ||
375 | *devattr, char *buf) | ||
376 | { | ||
377 | struct pc87427_data *data = dev_get_drvdata(dev); | ||
378 | |||
379 | return sprintf(buf, "%s\n", data->name); | ||
380 | } | ||
381 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); | ||
382 | |||
383 | |||
384 | /* | ||
385 | * Device detection, attach and detach | ||
386 | */ | ||
387 | |||
388 | static void __devinit pc87427_init_device(struct device *dev) | ||
389 | { | ||
390 | struct pc87427_data *data = dev_get_drvdata(dev); | ||
391 | int i; | ||
392 | u8 reg; | ||
393 | |||
394 | /* The FMC module should be ready */ | ||
395 | reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK); | ||
396 | if (!(reg & 0x80)) | ||
397 | dev_warn(dev, "FMC module not ready!\n"); | ||
398 | |||
399 | /* Check which fans are enabled */ | ||
400 | for (i = 0; i < 8; i++) { | ||
401 | reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i), | ||
402 | PC87427_REG_FAN_STATUS); | ||
403 | if (reg & FAN_STATUS_MONEN) | ||
404 | data->fan_enabled |= (1 << i); | ||
405 | } | ||
406 | |||
407 | if (!data->fan_enabled) { | ||
408 | dev_dbg(dev, "Enabling all fan inputs\n"); | ||
409 | for (i = 0; i < 8; i++) | ||
410 | pc87427_write8_bank(data, LD_FAN, BANK_FM(i), | ||
411 | PC87427_REG_FAN_STATUS, | ||
412 | FAN_STATUS_MONEN); | ||
413 | data->fan_enabled = 0xff; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | static int __devinit pc87427_probe(struct platform_device *pdev) | ||
418 | { | ||
419 | struct pc87427_data *data; | ||
420 | struct resource *res; | ||
421 | int i, err; | ||
422 | |||
423 | if (!(data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL))) { | ||
424 | err = -ENOMEM; | ||
425 | printk(KERN_ERR DRVNAME ": Out of memory\n"); | ||
426 | goto exit; | ||
427 | } | ||
428 | |||
429 | /* This will need to be revisited when we add support for | ||
430 | temperature and voltage monitoring. */ | ||
431 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
432 | data->address[0] = res->start; | ||
433 | |||
434 | mutex_init(&data->lock); | ||
435 | data->name = "pc87427"; | ||
436 | platform_set_drvdata(pdev, data); | ||
437 | pc87427_init_device(&pdev->dev); | ||
438 | |||
439 | /* Register sysfs hooks */ | ||
440 | if ((err = device_create_file(&pdev->dev, &dev_attr_name))) | ||
441 | goto exit_kfree; | ||
442 | for (i = 0; i < 8; i++) { | ||
443 | if (!(data->fan_enabled & (1 << i))) | ||
444 | continue; | ||
445 | if ((err = sysfs_create_group(&pdev->dev.kobj, | ||
446 | &pc87427_group_fan[i]))) | ||
447 | goto exit_remove_files; | ||
448 | } | ||
449 | |||
450 | data->class_dev = hwmon_device_register(&pdev->dev); | ||
451 | if (IS_ERR(data->class_dev)) { | ||
452 | err = PTR_ERR(data->class_dev); | ||
453 | dev_err(&pdev->dev, "Class registration failed (%d)\n", err); | ||
454 | goto exit_remove_files; | ||
455 | } | ||
456 | |||
457 | return 0; | ||
458 | |||
459 | exit_remove_files: | ||
460 | for (i = 0; i < 8; i++) { | ||
461 | if (!(data->fan_enabled & (1 << i))) | ||
462 | continue; | ||
463 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); | ||
464 | } | ||
465 | exit_kfree: | ||
466 | platform_set_drvdata(pdev, NULL); | ||
467 | kfree(data); | ||
468 | exit: | ||
469 | return err; | ||
470 | } | ||
471 | |||
472 | static int __devexit pc87427_remove(struct platform_device *pdev) | ||
473 | { | ||
474 | struct pc87427_data *data = platform_get_drvdata(pdev); | ||
475 | int i; | ||
476 | |||
477 | platform_set_drvdata(pdev, NULL); | ||
478 | hwmon_device_unregister(data->class_dev); | ||
479 | device_remove_file(&pdev->dev, &dev_attr_name); | ||
480 | for (i = 0; i < 8; i++) { | ||
481 | if (!(data->fan_enabled & (1 << i))) | ||
482 | continue; | ||
483 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); | ||
484 | } | ||
485 | kfree(data); | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | |||
491 | static struct platform_driver pc87427_driver = { | ||
492 | .driver = { | ||
493 | .owner = THIS_MODULE, | ||
494 | .name = DRVNAME, | ||
495 | }, | ||
496 | .probe = pc87427_probe, | ||
497 | .remove = __devexit_p(pc87427_remove), | ||
498 | }; | ||
499 | |||
500 | static int __init pc87427_device_add(unsigned short address) | ||
501 | { | ||
502 | struct resource res = { | ||
503 | .start = address, | ||
504 | .end = address + REGION_LENGTH - 1, | ||
505 | .name = logdev_str[0], | ||
506 | .flags = IORESOURCE_IO, | ||
507 | }; | ||
508 | int err; | ||
509 | |||
510 | pdev = platform_device_alloc(DRVNAME, address); | ||
511 | if (!pdev) { | ||
512 | err = -ENOMEM; | ||
513 | printk(KERN_ERR DRVNAME ": Device allocation failed\n"); | ||
514 | goto exit; | ||
515 | } | ||
516 | |||
517 | err = platform_device_add_resources(pdev, &res, 1); | ||
518 | if (err) { | ||
519 | printk(KERN_ERR DRVNAME ": Device resource addition failed " | ||
520 | "(%d)\n", err); | ||
521 | goto exit_device_put; | ||
522 | } | ||
523 | |||
524 | err = platform_device_add(pdev); | ||
525 | if (err) { | ||
526 | printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", | ||
527 | err); | ||
528 | goto exit_device_put; | ||
529 | } | ||
530 | |||
531 | return 0; | ||
532 | |||
533 | exit_device_put: | ||
534 | platform_device_put(pdev); | ||
535 | exit: | ||
536 | return err; | ||
537 | } | ||
538 | |||
539 | static int __init pc87427_find(int sioaddr, unsigned short *address) | ||
540 | { | ||
541 | u16 val; | ||
542 | int i, err = 0; | ||
543 | |||
544 | /* Identify device */ | ||
545 | val = superio_inb(sioaddr, SIOREG_DEVID); | ||
546 | if (val != 0xf2) { /* PC87427 */ | ||
547 | err = -ENODEV; | ||
548 | goto exit; | ||
549 | } | ||
550 | |||
551 | for (i = 0; i < 2; i++) { | ||
552 | address[i] = 0; | ||
553 | /* Select logical device */ | ||
554 | superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]); | ||
555 | |||
556 | val = superio_inb(sioaddr, SIOREG_ACT); | ||
557 | if (!(val & 0x01)) { | ||
558 | printk(KERN_INFO DRVNAME ": Logical device 0x%02x " | ||
559 | "not activated\n", logdev[i]); | ||
560 | continue; | ||
561 | } | ||
562 | |||
563 | val = superio_inb(sioaddr, SIOREG_MAP); | ||
564 | if (val & 0x01) { | ||
565 | printk(KERN_WARNING DRVNAME ": Logical device 0x%02x " | ||
566 | "is memory-mapped, can't use\n", logdev[i]); | ||
567 | continue; | ||
568 | } | ||
569 | |||
570 | val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8) | ||
571 | | superio_inb(sioaddr, SIOREG_IOBASE + 1); | ||
572 | if (!val) { | ||
573 | printk(KERN_INFO DRVNAME ": I/O base address not set " | ||
574 | "for logical device 0x%02x\n", logdev[i]); | ||
575 | continue; | ||
576 | } | ||
577 | address[i] = val; | ||
578 | } | ||
579 | |||
580 | exit: | ||
581 | superio_exit(sioaddr); | ||
582 | return err; | ||
583 | } | ||
584 | |||
585 | static int __init pc87427_init(void) | ||
586 | { | ||
587 | int err; | ||
588 | unsigned short address[2]; | ||
589 | |||
590 | if (pc87427_find(0x2e, address) | ||
591 | && pc87427_find(0x4e, address)) | ||
592 | return -ENODEV; | ||
593 | |||
594 | /* For now the driver only handles fans so we only care about the | ||
595 | first address. */ | ||
596 | if (!address[0]) | ||
597 | return -ENODEV; | ||
598 | |||
599 | err = platform_driver_register(&pc87427_driver); | ||
600 | if (err) | ||
601 | goto exit; | ||
602 | |||
603 | /* Sets global pdev as a side effect */ | ||
604 | err = pc87427_device_add(address[0]); | ||
605 | if (err) | ||
606 | goto exit_driver; | ||
607 | |||
608 | return 0; | ||
609 | |||
610 | exit_driver: | ||
611 | platform_driver_unregister(&pc87427_driver); | ||
612 | exit: | ||
613 | return err; | ||
614 | } | ||
615 | |||
616 | static void __exit pc87427_exit(void) | ||
617 | { | ||
618 | platform_device_unregister(pdev); | ||
619 | platform_driver_unregister(&pc87427_driver); | ||
620 | } | ||
621 | |||
622 | MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); | ||
623 | MODULE_DESCRIPTION("PC87427 hardware monitoring driver"); | ||
624 | MODULE_LICENSE("GPL"); | ||
625 | |||
626 | module_init(pc87427_init); | ||
627 | module_exit(pc87427_exit); | ||
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 2257806d0102..212a1558c63b 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c | |||
@@ -3,7 +3,7 @@ | |||
3 | the Winbond W83627EHF Super-I/O chip | 3 | the Winbond W83627EHF Super-I/O chip |
4 | Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> | 4 | Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> |
5 | Copyright (C) 2006 Yuan Mu (Winbond), | 5 | Copyright (C) 2006 Yuan Mu (Winbond), |
6 | Rudolf Marek <r.marek@sh.cvut.cz> | 6 | Rudolf Marek <r.marek@assembler.cz> |
7 | David Hubbard <david.c.hubbard@gmail.com> | 7 | David Hubbard <david.c.hubbard@gmail.com> |
8 | 8 | ||
9 | Shamelessly ripped from the w83627hf driver | 9 | Shamelessly ripped from the w83627hf driver |
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 4e108262576f..b0fa296740d1 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c | |||
@@ -3,7 +3,7 @@ | |||
3 | monitoring | 3 | monitoring |
4 | Copyright (C) 2004, 2005 Winbond Electronics Corp. | 4 | Copyright (C) 2004, 2005 Winbond Electronics Corp. |
5 | Chunhao Huang <DZShen@Winbond.com.tw>, | 5 | Chunhao Huang <DZShen@Winbond.com.tw>, |
6 | Rudolf Marek <r.marek@sh.cvut.cz> | 6 | Rudolf Marek <r.marek@assembler.cz> |
7 | 7 | ||
8 | This program is free software; you can redistribute it and/or modify | 8 | This program is free software; you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by | 9 | it under the terms of the GNU General Public License as published by |
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c new file mode 100644 index 000000000000..c12ac5abc2bb --- /dev/null +++ b/drivers/hwmon/w83793.c | |||
@@ -0,0 +1,1609 @@ | |||
1 | /* | ||
2 | w83793.c - Linux kernel driver for hardware monitoring | ||
3 | Copyright (C) 2006 Winbond Electronics Corp. | ||
4 | Yuan Mu | ||
5 | Rudolf Marek <r.marek@assembler.cz> | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation - version 2. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | 02110-1301 USA. | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | Supports following chips: | ||
24 | |||
25 | Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA | ||
26 | w83793 10 12 8 6 0x7b 0x5ca3 yes no | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/i2c.h> | ||
33 | #include <linux/hwmon.h> | ||
34 | #include <linux/hwmon-vid.h> | ||
35 | #include <linux/hwmon-sysfs.h> | ||
36 | #include <linux/err.h> | ||
37 | #include <linux/mutex.h> | ||
38 | |||
39 | /* Addresses to scan */ | ||
40 | static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; | ||
41 | |||
42 | /* Insmod parameters */ | ||
43 | I2C_CLIENT_INSMOD_1(w83793); | ||
44 | I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " | ||
45 | "{bus, clientaddr, subclientaddr1, subclientaddr2}"); | ||
46 | |||
47 | static int reset; | ||
48 | module_param(reset, bool, 0); | ||
49 | MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); | ||
50 | |||
51 | /* | ||
52 | Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved | ||
53 | as ID, Bank Select registers | ||
54 | */ | ||
55 | #define W83793_REG_BANKSEL 0x00 | ||
56 | #define W83793_REG_VENDORID 0x0d | ||
57 | #define W83793_REG_CHIPID 0x0e | ||
58 | #define W83793_REG_DEVICEID 0x0f | ||
59 | |||
60 | #define W83793_REG_CONFIG 0x40 | ||
61 | #define W83793_REG_MFC 0x58 | ||
62 | #define W83793_REG_FANIN_CTRL 0x5c | ||
63 | #define W83793_REG_FANIN_SEL 0x5d | ||
64 | #define W83793_REG_I2C_ADDR 0x0b | ||
65 | #define W83793_REG_I2C_SUBADDR 0x0c | ||
66 | #define W83793_REG_VID_INA 0x05 | ||
67 | #define W83793_REG_VID_INB 0x06 | ||
68 | #define W83793_REG_VID_LATCHA 0x07 | ||
69 | #define W83793_REG_VID_LATCHB 0x08 | ||
70 | #define W83793_REG_VID_CTRL 0x59 | ||
71 | |||
72 | static u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f }; | ||
73 | |||
74 | #define TEMP_READ 0 | ||
75 | #define TEMP_CRIT 1 | ||
76 | #define TEMP_CRIT_HYST 2 | ||
77 | #define TEMP_WARN 3 | ||
78 | #define TEMP_WARN_HYST 4 | ||
79 | /* only crit and crit_hyst affect real-time alarm status | ||
80 | current crit crit_hyst warn warn_hyst */ | ||
81 | static u16 W83793_REG_TEMP[][5] = { | ||
82 | {0x1c, 0x78, 0x79, 0x7a, 0x7b}, | ||
83 | {0x1d, 0x7c, 0x7d, 0x7e, 0x7f}, | ||
84 | {0x1e, 0x80, 0x81, 0x82, 0x83}, | ||
85 | {0x1f, 0x84, 0x85, 0x86, 0x87}, | ||
86 | {0x20, 0x88, 0x89, 0x8a, 0x8b}, | ||
87 | {0x21, 0x8c, 0x8d, 0x8e, 0x8f}, | ||
88 | }; | ||
89 | |||
90 | #define W83793_REG_TEMP_LOW_BITS 0x22 | ||
91 | |||
92 | #define W83793_REG_BEEP(index) (0x53 + (index)) | ||
93 | #define W83793_REG_ALARM(index) (0x4b + (index)) | ||
94 | |||
95 | #define W83793_REG_CLR_CHASSIS 0x4a /* SMI MASK4 */ | ||
96 | #define W83793_REG_IRQ_CTRL 0x50 | ||
97 | #define W83793_REG_OVT_CTRL 0x51 | ||
98 | #define W83793_REG_OVT_BEEP 0x52 | ||
99 | |||
100 | #define IN_READ 0 | ||
101 | #define IN_MAX 1 | ||
102 | #define IN_LOW 2 | ||
103 | static const u16 W83793_REG_IN[][3] = { | ||
104 | /* Current, High, Low */ | ||
105 | {0x10, 0x60, 0x61}, /* Vcore A */ | ||
106 | {0x11, 0x62, 0x63}, /* Vcore B */ | ||
107 | {0x12, 0x64, 0x65}, /* Vtt */ | ||
108 | {0x14, 0x6a, 0x6b}, /* VSEN1 */ | ||
109 | {0x15, 0x6c, 0x6d}, /* VSEN2 */ | ||
110 | {0x16, 0x6e, 0x6f}, /* +3VSEN */ | ||
111 | {0x17, 0x70, 0x71}, /* +12VSEN */ | ||
112 | {0x18, 0x72, 0x73}, /* 5VDD */ | ||
113 | {0x19, 0x74, 0x75}, /* 5VSB */ | ||
114 | {0x1a, 0x76, 0x77}, /* VBAT */ | ||
115 | }; | ||
116 | |||
117 | /* Low Bits of Vcore A/B Vtt Read/High/Low */ | ||
118 | static const u16 W83793_REG_IN_LOW_BITS[] = { 0x1b, 0x68, 0x69 }; | ||
119 | static u8 scale_in[] = { 2, 2, 2, 16, 16, 16, 8, 24, 24, 16 }; | ||
120 | |||
121 | #define W83793_REG_FAN(index) (0x23 + 2 * (index)) /* High byte */ | ||
122 | #define W83793_REG_FAN_MIN(index) (0x90 + 2 * (index)) /* High byte */ | ||
123 | |||
124 | #define W83793_REG_PWM_DEFAULT 0xb2 | ||
125 | #define W83793_REG_PWM_ENABLE 0x207 | ||
126 | #define W83793_REG_PWM_UPTIME 0xc3 /* Unit in 0.1 second */ | ||
127 | #define W83793_REG_PWM_DOWNTIME 0xc4 /* Unit in 0.1 second */ | ||
128 | #define W83793_REG_TEMP_CRITICAL 0xc5 | ||
129 | |||
130 | #define PWM_DUTY 0 | ||
131 | #define PWM_START 1 | ||
132 | #define PWM_NONSTOP 2 | ||
133 | #define W83793_REG_PWM(index, nr) (((nr) == 0 ? 0xb3 : \ | ||
134 | (nr) == 1 ? 0x220 : 0x218) + (index)) | ||
135 | |||
136 | /* bit field, fan1 is bit0, fan2 is bit1 ... */ | ||
137 | #define W83793_REG_TEMP_FAN_MAP(index) (0x201 + (index)) | ||
138 | #define W83793_REG_TEMP_TOL(index) (0x208 + (index)) | ||
139 | #define W83793_REG_TEMP_CRUISE(index) (0x210 + (index)) | ||
140 | #define W83793_REG_PWM_STOP_TIME(index) (0x228 + (index)) | ||
141 | #define W83793_REG_SF2_TEMP(index, nr) (0x230 + ((index) << 4) + (nr)) | ||
142 | #define W83793_REG_SF2_PWM(index, nr) (0x238 + ((index) << 4) + (nr)) | ||
143 | |||
144 | static inline unsigned long FAN_FROM_REG(u16 val) | ||
145 | { | ||
146 | if ((val >= 0xfff) || (val == 0)) | ||
147 | return 0; | ||
148 | return (1350000UL / val); | ||
149 | } | ||
150 | |||
151 | static inline u16 FAN_TO_REG(long rpm) | ||
152 | { | ||
153 | if (rpm <= 0) | ||
154 | return 0x0fff; | ||
155 | return SENSORS_LIMIT((1350000 + (rpm >> 1)) / rpm, 1, 0xffe); | ||
156 | } | ||
157 | |||
158 | static inline unsigned long TIME_FROM_REG(u8 reg) | ||
159 | { | ||
160 | return (reg * 100); | ||
161 | } | ||
162 | |||
163 | static inline u8 TIME_TO_REG(unsigned long val) | ||
164 | { | ||
165 | return SENSORS_LIMIT((val + 50) / 100, 0, 0xff); | ||
166 | } | ||
167 | |||
168 | static inline long TEMP_FROM_REG(s8 reg) | ||
169 | { | ||
170 | return (reg * 1000); | ||
171 | } | ||
172 | |||
173 | static inline s8 TEMP_TO_REG(long val, s8 min, s8 max) | ||
174 | { | ||
175 | return SENSORS_LIMIT((val + (val < 0 ? -500 : 500)) / 1000, min, max); | ||
176 | } | ||
177 | |||
178 | struct w83793_data { | ||
179 | struct i2c_client client; | ||
180 | struct i2c_client *lm75[2]; | ||
181 | struct class_device *class_dev; | ||
182 | struct mutex update_lock; | ||
183 | char valid; /* !=0 if following fields are valid */ | ||
184 | unsigned long last_updated; /* In jiffies */ | ||
185 | unsigned long last_nonvolatile; /* In jiffies, last time we update the | ||
186 | nonvolatile registers */ | ||
187 | |||
188 | u8 bank; | ||
189 | u8 vrm; | ||
190 | u8 vid[2]; | ||
191 | u8 in[10][3]; /* Register value, read/high/low */ | ||
192 | u8 in_low_bits[3]; /* Additional resolution for VCore A/B Vtt */ | ||
193 | |||
194 | u16 has_fan; /* Only fan1- fan5 has own pins */ | ||
195 | u16 fan[12]; /* Register value combine */ | ||
196 | u16 fan_min[12]; /* Register value combine */ | ||
197 | |||
198 | s8 temp[6][5]; /* current, crit, crit_hyst,warn, warn_hyst */ | ||
199 | u8 temp_low_bits; /* Additional resolution TD1-TD4 */ | ||
200 | u8 temp_mode[2]; /* byte 0: Temp D1-D4 mode each has 2 bits | ||
201 | byte 1: Temp R1,R2 mode, each has 1 bit */ | ||
202 | u8 temp_critical; /* If reached all fan will be at full speed */ | ||
203 | u8 temp_fan_map[6]; /* Temp controls which pwm fan, bit field */ | ||
204 | |||
205 | u8 has_pwm; | ||
206 | u8 pwm_enable; /* Register value, each Temp has 1 bit */ | ||
207 | u8 pwm_uptime; /* Register value */ | ||
208 | u8 pwm_downtime; /* Register value */ | ||
209 | u8 pwm_default; /* All fan default pwm, next poweron valid */ | ||
210 | u8 pwm[8][3]; /* Register value */ | ||
211 | u8 pwm_stop_time[8]; | ||
212 | u8 temp_cruise[6]; | ||
213 | |||
214 | u8 alarms[5]; /* realtime status registers */ | ||
215 | u8 beeps[5]; | ||
216 | u8 beep_enable; | ||
217 | u8 tolerance[3]; /* Temp tolerance(Smart Fan I/II) */ | ||
218 | u8 sf2_pwm[6][7]; /* Smart FanII: Fan duty cycle */ | ||
219 | u8 sf2_temp[6][7]; /* Smart FanII: Temp level point */ | ||
220 | }; | ||
221 | |||
222 | static u8 w83793_read_value(struct i2c_client *client, u16 reg); | ||
223 | static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value); | ||
224 | static int w83793_attach_adapter(struct i2c_adapter *adapter); | ||
225 | static int w83793_detect(struct i2c_adapter *adapter, int address, int kind); | ||
226 | static int w83793_detach_client(struct i2c_client *client); | ||
227 | static void w83793_init_client(struct i2c_client *client); | ||
228 | static void w83793_update_nonvolatile(struct device *dev); | ||
229 | static struct w83793_data *w83793_update_device(struct device *dev); | ||
230 | |||
231 | static struct i2c_driver w83793_driver = { | ||
232 | .driver = { | ||
233 | .name = "w83793", | ||
234 | }, | ||
235 | .attach_adapter = w83793_attach_adapter, | ||
236 | .detach_client = w83793_detach_client, | ||
237 | }; | ||
238 | |||
239 | static ssize_t | ||
240 | show_vrm(struct device *dev, struct device_attribute *attr, char *buf) | ||
241 | { | ||
242 | struct i2c_client *client = to_i2c_client(dev); | ||
243 | struct w83793_data *data = i2c_get_clientdata(client); | ||
244 | |||
245 | return sprintf(buf, "%d\n", data->vrm); | ||
246 | } | ||
247 | |||
248 | static ssize_t | ||
249 | show_vid(struct device *dev, struct device_attribute *attr, char *buf) | ||
250 | { | ||
251 | struct w83793_data *data = w83793_update_device(dev); | ||
252 | struct sensor_device_attribute_2 *sensor_attr = | ||
253 | to_sensor_dev_attr_2(attr); | ||
254 | int index = sensor_attr->index; | ||
255 | |||
256 | return sprintf(buf, "%d\n", vid_from_reg(data->vid[index], data->vrm)); | ||
257 | } | ||
258 | |||
259 | static ssize_t | ||
260 | store_vrm(struct device *dev, struct device_attribute *attr, | ||
261 | const char *buf, size_t count) | ||
262 | { | ||
263 | struct i2c_client *client = to_i2c_client(dev); | ||
264 | struct w83793_data *data = i2c_get_clientdata(client); | ||
265 | |||
266 | data->vrm = simple_strtoul(buf, NULL, 10); | ||
267 | return count; | ||
268 | } | ||
269 | |||
270 | #define ALARM_STATUS 0 | ||
271 | #define BEEP_ENABLE 1 | ||
272 | static ssize_t | ||
273 | show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf) | ||
274 | { | ||
275 | struct w83793_data *data = w83793_update_device(dev); | ||
276 | struct sensor_device_attribute_2 *sensor_attr = | ||
277 | to_sensor_dev_attr_2(attr); | ||
278 | int nr = sensor_attr->nr; | ||
279 | int index = sensor_attr->index >> 3; | ||
280 | int bit = sensor_attr->index & 0x07; | ||
281 | u8 val; | ||
282 | |||
283 | if (ALARM_STATUS == nr) { | ||
284 | val = (data->alarms[index] >> (bit)) & 1; | ||
285 | } else { /* BEEP_ENABLE */ | ||
286 | val = (data->beeps[index] >> (bit)) & 1; | ||
287 | } | ||
288 | |||
289 | return sprintf(buf, "%u\n", val); | ||
290 | } | ||
291 | |||
292 | static ssize_t | ||
293 | store_beep(struct device *dev, struct device_attribute *attr, | ||
294 | const char *buf, size_t count) | ||
295 | { | ||
296 | struct i2c_client *client = to_i2c_client(dev); | ||
297 | struct w83793_data *data = i2c_get_clientdata(client); | ||
298 | struct sensor_device_attribute_2 *sensor_attr = | ||
299 | to_sensor_dev_attr_2(attr); | ||
300 | int index = sensor_attr->index >> 3; | ||
301 | int shift = sensor_attr->index & 0x07; | ||
302 | u8 beep_bit = 1 << shift; | ||
303 | u8 val; | ||
304 | |||
305 | val = simple_strtoul(buf, NULL, 10); | ||
306 | if (val != 0 && val != 1) | ||
307 | return -EINVAL; | ||
308 | |||
309 | mutex_lock(&data->update_lock); | ||
310 | data->beeps[index] = w83793_read_value(client, W83793_REG_BEEP(index)); | ||
311 | data->beeps[index] &= ~beep_bit; | ||
312 | data->beeps[index] |= val << shift; | ||
313 | w83793_write_value(client, W83793_REG_BEEP(index), data->beeps[index]); | ||
314 | mutex_unlock(&data->update_lock); | ||
315 | |||
316 | return count; | ||
317 | } | ||
318 | |||
319 | static ssize_t | ||
320 | show_beep_enable(struct device *dev, struct device_attribute *attr, char *buf) | ||
321 | { | ||
322 | struct w83793_data *data = w83793_update_device(dev); | ||
323 | return sprintf(buf, "%u\n", (data->beep_enable >> 1) & 0x01); | ||
324 | } | ||
325 | |||
326 | static ssize_t | ||
327 | store_beep_enable(struct device *dev, struct device_attribute *attr, | ||
328 | const char *buf, size_t count) | ||
329 | { | ||
330 | struct i2c_client *client = to_i2c_client(dev); | ||
331 | struct w83793_data *data = i2c_get_clientdata(client); | ||
332 | u8 val = simple_strtoul(buf, NULL, 10); | ||
333 | |||
334 | if (val != 0 && val != 1) | ||
335 | return -EINVAL; | ||
336 | |||
337 | mutex_lock(&data->update_lock); | ||
338 | data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP) | ||
339 | & 0xfd; | ||
340 | data->beep_enable |= val << 1; | ||
341 | w83793_write_value(client, W83793_REG_OVT_BEEP, data->beep_enable); | ||
342 | mutex_unlock(&data->update_lock); | ||
343 | |||
344 | return count; | ||
345 | } | ||
346 | |||
347 | /* Write any value to clear chassis alarm */ | ||
348 | static ssize_t | ||
349 | store_chassis_clear(struct device *dev, | ||
350 | struct device_attribute *attr, const char *buf, | ||
351 | size_t count) | ||
352 | { | ||
353 | struct i2c_client *client = to_i2c_client(dev); | ||
354 | struct w83793_data *data = i2c_get_clientdata(client); | ||
355 | u8 val; | ||
356 | |||
357 | mutex_lock(&data->update_lock); | ||
358 | val = w83793_read_value(client, W83793_REG_CLR_CHASSIS); | ||
359 | val |= 0x80; | ||
360 | w83793_write_value(client, W83793_REG_CLR_CHASSIS, val); | ||
361 | mutex_unlock(&data->update_lock); | ||
362 | return count; | ||
363 | } | ||
364 | |||
365 | #define FAN_INPUT 0 | ||
366 | #define FAN_MIN 1 | ||
367 | static ssize_t | ||
368 | show_fan(struct device *dev, struct device_attribute *attr, char *buf) | ||
369 | { | ||
370 | struct sensor_device_attribute_2 *sensor_attr = | ||
371 | to_sensor_dev_attr_2(attr); | ||
372 | int nr = sensor_attr->nr; | ||
373 | int index = sensor_attr->index; | ||
374 | struct w83793_data *data = w83793_update_device(dev); | ||
375 | u16 val; | ||
376 | |||
377 | if (FAN_INPUT == nr) { | ||
378 | val = data->fan[index] & 0x0fff; | ||
379 | } else { | ||
380 | val = data->fan_min[index] & 0x0fff; | ||
381 | } | ||
382 | |||
383 | return sprintf(buf, "%lu\n", FAN_FROM_REG(val)); | ||
384 | } | ||
385 | |||
386 | static ssize_t | ||
387 | store_fan_min(struct device *dev, struct device_attribute *attr, | ||
388 | const char *buf, size_t count) | ||
389 | { | ||
390 | struct sensor_device_attribute_2 *sensor_attr = | ||
391 | to_sensor_dev_attr_2(attr); | ||
392 | int index = sensor_attr->index; | ||
393 | struct i2c_client *client = to_i2c_client(dev); | ||
394 | struct w83793_data *data = i2c_get_clientdata(client); | ||
395 | u16 val = FAN_TO_REG(simple_strtoul(buf, NULL, 10)); | ||
396 | |||
397 | mutex_lock(&data->update_lock); | ||
398 | data->fan_min[index] = val; | ||
399 | w83793_write_value(client, W83793_REG_FAN_MIN(index), | ||
400 | (val >> 8) & 0xff); | ||
401 | w83793_write_value(client, W83793_REG_FAN_MIN(index) + 1, val & 0xff); | ||
402 | mutex_unlock(&data->update_lock); | ||
403 | |||
404 | return count; | ||
405 | } | ||
406 | |||
407 | #define PWM_DUTY 0 | ||
408 | #define PWM_START 1 | ||
409 | #define PWM_NONSTOP 2 | ||
410 | #define PWM_STOP_TIME 3 | ||
411 | static ssize_t | ||
412 | show_pwm(struct device *dev, struct device_attribute *attr, char *buf) | ||
413 | { | ||
414 | struct sensor_device_attribute_2 *sensor_attr = | ||
415 | to_sensor_dev_attr_2(attr); | ||
416 | struct w83793_data *data = w83793_update_device(dev); | ||
417 | u16 val; | ||
418 | int nr = sensor_attr->nr; | ||
419 | int index = sensor_attr->index; | ||
420 | |||
421 | if (PWM_STOP_TIME == nr) | ||
422 | val = TIME_FROM_REG(data->pwm_stop_time[index]); | ||
423 | else | ||
424 | val = (data->pwm[index][nr] & 0x3f) << 2; | ||
425 | |||
426 | return sprintf(buf, "%d\n", val); | ||
427 | } | ||
428 | |||
429 | static ssize_t | ||
430 | store_pwm(struct device *dev, struct device_attribute *attr, | ||
431 | const char *buf, size_t count) | ||
432 | { | ||
433 | struct i2c_client *client = to_i2c_client(dev); | ||
434 | struct w83793_data *data = i2c_get_clientdata(client); | ||
435 | struct sensor_device_attribute_2 *sensor_attr = | ||
436 | to_sensor_dev_attr_2(attr); | ||
437 | int nr = sensor_attr->nr; | ||
438 | int index = sensor_attr->index; | ||
439 | u8 val; | ||
440 | |||
441 | mutex_lock(&data->update_lock); | ||
442 | if (PWM_STOP_TIME == nr) { | ||
443 | val = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); | ||
444 | data->pwm_stop_time[index] = val; | ||
445 | w83793_write_value(client, W83793_REG_PWM_STOP_TIME(index), | ||
446 | val); | ||
447 | } else { | ||
448 | val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 0xff) | ||
449 | >> 2; | ||
450 | data->pwm[index][nr] = | ||
451 | w83793_read_value(client, W83793_REG_PWM(index, nr)) & 0xc0; | ||
452 | data->pwm[index][nr] |= val; | ||
453 | w83793_write_value(client, W83793_REG_PWM(index, nr), | ||
454 | data->pwm[index][nr]); | ||
455 | } | ||
456 | |||
457 | mutex_unlock(&data->update_lock); | ||
458 | return count; | ||
459 | } | ||
460 | |||
461 | static ssize_t | ||
462 | show_temp(struct device *dev, struct device_attribute *attr, char *buf) | ||
463 | { | ||
464 | struct sensor_device_attribute_2 *sensor_attr = | ||
465 | to_sensor_dev_attr_2(attr); | ||
466 | int nr = sensor_attr->nr; | ||
467 | int index = sensor_attr->index; | ||
468 | struct w83793_data *data = w83793_update_device(dev); | ||
469 | long temp = TEMP_FROM_REG(data->temp[index][nr]); | ||
470 | |||
471 | if (TEMP_READ == nr && index < 4) { /* Only TD1-TD4 have low bits */ | ||
472 | int low = ((data->temp_low_bits >> (index * 2)) & 0x03) * 250; | ||
473 | temp += temp > 0 ? low : -low; | ||
474 | } | ||
475 | return sprintf(buf, "%ld\n", temp); | ||
476 | } | ||
477 | |||
478 | static ssize_t | ||
479 | store_temp(struct device *dev, struct device_attribute *attr, | ||
480 | const char *buf, size_t count) | ||
481 | { | ||
482 | struct sensor_device_attribute_2 *sensor_attr = | ||
483 | to_sensor_dev_attr_2(attr); | ||
484 | int nr = sensor_attr->nr; | ||
485 | int index = sensor_attr->index; | ||
486 | struct i2c_client *client = to_i2c_client(dev); | ||
487 | struct w83793_data *data = i2c_get_clientdata(client); | ||
488 | long tmp = simple_strtol(buf, NULL, 10); | ||
489 | |||
490 | mutex_lock(&data->update_lock); | ||
491 | data->temp[index][nr] = TEMP_TO_REG(tmp, -128, 127); | ||
492 | w83793_write_value(client, W83793_REG_TEMP[index][nr], | ||
493 | data->temp[index][nr]); | ||
494 | mutex_unlock(&data->update_lock); | ||
495 | return count; | ||
496 | } | ||
497 | |||
498 | /* | ||
499 | TD1-TD4 | ||
500 | each has 4 mode:(2 bits) | ||
501 | 0: Stop monitor | ||
502 | 1: Use internal temp sensor(default) | ||
503 | 2: Use sensor in AMD CPU and get result by AMDSI | ||
504 | 3: Use sensor in Intel CPU and get result by PECI | ||
505 | |||
506 | TR1-TR2 | ||
507 | each has 2 mode:(1 bit) | ||
508 | 0: Disable temp sensor monitor | ||
509 | 1: To enable temp sensors monitor | ||
510 | */ | ||
511 | |||
512 | /* 0 disable, 5 AMDSI, 6 PECI */ | ||
513 | static u8 TO_TEMP_MODE[] = { 0, 0, 5, 6 }; | ||
514 | |||
515 | static ssize_t | ||
516 | show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) | ||
517 | { | ||
518 | struct w83793_data *data = w83793_update_device(dev); | ||
519 | struct sensor_device_attribute_2 *sensor_attr = | ||
520 | to_sensor_dev_attr_2(attr); | ||
521 | int index = sensor_attr->index; | ||
522 | u8 mask = (index < 4) ? 0x03 : 0x01; | ||
523 | u8 shift = (index < 4) ? (2 * index) : (index - 4); | ||
524 | u8 tmp; | ||
525 | index = (index < 4) ? 0 : 1; | ||
526 | |||
527 | tmp = (data->temp_mode[index] >> shift) & mask; | ||
528 | |||
529 | /* for the internal sensor, found out if diode or thermistor */ | ||
530 | if (tmp == 1) { | ||
531 | tmp = index == 0 ? 3 : 4; | ||
532 | } else { | ||
533 | tmp = TO_TEMP_MODE[tmp]; | ||
534 | } | ||
535 | |||
536 | return sprintf(buf, "%d\n", tmp); | ||
537 | } | ||
538 | |||
539 | static ssize_t | ||
540 | store_temp_mode(struct device *dev, struct device_attribute *attr, | ||
541 | const char *buf, size_t count) | ||
542 | { | ||
543 | struct i2c_client *client = to_i2c_client(dev); | ||
544 | struct w83793_data *data = i2c_get_clientdata(client); | ||
545 | struct sensor_device_attribute_2 *sensor_attr = | ||
546 | to_sensor_dev_attr_2(attr); | ||
547 | int index = sensor_attr->index; | ||
548 | u8 mask = (index < 4) ? 0x03 : 0x01; | ||
549 | u8 shift = (index < 4) ? (2 * index) : (index - 4); | ||
550 | u8 val = simple_strtoul(buf, NULL, 10); | ||
551 | |||
552 | /* transform the sysfs interface values into table above */ | ||
553 | if ((val == 5 || val == 6) && (index < 4)) { | ||
554 | val -= 3; | ||
555 | } else if ((val == 3 && index < 4) | ||
556 | || (val == 4 && index >= 4) | ||
557 | || val == 0) { | ||
558 | /* transform diode or thermistor into internal enable */ | ||
559 | val = !!val; | ||
560 | } else { | ||
561 | return -EINVAL; | ||
562 | } | ||
563 | |||
564 | index = (index < 4) ? 0 : 1; | ||
565 | mutex_lock(&data->update_lock); | ||
566 | data->temp_mode[index] = | ||
567 | w83793_read_value(client, W83793_REG_TEMP_MODE[index]); | ||
568 | data->temp_mode[index] &= ~(mask << shift); | ||
569 | data->temp_mode[index] |= val << shift; | ||
570 | w83793_write_value(client, W83793_REG_TEMP_MODE[index], | ||
571 | data->temp_mode[index]); | ||
572 | mutex_unlock(&data->update_lock); | ||
573 | |||
574 | return count; | ||
575 | } | ||
576 | |||
577 | #define SETUP_PWM_DEFAULT 0 | ||
578 | #define SETUP_PWM_UPTIME 1 /* Unit in 0.1s */ | ||
579 | #define SETUP_PWM_DOWNTIME 2 /* Unit in 0.1s */ | ||
580 | #define SETUP_TEMP_CRITICAL 3 | ||
581 | static ssize_t | ||
582 | show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) | ||
583 | { | ||
584 | struct sensor_device_attribute_2 *sensor_attr = | ||
585 | to_sensor_dev_attr_2(attr); | ||
586 | int nr = sensor_attr->nr; | ||
587 | struct w83793_data *data = w83793_update_device(dev); | ||
588 | u32 val = 0; | ||
589 | |||
590 | if (SETUP_PWM_DEFAULT == nr) { | ||
591 | val = (data->pwm_default & 0x3f) << 2; | ||
592 | } else if (SETUP_PWM_UPTIME == nr) { | ||
593 | val = TIME_FROM_REG(data->pwm_uptime); | ||
594 | } else if (SETUP_PWM_DOWNTIME == nr) { | ||
595 | val = TIME_FROM_REG(data->pwm_downtime); | ||
596 | } else if (SETUP_TEMP_CRITICAL == nr) { | ||
597 | val = TEMP_FROM_REG(data->temp_critical & 0x7f); | ||
598 | } | ||
599 | |||
600 | return sprintf(buf, "%d\n", val); | ||
601 | } | ||
602 | |||
603 | static ssize_t | ||
604 | store_sf_setup(struct device *dev, struct device_attribute *attr, | ||
605 | const char *buf, size_t count) | ||
606 | { | ||
607 | struct sensor_device_attribute_2 *sensor_attr = | ||
608 | to_sensor_dev_attr_2(attr); | ||
609 | int nr = sensor_attr->nr; | ||
610 | struct i2c_client *client = to_i2c_client(dev); | ||
611 | struct w83793_data *data = i2c_get_clientdata(client); | ||
612 | |||
613 | mutex_lock(&data->update_lock); | ||
614 | if (SETUP_PWM_DEFAULT == nr) { | ||
615 | data->pwm_default = | ||
616 | w83793_read_value(client, W83793_REG_PWM_DEFAULT) & 0xc0; | ||
617 | data->pwm_default |= SENSORS_LIMIT(simple_strtoul(buf, NULL, | ||
618 | 10), | ||
619 | 0, 0xff) >> 2; | ||
620 | w83793_write_value(client, W83793_REG_PWM_DEFAULT, | ||
621 | data->pwm_default); | ||
622 | } else if (SETUP_PWM_UPTIME == nr) { | ||
623 | data->pwm_uptime = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); | ||
624 | data->pwm_uptime += data->pwm_uptime == 0 ? 1 : 0; | ||
625 | w83793_write_value(client, W83793_REG_PWM_UPTIME, | ||
626 | data->pwm_uptime); | ||
627 | } else if (SETUP_PWM_DOWNTIME == nr) { | ||
628 | data->pwm_downtime = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); | ||
629 | data->pwm_downtime += data->pwm_downtime == 0 ? 1 : 0; | ||
630 | w83793_write_value(client, W83793_REG_PWM_DOWNTIME, | ||
631 | data->pwm_downtime); | ||
632 | } else { /* SETUP_TEMP_CRITICAL */ | ||
633 | data->temp_critical = | ||
634 | w83793_read_value(client, W83793_REG_TEMP_CRITICAL) & 0x80; | ||
635 | data->temp_critical |= TEMP_TO_REG(simple_strtol(buf, NULL, 10), | ||
636 | 0, 0x7f); | ||
637 | w83793_write_value(client, W83793_REG_TEMP_CRITICAL, | ||
638 | data->temp_critical); | ||
639 | } | ||
640 | |||
641 | mutex_unlock(&data->update_lock); | ||
642 | return count; | ||
643 | } | ||
644 | |||
645 | /* | ||
646 | Temp SmartFan control | ||
647 | TEMP_FAN_MAP | ||
648 | Temp channel control which pwm fan, bitfield, bit 0 indicate pwm1... | ||
649 | It's possible two or more temp channels control the same fan, w83793 | ||
650 | always prefers to pick the most critical request and applies it to | ||
651 | the related Fan. | ||
652 | It's possible one fan is not in any mapping of 6 temp channels, this | ||
653 | means the fan is manual mode | ||
654 | |||
655 | TEMP_PWM_ENABLE | ||
656 | Each temp channel has its own SmartFan mode, and temp channel | ||
657 | control fans that are set by TEMP_FAN_MAP | ||
658 | 0: SmartFanII mode | ||
659 | 1: Thermal Cruise Mode | ||
660 | |||
661 | TEMP_CRUISE | ||
662 | Target temperature in thermal cruise mode, w83793 will try to turn | ||
663 | fan speed to keep the temperature of target device around this | ||
664 | temperature. | ||
665 | |||
666 | TEMP_TOLERANCE | ||
667 | If Temp higher or lower than target with this tolerance, w83793 | ||
668 | will take actions to speed up or slow down the fan to keep the | ||
669 | temperature within the tolerance range. | ||
670 | */ | ||
671 | |||
672 | #define TEMP_FAN_MAP 0 | ||
673 | #define TEMP_PWM_ENABLE 1 | ||
674 | #define TEMP_CRUISE 2 | ||
675 | #define TEMP_TOLERANCE 3 | ||
676 | static ssize_t | ||
677 | show_sf_ctrl(struct device *dev, struct device_attribute *attr, char *buf) | ||
678 | { | ||
679 | struct sensor_device_attribute_2 *sensor_attr = | ||
680 | to_sensor_dev_attr_2(attr); | ||
681 | int nr = sensor_attr->nr; | ||
682 | int index = sensor_attr->index; | ||
683 | struct w83793_data *data = w83793_update_device(dev); | ||
684 | u32 val; | ||
685 | |||
686 | if (TEMP_FAN_MAP == nr) { | ||
687 | val = data->temp_fan_map[index]; | ||
688 | } else if (TEMP_PWM_ENABLE == nr) { | ||
689 | /* +2 to transfrom into 2 and 3 to conform with sysfs intf */ | ||
690 | val = ((data->pwm_enable >> index) & 0x01) + 2; | ||
691 | } else if (TEMP_CRUISE == nr) { | ||
692 | val = TEMP_FROM_REG(data->temp_cruise[index] & 0x7f); | ||
693 | } else { /* TEMP_TOLERANCE */ | ||
694 | val = data->tolerance[index >> 1] >> ((index & 0x01) ? 4 : 0); | ||
695 | val = TEMP_FROM_REG(val & 0x0f); | ||
696 | } | ||
697 | return sprintf(buf, "%d\n", val); | ||
698 | } | ||
699 | |||
700 | static ssize_t | ||
701 | store_sf_ctrl(struct device *dev, struct device_attribute *attr, | ||
702 | const char *buf, size_t count) | ||
703 | { | ||
704 | struct sensor_device_attribute_2 *sensor_attr = | ||
705 | to_sensor_dev_attr_2(attr); | ||
706 | int nr = sensor_attr->nr; | ||
707 | int index = sensor_attr->index; | ||
708 | struct i2c_client *client = to_i2c_client(dev); | ||
709 | struct w83793_data *data = i2c_get_clientdata(client); | ||
710 | u32 val; | ||
711 | |||
712 | mutex_lock(&data->update_lock); | ||
713 | if (TEMP_FAN_MAP == nr) { | ||
714 | val = simple_strtoul(buf, NULL, 10) & 0xff; | ||
715 | w83793_write_value(client, W83793_REG_TEMP_FAN_MAP(index), val); | ||
716 | data->temp_fan_map[index] = val; | ||
717 | } else if (TEMP_PWM_ENABLE == nr) { | ||
718 | val = simple_strtoul(buf, NULL, 10); | ||
719 | if (2 == val || 3 == val) { | ||
720 | data->pwm_enable = | ||
721 | w83793_read_value(client, W83793_REG_PWM_ENABLE); | ||
722 | if (val - 2) | ||
723 | data->pwm_enable |= 1 << index; | ||
724 | else | ||
725 | data->pwm_enable &= ~(1 << index); | ||
726 | w83793_write_value(client, W83793_REG_PWM_ENABLE, | ||
727 | data->pwm_enable); | ||
728 | } else { | ||
729 | mutex_unlock(&data->update_lock); | ||
730 | return -EINVAL; | ||
731 | } | ||
732 | } else if (TEMP_CRUISE == nr) { | ||
733 | data->temp_cruise[index] = | ||
734 | w83793_read_value(client, W83793_REG_TEMP_CRUISE(index)); | ||
735 | val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x7f); | ||
736 | data->temp_cruise[index] &= 0x80; | ||
737 | data->temp_cruise[index] |= val; | ||
738 | |||
739 | w83793_write_value(client, W83793_REG_TEMP_CRUISE(index), | ||
740 | data->temp_cruise[index]); | ||
741 | } else { /* TEMP_TOLERANCE */ | ||
742 | int i = index >> 1; | ||
743 | u8 shift = (index & 0x01) ? 4 : 0; | ||
744 | data->tolerance[i] = | ||
745 | w83793_read_value(client, W83793_REG_TEMP_TOL(i)); | ||
746 | |||
747 | val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x0f); | ||
748 | data->tolerance[i] &= ~(0x0f << shift); | ||
749 | data->tolerance[i] |= val << shift; | ||
750 | w83793_write_value(client, W83793_REG_TEMP_TOL(i), | ||
751 | data->tolerance[i]); | ||
752 | } | ||
753 | |||
754 | mutex_unlock(&data->update_lock); | ||
755 | return count; | ||
756 | } | ||
757 | |||
758 | static ssize_t | ||
759 | show_sf2_pwm(struct device *dev, struct device_attribute *attr, char *buf) | ||
760 | { | ||
761 | struct sensor_device_attribute_2 *sensor_attr = | ||
762 | to_sensor_dev_attr_2(attr); | ||
763 | int nr = sensor_attr->nr; | ||
764 | int index = sensor_attr->index; | ||
765 | struct w83793_data *data = w83793_update_device(dev); | ||
766 | |||
767 | return sprintf(buf, "%d\n", (data->sf2_pwm[index][nr] & 0x3f) << 2); | ||
768 | } | ||
769 | |||
770 | static ssize_t | ||
771 | store_sf2_pwm(struct device *dev, struct device_attribute *attr, | ||
772 | const char *buf, size_t count) | ||
773 | { | ||
774 | struct i2c_client *client = to_i2c_client(dev); | ||
775 | struct w83793_data *data = i2c_get_clientdata(client); | ||
776 | struct sensor_device_attribute_2 *sensor_attr = | ||
777 | to_sensor_dev_attr_2(attr); | ||
778 | int nr = sensor_attr->nr; | ||
779 | int index = sensor_attr->index; | ||
780 | u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 0xff) >> 2; | ||
781 | |||
782 | mutex_lock(&data->update_lock); | ||
783 | data->sf2_pwm[index][nr] = | ||
784 | w83793_read_value(client, W83793_REG_SF2_PWM(index, nr)) & 0xc0; | ||
785 | data->sf2_pwm[index][nr] |= val; | ||
786 | w83793_write_value(client, W83793_REG_SF2_PWM(index, nr), | ||
787 | data->sf2_pwm[index][nr]); | ||
788 | mutex_unlock(&data->update_lock); | ||
789 | return count; | ||
790 | } | ||
791 | |||
792 | static ssize_t | ||
793 | show_sf2_temp(struct device *dev, struct device_attribute *attr, char *buf) | ||
794 | { | ||
795 | struct sensor_device_attribute_2 *sensor_attr = | ||
796 | to_sensor_dev_attr_2(attr); | ||
797 | int nr = sensor_attr->nr; | ||
798 | int index = sensor_attr->index; | ||
799 | struct w83793_data *data = w83793_update_device(dev); | ||
800 | |||
801 | return sprintf(buf, "%ld\n", | ||
802 | TEMP_FROM_REG(data->sf2_temp[index][nr] & 0x7f)); | ||
803 | } | ||
804 | |||
805 | static ssize_t | ||
806 | store_sf2_temp(struct device *dev, struct device_attribute *attr, | ||
807 | const char *buf, size_t count) | ||
808 | { | ||
809 | struct i2c_client *client = to_i2c_client(dev); | ||
810 | struct w83793_data *data = i2c_get_clientdata(client); | ||
811 | struct sensor_device_attribute_2 *sensor_attr = | ||
812 | to_sensor_dev_attr_2(attr); | ||
813 | int nr = sensor_attr->nr; | ||
814 | int index = sensor_attr->index; | ||
815 | u8 val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x7f); | ||
816 | |||
817 | mutex_lock(&data->update_lock); | ||
818 | data->sf2_temp[index][nr] = | ||
819 | w83793_read_value(client, W83793_REG_SF2_TEMP(index, nr)) & 0x80; | ||
820 | data->sf2_temp[index][nr] |= val; | ||
821 | w83793_write_value(client, W83793_REG_SF2_TEMP(index, nr), | ||
822 | data->sf2_temp[index][nr]); | ||
823 | mutex_unlock(&data->update_lock); | ||
824 | return count; | ||
825 | } | ||
826 | |||
827 | /* only Vcore A/B and Vtt have additional 2 bits precision */ | ||
828 | static ssize_t | ||
829 | show_in(struct device *dev, struct device_attribute *attr, char *buf) | ||
830 | { | ||
831 | struct sensor_device_attribute_2 *sensor_attr = | ||
832 | to_sensor_dev_attr_2(attr); | ||
833 | int nr = sensor_attr->nr; | ||
834 | int index = sensor_attr->index; | ||
835 | struct w83793_data *data = w83793_update_device(dev); | ||
836 | u16 val = data->in[index][nr]; | ||
837 | |||
838 | if (index < 3) { | ||
839 | val <<= 2; | ||
840 | val += (data->in_low_bits[nr] >> (index * 2)) & 0x3; | ||
841 | } | ||
842 | return sprintf(buf, "%d\n", val * scale_in[index]); | ||
843 | } | ||
844 | |||
845 | static ssize_t | ||
846 | store_in(struct device *dev, struct device_attribute *attr, | ||
847 | const char *buf, size_t count) | ||
848 | { | ||
849 | struct sensor_device_attribute_2 *sensor_attr = | ||
850 | to_sensor_dev_attr_2(attr); | ||
851 | int nr = sensor_attr->nr; | ||
852 | int index = sensor_attr->index; | ||
853 | struct i2c_client *client = to_i2c_client(dev); | ||
854 | struct w83793_data *data = i2c_get_clientdata(client); | ||
855 | u32 val; | ||
856 | |||
857 | val = | ||
858 | (simple_strtoul(buf, NULL, 10) + | ||
859 | scale_in[index] / 2) / scale_in[index]; | ||
860 | mutex_lock(&data->update_lock); | ||
861 | if (index > 2) { | ||
862 | val = SENSORS_LIMIT(val, 0, 255); | ||
863 | } else { | ||
864 | val = SENSORS_LIMIT(val, 0, 0x3FF); | ||
865 | data->in_low_bits[nr] = | ||
866 | w83793_read_value(client, W83793_REG_IN_LOW_BITS[nr]); | ||
867 | data->in_low_bits[nr] &= ~(0x03 << (2 * index)); | ||
868 | data->in_low_bits[nr] |= (val & 0x03) << (2 * index); | ||
869 | w83793_write_value(client, W83793_REG_IN_LOW_BITS[nr], | ||
870 | data->in_low_bits[nr]); | ||
871 | val >>= 2; | ||
872 | } | ||
873 | data->in[index][nr] = val; | ||
874 | w83793_write_value(client, W83793_REG_IN[index][nr], | ||
875 | data->in[index][nr]); | ||
876 | mutex_unlock(&data->update_lock); | ||
877 | return count; | ||
878 | } | ||
879 | |||
880 | #define NOT_USED -1 | ||
881 | |||
882 | #define SENSOR_ATTR_IN(index) \ | ||
883 | SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ | ||
884 | IN_READ, index), \ | ||
885 | SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \ | ||
886 | store_in, IN_MAX, index), \ | ||
887 | SENSOR_ATTR_2(in##index##_min, S_IRUGO | S_IWUSR, show_in, \ | ||
888 | store_in, IN_LOW, index), \ | ||
889 | SENSOR_ATTR_2(in##index##_alarm, S_IRUGO, show_alarm_beep, \ | ||
890 | NULL, ALARM_STATUS, index + ((index > 2) ? 1 : 0)), \ | ||
891 | SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \ | ||
892 | show_alarm_beep, store_beep, BEEP_ENABLE, \ | ||
893 | index + ((index > 2) ? 1 : 0)) | ||
894 | |||
895 | #define SENSOR_ATTR_FAN(index) \ | ||
896 | SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \ | ||
897 | NULL, ALARM_STATUS, index + 17), \ | ||
898 | SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \ | ||
899 | show_alarm_beep, store_beep, BEEP_ENABLE, index + 17), \ | ||
900 | SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ | ||
901 | NULL, FAN_INPUT, index - 1), \ | ||
902 | SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \ | ||
903 | show_fan, store_fan_min, FAN_MIN, index - 1) | ||
904 | |||
905 | #define SENSOR_ATTR_PWM(index) \ | ||
906 | SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ | ||
907 | store_pwm, PWM_DUTY, index - 1), \ | ||
908 | SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ | ||
909 | show_pwm, store_pwm, PWM_NONSTOP, index - 1), \ | ||
910 | SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \ | ||
911 | show_pwm, store_pwm, PWM_START, index - 1), \ | ||
912 | SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ | ||
913 | show_pwm, store_pwm, PWM_STOP_TIME, index - 1) | ||
914 | |||
915 | #define SENSOR_ATTR_TEMP(index) \ | ||
916 | SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR, \ | ||
917 | show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ | ||
918 | SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ | ||
919 | NULL, TEMP_READ, index - 1), \ | ||
920 | SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \ | ||
921 | store_temp, TEMP_CRIT, index - 1), \ | ||
922 | SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ | ||
923 | show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \ | ||
924 | SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_temp, \ | ||
925 | store_temp, TEMP_WARN, index - 1), \ | ||
926 | SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \ | ||
927 | show_temp, store_temp, TEMP_WARN_HYST, index - 1), \ | ||
928 | SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ | ||
929 | show_alarm_beep, NULL, ALARM_STATUS, index + 11), \ | ||
930 | SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ | ||
931 | show_alarm_beep, store_beep, BEEP_ENABLE, index + 11), \ | ||
932 | SENSOR_ATTR_2(temp##index##_auto_channels_pwm, \ | ||
933 | S_IRUGO | S_IWUSR, show_sf_ctrl, store_sf_ctrl, \ | ||
934 | TEMP_FAN_MAP, index - 1), \ | ||
935 | SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO, \ | ||
936 | show_sf_ctrl, store_sf_ctrl, TEMP_PWM_ENABLE, \ | ||
937 | index - 1), \ | ||
938 | SENSOR_ATTR_2(thermal_cruise##index, S_IRUGO | S_IWUSR, \ | ||
939 | show_sf_ctrl, store_sf_ctrl, TEMP_CRUISE, index - 1), \ | ||
940 | SENSOR_ATTR_2(tolerance##index, S_IRUGO | S_IWUSR, show_sf_ctrl,\ | ||
941 | store_sf_ctrl, TEMP_TOLERANCE, index - 1), \ | ||
942 | SENSOR_ATTR_2(temp##index##_auto_point1_pwm, S_IRUGO | S_IWUSR, \ | ||
943 | show_sf2_pwm, store_sf2_pwm, 0, index - 1), \ | ||
944 | SENSOR_ATTR_2(temp##index##_auto_point2_pwm, S_IRUGO | S_IWUSR, \ | ||
945 | show_sf2_pwm, store_sf2_pwm, 1, index - 1), \ | ||
946 | SENSOR_ATTR_2(temp##index##_auto_point3_pwm, S_IRUGO | S_IWUSR, \ | ||
947 | show_sf2_pwm, store_sf2_pwm, 2, index - 1), \ | ||
948 | SENSOR_ATTR_2(temp##index##_auto_point4_pwm, S_IRUGO | S_IWUSR, \ | ||
949 | show_sf2_pwm, store_sf2_pwm, 3, index - 1), \ | ||
950 | SENSOR_ATTR_2(temp##index##_auto_point5_pwm, S_IRUGO | S_IWUSR, \ | ||
951 | show_sf2_pwm, store_sf2_pwm, 4, index - 1), \ | ||
952 | SENSOR_ATTR_2(temp##index##_auto_point6_pwm, S_IRUGO | S_IWUSR, \ | ||
953 | show_sf2_pwm, store_sf2_pwm, 5, index - 1), \ | ||
954 | SENSOR_ATTR_2(temp##index##_auto_point7_pwm, S_IRUGO | S_IWUSR, \ | ||
955 | show_sf2_pwm, store_sf2_pwm, 6, index - 1), \ | ||
956 | SENSOR_ATTR_2(temp##index##_auto_point1_temp, S_IRUGO | S_IWUSR,\ | ||
957 | show_sf2_temp, store_sf2_temp, 0, index - 1), \ | ||
958 | SENSOR_ATTR_2(temp##index##_auto_point2_temp, S_IRUGO | S_IWUSR,\ | ||
959 | show_sf2_temp, store_sf2_temp, 1, index - 1), \ | ||
960 | SENSOR_ATTR_2(temp##index##_auto_point3_temp, S_IRUGO | S_IWUSR,\ | ||
961 | show_sf2_temp, store_sf2_temp, 2, index - 1), \ | ||
962 | SENSOR_ATTR_2(temp##index##_auto_point4_temp, S_IRUGO | S_IWUSR,\ | ||
963 | show_sf2_temp, store_sf2_temp, 3, index - 1), \ | ||
964 | SENSOR_ATTR_2(temp##index##_auto_point5_temp, S_IRUGO | S_IWUSR,\ | ||
965 | show_sf2_temp, store_sf2_temp, 4, index - 1), \ | ||
966 | SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\ | ||
967 | show_sf2_temp, store_sf2_temp, 5, index - 1), \ | ||
968 | SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\ | ||
969 | show_sf2_temp, store_sf2_temp, 6, index - 1) | ||
970 | |||
971 | static struct sensor_device_attribute_2 w83793_sensor_attr_2[] = { | ||
972 | SENSOR_ATTR_IN(0), | ||
973 | SENSOR_ATTR_IN(1), | ||
974 | SENSOR_ATTR_IN(2), | ||
975 | SENSOR_ATTR_IN(3), | ||
976 | SENSOR_ATTR_IN(4), | ||
977 | SENSOR_ATTR_IN(5), | ||
978 | SENSOR_ATTR_IN(6), | ||
979 | SENSOR_ATTR_IN(7), | ||
980 | SENSOR_ATTR_IN(8), | ||
981 | SENSOR_ATTR_IN(9), | ||
982 | SENSOR_ATTR_TEMP(1), | ||
983 | SENSOR_ATTR_TEMP(2), | ||
984 | SENSOR_ATTR_TEMP(3), | ||
985 | SENSOR_ATTR_TEMP(4), | ||
986 | SENSOR_ATTR_TEMP(5), | ||
987 | SENSOR_ATTR_TEMP(6), | ||
988 | SENSOR_ATTR_FAN(1), | ||
989 | SENSOR_ATTR_FAN(2), | ||
990 | SENSOR_ATTR_FAN(3), | ||
991 | SENSOR_ATTR_FAN(4), | ||
992 | SENSOR_ATTR_FAN(5), | ||
993 | SENSOR_ATTR_PWM(1), | ||
994 | SENSOR_ATTR_PWM(2), | ||
995 | SENSOR_ATTR_PWM(3), | ||
996 | }; | ||
997 | |||
998 | /* Fan6-Fan12 */ | ||
999 | static struct sensor_device_attribute_2 w83793_left_fan[] = { | ||
1000 | SENSOR_ATTR_FAN(6), | ||
1001 | SENSOR_ATTR_FAN(7), | ||
1002 | SENSOR_ATTR_FAN(8), | ||
1003 | SENSOR_ATTR_FAN(9), | ||
1004 | SENSOR_ATTR_FAN(10), | ||
1005 | SENSOR_ATTR_FAN(11), | ||
1006 | SENSOR_ATTR_FAN(12), | ||
1007 | }; | ||
1008 | |||
1009 | /* Pwm4-Pwm8 */ | ||
1010 | static struct sensor_device_attribute_2 w83793_left_pwm[] = { | ||
1011 | SENSOR_ATTR_PWM(4), | ||
1012 | SENSOR_ATTR_PWM(5), | ||
1013 | SENSOR_ATTR_PWM(6), | ||
1014 | SENSOR_ATTR_PWM(7), | ||
1015 | SENSOR_ATTR_PWM(8), | ||
1016 | }; | ||
1017 | |||
1018 | static struct sensor_device_attribute_2 sda_single_files[] = { | ||
1019 | SENSOR_ATTR_2(cpu0_vid, S_IRUGO, show_vid, NULL, NOT_USED, 0), | ||
1020 | SENSOR_ATTR_2(cpu1_vid, S_IRUGO, show_vid, NULL, NOT_USED, 1), | ||
1021 | SENSOR_ATTR_2(vrm, S_IWUSR | S_IRUGO, show_vrm, store_vrm, | ||
1022 | NOT_USED, NOT_USED), | ||
1023 | SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep, | ||
1024 | store_chassis_clear, ALARM_STATUS, 30), | ||
1025 | SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable, | ||
1026 | store_beep_enable, NOT_USED, NOT_USED), | ||
1027 | SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup, | ||
1028 | store_sf_setup, SETUP_PWM_DEFAULT, NOT_USED), | ||
1029 | SENSOR_ATTR_2(pwm_uptime, S_IWUSR | S_IRUGO, show_sf_setup, | ||
1030 | store_sf_setup, SETUP_PWM_UPTIME, NOT_USED), | ||
1031 | SENSOR_ATTR_2(pwm_downtime, S_IWUSR | S_IRUGO, show_sf_setup, | ||
1032 | store_sf_setup, SETUP_PWM_DOWNTIME, NOT_USED), | ||
1033 | SENSOR_ATTR_2(temp_critical, S_IWUSR | S_IRUGO, show_sf_setup, | ||
1034 | store_sf_setup, SETUP_TEMP_CRITICAL, NOT_USED), | ||
1035 | }; | ||
1036 | |||
1037 | static void w83793_init_client(struct i2c_client *client) | ||
1038 | { | ||
1039 | if (reset) { | ||
1040 | w83793_write_value(client, W83793_REG_CONFIG, 0x80); | ||
1041 | } | ||
1042 | |||
1043 | /* Start monitoring */ | ||
1044 | w83793_write_value(client, W83793_REG_CONFIG, | ||
1045 | w83793_read_value(client, W83793_REG_CONFIG) | 0x01); | ||
1046 | |||
1047 | } | ||
1048 | |||
1049 | static int w83793_attach_adapter(struct i2c_adapter *adapter) | ||
1050 | { | ||
1051 | if (!(adapter->class & I2C_CLASS_HWMON)) | ||
1052 | return 0; | ||
1053 | return i2c_probe(adapter, &addr_data, w83793_detect); | ||
1054 | } | ||
1055 | |||
1056 | static int w83793_detach_client(struct i2c_client *client) | ||
1057 | { | ||
1058 | struct w83793_data *data = i2c_get_clientdata(client); | ||
1059 | struct device *dev = &client->dev; | ||
1060 | int err, i; | ||
1061 | |||
1062 | /* main client */ | ||
1063 | if (data) { | ||
1064 | hwmon_device_unregister(data->class_dev); | ||
1065 | |||
1066 | for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) | ||
1067 | device_remove_file(dev, | ||
1068 | &w83793_sensor_attr_2[i].dev_attr); | ||
1069 | |||
1070 | for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) | ||
1071 | device_remove_file(dev, &sda_single_files[i].dev_attr); | ||
1072 | |||
1073 | for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++) | ||
1074 | device_remove_file(dev, &w83793_left_fan[i].dev_attr); | ||
1075 | |||
1076 | for (i = 0; i < ARRAY_SIZE(w83793_left_pwm); i++) | ||
1077 | device_remove_file(dev, &w83793_left_pwm[i].dev_attr); | ||
1078 | } | ||
1079 | |||
1080 | if ((err = i2c_detach_client(client))) | ||
1081 | return err; | ||
1082 | |||
1083 | /* main client */ | ||
1084 | if (data) | ||
1085 | kfree(data); | ||
1086 | /* subclient */ | ||
1087 | else | ||
1088 | kfree(client); | ||
1089 | |||
1090 | return 0; | ||
1091 | } | ||
1092 | |||
1093 | static int | ||
1094 | w83793_create_subclient(struct i2c_adapter *adapter, | ||
1095 | struct i2c_client *client, int addr, | ||
1096 | struct i2c_client **sub_cli) | ||
1097 | { | ||
1098 | int err = 0; | ||
1099 | struct i2c_client *sub_client; | ||
1100 | |||
1101 | (*sub_cli) = sub_client = | ||
1102 | kzalloc(sizeof(struct i2c_client), GFP_KERNEL); | ||
1103 | if (!(sub_client)) { | ||
1104 | return -ENOMEM; | ||
1105 | } | ||
1106 | sub_client->addr = 0x48 + addr; | ||
1107 | i2c_set_clientdata(sub_client, NULL); | ||
1108 | sub_client->adapter = adapter; | ||
1109 | sub_client->driver = &w83793_driver; | ||
1110 | strlcpy(sub_client->name, "w83793 subclient", I2C_NAME_SIZE); | ||
1111 | if ((err = i2c_attach_client(sub_client))) { | ||
1112 | dev_err(&client->dev, "subclient registration " | ||
1113 | "at address 0x%x failed\n", sub_client->addr); | ||
1114 | kfree(sub_client); | ||
1115 | } | ||
1116 | return err; | ||
1117 | } | ||
1118 | |||
1119 | static int | ||
1120 | w83793_detect_subclients(struct i2c_adapter *adapter, int address, | ||
1121 | int kind, struct i2c_client *client) | ||
1122 | { | ||
1123 | int i, id, err; | ||
1124 | u8 tmp; | ||
1125 | struct w83793_data *data = i2c_get_clientdata(client); | ||
1126 | |||
1127 | id = i2c_adapter_id(adapter); | ||
1128 | if (force_subclients[0] == id && force_subclients[1] == address) { | ||
1129 | for (i = 2; i <= 3; i++) { | ||
1130 | if (force_subclients[i] < 0x48 | ||
1131 | || force_subclients[i] > 0x4f) { | ||
1132 | dev_err(&client->dev, | ||
1133 | "invalid subclient " | ||
1134 | "address %d; must be 0x48-0x4f\n", | ||
1135 | force_subclients[i]); | ||
1136 | err = -EINVAL; | ||
1137 | goto ERROR_SC_0; | ||
1138 | } | ||
1139 | } | ||
1140 | w83793_write_value(client, W83793_REG_I2C_SUBADDR, | ||
1141 | (force_subclients[2] & 0x07) | | ||
1142 | ((force_subclients[3] & 0x07) << 4)); | ||
1143 | } | ||
1144 | |||
1145 | tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR); | ||
1146 | if (!(tmp & 0x08)) { | ||
1147 | err = | ||
1148 | w83793_create_subclient(adapter, client, tmp & 0x7, | ||
1149 | &data->lm75[0]); | ||
1150 | if (err < 0) | ||
1151 | goto ERROR_SC_0; | ||
1152 | } | ||
1153 | if (!(tmp & 0x80)) { | ||
1154 | if ((data->lm75[0] != NULL) | ||
1155 | && ((tmp & 0x7) == ((tmp >> 4) & 0x7))) { | ||
1156 | dev_err(&client->dev, | ||
1157 | "duplicate addresses 0x%x, " | ||
1158 | "use force_subclients\n", data->lm75[0]->addr); | ||
1159 | err = -ENODEV; | ||
1160 | goto ERROR_SC_1; | ||
1161 | } | ||
1162 | err = w83793_create_subclient(adapter, client, | ||
1163 | (tmp >> 4) & 0x7, &data->lm75[1]); | ||
1164 | if (err < 0) | ||
1165 | goto ERROR_SC_1; | ||
1166 | } | ||
1167 | |||
1168 | return 0; | ||
1169 | |||
1170 | /* Undo inits in case of errors */ | ||
1171 | |||
1172 | ERROR_SC_1: | ||
1173 | if (data->lm75[0] != NULL) { | ||
1174 | i2c_detach_client(data->lm75[0]); | ||
1175 | kfree(data->lm75[0]); | ||
1176 | } | ||
1177 | ERROR_SC_0: | ||
1178 | return err; | ||
1179 | } | ||
1180 | |||
1181 | static int w83793_detect(struct i2c_adapter *adapter, int address, int kind) | ||
1182 | { | ||
1183 | int i; | ||
1184 | u8 tmp, val; | ||
1185 | struct i2c_client *client; | ||
1186 | struct device *dev; | ||
1187 | struct w83793_data *data; | ||
1188 | int files_fan = ARRAY_SIZE(w83793_left_fan) / 7; | ||
1189 | int files_pwm = ARRAY_SIZE(w83793_left_pwm) / 5; | ||
1190 | int err = 0; | ||
1191 | |||
1192 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { | ||
1193 | goto exit; | ||
1194 | } | ||
1195 | |||
1196 | /* OK. For now, we presume we have a valid client. We now create the | ||
1197 | client structure, even though we cannot fill it completely yet. | ||
1198 | But it allows us to access w83793_{read,write}_value. */ | ||
1199 | |||
1200 | if (!(data = kzalloc(sizeof(struct w83793_data), GFP_KERNEL))) { | ||
1201 | err = -ENOMEM; | ||
1202 | goto exit; | ||
1203 | } | ||
1204 | |||
1205 | client = &data->client; | ||
1206 | dev = &client->dev; | ||
1207 | i2c_set_clientdata(client, data); | ||
1208 | client->addr = address; | ||
1209 | client->adapter = adapter; | ||
1210 | client->driver = &w83793_driver; | ||
1211 | |||
1212 | data->bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); | ||
1213 | |||
1214 | /* Now, we do the remaining detection. */ | ||
1215 | if (kind < 0) { | ||
1216 | tmp = data->bank & 0x80 ? 0x5c : 0xa3; | ||
1217 | /* Check Winbond vendor ID */ | ||
1218 | if (tmp != i2c_smbus_read_byte_data(client, | ||
1219 | W83793_REG_VENDORID)) { | ||
1220 | pr_debug("w83793: Detection failed at check " | ||
1221 | "vendor id\n"); | ||
1222 | err = -ENODEV; | ||
1223 | goto free_mem; | ||
1224 | } | ||
1225 | |||
1226 | /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR | ||
1227 | should match */ | ||
1228 | if ((data->bank & 0x07) == 0 | ||
1229 | && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) != | ||
1230 | (address << 1)) { | ||
1231 | pr_debug("w83793: Detection failed at check " | ||
1232 | "i2c addr\n"); | ||
1233 | err = -ENODEV; | ||
1234 | goto free_mem; | ||
1235 | } | ||
1236 | |||
1237 | } | ||
1238 | |||
1239 | /* We have either had a force parameter, or we have already detected the | ||
1240 | Winbond. Determine the chip type now */ | ||
1241 | |||
1242 | if (kind <= 0) { | ||
1243 | if (0x7b == w83793_read_value(client, W83793_REG_CHIPID)) { | ||
1244 | kind = w83793; | ||
1245 | } else { | ||
1246 | if (kind == 0) | ||
1247 | dev_warn(&adapter->dev, "w83793: Ignoring " | ||
1248 | "'force' parameter for unknown chip " | ||
1249 | "at address 0x%02x\n", address); | ||
1250 | err = -ENODEV; | ||
1251 | goto free_mem; | ||
1252 | } | ||
1253 | } | ||
1254 | |||
1255 | /* Fill in the remaining client fields and put into the global list */ | ||
1256 | strlcpy(client->name, "w83793", I2C_NAME_SIZE); | ||
1257 | |||
1258 | mutex_init(&data->update_lock); | ||
1259 | |||
1260 | /* Tell the I2C layer a new client has arrived */ | ||
1261 | if ((err = i2c_attach_client(client))) | ||
1262 | goto free_mem; | ||
1263 | |||
1264 | if ((err = w83793_detect_subclients(adapter, address, kind, client))) | ||
1265 | goto detach_client; | ||
1266 | |||
1267 | /* Initialize the chip */ | ||
1268 | w83793_init_client(client); | ||
1269 | |||
1270 | data->vrm = vid_which_vrm(); | ||
1271 | /* | ||
1272 | Only fan 1-5 has their own input pins, | ||
1273 | Pwm 1-3 has their own pins | ||
1274 | */ | ||
1275 | data->has_fan = 0x1f; | ||
1276 | data->has_pwm = 0x07; | ||
1277 | tmp = w83793_read_value(client, W83793_REG_MFC); | ||
1278 | val = w83793_read_value(client, W83793_REG_FANIN_CTRL); | ||
1279 | |||
1280 | /* check the function of pins 49-56 */ | ||
1281 | if (!(tmp & 0x80)) { | ||
1282 | data->has_pwm |= 0x18; /* pwm 4,5 */ | ||
1283 | if (val & 0x01) { /* fan 6 */ | ||
1284 | data->has_fan |= 0x20; | ||
1285 | data->has_pwm |= 0x20; | ||
1286 | } | ||
1287 | if (val & 0x02) { /* fan 7 */ | ||
1288 | data->has_fan |= 0x40; | ||
1289 | data->has_pwm |= 0x40; | ||
1290 | } | ||
1291 | if (!(tmp & 0x40) && (val & 0x04)) { /* fan 8 */ | ||
1292 | data->has_fan |= 0x80; | ||
1293 | data->has_pwm |= 0x80; | ||
1294 | } | ||
1295 | } | ||
1296 | |||
1297 | if (0x08 == (tmp & 0x0c)) { | ||
1298 | if (val & 0x08) /* fan 9 */ | ||
1299 | data->has_fan |= 0x100; | ||
1300 | if (val & 0x10) /* fan 10 */ | ||
1301 | data->has_fan |= 0x200; | ||
1302 | } | ||
1303 | |||
1304 | if (0x20 == (tmp & 0x30)) { | ||
1305 | if (val & 0x20) /* fan 11 */ | ||
1306 | data->has_fan |= 0x400; | ||
1307 | if (val & 0x40) /* fan 12 */ | ||
1308 | data->has_fan |= 0x800; | ||
1309 | } | ||
1310 | |||
1311 | if ((tmp & 0x01) && (val & 0x04)) { /* fan 8, second location */ | ||
1312 | data->has_fan |= 0x80; | ||
1313 | data->has_pwm |= 0x80; | ||
1314 | } | ||
1315 | |||
1316 | /* Register sysfs hooks */ | ||
1317 | for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) { | ||
1318 | err = device_create_file(dev, | ||
1319 | &w83793_sensor_attr_2[i].dev_attr); | ||
1320 | if (err) | ||
1321 | goto exit_remove; | ||
1322 | } | ||
1323 | |||
1324 | for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { | ||
1325 | err = device_create_file(dev, &sda_single_files[i].dev_attr); | ||
1326 | if (err) | ||
1327 | goto exit_remove; | ||
1328 | |||
1329 | } | ||
1330 | |||
1331 | for (i = 5; i < 12; i++) { | ||
1332 | int j; | ||
1333 | if (!(data->has_fan & (1 << i))) | ||
1334 | continue; | ||
1335 | for (j = 0; j < files_fan; j++) { | ||
1336 | err = device_create_file(dev, | ||
1337 | &w83793_left_fan[(i - 5) * files_fan | ||
1338 | + j].dev_attr); | ||
1339 | if (err) | ||
1340 | goto exit_remove; | ||
1341 | } | ||
1342 | } | ||
1343 | |||
1344 | for (i = 3; i < 8; i++) { | ||
1345 | int j; | ||
1346 | if (!(data->has_pwm & (1 << i))) | ||
1347 | continue; | ||
1348 | for (j = 0; j < files_pwm; j++) { | ||
1349 | err = device_create_file(dev, | ||
1350 | &w83793_left_pwm[(i - 3) * files_pwm | ||
1351 | + j].dev_attr); | ||
1352 | if (err) | ||
1353 | goto exit_remove; | ||
1354 | } | ||
1355 | } | ||
1356 | |||
1357 | data->class_dev = hwmon_device_register(dev); | ||
1358 | if (IS_ERR(data->class_dev)) { | ||
1359 | err = PTR_ERR(data->class_dev); | ||
1360 | goto exit_remove; | ||
1361 | } | ||
1362 | |||
1363 | return 0; | ||
1364 | |||
1365 | /* Unregister sysfs hooks */ | ||
1366 | |||
1367 | exit_remove: | ||
1368 | for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) | ||
1369 | device_remove_file(dev, &w83793_sensor_attr_2[i].dev_attr); | ||
1370 | |||
1371 | for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) | ||
1372 | device_remove_file(dev, &sda_single_files[i].dev_attr); | ||
1373 | |||
1374 | for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++) | ||
1375 | device_remove_file(dev, &w83793_left_fan[i].dev_attr); | ||
1376 | |||
1377 | for (i = 0; i < ARRAY_SIZE(w83793_left_pwm); i++) | ||
1378 | device_remove_file(dev, &w83793_left_pwm[i].dev_attr); | ||
1379 | |||
1380 | if (data->lm75[0] != NULL) { | ||
1381 | i2c_detach_client(data->lm75[0]); | ||
1382 | kfree(data->lm75[0]); | ||
1383 | } | ||
1384 | if (data->lm75[1] != NULL) { | ||
1385 | i2c_detach_client(data->lm75[1]); | ||
1386 | kfree(data->lm75[1]); | ||
1387 | } | ||
1388 | detach_client: | ||
1389 | i2c_detach_client(client); | ||
1390 | free_mem: | ||
1391 | kfree(data); | ||
1392 | exit: | ||
1393 | return err; | ||
1394 | } | ||
1395 | |||
1396 | static void w83793_update_nonvolatile(struct device *dev) | ||
1397 | { | ||
1398 | struct i2c_client *client = to_i2c_client(dev); | ||
1399 | struct w83793_data *data = i2c_get_clientdata(client); | ||
1400 | int i, j; | ||
1401 | /* | ||
1402 | They are somewhat "stable" registers, and to update them everytime | ||
1403 | takes so much time, it's just not worthy. Update them in a long | ||
1404 | interval to avoid exception. | ||
1405 | */ | ||
1406 | if (!(time_after(jiffies, data->last_nonvolatile + HZ * 300) | ||
1407 | || !data->valid)) | ||
1408 | return; | ||
1409 | /* update voltage limits */ | ||
1410 | for (i = 1; i < 3; i++) { | ||
1411 | for (j = 0; j < ARRAY_SIZE(data->in); j++) { | ||
1412 | data->in[j][i] = | ||
1413 | w83793_read_value(client, W83793_REG_IN[j][i]); | ||
1414 | } | ||
1415 | data->in_low_bits[i] = | ||
1416 | w83793_read_value(client, W83793_REG_IN_LOW_BITS[i]); | ||
1417 | } | ||
1418 | |||
1419 | for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { | ||
1420 | /* Update the Fan measured value and limits */ | ||
1421 | if (!(data->has_fan & (1 << i))) { | ||
1422 | continue; | ||
1423 | } | ||
1424 | data->fan_min[i] = | ||
1425 | w83793_read_value(client, W83793_REG_FAN_MIN(i)) << 8; | ||
1426 | data->fan_min[i] |= | ||
1427 | w83793_read_value(client, W83793_REG_FAN_MIN(i) + 1); | ||
1428 | } | ||
1429 | |||
1430 | for (i = 0; i < ARRAY_SIZE(data->temp_fan_map); i++) { | ||
1431 | data->temp_fan_map[i] = | ||
1432 | w83793_read_value(client, W83793_REG_TEMP_FAN_MAP(i)); | ||
1433 | for (j = 1; j < 5; j++) { | ||
1434 | data->temp[i][j] = | ||
1435 | w83793_read_value(client, W83793_REG_TEMP[i][j]); | ||
1436 | } | ||
1437 | data->temp_cruise[i] = | ||
1438 | w83793_read_value(client, W83793_REG_TEMP_CRUISE(i)); | ||
1439 | for (j = 0; j < 7; j++) { | ||
1440 | data->sf2_pwm[i][j] = | ||
1441 | w83793_read_value(client, W83793_REG_SF2_PWM(i, j)); | ||
1442 | data->sf2_temp[i][j] = | ||
1443 | w83793_read_value(client, | ||
1444 | W83793_REG_SF2_TEMP(i, j)); | ||
1445 | } | ||
1446 | } | ||
1447 | |||
1448 | for (i = 0; i < ARRAY_SIZE(data->temp_mode); i++) | ||
1449 | data->temp_mode[i] = | ||
1450 | w83793_read_value(client, W83793_REG_TEMP_MODE[i]); | ||
1451 | |||
1452 | for (i = 0; i < ARRAY_SIZE(data->tolerance); i++) { | ||
1453 | data->tolerance[i] = | ||
1454 | w83793_read_value(client, W83793_REG_TEMP_TOL(i)); | ||
1455 | } | ||
1456 | |||
1457 | for (i = 0; i < ARRAY_SIZE(data->pwm); i++) { | ||
1458 | if (!(data->has_pwm & (1 << i))) | ||
1459 | continue; | ||
1460 | data->pwm[i][PWM_NONSTOP] = | ||
1461 | w83793_read_value(client, W83793_REG_PWM(i, PWM_NONSTOP)); | ||
1462 | data->pwm[i][PWM_START] = | ||
1463 | w83793_read_value(client, W83793_REG_PWM(i, PWM_START)); | ||
1464 | data->pwm_stop_time[i] = | ||
1465 | w83793_read_value(client, W83793_REG_PWM_STOP_TIME(i)); | ||
1466 | } | ||
1467 | |||
1468 | data->pwm_default = w83793_read_value(client, W83793_REG_PWM_DEFAULT); | ||
1469 | data->pwm_enable = w83793_read_value(client, W83793_REG_PWM_ENABLE); | ||
1470 | data->pwm_uptime = w83793_read_value(client, W83793_REG_PWM_UPTIME); | ||
1471 | data->pwm_downtime = w83793_read_value(client, W83793_REG_PWM_DOWNTIME); | ||
1472 | data->temp_critical = | ||
1473 | w83793_read_value(client, W83793_REG_TEMP_CRITICAL); | ||
1474 | data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP); | ||
1475 | |||
1476 | for (i = 0; i < ARRAY_SIZE(data->beeps); i++) { | ||
1477 | data->beeps[i] = w83793_read_value(client, W83793_REG_BEEP(i)); | ||
1478 | } | ||
1479 | |||
1480 | data->last_nonvolatile = jiffies; | ||
1481 | } | ||
1482 | |||
1483 | static struct w83793_data *w83793_update_device(struct device *dev) | ||
1484 | { | ||
1485 | struct i2c_client *client = to_i2c_client(dev); | ||
1486 | struct w83793_data *data = i2c_get_clientdata(client); | ||
1487 | int i; | ||
1488 | |||
1489 | mutex_lock(&data->update_lock); | ||
1490 | |||
1491 | if (!(time_after(jiffies, data->last_updated + HZ * 2) | ||
1492 | || !data->valid)) | ||
1493 | goto END; | ||
1494 | |||
1495 | /* Update the voltages measured value and limits */ | ||
1496 | for (i = 0; i < ARRAY_SIZE(data->in); i++) | ||
1497 | data->in[i][IN_READ] = | ||
1498 | w83793_read_value(client, W83793_REG_IN[i][IN_READ]); | ||
1499 | |||
1500 | data->in_low_bits[IN_READ] = | ||
1501 | w83793_read_value(client, W83793_REG_IN_LOW_BITS[IN_READ]); | ||
1502 | |||
1503 | for (i = 0; i < ARRAY_SIZE(data->fan); i++) { | ||
1504 | if (!(data->has_fan & (1 << i))) { | ||
1505 | continue; | ||
1506 | } | ||
1507 | data->fan[i] = | ||
1508 | w83793_read_value(client, W83793_REG_FAN(i)) << 8; | ||
1509 | data->fan[i] |= | ||
1510 | w83793_read_value(client, W83793_REG_FAN(i) + 1); | ||
1511 | } | ||
1512 | |||
1513 | for (i = 0; i < ARRAY_SIZE(data->temp); i++) | ||
1514 | data->temp[i][TEMP_READ] = | ||
1515 | w83793_read_value(client, W83793_REG_TEMP[i][TEMP_READ]); | ||
1516 | |||
1517 | data->temp_low_bits = | ||
1518 | w83793_read_value(client, W83793_REG_TEMP_LOW_BITS); | ||
1519 | |||
1520 | for (i = 0; i < ARRAY_SIZE(data->pwm); i++) { | ||
1521 | if (data->has_pwm & (1 << i)) | ||
1522 | data->pwm[i][PWM_DUTY] = | ||
1523 | w83793_read_value(client, | ||
1524 | W83793_REG_PWM(i, PWM_DUTY)); | ||
1525 | } | ||
1526 | |||
1527 | for (i = 0; i < ARRAY_SIZE(data->alarms); i++) | ||
1528 | data->alarms[i] = | ||
1529 | w83793_read_value(client, W83793_REG_ALARM(i)); | ||
1530 | data->vid[0] = w83793_read_value(client, W83793_REG_VID_INA); | ||
1531 | data->vid[1] = w83793_read_value(client, W83793_REG_VID_INB); | ||
1532 | w83793_update_nonvolatile(dev); | ||
1533 | data->last_updated = jiffies; | ||
1534 | data->valid = 1; | ||
1535 | |||
1536 | END: | ||
1537 | mutex_unlock(&data->update_lock); | ||
1538 | return data; | ||
1539 | } | ||
1540 | |||
1541 | /* Ignore the possibility that somebody change bank outside the driver | ||
1542 | Must be called with data->update_lock held, except during initialization */ | ||
1543 | static u8 w83793_read_value(struct i2c_client *client, u16 reg) | ||
1544 | { | ||
1545 | struct w83793_data *data = i2c_get_clientdata(client); | ||
1546 | u8 res = 0xff; | ||
1547 | u8 new_bank = reg >> 8; | ||
1548 | |||
1549 | new_bank |= data->bank & 0xfc; | ||
1550 | if (data->bank != new_bank) { | ||
1551 | if (i2c_smbus_write_byte_data | ||
1552 | (client, W83793_REG_BANKSEL, new_bank) >= 0) | ||
1553 | data->bank = new_bank; | ||
1554 | else { | ||
1555 | dev_err(&client->dev, | ||
1556 | "set bank to %d failed, fall back " | ||
1557 | "to bank %d, read reg 0x%x error\n", | ||
1558 | new_bank, data->bank, reg); | ||
1559 | res = 0x0; /* read 0x0 from the chip */ | ||
1560 | goto END; | ||
1561 | } | ||
1562 | } | ||
1563 | res = i2c_smbus_read_byte_data(client, reg & 0xff); | ||
1564 | END: | ||
1565 | return res; | ||
1566 | } | ||
1567 | |||
1568 | /* Must be called with data->update_lock held, except during initialization */ | ||
1569 | static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value) | ||
1570 | { | ||
1571 | struct w83793_data *data = i2c_get_clientdata(client); | ||
1572 | int res; | ||
1573 | u8 new_bank = reg >> 8; | ||
1574 | |||
1575 | new_bank |= data->bank & 0xfc; | ||
1576 | if (data->bank != new_bank) { | ||
1577 | if ((res = i2c_smbus_write_byte_data | ||
1578 | (client, W83793_REG_BANKSEL, new_bank)) >= 0) | ||
1579 | data->bank = new_bank; | ||
1580 | else { | ||
1581 | dev_err(&client->dev, | ||
1582 | "set bank to %d failed, fall back " | ||
1583 | "to bank %d, write reg 0x%x error\n", | ||
1584 | new_bank, data->bank, reg); | ||
1585 | goto END; | ||
1586 | } | ||
1587 | } | ||
1588 | |||
1589 | res = i2c_smbus_write_byte_data(client, reg & 0xff, value); | ||
1590 | END: | ||
1591 | return res; | ||
1592 | } | ||
1593 | |||
1594 | static int __init sensors_w83793_init(void) | ||
1595 | { | ||
1596 | return i2c_add_driver(&w83793_driver); | ||
1597 | } | ||
1598 | |||
1599 | static void __exit sensors_w83793_exit(void) | ||
1600 | { | ||
1601 | i2c_del_driver(&w83793_driver); | ||
1602 | } | ||
1603 | |||
1604 | MODULE_AUTHOR("Yuan Mu"); | ||
1605 | MODULE_DESCRIPTION("w83793 driver"); | ||
1606 | MODULE_LICENSE("GPL"); | ||
1607 | |||
1608 | module_init(sensors_w83793_init); | ||
1609 | module_exit(sensors_w83793_exit); | ||
diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index 33fbb47100a3..8e1e3f8e40a4 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge | 2 | * i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge |
3 | * | 3 | * |
4 | * Copyright (C) 2004 Patrick Mochel | 4 | * Copyright (C) 2004 Patrick Mochel |
5 | * 2005 Rudolf Marek <r.marek@sh.cvut.cz> | 5 | * 2005 Rudolf Marek <r.marek@assembler.cz> |
6 | * | 6 | * |
7 | * The 1563 southbridge is deceptively similar to the 1533, with a | 7 | * The 1563 southbridge is deceptively similar to the 1533, with a |
8 | * few notable exceptions. One of those happens to be the fact they | 8 | * few notable exceptions. One of those happens to be the fact they |
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 7ae3c3326643..d38778f2fbec 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h | |||
@@ -142,7 +142,6 @@ | |||
142 | #define I2C_DRIVERID_MTP008 1023 | 142 | #define I2C_DRIVERID_MTP008 1023 |
143 | #define I2C_DRIVERID_DS1621 1024 | 143 | #define I2C_DRIVERID_DS1621 1024 |
144 | #define I2C_DRIVERID_ADM1024 1025 | 144 | #define I2C_DRIVERID_ADM1024 1025 |
145 | #define I2C_DRIVERID_IT87 1026 | ||
146 | #define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */ | 145 | #define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */ |
147 | #define I2C_DRIVERID_FSCPOS 1028 | 146 | #define I2C_DRIVERID_FSCPOS 1028 |
148 | #define I2C_DRIVERID_FSCSCY 1029 | 147 | #define I2C_DRIVERID_FSCSCY 1029 |