Commit 3cb603284b3d256ae9ae9e65887cee8416bfef15
Exists in
master
and in
6 other branches
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (26 commits) hwmon: (w83627ehf) Better fix for negative temperature values hwmon: (w83627ehf) Uninline is_word_sized hwmon: (lm75) Document why clones are not detected hwmon: (w83627ehf) Move fan pins check to a separate function hwmon: (w83627ehf) Skip reading unused voltage registers hwmon: (lm75) Add support for Analog Devices ADT75 hwmon: (pmbus_core) Simplify sign extensions hwmon: (pmbus) Add support for Lineage Power DC-DC converters hwmon: (pmbus/ltc2978) Add support for LTC3880 to LTC2978 driver hwmon: (pmbus/ltc2978) Explicit driver for LTC2978 hwmon: (pmbus) Add support for TEMP2 peak attributes hwmon: AD7314 driver (ported from IIO) hwmon: (pmbus) Add support for Intersil power management chips hwmon: (pmbus) Always call _pmbus_read_byte in core driver hwmon: (pmbus) Replace EINVAL return codes with more appropriate errors hwmon: (pmbus) Provide more documentation hwmon/f71882fg: Make the decision wether to register fan attr. per fan hwmon/f71882fg: Add a f71882fg_create_fan_sysfs_files helper function hwmon/f71882fg: Make all fan/pwm attr tables 2 dimensional hwmon: (exynos4_tmu) Remove IRQF_DISABLED ...
Showing 30 changed files Side-by-side Diff
- Documentation/hwmon/ad7314
- Documentation/hwmon/adm1275
- Documentation/hwmon/exynos4_tmu
- Documentation/hwmon/lm75
- Documentation/hwmon/ltc2978
- Documentation/hwmon/pmbus
- Documentation/hwmon/pmbus-core
- Documentation/hwmon/zl6100
- drivers/hwmon/Kconfig
- drivers/hwmon/Makefile
- drivers/hwmon/ad7314.c
- drivers/hwmon/exynos4_tmu.c
- drivers/hwmon/f71882fg.c
- drivers/hwmon/lm75.c
- drivers/hwmon/pmbus/Kconfig
- drivers/hwmon/pmbus/Makefile
- drivers/hwmon/pmbus/adm1275.c
- drivers/hwmon/pmbus/lm25066.c
- drivers/hwmon/pmbus/ltc2978.c
- drivers/hwmon/pmbus/max16064.c
- drivers/hwmon/pmbus/max34440.c
- drivers/hwmon/pmbus/max8688.c
- drivers/hwmon/pmbus/pmbus.c
- drivers/hwmon/pmbus/pmbus.h
- drivers/hwmon/pmbus/pmbus_core.c
- drivers/hwmon/pmbus/ucd9000.c
- drivers/hwmon/pmbus/ucd9200.c
- drivers/hwmon/pmbus/zl6100.c
- drivers/hwmon/w83627ehf.c
- include/linux/platform_data/exynos4_tmu.h
Documentation/hwmon/ad7314
1 | +Kernel driver ad7314 | |
2 | +==================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * Analog Devices AD7314 | |
6 | + Prefix: 'ad7314' | |
7 | + Datasheet: Publicly available at Analog Devices website. | |
8 | + * Analog Devices ADT7301 | |
9 | + Prefix: 'adt7301' | |
10 | + Datasheet: Publicly available at Analog Devices website. | |
11 | + * Analog Devices ADT7302 | |
12 | + Prefix: 'adt7302' | |
13 | + Datasheet: Publicly available at Analog Devices website. | |
14 | + | |
15 | +Description | |
16 | +----------- | |
17 | + | |
18 | +Driver supports the above parts. The ad7314 has a 10 bit | |
19 | +sensor with 1lsb = 0.25 degrees centigrade. The adt7301 and | |
20 | +adt7302 have 14 bit sensors with 1lsb = 0.03125 degrees centigrade. | |
21 | + | |
22 | +Notes | |
23 | +----- | |
24 | + | |
25 | +Currently power down mode is not supported. |
Documentation/hwmon/adm1275
... | ... | @@ -6,6 +6,10 @@ |
6 | 6 | Prefix: 'adm1275' |
7 | 7 | Addresses scanned: - |
8 | 8 | Datasheet: www.analog.com/static/imported-files/data_sheets/ADM1275.pdf |
9 | + * Analog Devices ADM1276 | |
10 | + Prefix: 'adm1276' | |
11 | + Addresses scanned: - | |
12 | + Datasheet: www.analog.com/static/imported-files/data_sheets/ADM1276.pdf | |
9 | 13 | |
10 | 14 | Author: Guenter Roeck <guenter.roeck@ericsson.com> |
11 | 15 | |
12 | 16 | |
... | ... | @@ -13,13 +17,13 @@ |
13 | 17 | Description |
14 | 18 | ----------- |
15 | 19 | |
16 | -This driver supports hardware montoring for Analog Devices ADM1275 Hot-Swap | |
17 | -Controller and Digital Power Monitor. | |
20 | +This driver supports hardware montoring for Analog Devices ADM1275 and ADM1276 | |
21 | +Hot-Swap Controller and Digital Power Monitor. | |
18 | 22 | |
19 | -The ADM1275 is a hot-swap controller that allows a circuit board to be removed | |
20 | -from or inserted into a live backplane. It also features current and voltage | |
21 | -readback via an integrated 12-bit analog-to-digital converter (ADC), accessed | |
22 | -using a PMBus. interface. | |
23 | +ADM1275 and ADM1276 are hot-swap controllers that allow a circuit board to be | |
24 | +removed from or inserted into a live backplane. They also feature current and | |
25 | +voltage readback via an integrated 12-bit analog-to-digital converter (ADC), | |
26 | +accessed using a PMBus interface. | |
23 | 27 | |
24 | 28 | The driver is a client driver to the core PMBus driver. Please see |
25 | 29 | Documentation/hwmon/pmbus for details on PMBus client drivers. |
26 | 30 | |
... | ... | @@ -48,18 +52,26 @@ |
48 | 52 | |
49 | 53 | in1_label "vin1" or "vout1" depending on chip variant and |
50 | 54 | configuration. |
51 | -in1_input Measured voltage. From READ_VOUT register. | |
52 | -in1_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register. | |
53 | -in1_max Maximum voltage. From VOUT_OV_WARN_LIMIT register. | |
54 | -in1_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status. | |
55 | -in1_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status. | |
55 | +in1_input Measured voltage. | |
56 | +in1_min Minumum Voltage. | |
57 | +in1_max Maximum voltage. | |
58 | +in1_min_alarm Voltage low alarm. | |
59 | +in1_max_alarm Voltage high alarm. | |
56 | 60 | in1_highest Historical maximum voltage. |
57 | 61 | in1_reset_history Write any value to reset history. |
58 | 62 | |
59 | 63 | curr1_label "iout1" |
60 | -curr1_input Measured current. From READ_IOUT register. | |
61 | -curr1_max Maximum current. From IOUT_OC_WARN_LIMIT register. | |
62 | -curr1_max_alarm Current high alarm. From IOUT_OC_WARN_LIMIT register. | |
64 | +curr1_input Measured current. | |
65 | +curr1_max Maximum current. | |
66 | +curr1_max_alarm Current high alarm. | |
67 | +curr1_lcrit Critical minimum current. Depending on the chip | |
68 | + configuration, either curr1_lcrit or curr1_crit is | |
69 | + supported, but not both. | |
70 | +curr1_lcrit_alarm Critical current low alarm. | |
71 | +curr1_crit Critical maximum current. Depending on the chip | |
72 | + configuration, either curr1_lcrit or curr1_crit is | |
73 | + supported, but not both. | |
74 | +curr1_crit_alarm Critical current high alarm. | |
63 | 75 | curr1_highest Historical maximum current. |
64 | 76 | curr1_reset_history Write any value to reset history. |
Documentation/hwmon/exynos4_tmu
1 | +Kernel driver exynos4_tmu | |
2 | +================= | |
3 | + | |
4 | +Supported chips: | |
5 | +* ARM SAMSUNG EXYNOS4 series of SoC | |
6 | + Prefix: 'exynos4-tmu' | |
7 | + Datasheet: Not publicly available | |
8 | + | |
9 | +Authors: Donggeun Kim <dg77.kim@samsung.com> | |
10 | + | |
11 | +Description | |
12 | +----------- | |
13 | + | |
14 | +This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC. | |
15 | + | |
16 | +The chip only exposes the measured 8-bit temperature code value | |
17 | +through a register. | |
18 | +Temperature can be taken from the temperature code. | |
19 | +There are three equations converting from temperature to temperature code. | |
20 | + | |
21 | +The three equations are: | |
22 | + 1. Two point trimming | |
23 | + Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1 | |
24 | + | |
25 | + 2. One point trimming | |
26 | + Tc = T + TI1 - 25 | |
27 | + | |
28 | + 3. No trimming | |
29 | + Tc = T + 50 | |
30 | + | |
31 | + Tc: Temperature code, T: Temperature, | |
32 | + TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register) | |
33 | + Temperature code measured at 25 degree Celsius which is unchanged | |
34 | + TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register) | |
35 | + Temperature code measured at 85 degree Celsius which is unchanged | |
36 | + | |
37 | +TMU(Thermal Management Unit) in EXYNOS4 generates interrupt | |
38 | +when temperature exceeds pre-defined levels. | |
39 | +The maximum number of configurable threshold is four. | |
40 | +The threshold levels are defined as follows: | |
41 | + Level_0: current temperature > trigger_level_0 + threshold | |
42 | + Level_1: current temperature > trigger_level_1 + threshold | |
43 | + Level_2: current temperature > trigger_level_2 + threshold | |
44 | + Level_3: current temperature > trigger_level_3 + threshold | |
45 | + | |
46 | + The threshold and each trigger_level are set | |
47 | + through the corresponding registers. | |
48 | + | |
49 | +When an interrupt occurs, this driver notify user space of | |
50 | +one of four threshold levels for the interrupt | |
51 | +through kobject_uevent_env and sysfs_notify functions. | |
52 | +Although an interrupt condition for level_0 can be set, | |
53 | +it is not notified to user space through sysfs_notify function. | |
54 | + | |
55 | +Sysfs Interface | |
56 | +--------------- | |
57 | +name name of the temperature sensor | |
58 | + RO | |
59 | + | |
60 | +temp1_input temperature | |
61 | + RO | |
62 | + | |
63 | +temp1_max temperature for level_1 interrupt | |
64 | + RO | |
65 | + | |
66 | +temp1_crit temperature for level_2 interrupt | |
67 | + RO | |
68 | + | |
69 | +temp1_emergency temperature for level_3 interrupt | |
70 | + RO | |
71 | + | |
72 | +temp1_max_alarm alarm for level_1 interrupt | |
73 | + RO | |
74 | + | |
75 | +temp1_crit_alarm | |
76 | + alarm for level_2 interrupt | |
77 | + RO | |
78 | + | |
79 | +temp1_emergency_alarm | |
80 | + alarm for level_3 interrupt | |
81 | + RO |
Documentation/hwmon/lm75
... | ... | @@ -12,26 +12,46 @@ |
12 | 12 | Addresses scanned: I2C 0x48 - 0x4f |
13 | 13 | Datasheet: Publicly available at the National Semiconductor website |
14 | 14 | http://www.national.com/ |
15 | - * Dallas Semiconductor DS75 | |
16 | - Prefix: 'lm75' | |
17 | - Addresses scanned: I2C 0x48 - 0x4f | |
15 | + * Dallas Semiconductor DS75, DS1775 | |
16 | + Prefixes: 'ds75', 'ds1775' | |
17 | + Addresses scanned: none | |
18 | 18 | Datasheet: Publicly available at the Dallas Semiconductor website |
19 | 19 | http://www.maxim-ic.com/ |
20 | - * Dallas Semiconductor DS1775 | |
21 | - Prefix: 'lm75' | |
22 | - Addresses scanned: I2C 0x48 - 0x4f | |
23 | - Datasheet: Publicly available at the Dallas Semiconductor website | |
24 | - http://www.maxim-ic.com/ | |
25 | 20 | * Maxim MAX6625, MAX6626 |
26 | - Prefix: 'lm75' | |
27 | - Addresses scanned: I2C 0x48 - 0x4b | |
21 | + Prefixes: 'max6625', 'max6626' | |
22 | + Addresses scanned: none | |
28 | 23 | Datasheet: Publicly available at the Maxim website |
29 | 24 | http://www.maxim-ic.com/ |
30 | 25 | * Microchip (TelCom) TCN75 |
31 | 26 | Prefix: 'lm75' |
32 | - Addresses scanned: I2C 0x48 - 0x4f | |
27 | + Addresses scanned: none | |
33 | 28 | Datasheet: Publicly available at the Microchip website |
34 | 29 | http://www.microchip.com/ |
30 | + * Microchip MCP9800, MCP9801, MCP9802, MCP9803 | |
31 | + Prefix: 'mcp980x' | |
32 | + Addresses scanned: none | |
33 | + Datasheet: Publicly available at the Microchip website | |
34 | + http://www.microchip.com/ | |
35 | + * Analog Devices ADT75 | |
36 | + Prefix: 'adt75' | |
37 | + Addresses scanned: none | |
38 | + Datasheet: Publicly available at the Analog Devices website | |
39 | + http://www.analog.com/adt75 | |
40 | + * ST Microelectronics STDS75 | |
41 | + Prefix: 'stds75' | |
42 | + Addresses scanned: none | |
43 | + Datasheet: Publicly available at the ST website | |
44 | + http://www.st.com/internet/analog/product/121769.jsp | |
45 | + * Texas Instruments TMP100, TMP101, TMP105, TMP75, TMP175, TMP275 | |
46 | + Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp175', 'tmp75', 'tmp275' | |
47 | + Addresses scanned: none | |
48 | + Datasheet: Publicly available at the Texas Instruments website | |
49 | + http://www.ti.com/product/tmp100 | |
50 | + http://www.ti.com/product/tmp101 | |
51 | + http://www.ti.com/product/tmp105 | |
52 | + http://www.ti.com/product/tmp75 | |
53 | + http://www.ti.com/product/tmp175 | |
54 | + http://www.ti.com/product/tmp275 | |
35 | 55 | |
36 | 56 | Author: Frodo Looijaard <frodol@dds.nl> |
37 | 57 | |
38 | 58 | |
39 | 59 | |
... | ... | @@ -50,21 +70,16 @@ |
50 | 70 | The LM75 only updates its values each 1.5 seconds; reading it more often |
51 | 71 | will do no harm, but will return 'old' values. |
52 | 72 | |
53 | -The LM75 is usually used in combination with LM78-like chips, to measure | |
54 | -the temperature of the processor(s). | |
73 | +The original LM75 was typically used in combination with LM78-like chips | |
74 | +on PC motherboards, to measure the temperature of the processor(s). Clones | |
75 | +are now used in various embedded designs. | |
55 | 76 | |
56 | -The DS75, DS1775, MAX6625, and MAX6626 are supported as well. | |
57 | -They are not distinguished from an LM75. While most of these chips | |
58 | -have three additional bits of accuracy (12 vs. 9 for the LM75), | |
59 | -the additional bits are not supported. Not only that, but these chips will | |
60 | -not be detected if not in 9-bit precision mode (use the force parameter if | |
61 | -needed). | |
62 | - | |
63 | -The TCN75 is supported as well, and is not distinguished from an LM75. | |
64 | - | |
65 | 77 | The LM75 is essentially an industry standard; there may be other |
66 | 78 | LM75 clones not listed here, with or without various enhancements, |
67 | -that are supported. | |
79 | +that are supported. The clones are not detected by the driver, unless | |
80 | +they reproduce the exact register tricks of the original LM75, and must | |
81 | +therefore be instantiated explicitly. The specific enhancements (such as | |
82 | +higher resolution) are not currently supported by the driver. | |
68 | 83 | |
69 | 84 | The LM77 is not supported, contrary to what we pretended for a long time. |
70 | 85 | Both chips are simply not compatible, value encoding differs. |
Documentation/hwmon/ltc2978
1 | +Kernel driver ltc2978 | |
2 | +===================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * Linear Technology LTC2978 | |
6 | + Prefix: 'ltc2978' | |
7 | + Addresses scanned: - | |
8 | + Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf | |
9 | + * Linear Technology LTC3880 | |
10 | + Prefix: 'ltc3880' | |
11 | + Addresses scanned: - | |
12 | + Datasheet: http://cds.linear.com/docs/Datasheet/3880f.pdf | |
13 | + | |
14 | +Author: Guenter Roeck <guenter.roeck@ericsson.com> | |
15 | + | |
16 | + | |
17 | +Description | |
18 | +----------- | |
19 | + | |
20 | +The LTC2978 is an octal power supply monitor, supervisor, sequencer and | |
21 | +margin controller. The LTC3880 is a dual, PolyPhase DC/DC synchronous | |
22 | +step-down switching regulator controller. | |
23 | + | |
24 | + | |
25 | +Usage Notes | |
26 | +----------- | |
27 | + | |
28 | +This driver does not probe for PMBus devices. You will have to instantiate | |
29 | +devices explicitly. | |
30 | + | |
31 | +Example: the following commands will load the driver for an LTC2978 at address | |
32 | +0x60 on I2C bus #1: | |
33 | + | |
34 | +# modprobe ltc2978 | |
35 | +# echo ltc2978 0x60 > /sys/bus/i2c/devices/i2c-1/new_device | |
36 | + | |
37 | + | |
38 | +Sysfs attributes | |
39 | +---------------- | |
40 | + | |
41 | +in1_label "vin" | |
42 | +in1_input Measured input voltage. | |
43 | +in1_min Minimum input voltage. | |
44 | +in1_max Maximum input voltage. | |
45 | +in1_lcrit Critical minimum input voltage. | |
46 | +in1_crit Critical maximum input voltage. | |
47 | +in1_min_alarm Input voltage low alarm. | |
48 | +in1_max_alarm Input voltage high alarm. | |
49 | +in1_lcrit_alarm Input voltage critical low alarm. | |
50 | +in1_crit_alarm Input voltage critical high alarm. | |
51 | +in1_lowest Lowest input voltage. LTC2978 only. | |
52 | +in1_highest Highest input voltage. | |
53 | +in1_reset_history Reset history. Writing into this attribute will reset | |
54 | + history for all attributes. | |
55 | + | |
56 | +in[2-9]_label "vout[1-8]". Channels 3 to 9 on LTC2978 only. | |
57 | +in[2-9]_input Measured output voltage. | |
58 | +in[2-9]_min Minimum output voltage. | |
59 | +in[2-9]_max Maximum output voltage. | |
60 | +in[2-9]_lcrit Critical minimum output voltage. | |
61 | +in[2-9]_crit Critical maximum output voltage. | |
62 | +in[2-9]_min_alarm Output voltage low alarm. | |
63 | +in[2-9]_max_alarm Output voltage high alarm. | |
64 | +in[2-9]_lcrit_alarm Output voltage critical low alarm. | |
65 | +in[2-9]_crit_alarm Output voltage critical high alarm. | |
66 | +in[2-9]_lowest Lowest output voltage. LTC2978 only. | |
67 | +in[2-9]_highest Lowest output voltage. | |
68 | +in[2-9]_reset_history Reset history. Writing into this attribute will reset | |
69 | + history for all attributes. | |
70 | + | |
71 | +temp[1-3]_input Measured temperature. | |
72 | + On LTC2978, only one temperature measurement is | |
73 | + supported and reflects the internal temperature. | |
74 | + On LTC3880, temp1 and temp2 report external | |
75 | + temperatures, and temp3 reports the internal | |
76 | + temperature. | |
77 | +temp[1-3]_min Mimimum temperature. | |
78 | +temp[1-3]_max Maximum temperature. | |
79 | +temp[1-3]_lcrit Critical low temperature. | |
80 | +temp[1-3]_crit Critical high temperature. | |
81 | +temp[1-3]_min_alarm Chip temperature low alarm. | |
82 | +temp[1-3]_max_alarm Chip temperature high alarm. | |
83 | +temp[1-3]_lcrit_alarm Chip temperature critical low alarm. | |
84 | +temp[1-3]_crit_alarm Chip temperature critical high alarm. | |
85 | +temp[1-3]_lowest Lowest measured temperature. LTC2978 only. | |
86 | +temp[1-3]_highest Highest measured temperature. | |
87 | +temp[1-3]_reset_history Reset history. Writing into this attribute will reset | |
88 | + history for all attributes. | |
89 | + | |
90 | +power[1-2]_label "pout[1-2]". LTC3880 only. | |
91 | +power[1-2]_input Measured power. | |
92 | + | |
93 | +curr1_label "iin". LTC3880 only. | |
94 | +curr1_input Measured input current. | |
95 | +curr1_max Maximum input current. | |
96 | +curr1_max_alarm Input current high alarm. | |
97 | + | |
98 | +curr[2-3]_label "iout[1-2]". LTC3880 only. | |
99 | +curr[2-3]_input Measured input current. | |
100 | +curr[2-3]_max Maximum input current. | |
101 | +curr[2-3]_crit Critical input current. | |
102 | +curr[2-3]_max_alarm Input current high alarm. | |
103 | +curr[2-3]_crit_alarm Input current critical high alarm. |
Documentation/hwmon/pmbus
... | ... | @@ -8,11 +8,6 @@ |
8 | 8 | Addresses scanned: - |
9 | 9 | Datasheet: |
10 | 10 | http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146395 |
11 | - * Linear Technology LTC2978 | |
12 | - Octal PMBus Power Supply Monitor and Controller | |
13 | - Prefix: 'ltc2978' | |
14 | - Addresses scanned: - | |
15 | - Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf | |
16 | 11 | * ON Semiconductor ADP4000, NCP4200, NCP4208 |
17 | 12 | Prefixes: 'adp4000', 'ncp4200', 'ncp4208' |
18 | 13 | Addresses scanned: - |
... | ... | @@ -20,6 +15,14 @@ |
20 | 15 | http://www.onsemi.com/pub_link/Collateral/ADP4000-D.PDF |
21 | 16 | http://www.onsemi.com/pub_link/Collateral/NCP4200-D.PDF |
22 | 17 | http://www.onsemi.com/pub_link/Collateral/JUNE%202009-%20REV.%200.PDF |
18 | + * Lineage Power | |
19 | + Prefixes: 'pdt003', 'pdt006', 'pdt012', 'udt020' | |
20 | + Addresses scanned: - | |
21 | + Datasheets: | |
22 | + http://www.lineagepower.com/oem/pdf/PDT003A0X.pdf | |
23 | + http://www.lineagepower.com/oem/pdf/PDT006A0X.pdf | |
24 | + http://www.lineagepower.com/oem/pdf/PDT012A0X.pdf | |
25 | + http://www.lineagepower.com/oem/pdf/UDT020A0X.pdf | |
23 | 26 | * Generic PMBus devices |
24 | 27 | Prefix: 'pmbus' |
25 | 28 | Addresses scanned: - |
Documentation/hwmon/pmbus-core
1 | +PMBus core driver and internal API | |
2 | +================================== | |
3 | + | |
4 | +Introduction | |
5 | +============ | |
6 | + | |
7 | +[from pmbus.org] The Power Management Bus (PMBus) is an open standard | |
8 | +power-management protocol with a fully defined command language that facilitates | |
9 | +communication with power converters and other devices in a power system. The | |
10 | +protocol is implemented over the industry-standard SMBus serial interface and | |
11 | +enables programming, control, and real-time monitoring of compliant power | |
12 | +conversion products. This flexible and highly versatile standard allows for | |
13 | +communication between devices based on both analog and digital technologies, and | |
14 | +provides true interoperability which will reduce design complexity and shorten | |
15 | +time to market for power system designers. Pioneered by leading power supply and | |
16 | +semiconductor companies, this open power system standard is maintained and | |
17 | +promoted by the PMBus Implementers Forum (PMBus-IF), comprising 30+ adopters | |
18 | +with the objective to provide support to, and facilitate adoption among, users. | |
19 | + | |
20 | +Unfortunately, while PMBus commands are standardized, there are no mandatory | |
21 | +commands, and manufacturers can add as many non-standard commands as they like. | |
22 | +Also, different PMBUs devices act differently if non-supported commands are | |
23 | +executed. Some devices return an error, some devices return 0xff or 0xffff and | |
24 | +set a status error flag, and some devices may simply hang up. | |
25 | + | |
26 | +Despite all those difficulties, a generic PMBus device driver is still useful | |
27 | +and supported since kernel version 2.6.39. However, it was necessary to support | |
28 | +device specific extensions in addition to the core PMBus driver, since it is | |
29 | +simply unknown what new device specific functionality PMBus device developers | |
30 | +come up with next. | |
31 | + | |
32 | +To make device specific extensions as scalable as possible, and to avoid having | |
33 | +to modify the core PMBus driver repeatedly for new devices, the PMBus driver was | |
34 | +split into core, generic, and device specific code. The core code (in | |
35 | +pmbus_core.c) provides generic functionality. The generic code (in pmbus.c) | |
36 | +provides support for generic PMBus devices. Device specific code is responsible | |
37 | +for device specific initialization and, if needed, maps device specific | |
38 | +functionality into generic functionality. This is to some degree comparable | |
39 | +to PCI code, where generic code is augmented as needed with quirks for all kinds | |
40 | +of devices. | |
41 | + | |
42 | +PMBus device capabilities auto-detection | |
43 | +======================================== | |
44 | + | |
45 | +For generic PMBus devices, code in pmbus.c attempts to auto-detect all supported | |
46 | +PMBus commands. Auto-detection is somewhat limited, since there are simply too | |
47 | +many variables to consider. For example, it is almost impossible to autodetect | |
48 | +which PMBus commands are paged and which commands are replicated across all | |
49 | +pages (see the PMBus specification for details on multi-page PMBus devices). | |
50 | + | |
51 | +For this reason, it often makes sense to provide a device specific driver if not | |
52 | +all commands can be auto-detected. The data structures in this driver can be | |
53 | +used to inform the core driver about functionality supported by individual | |
54 | +chips. | |
55 | + | |
56 | +Some commands are always auto-detected. This applies to all limit commands | |
57 | +(lcrit, min, max, and crit attributes) as well as associated alarm attributes. | |
58 | +Limits and alarm attributes are auto-detected because there are simply too many | |
59 | +possible combinations to provide a manual configuration interface. | |
60 | + | |
61 | +PMBus internal API | |
62 | +================== | |
63 | + | |
64 | +The API between core and device specific PMBus code is defined in | |
65 | +drivers/hwmon/pmbus/pmbus.h. In addition to the internal API, pmbus.h defines | |
66 | +standard PMBus commands and virtual PMBus commands. | |
67 | + | |
68 | +Standard PMBus commands | |
69 | +----------------------- | |
70 | + | |
71 | +Standard PMBus commands (commands values 0x00 to 0xff) are defined in the PMBUs | |
72 | +specification. | |
73 | + | |
74 | +Virtual PMBus commands | |
75 | +---------------------- | |
76 | + | |
77 | +Virtual PMBus commands are provided to enable support for non-standard | |
78 | +functionality which has been implemented by several chip vendors and is thus | |
79 | +desirable to support. | |
80 | + | |
81 | +Virtual PMBus commands start with command value 0x100 and can thus easily be | |
82 | +distinguished from standard PMBus commands (which can not have values larger | |
83 | +than 0xff). Support for virtual PMBus commands is device specific and thus has | |
84 | +to be implemented in device specific code. | |
85 | + | |
86 | +Virtual commands are named PMBUS_VIRT_xxx and start with PMBUS_VIRT_BASE. All | |
87 | +virtual commands are word sized. | |
88 | + | |
89 | +There are currently two types of virtual commands. | |
90 | + | |
91 | +- READ commands are read-only; writes are either ignored or return an error. | |
92 | +- RESET commands are read/write. Reading reset registers returns zero | |
93 | + (used for detection), writing any value causes the associated history to be | |
94 | + reset. | |
95 | + | |
96 | +Virtual commands have to be handled in device specific driver code. Chip driver | |
97 | +code returns non-negative values if a virtual command is supported, or a | |
98 | +negative error code if not. The chip driver may return -ENODATA or any other | |
99 | +Linux error code in this case, though an error code other than -ENODATA is | |
100 | +handled more efficiently and thus preferred. Either case, the calling PMBus | |
101 | +core code will abort if the chip driver returns an error code when reading | |
102 | +or writing virtual registers (in other words, the PMBus core code will never | |
103 | +send a virtual command to a chip). | |
104 | + | |
105 | +PMBus driver information | |
106 | +------------------------ | |
107 | + | |
108 | +PMBus driver information, defined in struct pmbus_driver_info, is the main means | |
109 | +for device specific drivers to pass information to the core PMBus driver. | |
110 | +Specifically, it provides the following information. | |
111 | + | |
112 | +- For devices supporting its data in Direct Data Format, it provides coefficients | |
113 | + for converting register values into normalized data. This data is usually | |
114 | + provided by chip manufacturers in device datasheets. | |
115 | +- Supported chip functionality can be provided to the core driver. This may be | |
116 | + necessary for chips which react badly if non-supported commands are executed, | |
117 | + and/or to speed up device detection and initialization. | |
118 | +- Several function entry points are provided to support overriding and/or | |
119 | + augmenting generic command execution. This functionality can be used to map | |
120 | + non-standard PMBus commands to standard commands, or to augment standard | |
121 | + command return values with device specific information. | |
122 | + | |
123 | + API functions | |
124 | + ------------- | |
125 | + | |
126 | + Functions provided by chip driver | |
127 | + --------------------------------- | |
128 | + | |
129 | + All functions return the command return value (read) or zero (write) if | |
130 | + successful. A return value of -ENODATA indicates that there is no manufacturer | |
131 | + specific command, but that a standard PMBus command may exist. Any other | |
132 | + negative return value indicates that the commands does not exist for this | |
133 | + chip, and that no attempt should be made to read or write the standard | |
134 | + command. | |
135 | + | |
136 | + As mentioned above, an exception to this rule applies to virtual commands, | |
137 | + which _must_ be handled in driver specific code. See "Virtual PMBus Commands" | |
138 | + above for more details. | |
139 | + | |
140 | + Command execution in the core PMBus driver code is as follows. | |
141 | + | |
142 | + if (chip_access_function) { | |
143 | + status = chip_access_function(); | |
144 | + if (status != -ENODATA) | |
145 | + return status; | |
146 | + } | |
147 | + if (command >= PMBUS_VIRT_BASE) /* For word commands/registers only */ | |
148 | + return -EINVAL; | |
149 | + return generic_access(); | |
150 | + | |
151 | + Chip drivers may provide pointers to the following functions in struct | |
152 | + pmbus_driver_info. All functions are optional. | |
153 | + | |
154 | + int (*read_byte_data)(struct i2c_client *client, int page, int reg); | |
155 | + | |
156 | + Read byte from page <page>, register <reg>. | |
157 | + <page> may be -1, which means "current page". | |
158 | + | |
159 | + int (*read_word_data)(struct i2c_client *client, int page, int reg); | |
160 | + | |
161 | + Read word from page <page>, register <reg>. | |
162 | + | |
163 | + int (*write_word_data)(struct i2c_client *client, int page, int reg, | |
164 | + u16 word); | |
165 | + | |
166 | + Write word to page <page>, register <reg>. | |
167 | + | |
168 | + int (*write_byte)(struct i2c_client *client, int page, u8 value); | |
169 | + | |
170 | + Write byte to page <page>, register <reg>. | |
171 | + <page> may be -1, which means "current page". | |
172 | + | |
173 | + int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info); | |
174 | + | |
175 | + Determine supported PMBus functionality. This function is only necessary | |
176 | + if a chip driver supports multiple chips, and the chip functionality is not | |
177 | + pre-determined. It is currently only used by the generic pmbus driver | |
178 | + (pmbus.c). | |
179 | + | |
180 | + Functions exported by core driver | |
181 | + --------------------------------- | |
182 | + | |
183 | + Chip drivers are expected to use the following functions to read or write | |
184 | + PMBus registers. Chip drivers may also use direct I2C commands. If direct I2C | |
185 | + commands are used, the chip driver code must not directly modify the current | |
186 | + page, since the selected page is cached in the core driver and the core driver | |
187 | + will assume that it is selected. Using pmbus_set_page() to select a new page | |
188 | + is mandatory. | |
189 | + | |
190 | + int pmbus_set_page(struct i2c_client *client, u8 page); | |
191 | + | |
192 | + Set PMBus page register to <page> for subsequent commands. | |
193 | + | |
194 | + int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); | |
195 | + | |
196 | + Read word data from <page>, <reg>. Similar to i2c_smbus_read_word_data(), but | |
197 | + selects page first. | |
198 | + | |
199 | + int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, | |
200 | + u16 word); | |
201 | + | |
202 | + Write word data to <page>, <reg>. Similar to i2c_smbus_write_word_data(), but | |
203 | + selects page first. | |
204 | + | |
205 | + int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); | |
206 | + | |
207 | + Read byte data from <page>, <reg>. Similar to i2c_smbus_read_byte_data(), but | |
208 | + selects page first. <page> may be -1, which means "current page". | |
209 | + | |
210 | + int pmbus_write_byte(struct i2c_client *client, int page, u8 value); | |
211 | + | |
212 | + Write byte data to <page>, <reg>. Similar to i2c_smbus_write_byte(), but | |
213 | + selects page first. <page> may be -1, which means "current page". | |
214 | + | |
215 | + void pmbus_clear_faults(struct i2c_client *client); | |
216 | + | |
217 | + Execute PMBus "Clear Fault" command on all chip pages. | |
218 | + This function calls the device specific write_byte function if defined. | |
219 | + Therefore, it must _not_ be called from that function. | |
220 | + | |
221 | + bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); | |
222 | + | |
223 | + Check if byte register exists. Return true if the register exists, false | |
224 | + otherwise. | |
225 | + This function calls the device specific write_byte function if defined to | |
226 | + obtain the chip status. Therefore, it must _not_ be called from that function. | |
227 | + | |
228 | + bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); | |
229 | + | |
230 | + Check if word register exists. Return true if the register exists, false | |
231 | + otherwise. | |
232 | + This function calls the device specific write_byte function if defined to | |
233 | + obtain the chip status. Therefore, it must _not_ be called from that function. | |
234 | + | |
235 | + int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, | |
236 | + struct pmbus_driver_info *info); | |
237 | + | |
238 | + Execute probe function. Similar to standard probe function for other drivers, | |
239 | + with the pointer to struct pmbus_driver_info as additional argument. Calls | |
240 | + identify function if supported. Must only be called from device probe | |
241 | + function. | |
242 | + | |
243 | + void pmbus_do_remove(struct i2c_client *client); | |
244 | + | |
245 | + Execute driver remove function. Similar to standard driver remove function. | |
246 | + | |
247 | + const struct pmbus_driver_info | |
248 | + *pmbus_get_driver_info(struct i2c_client *client); | |
249 | + | |
250 | + Return pointer to struct pmbus_driver_info as passed to pmbus_do_probe(). | |
251 | + | |
252 | + | |
253 | +PMBus driver platform data | |
254 | +========================== | |
255 | + | |
256 | +PMBus platform data is defined in include/linux/i2c/pmbus.h. Platform data | |
257 | +currently only provides a flag field with a single bit used. | |
258 | + | |
259 | +#define PMBUS_SKIP_STATUS_CHECK (1 << 0) | |
260 | + | |
261 | +struct pmbus_platform_data { | |
262 | + u32 flags; /* Device specific flags */ | |
263 | +}; | |
264 | + | |
265 | + | |
266 | +Flags | |
267 | +----- | |
268 | + | |
269 | +PMBUS_SKIP_STATUS_CHECK | |
270 | + | |
271 | +During register detection, skip checking the status register for | |
272 | +communication or command errors. | |
273 | + | |
274 | +Some PMBus chips respond with valid data when trying to read an unsupported | |
275 | +register. For such chips, checking the status register is mandatory when | |
276 | +trying to determine if a chip register exists or not. | |
277 | +Other PMBus chips don't support the STATUS_CML register, or report | |
278 | +communication errors for no explicable reason. For such chips, checking the | |
279 | +status register must be disabled. | |
280 | + | |
281 | +Some i2c controllers do not support single-byte commands (write commands with | |
282 | +no data, i2c_smbus_write_byte()). With such controllers, clearing the status | |
283 | +register is impossible, and the PMBUS_SKIP_STATUS_CHECK flag must be set. |
Documentation/hwmon/zl6100
1 | +Kernel driver zl6100 | |
2 | +==================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * Intersil / Zilker Labs ZL2004 | |
6 | + Prefix: 'zl2004' | |
7 | + Addresses scanned: - | |
8 | + Datasheet: http://www.intersil.com/data/fn/fn6847.pdf | |
9 | + * Intersil / Zilker Labs ZL2006 | |
10 | + Prefix: 'zl2006' | |
11 | + Addresses scanned: - | |
12 | + Datasheet: http://www.intersil.com/data/fn/fn6850.pdf | |
13 | + * Intersil / Zilker Labs ZL2008 | |
14 | + Prefix: 'zl2008' | |
15 | + Addresses scanned: - | |
16 | + Datasheet: http://www.intersil.com/data/fn/fn6859.pdf | |
17 | + * Intersil / Zilker Labs ZL2105 | |
18 | + Prefix: 'zl2105' | |
19 | + Addresses scanned: - | |
20 | + Datasheet: http://www.intersil.com/data/fn/fn6851.pdf | |
21 | + * Intersil / Zilker Labs ZL2106 | |
22 | + Prefix: 'zl2106' | |
23 | + Addresses scanned: - | |
24 | + Datasheet: http://www.intersil.com/data/fn/fn6852.pdf | |
25 | + * Intersil / Zilker Labs ZL6100 | |
26 | + Prefix: 'zl6100' | |
27 | + Addresses scanned: - | |
28 | + Datasheet: http://www.intersil.com/data/fn/fn6876.pdf | |
29 | + * Intersil / Zilker Labs ZL6105 | |
30 | + Prefix: 'zl6105' | |
31 | + Addresses scanned: - | |
32 | + Datasheet: http://www.intersil.com/data/fn/fn6906.pdf | |
33 | + | |
34 | +Author: Guenter Roeck <guenter.roeck@ericsson.com> | |
35 | + | |
36 | + | |
37 | +Description | |
38 | +----------- | |
39 | + | |
40 | +This driver supports hardware montoring for Intersil / Zilker Labs ZL6100 and | |
41 | +compatible digital DC-DC controllers. | |
42 | + | |
43 | +The driver is a client driver to the core PMBus driver. Please see | |
44 | +Documentation/hwmon/pmbus and Documentation.hwmon/pmbus-core for details | |
45 | +on PMBus client drivers. | |
46 | + | |
47 | + | |
48 | +Usage Notes | |
49 | +----------- | |
50 | + | |
51 | +This driver does not auto-detect devices. You will have to instantiate the | |
52 | +devices explicitly. Please see Documentation/i2c/instantiating-devices for | |
53 | +details. | |
54 | + | |
55 | +WARNING: Do not access chip registers using the i2cdump command, and do not use | |
56 | +any of the i2ctools commands on a command register used to save and restore | |
57 | +configuration data (0x11, 0x12, 0x15, 0x16, and 0xf4). The chips supported by | |
58 | +this driver interpret any access to those command registers (including read | |
59 | +commands) as request to execute the command in question. Unless write accesses | |
60 | +to those registers are protected, this may result in power loss, board resets, | |
61 | +and/or Flash corruption. Worst case, your board may turn into a brick. | |
62 | + | |
63 | + | |
64 | +Platform data support | |
65 | +--------------------- | |
66 | + | |
67 | +The driver supports standard PMBus driver platform data. | |
68 | + | |
69 | + | |
70 | +Module parameters | |
71 | +----------------- | |
72 | + | |
73 | +delay | |
74 | +----- | |
75 | + | |
76 | +Some Intersil/Zilker Labs DC-DC controllers require a minimum interval between | |
77 | +I2C bus accesses. According to Intersil, the minimum interval is 2 ms, though | |
78 | +1 ms appears to be sufficient and has not caused any problems in testing. | |
79 | +The problem is known to affect ZL6100, ZL2105, and ZL2008. It is known not to | |
80 | +affect ZL2004 and ZL6105. The driver automatically sets the interval to 1 ms | |
81 | +except for ZL2004 and ZL6105. To enable manual override, the driver provides a | |
82 | +writeable module parameter, 'delay', which can be used to set the interval to | |
83 | +a value between 0 and 65,535 microseconds. | |
84 | + | |
85 | + | |
86 | +Sysfs entries | |
87 | +------------- | |
88 | + | |
89 | +The following attributes are supported. Limits are read-write; all other | |
90 | +attributes are read-only. | |
91 | + | |
92 | +in1_label "vin" | |
93 | +in1_input Measured input voltage. | |
94 | +in1_min Minimum input voltage. | |
95 | +in1_max Maximum input voltage. | |
96 | +in1_lcrit Critical minumum input voltage. | |
97 | +in1_crit Critical maximum input voltage. | |
98 | +in1_min_alarm Input voltage low alarm. | |
99 | +in1_max_alarm Input voltage high alarm. | |
100 | +in1_lcrit_alarm Input voltage critical low alarm. | |
101 | +in1_crit_alarm Input voltage critical high alarm. | |
102 | + | |
103 | +in2_label "vout1" | |
104 | +in2_input Measured output voltage. | |
105 | +in2_lcrit Critical minumum output Voltage. | |
106 | +in2_crit Critical maximum output voltage. | |
107 | +in2_lcrit_alarm Critical output voltage critical low alarm. | |
108 | +in2_crit_alarm Critical output voltage critical high alarm. | |
109 | + | |
110 | +curr1_label "iout1" | |
111 | +curr1_input Measured output current. | |
112 | +curr1_lcrit Critical minimum output current. | |
113 | +curr1_crit Critical maximum output current. | |
114 | +curr1_lcrit_alarm Output current critical low alarm. | |
115 | +curr1_crit_alarm Output current critical high alarm. | |
116 | + | |
117 | +temp[12]_input Measured temperature. | |
118 | +temp[12]_min Minimum temperature. | |
119 | +temp[12]_max Maximum temperature. | |
120 | +temp[12]_lcrit Critical low temperature. | |
121 | +temp[12]_crit Critical high temperature. | |
122 | +temp[12]_min_alarm Chip temperature low alarm. | |
123 | +temp[12]_max_alarm Chip temperature high alarm. | |
124 | +temp[12]_lcrit_alarm Chip temperature critical low alarm. | |
125 | +temp[12]_crit_alarm Chip temperature critical high alarm. |
drivers/hwmon/Kconfig
... | ... | @@ -68,6 +68,16 @@ |
68 | 68 | This driver can also be built as a module. If so, the module |
69 | 69 | will be called abituguru3. |
70 | 70 | |
71 | +config SENSORS_AD7314 | |
72 | + tristate "Analog Devices AD7314 and compatibles" | |
73 | + depends on SPI && EXPERIMENTAL | |
74 | + help | |
75 | + If you say yes here you get support for the Analog Devices | |
76 | + AD7314, ADT7301 and ADT7302 temperature sensors. | |
77 | + | |
78 | + This driver can also be built as a module. If so, the module | |
79 | + will be called ad7314. | |
80 | + | |
71 | 81 | config SENSORS_AD7414 |
72 | 82 | tristate "Analog Devices AD7414" |
73 | 83 | depends on I2C && EXPERIMENTAL |
... | ... | @@ -303,6 +313,16 @@ |
303 | 313 | This driver can also be built as a module. If so, the module |
304 | 314 | will be called ds1621. |
305 | 315 | |
316 | +config SENSORS_EXYNOS4_TMU | |
317 | + tristate "Temperature sensor on Samsung EXYNOS4" | |
318 | + depends on EXYNOS4_DEV_TMU | |
319 | + help | |
320 | + If you say yes here you get support for TMU (Thermal Managment | |
321 | + Unit) on SAMSUNG EXYNOS4 series of SoC. | |
322 | + | |
323 | + This driver can also be built as a module. If so, the module | |
324 | + will be called exynos4-tmu. | |
325 | + | |
306 | 326 | config SENSORS_I5K_AMB |
307 | 327 | tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets" |
308 | 328 | depends on PCI && EXPERIMENTAL |
... | ... | @@ -531,6 +551,7 @@ |
531 | 551 | If you say yes here you get support for one common type of |
532 | 552 | temperature sensor chip, with models including: |
533 | 553 | |
554 | + - Analog Devices ADT75 | |
534 | 555 | - Dallas Semiconductor DS75 and DS1775 |
535 | 556 | - Maxim MAX6625 and MAX6626 |
536 | 557 | - Microchip MCP980x |
drivers/hwmon/Makefile
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | |
22 | 22 | obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o |
23 | 23 | obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o |
24 | +obj-$(CONFIG_SENSORS_AD7314) += ad7314.o | |
24 | 25 | obj-$(CONFIG_SENSORS_AD7414) += ad7414.o |
25 | 26 | obj-$(CONFIG_SENSORS_AD7418) += ad7418.o |
26 | 27 | obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o |
... | ... | @@ -47,6 +48,7 @@ |
47 | 48 | obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o |
48 | 49 | obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o |
49 | 50 | obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o |
51 | +obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o | |
50 | 52 | obj-$(CONFIG_SENSORS_F71805F) += f71805f.o |
51 | 53 | obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o |
52 | 54 | obj-$(CONFIG_SENSORS_F75375S) += f75375s.o |
drivers/hwmon/ad7314.c
1 | +/* | |
2 | + * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302 | |
3 | + * | |
4 | + * Copyright 2010 Analog Devices Inc. | |
5 | + * | |
6 | + * Licensed under the GPL-2 or later. | |
7 | + * | |
8 | + * Conversion to hwmon from IIO done by Jonathan Cameron <jic23@cam.ac.uk> | |
9 | + */ | |
10 | +#include <linux/device.h> | |
11 | +#include <linux/kernel.h> | |
12 | +#include <linux/slab.h> | |
13 | +#include <linux/sysfs.h> | |
14 | +#include <linux/spi/spi.h> | |
15 | +#include <linux/module.h> | |
16 | +#include <linux/err.h> | |
17 | +#include <linux/hwmon.h> | |
18 | +#include <linux/hwmon-sysfs.h> | |
19 | + | |
20 | +/* | |
21 | + * AD7314 power mode | |
22 | + */ | |
23 | +#define AD7314_PD 0x2000 | |
24 | + | |
25 | +/* | |
26 | + * AD7314 temperature masks | |
27 | + */ | |
28 | +#define AD7314_TEMP_SIGN 0x200 | |
29 | +#define AD7314_TEMP_MASK 0x7FE0 | |
30 | +#define AD7314_TEMP_OFFSET 5 | |
31 | + | |
32 | +/* | |
33 | + * ADT7301 and ADT7302 temperature masks | |
34 | + */ | |
35 | +#define ADT7301_TEMP_SIGN 0x2000 | |
36 | +#define ADT7301_TEMP_MASK 0x3FFF | |
37 | + | |
38 | +enum ad7314_variant { | |
39 | + adt7301, | |
40 | + adt7302, | |
41 | + ad7314, | |
42 | +}; | |
43 | + | |
44 | +struct ad7314_data { | |
45 | + struct spi_device *spi_dev; | |
46 | + struct device *hwmon_dev; | |
47 | + u16 rx ____cacheline_aligned; | |
48 | +}; | |
49 | + | |
50 | +static int ad7314_spi_read(struct ad7314_data *chip, s16 *data) | |
51 | +{ | |
52 | + int ret; | |
53 | + | |
54 | + ret = spi_read(chip->spi_dev, (u8 *)&chip->rx, sizeof(chip->rx)); | |
55 | + if (ret < 0) { | |
56 | + dev_err(&chip->spi_dev->dev, "SPI read error\n"); | |
57 | + return ret; | |
58 | + } | |
59 | + | |
60 | + *data = be16_to_cpu(chip->rx); | |
61 | + | |
62 | + return ret; | |
63 | +} | |
64 | + | |
65 | +static ssize_t ad7314_show_temperature(struct device *dev, | |
66 | + struct device_attribute *attr, | |
67 | + char *buf) | |
68 | +{ | |
69 | + struct ad7314_data *chip = dev_get_drvdata(dev); | |
70 | + s16 data; | |
71 | + int ret; | |
72 | + | |
73 | + ret = ad7314_spi_read(chip, &data); | |
74 | + if (ret < 0) | |
75 | + return ret; | |
76 | + switch (spi_get_device_id(chip->spi_dev)->driver_data) { | |
77 | + case ad7314: | |
78 | + data = (data & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET; | |
79 | + data = (data << 6) >> 6; | |
80 | + | |
81 | + return sprintf(buf, "%d\n", 250 * data); | |
82 | + case adt7301: | |
83 | + case adt7302: | |
84 | + /* | |
85 | + * Documented as a 13 bit twos complement register | |
86 | + * with a sign bit - which is a 14 bit 2's complement | |
87 | + * register. 1lsb - 31.25 milli degrees centigrade | |
88 | + */ | |
89 | + data &= ADT7301_TEMP_MASK; | |
90 | + data = (data << 2) >> 2; | |
91 | + | |
92 | + return sprintf(buf, "%d\n", | |
93 | + DIV_ROUND_CLOSEST(data * 3125, 100)); | |
94 | + default: | |
95 | + return -EINVAL; | |
96 | + } | |
97 | +} | |
98 | + | |
99 | +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, | |
100 | + ad7314_show_temperature, NULL, 0); | |
101 | + | |
102 | +static struct attribute *ad7314_attributes[] = { | |
103 | + &sensor_dev_attr_temp1_input.dev_attr.attr, | |
104 | + NULL, | |
105 | +}; | |
106 | + | |
107 | +static const struct attribute_group ad7314_group = { | |
108 | + .attrs = ad7314_attributes, | |
109 | +}; | |
110 | + | |
111 | +static int __devinit ad7314_probe(struct spi_device *spi_dev) | |
112 | +{ | |
113 | + int ret; | |
114 | + struct ad7314_data *chip; | |
115 | + | |
116 | + chip = kzalloc(sizeof(*chip), GFP_KERNEL); | |
117 | + if (chip == NULL) { | |
118 | + ret = -ENOMEM; | |
119 | + goto error_ret; | |
120 | + } | |
121 | + dev_set_drvdata(&spi_dev->dev, chip); | |
122 | + | |
123 | + ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group); | |
124 | + if (ret < 0) | |
125 | + goto error_free_chip; | |
126 | + chip->hwmon_dev = hwmon_device_register(&spi_dev->dev); | |
127 | + if (IS_ERR(chip->hwmon_dev)) { | |
128 | + ret = PTR_ERR(chip->hwmon_dev); | |
129 | + goto error_remove_group; | |
130 | + } | |
131 | + | |
132 | + return 0; | |
133 | +error_remove_group: | |
134 | + sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group); | |
135 | +error_free_chip: | |
136 | + kfree(chip); | |
137 | +error_ret: | |
138 | + return ret; | |
139 | +} | |
140 | + | |
141 | +static int __devexit ad7314_remove(struct spi_device *spi_dev) | |
142 | +{ | |
143 | + struct ad7314_data *chip = dev_get_drvdata(&spi_dev->dev); | |
144 | + | |
145 | + hwmon_device_unregister(chip->hwmon_dev); | |
146 | + sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group); | |
147 | + kfree(chip); | |
148 | + | |
149 | + return 0; | |
150 | +} | |
151 | + | |
152 | +static const struct spi_device_id ad7314_id[] = { | |
153 | + { "adt7301", adt7301 }, | |
154 | + { "adt7302", adt7302 }, | |
155 | + { "ad7314", ad7314 }, | |
156 | + { } | |
157 | +}; | |
158 | +MODULE_DEVICE_TABLE(spi, ad7314_id); | |
159 | + | |
160 | +static struct spi_driver ad7314_driver = { | |
161 | + .driver = { | |
162 | + .name = "ad7314", | |
163 | + .bus = &spi_bus_type, | |
164 | + .owner = THIS_MODULE, | |
165 | + }, | |
166 | + .probe = ad7314_probe, | |
167 | + .remove = __devexit_p(ad7314_remove), | |
168 | + .id_table = ad7314_id, | |
169 | +}; | |
170 | + | |
171 | +static __init int ad7314_init(void) | |
172 | +{ | |
173 | + return spi_register_driver(&ad7314_driver); | |
174 | +} | |
175 | +module_init(ad7314_init); | |
176 | + | |
177 | +static __exit void ad7314_exit(void) | |
178 | +{ | |
179 | + spi_unregister_driver(&ad7314_driver); | |
180 | +} | |
181 | +module_exit(ad7314_exit); | |
182 | + | |
183 | +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); | |
184 | +MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital" | |
185 | + " temperature sensor driver"); | |
186 | +MODULE_LICENSE("GPL v2"); |
drivers/hwmon/exynos4_tmu.c
1 | +/* | |
2 | + * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit) | |
3 | + * | |
4 | + * Copyright (C) 2011 Samsung Electronics | |
5 | + * Donggeun Kim <dg77.kim@samsung.com> | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | + * | |
21 | + */ | |
22 | + | |
23 | +#include <linux/module.h> | |
24 | +#include <linux/err.h> | |
25 | +#include <linux/kernel.h> | |
26 | +#include <linux/slab.h> | |
27 | +#include <linux/platform_device.h> | |
28 | +#include <linux/interrupt.h> | |
29 | +#include <linux/clk.h> | |
30 | +#include <linux/workqueue.h> | |
31 | +#include <linux/sysfs.h> | |
32 | +#include <linux/kobject.h> | |
33 | +#include <linux/io.h> | |
34 | +#include <linux/mutex.h> | |
35 | + | |
36 | +#include <linux/hwmon.h> | |
37 | +#include <linux/hwmon-sysfs.h> | |
38 | + | |
39 | +#include <linux/platform_data/exynos4_tmu.h> | |
40 | + | |
41 | +#define EXYNOS4_TMU_REG_TRIMINFO 0x0 | |
42 | +#define EXYNOS4_TMU_REG_CONTROL 0x20 | |
43 | +#define EXYNOS4_TMU_REG_STATUS 0x28 | |
44 | +#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40 | |
45 | +#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44 | |
46 | +#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50 | |
47 | +#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54 | |
48 | +#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58 | |
49 | +#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C | |
50 | +#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60 | |
51 | +#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64 | |
52 | +#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68 | |
53 | +#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C | |
54 | +#define EXYNOS4_TMU_REG_INTEN 0x70 | |
55 | +#define EXYNOS4_TMU_REG_INTSTAT 0x74 | |
56 | +#define EXYNOS4_TMU_REG_INTCLEAR 0x78 | |
57 | + | |
58 | +#define EXYNOS4_TMU_GAIN_SHIFT 8 | |
59 | +#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24 | |
60 | + | |
61 | +#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff | |
62 | +#define EXYNOS4_TMU_CORE_ON 3 | |
63 | +#define EXYNOS4_TMU_CORE_OFF 2 | |
64 | +#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50 | |
65 | +#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1 | |
66 | +#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10 | |
67 | +#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100 | |
68 | +#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000 | |
69 | +#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111 | |
70 | + | |
71 | +struct exynos4_tmu_data { | |
72 | + struct exynos4_tmu_platform_data *pdata; | |
73 | + struct device *hwmon_dev; | |
74 | + struct resource *mem; | |
75 | + void __iomem *base; | |
76 | + int irq; | |
77 | + struct work_struct irq_work; | |
78 | + struct mutex lock; | |
79 | + struct clk *clk; | |
80 | + u8 temp_error1, temp_error2; | |
81 | +}; | |
82 | + | |
83 | +/* | |
84 | + * TMU treats temperature as a mapped temperature code. | |
85 | + * The temperature is converted differently depending on the calibration type. | |
86 | + */ | |
87 | +static int temp_to_code(struct exynos4_tmu_data *data, u8 temp) | |
88 | +{ | |
89 | + struct exynos4_tmu_platform_data *pdata = data->pdata; | |
90 | + int temp_code; | |
91 | + | |
92 | + /* temp should range between 25 and 125 */ | |
93 | + if (temp < 25 || temp > 125) { | |
94 | + temp_code = -EINVAL; | |
95 | + goto out; | |
96 | + } | |
97 | + | |
98 | + switch (pdata->cal_type) { | |
99 | + case TYPE_TWO_POINT_TRIMMING: | |
100 | + temp_code = (temp - 25) * | |
101 | + (data->temp_error2 - data->temp_error1) / | |
102 | + (85 - 25) + data->temp_error1; | |
103 | + break; | |
104 | + case TYPE_ONE_POINT_TRIMMING: | |
105 | + temp_code = temp + data->temp_error1 - 25; | |
106 | + break; | |
107 | + default: | |
108 | + temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; | |
109 | + break; | |
110 | + } | |
111 | +out: | |
112 | + return temp_code; | |
113 | +} | |
114 | + | |
115 | +/* | |
116 | + * Calculate a temperature value from a temperature code. | |
117 | + * The unit of the temperature is degree Celsius. | |
118 | + */ | |
119 | +static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code) | |
120 | +{ | |
121 | + struct exynos4_tmu_platform_data *pdata = data->pdata; | |
122 | + int temp; | |
123 | + | |
124 | + /* temp_code should range between 75 and 175 */ | |
125 | + if (temp_code < 75 || temp_code > 175) { | |
126 | + temp = -ENODATA; | |
127 | + goto out; | |
128 | + } | |
129 | + | |
130 | + switch (pdata->cal_type) { | |
131 | + case TYPE_TWO_POINT_TRIMMING: | |
132 | + temp = (temp_code - data->temp_error1) * (85 - 25) / | |
133 | + (data->temp_error2 - data->temp_error1) + 25; | |
134 | + break; | |
135 | + case TYPE_ONE_POINT_TRIMMING: | |
136 | + temp = temp_code - data->temp_error1 + 25; | |
137 | + break; | |
138 | + default: | |
139 | + temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; | |
140 | + break; | |
141 | + } | |
142 | +out: | |
143 | + return temp; | |
144 | +} | |
145 | + | |
146 | +static int exynos4_tmu_initialize(struct platform_device *pdev) | |
147 | +{ | |
148 | + struct exynos4_tmu_data *data = platform_get_drvdata(pdev); | |
149 | + struct exynos4_tmu_platform_data *pdata = data->pdata; | |
150 | + unsigned int status, trim_info; | |
151 | + int ret = 0, threshold_code; | |
152 | + | |
153 | + mutex_lock(&data->lock); | |
154 | + clk_enable(data->clk); | |
155 | + | |
156 | + status = readb(data->base + EXYNOS4_TMU_REG_STATUS); | |
157 | + if (!status) { | |
158 | + ret = -EBUSY; | |
159 | + goto out; | |
160 | + } | |
161 | + | |
162 | + /* Save trimming info in order to perform calibration */ | |
163 | + trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO); | |
164 | + data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK; | |
165 | + data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK); | |
166 | + | |
167 | + /* Write temperature code for threshold */ | |
168 | + threshold_code = temp_to_code(data, pdata->threshold); | |
169 | + if (threshold_code < 0) { | |
170 | + ret = threshold_code; | |
171 | + goto out; | |
172 | + } | |
173 | + writeb(threshold_code, | |
174 | + data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP); | |
175 | + | |
176 | + writeb(pdata->trigger_levels[0], | |
177 | + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0); | |
178 | + writeb(pdata->trigger_levels[1], | |
179 | + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1); | |
180 | + writeb(pdata->trigger_levels[2], | |
181 | + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2); | |
182 | + writeb(pdata->trigger_levels[3], | |
183 | + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3); | |
184 | + | |
185 | + writel(EXYNOS4_TMU_INTCLEAR_VAL, | |
186 | + data->base + EXYNOS4_TMU_REG_INTCLEAR); | |
187 | +out: | |
188 | + clk_disable(data->clk); | |
189 | + mutex_unlock(&data->lock); | |
190 | + | |
191 | + return ret; | |
192 | +} | |
193 | + | |
194 | +static void exynos4_tmu_control(struct platform_device *pdev, bool on) | |
195 | +{ | |
196 | + struct exynos4_tmu_data *data = platform_get_drvdata(pdev); | |
197 | + struct exynos4_tmu_platform_data *pdata = data->pdata; | |
198 | + unsigned int con, interrupt_en; | |
199 | + | |
200 | + mutex_lock(&data->lock); | |
201 | + clk_enable(data->clk); | |
202 | + | |
203 | + con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT | | |
204 | + pdata->gain << EXYNOS4_TMU_GAIN_SHIFT; | |
205 | + if (on) { | |
206 | + con |= EXYNOS4_TMU_CORE_ON; | |
207 | + interrupt_en = pdata->trigger_level3_en << 12 | | |
208 | + pdata->trigger_level2_en << 8 | | |
209 | + pdata->trigger_level1_en << 4 | | |
210 | + pdata->trigger_level0_en; | |
211 | + } else { | |
212 | + con |= EXYNOS4_TMU_CORE_OFF; | |
213 | + interrupt_en = 0; /* Disable all interrupts */ | |
214 | + } | |
215 | + writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN); | |
216 | + writel(con, data->base + EXYNOS4_TMU_REG_CONTROL); | |
217 | + | |
218 | + clk_disable(data->clk); | |
219 | + mutex_unlock(&data->lock); | |
220 | +} | |
221 | + | |
222 | +static int exynos4_tmu_read(struct exynos4_tmu_data *data) | |
223 | +{ | |
224 | + u8 temp_code; | |
225 | + int temp; | |
226 | + | |
227 | + mutex_lock(&data->lock); | |
228 | + clk_enable(data->clk); | |
229 | + | |
230 | + temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP); | |
231 | + temp = code_to_temp(data, temp_code); | |
232 | + | |
233 | + clk_disable(data->clk); | |
234 | + mutex_unlock(&data->lock); | |
235 | + | |
236 | + return temp; | |
237 | +} | |
238 | + | |
239 | +static void exynos4_tmu_work(struct work_struct *work) | |
240 | +{ | |
241 | + struct exynos4_tmu_data *data = container_of(work, | |
242 | + struct exynos4_tmu_data, irq_work); | |
243 | + | |
244 | + mutex_lock(&data->lock); | |
245 | + clk_enable(data->clk); | |
246 | + | |
247 | + writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR); | |
248 | + | |
249 | + kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE); | |
250 | + | |
251 | + enable_irq(data->irq); | |
252 | + | |
253 | + clk_disable(data->clk); | |
254 | + mutex_unlock(&data->lock); | |
255 | +} | |
256 | + | |
257 | +static irqreturn_t exynos4_tmu_irq(int irq, void *id) | |
258 | +{ | |
259 | + struct exynos4_tmu_data *data = id; | |
260 | + | |
261 | + disable_irq_nosync(irq); | |
262 | + schedule_work(&data->irq_work); | |
263 | + | |
264 | + return IRQ_HANDLED; | |
265 | +} | |
266 | + | |
267 | +static ssize_t exynos4_tmu_show_name(struct device *dev, | |
268 | + struct device_attribute *attr, char *buf) | |
269 | +{ | |
270 | + return sprintf(buf, "exynos4-tmu\n"); | |
271 | +} | |
272 | + | |
273 | +static ssize_t exynos4_tmu_show_temp(struct device *dev, | |
274 | + struct device_attribute *attr, char *buf) | |
275 | +{ | |
276 | + struct exynos4_tmu_data *data = dev_get_drvdata(dev); | |
277 | + int ret; | |
278 | + | |
279 | + ret = exynos4_tmu_read(data); | |
280 | + if (ret < 0) | |
281 | + return ret; | |
282 | + | |
283 | + /* convert from degree Celsius to millidegree Celsius */ | |
284 | + return sprintf(buf, "%d\n", ret * 1000); | |
285 | +} | |
286 | + | |
287 | +static ssize_t exynos4_tmu_show_alarm(struct device *dev, | |
288 | + struct device_attribute *devattr, char *buf) | |
289 | +{ | |
290 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
291 | + struct exynos4_tmu_data *data = dev_get_drvdata(dev); | |
292 | + struct exynos4_tmu_platform_data *pdata = data->pdata; | |
293 | + int temp; | |
294 | + unsigned int trigger_level; | |
295 | + | |
296 | + temp = exynos4_tmu_read(data); | |
297 | + if (temp < 0) | |
298 | + return temp; | |
299 | + | |
300 | + trigger_level = pdata->threshold + pdata->trigger_levels[attr->index]; | |
301 | + | |
302 | + return sprintf(buf, "%d\n", !!(temp > trigger_level)); | |
303 | +} | |
304 | + | |
305 | +static ssize_t exynos4_tmu_show_level(struct device *dev, | |
306 | + struct device_attribute *devattr, char *buf) | |
307 | +{ | |
308 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
309 | + struct exynos4_tmu_data *data = dev_get_drvdata(dev); | |
310 | + struct exynos4_tmu_platform_data *pdata = data->pdata; | |
311 | + unsigned int temp = pdata->threshold + | |
312 | + pdata->trigger_levels[attr->index]; | |
313 | + | |
314 | + return sprintf(buf, "%u\n", temp * 1000); | |
315 | +} | |
316 | + | |
317 | +static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL); | |
318 | +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0); | |
319 | + | |
320 | +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, | |
321 | + exynos4_tmu_show_alarm, NULL, 1); | |
322 | +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, | |
323 | + exynos4_tmu_show_alarm, NULL, 2); | |
324 | +static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, | |
325 | + exynos4_tmu_show_alarm, NULL, 3); | |
326 | + | |
327 | +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1); | |
328 | +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2); | |
329 | +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO, | |
330 | + exynos4_tmu_show_level, NULL, 3); | |
331 | + | |
332 | +static struct attribute *exynos4_tmu_attributes[] = { | |
333 | + &dev_attr_name.attr, | |
334 | + &sensor_dev_attr_temp1_input.dev_attr.attr, | |
335 | + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, | |
336 | + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, | |
337 | + &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, | |
338 | + &sensor_dev_attr_temp1_max.dev_attr.attr, | |
339 | + &sensor_dev_attr_temp1_crit.dev_attr.attr, | |
340 | + &sensor_dev_attr_temp1_emergency.dev_attr.attr, | |
341 | + NULL, | |
342 | +}; | |
343 | + | |
344 | +static const struct attribute_group exynos4_tmu_attr_group = { | |
345 | + .attrs = exynos4_tmu_attributes, | |
346 | +}; | |
347 | + | |
348 | +static int __devinit exynos4_tmu_probe(struct platform_device *pdev) | |
349 | +{ | |
350 | + struct exynos4_tmu_data *data; | |
351 | + struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data; | |
352 | + int ret; | |
353 | + | |
354 | + if (!pdata) { | |
355 | + dev_err(&pdev->dev, "No platform init data supplied.\n"); | |
356 | + return -ENODEV; | |
357 | + } | |
358 | + | |
359 | + data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL); | |
360 | + if (!data) { | |
361 | + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); | |
362 | + return -ENOMEM; | |
363 | + } | |
364 | + | |
365 | + data->irq = platform_get_irq(pdev, 0); | |
366 | + if (data->irq < 0) { | |
367 | + ret = data->irq; | |
368 | + dev_err(&pdev->dev, "Failed to get platform irq\n"); | |
369 | + goto err_free; | |
370 | + } | |
371 | + | |
372 | + INIT_WORK(&data->irq_work, exynos4_tmu_work); | |
373 | + | |
374 | + data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
375 | + if (!data->mem) { | |
376 | + ret = -ENOENT; | |
377 | + dev_err(&pdev->dev, "Failed to get platform resource\n"); | |
378 | + goto err_free; | |
379 | + } | |
380 | + | |
381 | + data->mem = request_mem_region(data->mem->start, | |
382 | + resource_size(data->mem), pdev->name); | |
383 | + if (!data->mem) { | |
384 | + ret = -ENODEV; | |
385 | + dev_err(&pdev->dev, "Failed to request memory region\n"); | |
386 | + goto err_free; | |
387 | + } | |
388 | + | |
389 | + data->base = ioremap(data->mem->start, resource_size(data->mem)); | |
390 | + if (!data->base) { | |
391 | + ret = -ENODEV; | |
392 | + dev_err(&pdev->dev, "Failed to ioremap memory\n"); | |
393 | + goto err_mem_region; | |
394 | + } | |
395 | + | |
396 | + ret = request_irq(data->irq, exynos4_tmu_irq, | |
397 | + IRQF_TRIGGER_RISING, | |
398 | + "exynos4-tmu", data); | |
399 | + if (ret) { | |
400 | + dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); | |
401 | + goto err_io_remap; | |
402 | + } | |
403 | + | |
404 | + data->clk = clk_get(NULL, "tmu_apbif"); | |
405 | + if (IS_ERR(data->clk)) { | |
406 | + ret = PTR_ERR(data->clk); | |
407 | + dev_err(&pdev->dev, "Failed to get clock\n"); | |
408 | + goto err_irq; | |
409 | + } | |
410 | + | |
411 | + data->pdata = pdata; | |
412 | + platform_set_drvdata(pdev, data); | |
413 | + mutex_init(&data->lock); | |
414 | + | |
415 | + ret = exynos4_tmu_initialize(pdev); | |
416 | + if (ret) { | |
417 | + dev_err(&pdev->dev, "Failed to initialize TMU\n"); | |
418 | + goto err_clk; | |
419 | + } | |
420 | + | |
421 | + ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); | |
422 | + if (ret) { | |
423 | + dev_err(&pdev->dev, "Failed to create sysfs group\n"); | |
424 | + goto err_clk; | |
425 | + } | |
426 | + | |
427 | + data->hwmon_dev = hwmon_device_register(&pdev->dev); | |
428 | + if (IS_ERR(data->hwmon_dev)) { | |
429 | + ret = PTR_ERR(data->hwmon_dev); | |
430 | + dev_err(&pdev->dev, "Failed to register hwmon device\n"); | |
431 | + goto err_create_group; | |
432 | + } | |
433 | + | |
434 | + exynos4_tmu_control(pdev, true); | |
435 | + | |
436 | + return 0; | |
437 | + | |
438 | +err_create_group: | |
439 | + sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); | |
440 | +err_clk: | |
441 | + platform_set_drvdata(pdev, NULL); | |
442 | + clk_put(data->clk); | |
443 | +err_irq: | |
444 | + free_irq(data->irq, data); | |
445 | +err_io_remap: | |
446 | + iounmap(data->base); | |
447 | +err_mem_region: | |
448 | + release_mem_region(data->mem->start, resource_size(data->mem)); | |
449 | +err_free: | |
450 | + kfree(data); | |
451 | + | |
452 | + return ret; | |
453 | +} | |
454 | + | |
455 | +static int __devexit exynos4_tmu_remove(struct platform_device *pdev) | |
456 | +{ | |
457 | + struct exynos4_tmu_data *data = platform_get_drvdata(pdev); | |
458 | + | |
459 | + exynos4_tmu_control(pdev, false); | |
460 | + | |
461 | + hwmon_device_unregister(data->hwmon_dev); | |
462 | + sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); | |
463 | + | |
464 | + clk_put(data->clk); | |
465 | + | |
466 | + free_irq(data->irq, data); | |
467 | + | |
468 | + iounmap(data->base); | |
469 | + release_mem_region(data->mem->start, resource_size(data->mem)); | |
470 | + | |
471 | + platform_set_drvdata(pdev, NULL); | |
472 | + | |
473 | + kfree(data); | |
474 | + | |
475 | + return 0; | |
476 | +} | |
477 | + | |
478 | +#ifdef CONFIG_PM | |
479 | +static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state) | |
480 | +{ | |
481 | + exynos4_tmu_control(pdev, false); | |
482 | + | |
483 | + return 0; | |
484 | +} | |
485 | + | |
486 | +static int exynos4_tmu_resume(struct platform_device *pdev) | |
487 | +{ | |
488 | + exynos4_tmu_initialize(pdev); | |
489 | + exynos4_tmu_control(pdev, true); | |
490 | + | |
491 | + return 0; | |
492 | +} | |
493 | +#else | |
494 | +#define exynos4_tmu_suspend NULL | |
495 | +#define exynos4_tmu_resume NULL | |
496 | +#endif | |
497 | + | |
498 | +static struct platform_driver exynos4_tmu_driver = { | |
499 | + .driver = { | |
500 | + .name = "exynos4-tmu", | |
501 | + .owner = THIS_MODULE, | |
502 | + }, | |
503 | + .probe = exynos4_tmu_probe, | |
504 | + .remove = __devexit_p(exynos4_tmu_remove), | |
505 | + .suspend = exynos4_tmu_suspend, | |
506 | + .resume = exynos4_tmu_resume, | |
507 | +}; | |
508 | + | |
509 | +static int __init exynos4_tmu_driver_init(void) | |
510 | +{ | |
511 | + return platform_driver_register(&exynos4_tmu_driver); | |
512 | +} | |
513 | +module_init(exynos4_tmu_driver_init); | |
514 | + | |
515 | +static void __exit exynos4_tmu_driver_exit(void) | |
516 | +{ | |
517 | + platform_driver_unregister(&exynos4_tmu_driver); | |
518 | +} | |
519 | +module_exit(exynos4_tmu_driver_exit); | |
520 | + | |
521 | +MODULE_DESCRIPTION("EXYNOS4 TMU Driver"); | |
522 | +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); | |
523 | +MODULE_LICENSE("GPL"); | |
524 | +MODULE_ALIAS("platform:exynos4-tmu"); |
drivers/hwmon/f71882fg.c
... | ... | @@ -605,7 +605,7 @@ |
605 | 605 | |
606 | 606 | /* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the |
607 | 607 | standard models */ |
608 | -static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { | |
608 | +static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { { | |
609 | 609 | SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, |
610 | 610 | show_pwm_auto_point_channel, |
611 | 611 | store_pwm_auto_point_channel, 0, 0), |
... | ... | @@ -627,7 +627,7 @@ |
627 | 627 | 0, 0), |
628 | 628 | SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, |
629 | 629 | show_pwm_auto_point_temp_hyst, NULL, 3, 0), |
630 | - | |
630 | +}, { | |
631 | 631 | SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, |
632 | 632 | show_pwm_auto_point_channel, |
633 | 633 | store_pwm_auto_point_channel, 0, 1), |
... | ... | @@ -649,7 +649,7 @@ |
649 | 649 | 0, 1), |
650 | 650 | SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, |
651 | 651 | show_pwm_auto_point_temp_hyst, NULL, 3, 1), |
652 | - | |
652 | +}, { | |
653 | 653 | SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, |
654 | 654 | show_pwm_auto_point_channel, |
655 | 655 | store_pwm_auto_point_channel, 0, 2), |
656 | 656 | |
... | ... | @@ -671,12 +671,12 @@ |
671 | 671 | 0, 2), |
672 | 672 | SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, |
673 | 673 | show_pwm_auto_point_temp_hyst, NULL, 3, 2), |
674 | -}; | |
674 | +} }; | |
675 | 675 | |
676 | 676 | /* PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the |
677 | 677 | pwm setting when the temperature is above the pwmX_auto_point1_temp can be |
678 | 678 | programmed instead of being hardcoded to 0xff */ |
679 | -static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = { | |
679 | +static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { { | |
680 | 680 | SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, |
681 | 681 | show_pwm_auto_point_channel, |
682 | 682 | store_pwm_auto_point_channel, 0, 0), |
... | ... | @@ -701,7 +701,7 @@ |
701 | 701 | 0, 0), |
702 | 702 | SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, |
703 | 703 | show_pwm_auto_point_temp_hyst, NULL, 3, 0), |
704 | - | |
704 | +}, { | |
705 | 705 | SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, |
706 | 706 | show_pwm_auto_point_channel, |
707 | 707 | store_pwm_auto_point_channel, 0, 1), |
... | ... | @@ -726,7 +726,7 @@ |
726 | 726 | 0, 1), |
727 | 727 | SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, |
728 | 728 | show_pwm_auto_point_temp_hyst, NULL, 3, 1), |
729 | - | |
729 | +}, { | |
730 | 730 | SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, |
731 | 731 | show_pwm_auto_point_channel, |
732 | 732 | store_pwm_auto_point_channel, 0, 2), |
... | ... | @@ -751,7 +751,7 @@ |
751 | 751 | 0, 2), |
752 | 752 | SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, |
753 | 753 | show_pwm_auto_point_temp_hyst, NULL, 3, 2), |
754 | -}; | |
754 | +} }; | |
755 | 755 | |
756 | 756 | /* PWM attr for the standard models */ |
757 | 757 | static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { { |
... | ... | @@ -928,7 +928,7 @@ |
928 | 928 | /* PWM attr for the f8000, zones mapped to temp instead of to pwm! |
929 | 929 | Also the register block at offset A0 maps to TEMP1 (so our temp2, as the |
930 | 930 | F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ |
931 | -static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { | |
931 | +static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { { | |
932 | 932 | SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, |
933 | 933 | show_pwm_auto_point_channel, |
934 | 934 | store_pwm_auto_point_channel, 0, 0), |
... | ... | @@ -969,7 +969,7 @@ |
969 | 969 | show_pwm_auto_point_temp_hyst, NULL, 2, 2), |
970 | 970 | SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, |
971 | 971 | show_pwm_auto_point_temp_hyst, NULL, 3, 2), |
972 | - | |
972 | +}, { | |
973 | 973 | SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, |
974 | 974 | show_pwm_auto_point_channel, |
975 | 975 | store_pwm_auto_point_channel, 0, 1), |
... | ... | @@ -1010,7 +1010,7 @@ |
1010 | 1010 | show_pwm_auto_point_temp_hyst, NULL, 2, 0), |
1011 | 1011 | SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, |
1012 | 1012 | show_pwm_auto_point_temp_hyst, NULL, 3, 0), |
1013 | - | |
1013 | +}, { | |
1014 | 1014 | SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, |
1015 | 1015 | show_pwm_auto_point_channel, |
1016 | 1016 | store_pwm_auto_point_channel, 0, 2), |
... | ... | @@ -1051,7 +1051,7 @@ |
1051 | 1051 | show_pwm_auto_point_temp_hyst, NULL, 2, 1), |
1052 | 1052 | SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO, |
1053 | 1053 | show_pwm_auto_point_temp_hyst, NULL, 3, 1), |
1054 | -}; | |
1054 | +} }; | |
1055 | 1055 | |
1056 | 1056 | /* Super I/O functions */ |
1057 | 1057 | static inline int superio_inb(int base, int reg) |
... | ... | @@ -2154,6 +2154,104 @@ |
2154 | 2154 | device_remove_file(&pdev->dev, &attr[i].dev_attr); |
2155 | 2155 | } |
2156 | 2156 | |
2157 | +static int __devinit f71882fg_create_fan_sysfs_files( | |
2158 | + struct platform_device *pdev, int idx) | |
2159 | +{ | |
2160 | + struct f71882fg_data *data = platform_get_drvdata(pdev); | |
2161 | + int err; | |
2162 | + | |
2163 | + /* Sanity check the pwm setting */ | |
2164 | + err = 0; | |
2165 | + switch (data->type) { | |
2166 | + case f71858fg: | |
2167 | + if (((data->pwm_enable >> (idx * 2)) & 3) == 3) | |
2168 | + err = 1; | |
2169 | + break; | |
2170 | + case f71862fg: | |
2171 | + if (((data->pwm_enable >> (idx * 2)) & 1) != 1) | |
2172 | + err = 1; | |
2173 | + break; | |
2174 | + case f8000: | |
2175 | + if (idx == 2) | |
2176 | + err = data->pwm_enable & 0x20; | |
2177 | + break; | |
2178 | + default: | |
2179 | + break; | |
2180 | + } | |
2181 | + if (err) { | |
2182 | + dev_err(&pdev->dev, | |
2183 | + "Invalid (reserved) pwm settings: 0x%02x, " | |
2184 | + "skipping fan %d\n", | |
2185 | + (data->pwm_enable >> (idx * 2)) & 3, idx + 1); | |
2186 | + return 0; /* This is a non fatal condition */ | |
2187 | + } | |
2188 | + | |
2189 | + err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[idx][0], | |
2190 | + ARRAY_SIZE(fxxxx_fan_attr[0])); | |
2191 | + if (err) | |
2192 | + return err; | |
2193 | + | |
2194 | + if (f71882fg_fan_has_beep[data->type]) { | |
2195 | + err = f71882fg_create_sysfs_files(pdev, | |
2196 | + &fxxxx_fan_beep_attr[idx], | |
2197 | + 1); | |
2198 | + if (err) | |
2199 | + return err; | |
2200 | + } | |
2201 | + | |
2202 | + dev_info(&pdev->dev, "Fan: %d is in %s mode\n", idx + 1, | |
2203 | + (data->pwm_enable & (1 << (2 * idx))) ? "duty-cycle" : "RPM"); | |
2204 | + | |
2205 | + /* Check for unsupported auto pwm settings */ | |
2206 | + switch (data->type) { | |
2207 | + case f71808e: | |
2208 | + case f71808a: | |
2209 | + case f71869: | |
2210 | + case f71869a: | |
2211 | + case f71889fg: | |
2212 | + case f71889ed: | |
2213 | + case f71889a: | |
2214 | + data->pwm_auto_point_mapping[idx] = | |
2215 | + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(idx)); | |
2216 | + if ((data->pwm_auto_point_mapping[idx] & 0x80) || | |
2217 | + (data->pwm_auto_point_mapping[idx] & 3) == 0) { | |
2218 | + dev_warn(&pdev->dev, | |
2219 | + "Auto pwm controlled by raw digital " | |
2220 | + "data, disabling pwm auto_point " | |
2221 | + "sysfs attributes for fan %d\n", idx + 1); | |
2222 | + return 0; /* This is a non fatal condition */ | |
2223 | + } | |
2224 | + break; | |
2225 | + default: | |
2226 | + break; | |
2227 | + } | |
2228 | + | |
2229 | + switch (data->type) { | |
2230 | + case f71862fg: | |
2231 | + err = f71882fg_create_sysfs_files(pdev, | |
2232 | + &f71862fg_auto_pwm_attr[idx][0], | |
2233 | + ARRAY_SIZE(f71862fg_auto_pwm_attr[0])); | |
2234 | + break; | |
2235 | + case f71808e: | |
2236 | + case f71869: | |
2237 | + err = f71882fg_create_sysfs_files(pdev, | |
2238 | + &f71869_auto_pwm_attr[idx][0], | |
2239 | + ARRAY_SIZE(f71869_auto_pwm_attr[0])); | |
2240 | + break; | |
2241 | + case f8000: | |
2242 | + err = f71882fg_create_sysfs_files(pdev, | |
2243 | + &f8000_auto_pwm_attr[idx][0], | |
2244 | + ARRAY_SIZE(f8000_auto_pwm_attr[0])); | |
2245 | + break; | |
2246 | + default: | |
2247 | + err = f71882fg_create_sysfs_files(pdev, | |
2248 | + &fxxxx_auto_pwm_attr[idx][0], | |
2249 | + ARRAY_SIZE(fxxxx_auto_pwm_attr[0])); | |
2250 | + } | |
2251 | + | |
2252 | + return err; | |
2253 | +} | |
2254 | + | |
2157 | 2255 | static int __devinit f71882fg_probe(struct platform_device *pdev) |
2158 | 2256 | { |
2159 | 2257 | struct f71882fg_data *data; |
2160 | 2258 | |
2161 | 2259 | |
2162 | 2260 | |
2163 | 2261 | |
2164 | 2262 | |
2165 | 2263 | |
2166 | 2264 | |
2167 | 2265 | |
... | ... | @@ -2272,117 +2370,29 @@ |
2272 | 2370 | data->pwm_enable = |
2273 | 2371 | f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); |
2274 | 2372 | |
2275 | - /* Sanity check the pwm settings */ | |
2276 | - switch (data->type) { | |
2277 | - case f71858fg: | |
2278 | - err = 0; | |
2279 | - for (i = 0; i < nr_fans; i++) | |
2280 | - if (((data->pwm_enable >> (i * 2)) & 3) == 3) | |
2281 | - err = 1; | |
2282 | - break; | |
2283 | - case f71862fg: | |
2284 | - err = (data->pwm_enable & 0x15) != 0x15; | |
2285 | - break; | |
2286 | - case f8000: | |
2287 | - err = data->pwm_enable & 0x20; | |
2288 | - break; | |
2289 | - default: | |
2290 | - err = 0; | |
2291 | - break; | |
2292 | - } | |
2293 | - if (err) { | |
2294 | - dev_err(&pdev->dev, | |
2295 | - "Invalid (reserved) pwm settings: 0x%02x\n", | |
2296 | - (unsigned int)data->pwm_enable); | |
2297 | - err = -ENODEV; | |
2298 | - goto exit_unregister_sysfs; | |
2299 | - } | |
2300 | - | |
2301 | - err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0], | |
2302 | - ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); | |
2303 | - if (err) | |
2304 | - goto exit_unregister_sysfs; | |
2305 | - | |
2306 | - if (f71882fg_fan_has_beep[data->type]) { | |
2307 | - err = f71882fg_create_sysfs_files(pdev, | |
2308 | - fxxxx_fan_beep_attr, nr_fans); | |
2373 | + for (i = 0; i < nr_fans; i++) { | |
2374 | + err = f71882fg_create_fan_sysfs_files(pdev, i); | |
2309 | 2375 | if (err) |
2310 | 2376 | goto exit_unregister_sysfs; |
2311 | 2377 | } |
2312 | 2378 | |
2379 | + /* Some types have 1 extra fan with limited functionality */ | |
2313 | 2380 | switch (data->type) { |
2314 | - case f71808e: | |
2315 | 2381 | case f71808a: |
2316 | - case f71869: | |
2317 | - case f71869a: | |
2318 | - case f71889fg: | |
2319 | - case f71889ed: | |
2320 | - case f71889a: | |
2321 | - for (i = 0; i < nr_fans; i++) { | |
2322 | - data->pwm_auto_point_mapping[i] = | |
2323 | - f71882fg_read8(data, | |
2324 | - F71882FG_REG_POINT_MAPPING(i)); | |
2325 | - if ((data->pwm_auto_point_mapping[i] & 0x80) || | |
2326 | - (data->pwm_auto_point_mapping[i] & 3) == 0) | |
2327 | - break; | |
2328 | - } | |
2329 | - if (i != nr_fans) { | |
2330 | - dev_warn(&pdev->dev, | |
2331 | - "Auto pwm controlled by raw digital " | |
2332 | - "data, disabling pwm auto_point " | |
2333 | - "sysfs attributes\n"); | |
2334 | - goto no_pwm_auto_point; | |
2335 | - } | |
2336 | - break; | |
2337 | - default: | |
2338 | - break; | |
2339 | - } | |
2340 | - | |
2341 | - switch (data->type) { | |
2342 | - case f71808a: | |
2343 | 2382 | err = f71882fg_create_sysfs_files(pdev, |
2344 | - &fxxxx_auto_pwm_attr[0][0], | |
2345 | - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); | |
2346 | - if (err) | |
2347 | - goto exit_unregister_sysfs; | |
2348 | - err = f71882fg_create_sysfs_files(pdev, | |
2349 | 2383 | f71808a_fan3_attr, |
2350 | 2384 | ARRAY_SIZE(f71808a_fan3_attr)); |
2351 | 2385 | break; |
2352 | - case f71862fg: | |
2353 | - err = f71882fg_create_sysfs_files(pdev, | |
2354 | - f71862fg_auto_pwm_attr, | |
2355 | - ARRAY_SIZE(f71862fg_auto_pwm_attr)); | |
2356 | - break; | |
2357 | - case f71808e: | |
2358 | - case f71869: | |
2359 | - err = f71882fg_create_sysfs_files(pdev, | |
2360 | - f71869_auto_pwm_attr, | |
2361 | - ARRAY_SIZE(f71869_auto_pwm_attr)); | |
2362 | - break; | |
2363 | 2386 | case f8000: |
2364 | 2387 | err = f71882fg_create_sysfs_files(pdev, |
2365 | 2388 | f8000_fan_attr, |
2366 | 2389 | ARRAY_SIZE(f8000_fan_attr)); |
2367 | - if (err) | |
2368 | - goto exit_unregister_sysfs; | |
2369 | - err = f71882fg_create_sysfs_files(pdev, | |
2370 | - f8000_auto_pwm_attr, | |
2371 | - ARRAY_SIZE(f8000_auto_pwm_attr)); | |
2372 | 2390 | break; |
2373 | 2391 | default: |
2374 | - err = f71882fg_create_sysfs_files(pdev, | |
2375 | - &fxxxx_auto_pwm_attr[0][0], | |
2376 | - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); | |
2392 | + break; | |
2377 | 2393 | } |
2378 | 2394 | if (err) |
2379 | 2395 | goto exit_unregister_sysfs; |
2380 | - | |
2381 | -no_pwm_auto_point: | |
2382 | - for (i = 0; i < nr_fans; i++) | |
2383 | - dev_info(&pdev->dev, "Fan: %d is in %s mode\n", i + 1, | |
2384 | - (data->pwm_enable & (1 << 2 * i)) ? | |
2385 | - "duty-cycle" : "RPM"); | |
2386 | 2396 | } |
2387 | 2397 | |
2388 | 2398 | data->hwmon_dev = hwmon_device_register(&pdev->dev); |
2389 | 2399 | |
2390 | 2400 | |
... | ... | @@ -2476,22 +2486,23 @@ |
2476 | 2486 | break; |
2477 | 2487 | case f71862fg: |
2478 | 2488 | f71882fg_remove_sysfs_files(pdev, |
2479 | - f71862fg_auto_pwm_attr, | |
2480 | - ARRAY_SIZE(f71862fg_auto_pwm_attr)); | |
2489 | + &f71862fg_auto_pwm_attr[0][0], | |
2490 | + ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) * | |
2491 | + nr_fans); | |
2481 | 2492 | break; |
2482 | 2493 | case f71808e: |
2483 | 2494 | case f71869: |
2484 | 2495 | f71882fg_remove_sysfs_files(pdev, |
2485 | - f71869_auto_pwm_attr, | |
2486 | - ARRAY_SIZE(f71869_auto_pwm_attr)); | |
2496 | + &f71869_auto_pwm_attr[0][0], | |
2497 | + ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans); | |
2487 | 2498 | break; |
2488 | 2499 | case f8000: |
2489 | 2500 | f71882fg_remove_sysfs_files(pdev, |
2490 | 2501 | f8000_fan_attr, |
2491 | 2502 | ARRAY_SIZE(f8000_fan_attr)); |
2492 | 2503 | f71882fg_remove_sysfs_files(pdev, |
2493 | - f8000_auto_pwm_attr, | |
2494 | - ARRAY_SIZE(f8000_auto_pwm_attr)); | |
2504 | + &f8000_auto_pwm_attr[0][0], | |
2505 | + ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans); | |
2495 | 2506 | break; |
2496 | 2507 | default: |
2497 | 2508 | f71882fg_remove_sysfs_files(pdev, |
drivers/hwmon/lm75.c
... | ... | @@ -35,6 +35,7 @@ |
35 | 35 | */ |
36 | 36 | |
37 | 37 | enum lm75_type { /* keep sorted in alphabetical order */ |
38 | + adt75, | |
38 | 39 | ds1775, |
39 | 40 | ds75, |
40 | 41 | lm75, |
... | ... | @@ -213,6 +214,7 @@ |
213 | 214 | } |
214 | 215 | |
215 | 216 | static const struct i2c_device_id lm75_ids[] = { |
217 | + { "adt75", adt75, }, | |
216 | 218 | { "ds1775", ds1775, }, |
217 | 219 | { "ds75", ds75, }, |
218 | 220 | { "lm75", lm75, }, |
... | ... | @@ -247,19 +249,30 @@ |
247 | 249 | I2C_FUNC_SMBUS_WORD_DATA)) |
248 | 250 | return -ENODEV; |
249 | 251 | |
250 | - /* Now, we do the remaining detection. There is no identification- | |
251 | - dedicated register so we have to rely on several tricks: | |
252 | - unused bits, registers cycling over 8-address boundaries, | |
253 | - addresses 0x04-0x07 returning the last read value. | |
254 | - The cycling+unused addresses combination is not tested, | |
255 | - since it would significantly slow the detection down and would | |
256 | - hardly add any value. | |
257 | - | |
258 | - The National Semiconductor LM75A is different than earlier | |
259 | - LM75s. It has an ID byte of 0xaX (where X is the chip | |
260 | - revision, with 1 being the only revision in existence) in | |
261 | - register 7, and unused registers return 0xff rather than the | |
262 | - last read value. */ | |
252 | + /* | |
253 | + * Now, we do the remaining detection. There is no identification- | |
254 | + * dedicated register so we have to rely on several tricks: | |
255 | + * unused bits, registers cycling over 8-address boundaries, | |
256 | + * addresses 0x04-0x07 returning the last read value. | |
257 | + * The cycling+unused addresses combination is not tested, | |
258 | + * since it would significantly slow the detection down and would | |
259 | + * hardly add any value. | |
260 | + * | |
261 | + * The National Semiconductor LM75A is different than earlier | |
262 | + * LM75s. It has an ID byte of 0xaX (where X is the chip | |
263 | + * revision, with 1 being the only revision in existence) in | |
264 | + * register 7, and unused registers return 0xff rather than the | |
265 | + * last read value. | |
266 | + * | |
267 | + * Note that this function only detects the original National | |
268 | + * Semiconductor LM75 and the LM75A. Clones from other vendors | |
269 | + * aren't detected, on purpose, because they are typically never | |
270 | + * found on PC hardware. They are found on embedded designs where | |
271 | + * they can be instantiated explicitly so detection is not needed. | |
272 | + * The absence of identification registers on all these clones | |
273 | + * would make their exhaustive detection very difficult and weak, | |
274 | + * and odds are that the driver would bind to unsupported devices. | |
275 | + */ | |
263 | 276 | |
264 | 277 | /* Unused bits */ |
265 | 278 | conf = i2c_smbus_read_byte_data(new_client, 1); |
drivers/hwmon/pmbus/Kconfig
... | ... | @@ -20,17 +20,18 @@ |
20 | 20 | help |
21 | 21 | If you say yes here you get hardware monitoring support for generic |
22 | 22 | PMBus devices, including but not limited to ADP4000, BMR450, BMR451, |
23 | - BMR453, BMR454, LTC2978, NCP4200, and NCP4208. | |
23 | + BMR453, BMR454, NCP4200, and NCP4208. | |
24 | 24 | |
25 | 25 | This driver can also be built as a module. If so, the module will |
26 | 26 | be called pmbus. |
27 | 27 | |
28 | 28 | config SENSORS_ADM1275 |
29 | - tristate "Analog Devices ADM1275" | |
29 | + tristate "Analog Devices ADM1275 and compatibles" | |
30 | 30 | default n |
31 | 31 | help |
32 | 32 | If you say yes here you get hardware monitoring support for Analog |
33 | - Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. | |
33 | + Devices ADM1275 and ADM1276 Hot-Swap Controller and Digital Power | |
34 | + Monitor. | |
34 | 35 | |
35 | 36 | This driver can also be built as a module. If so, the module will |
36 | 37 | be called adm1275. |
... | ... | @@ -45,6 +46,16 @@ |
45 | 46 | This driver can also be built as a module. If so, the module will |
46 | 47 | be called lm25066. |
47 | 48 | |
49 | +config SENSORS_LTC2978 | |
50 | + tristate "Linear Technologies LTC2978 and LTC3880" | |
51 | + default n | |
52 | + help | |
53 | + If you say yes here you get hardware monitoring support for Linear | |
54 | + Technology LTC2978 and LTC3880. | |
55 | + | |
56 | + This driver can also be built as a module. If so, the module will | |
57 | + be called ltc2978. | |
58 | + | |
48 | 59 | config SENSORS_MAX16064 |
49 | 60 | tristate "Maxim MAX16064" |
50 | 61 | default n |
... | ... | @@ -96,6 +107,17 @@ |
96 | 107 | |
97 | 108 | This driver can also be built as a module. If so, the module will |
98 | 109 | be called ucd9200. |
110 | + | |
111 | +config SENSORS_ZL6100 | |
112 | + tristate "Intersil ZL6100 and compatibles" | |
113 | + default n | |
114 | + help | |
115 | + If you say yes here you get hardware monitoring support for Intersil | |
116 | + ZL2004, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, and ZL6105 Digital | |
117 | + DC/DC Controllers. | |
118 | + | |
119 | + This driver can also be built as a module. If so, the module will | |
120 | + be called zl6100. | |
99 | 121 | |
100 | 122 | endif # PMBUS |
drivers/hwmon/pmbus/Makefile
... | ... | @@ -6,9 +6,11 @@ |
6 | 6 | obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o |
7 | 7 | obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o |
8 | 8 | obj-$(CONFIG_SENSORS_LM25066) += lm25066.o |
9 | +obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o | |
9 | 10 | obj-$(CONFIG_SENSORS_MAX16064) += max16064.o |
10 | 11 | obj-$(CONFIG_SENSORS_MAX34440) += max34440.o |
11 | 12 | obj-$(CONFIG_SENSORS_MAX8688) += max8688.o |
12 | 13 | obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o |
13 | 14 | obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o |
15 | +obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o |
drivers/hwmon/pmbus/adm1275.c
... | ... | @@ -23,6 +23,8 @@ |
23 | 23 | #include <linux/i2c.h> |
24 | 24 | #include "pmbus.h" |
25 | 25 | |
26 | +enum chips { adm1275, adm1276 }; | |
27 | + | |
26 | 28 | #define ADM1275_PEAK_IOUT 0xd0 |
27 | 29 | #define ADM1275_PEAK_VIN 0xd1 |
28 | 30 | #define ADM1275_PEAK_VOUT 0xd2 |
29 | 31 | |
30 | 32 | |
31 | 33 | |
... | ... | @@ -31,14 +33,47 @@ |
31 | 33 | #define ADM1275_VIN_VOUT_SELECT (1 << 6) |
32 | 34 | #define ADM1275_VRANGE (1 << 5) |
33 | 35 | |
36 | +#define ADM1275_IOUT_WARN2_LIMIT 0xd7 | |
37 | +#define ADM1275_DEVICE_CONFIG 0xd8 | |
38 | + | |
39 | +#define ADM1275_IOUT_WARN2_SELECT (1 << 4) | |
40 | + | |
41 | +#define ADM1276_PEAK_PIN 0xda | |
42 | + | |
43 | +#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0) | |
44 | + | |
45 | +struct adm1275_data { | |
46 | + int id; | |
47 | + bool have_oc_fault; | |
48 | + struct pmbus_driver_info info; | |
49 | +}; | |
50 | + | |
51 | +#define to_adm1275_data(x) container_of(x, struct adm1275_data, info) | |
52 | + | |
34 | 53 | static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) |
35 | 54 | { |
36 | - int ret; | |
55 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
56 | + const struct adm1275_data *data = to_adm1275_data(info); | |
57 | + int ret = 0; | |
37 | 58 | |
38 | 59 | if (page) |
39 | - return -EINVAL; | |
60 | + return -ENXIO; | |
40 | 61 | |
41 | 62 | switch (reg) { |
63 | + case PMBUS_IOUT_UC_FAULT_LIMIT: | |
64 | + if (data->have_oc_fault) { | |
65 | + ret = -ENXIO; | |
66 | + break; | |
67 | + } | |
68 | + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); | |
69 | + break; | |
70 | + case PMBUS_IOUT_OC_FAULT_LIMIT: | |
71 | + if (!data->have_oc_fault) { | |
72 | + ret = -ENXIO; | |
73 | + break; | |
74 | + } | |
75 | + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); | |
76 | + break; | |
42 | 77 | case PMBUS_VIRT_READ_IOUT_MAX: |
43 | 78 | ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); |
44 | 79 | break; |
45 | 80 | |
46 | 81 | |
... | ... | @@ -48,11 +83,21 @@ |
48 | 83 | case PMBUS_VIRT_READ_VIN_MAX: |
49 | 84 | ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); |
50 | 85 | break; |
86 | + case PMBUS_VIRT_READ_PIN_MAX: | |
87 | + if (data->id != adm1276) { | |
88 | + ret = -ENXIO; | |
89 | + break; | |
90 | + } | |
91 | + ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN); | |
92 | + break; | |
51 | 93 | case PMBUS_VIRT_RESET_IOUT_HISTORY: |
52 | 94 | case PMBUS_VIRT_RESET_VOUT_HISTORY: |
53 | 95 | case PMBUS_VIRT_RESET_VIN_HISTORY: |
54 | - ret = 0; | |
55 | 96 | break; |
97 | + case PMBUS_VIRT_RESET_PIN_HISTORY: | |
98 | + if (data->id != adm1276) | |
99 | + ret = -ENXIO; | |
100 | + break; | |
56 | 101 | default: |
57 | 102 | ret = -ENODATA; |
58 | 103 | break; |
59 | 104 | |
... | ... | @@ -66,9 +111,14 @@ |
66 | 111 | int ret; |
67 | 112 | |
68 | 113 | if (page) |
69 | - return -EINVAL; | |
114 | + return -ENXIO; | |
70 | 115 | |
71 | 116 | switch (reg) { |
117 | + case PMBUS_IOUT_UC_FAULT_LIMIT: | |
118 | + case PMBUS_IOUT_OC_FAULT_LIMIT: | |
119 | + ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT, | |
120 | + word); | |
121 | + break; | |
72 | 122 | case PMBUS_VIRT_RESET_IOUT_HISTORY: |
73 | 123 | ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); |
74 | 124 | break; |
... | ... | @@ -78,6 +128,9 @@ |
78 | 128 | case PMBUS_VIRT_RESET_VIN_HISTORY: |
79 | 129 | ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0); |
80 | 130 | break; |
131 | + case PMBUS_VIRT_RESET_PIN_HISTORY: | |
132 | + ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0); | |
133 | + break; | |
81 | 134 | default: |
82 | 135 | ret = -ENODATA; |
83 | 136 | break; |
84 | 137 | |
85 | 138 | |
86 | 139 | |
... | ... | @@ -85,19 +138,52 @@ |
85 | 138 | return ret; |
86 | 139 | } |
87 | 140 | |
141 | +static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) | |
142 | +{ | |
143 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
144 | + const struct adm1275_data *data = to_adm1275_data(info); | |
145 | + int mfr_status, ret; | |
146 | + | |
147 | + if (page > 0) | |
148 | + return -ENXIO; | |
149 | + | |
150 | + switch (reg) { | |
151 | + case PMBUS_STATUS_IOUT: | |
152 | + ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT); | |
153 | + if (ret < 0) | |
154 | + break; | |
155 | + mfr_status = pmbus_read_byte_data(client, page, | |
156 | + PMBUS_STATUS_MFR_SPECIFIC); | |
157 | + if (mfr_status < 0) { | |
158 | + ret = mfr_status; | |
159 | + break; | |
160 | + } | |
161 | + if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) { | |
162 | + ret |= data->have_oc_fault ? | |
163 | + PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT; | |
164 | + } | |
165 | + break; | |
166 | + default: | |
167 | + ret = -ENODATA; | |
168 | + break; | |
169 | + } | |
170 | + return ret; | |
171 | +} | |
172 | + | |
88 | 173 | static int adm1275_probe(struct i2c_client *client, |
89 | 174 | const struct i2c_device_id *id) |
90 | 175 | { |
91 | - int config; | |
176 | + int config, device_config; | |
92 | 177 | int ret; |
93 | 178 | struct pmbus_driver_info *info; |
179 | + struct adm1275_data *data; | |
94 | 180 | |
95 | 181 | if (!i2c_check_functionality(client->adapter, |
96 | 182 | I2C_FUNC_SMBUS_READ_BYTE_DATA)) |
97 | 183 | return -ENODEV; |
98 | 184 | |
99 | - info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL); | |
100 | - if (!info) | |
185 | + data = kzalloc(sizeof(struct adm1275_data), GFP_KERNEL); | |
186 | + if (!data) | |
101 | 187 | return -ENOMEM; |
102 | 188 | |
103 | 189 | config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); |
... | ... | @@ -106,6 +192,15 @@ |
106 | 192 | goto err_mem; |
107 | 193 | } |
108 | 194 | |
195 | + device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG); | |
196 | + if (device_config < 0) { | |
197 | + ret = device_config; | |
198 | + goto err_mem; | |
199 | + } | |
200 | + | |
201 | + data->id = id->driver_data; | |
202 | + info = &data->info; | |
203 | + | |
109 | 204 | info->pages = 1; |
110 | 205 | info->format[PSC_VOLTAGE_IN] = direct; |
111 | 206 | info->format[PSC_VOLTAGE_OUT] = direct; |
... | ... | @@ -116,6 +211,7 @@ |
116 | 211 | info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; |
117 | 212 | |
118 | 213 | info->read_word_data = adm1275_read_word_data; |
214 | + info->read_byte_data = adm1275_read_byte_data; | |
119 | 215 | info->write_word_data = adm1275_write_word_data; |
120 | 216 | |
121 | 217 | if (config & ADM1275_VRANGE) { |
122 | 218 | |
123 | 219 | |
124 | 220 | |
125 | 221 | |
126 | 222 | |
... | ... | @@ -134,33 +230,60 @@ |
134 | 230 | info->R[PSC_VOLTAGE_OUT] = -1; |
135 | 231 | } |
136 | 232 | |
137 | - if (config & ADM1275_VIN_VOUT_SELECT) | |
138 | - info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; | |
139 | - else | |
140 | - info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; | |
233 | + if (device_config & ADM1275_IOUT_WARN2_SELECT) | |
234 | + data->have_oc_fault = true; | |
141 | 235 | |
236 | + switch (id->driver_data) { | |
237 | + case adm1275: | |
238 | + if (config & ADM1275_VIN_VOUT_SELECT) | |
239 | + info->func[0] |= | |
240 | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; | |
241 | + else | |
242 | + info->func[0] |= | |
243 | + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; | |
244 | + break; | |
245 | + case adm1276: | |
246 | + info->format[PSC_POWER] = direct; | |
247 | + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | |
248 | + | PMBUS_HAVE_STATUS_INPUT; | |
249 | + if (config & ADM1275_VIN_VOUT_SELECT) | |
250 | + info->func[0] |= | |
251 | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; | |
252 | + if (config & ADM1275_VRANGE) { | |
253 | + info->m[PSC_POWER] = 6043; | |
254 | + info->b[PSC_POWER] = 0; | |
255 | + info->R[PSC_POWER] = -2; | |
256 | + } else { | |
257 | + info->m[PSC_POWER] = 2115; | |
258 | + info->b[PSC_POWER] = 0; | |
259 | + info->R[PSC_POWER] = -1; | |
260 | + } | |
261 | + break; | |
262 | + } | |
263 | + | |
142 | 264 | ret = pmbus_do_probe(client, id, info); |
143 | 265 | if (ret) |
144 | 266 | goto err_mem; |
145 | 267 | return 0; |
146 | 268 | |
147 | 269 | err_mem: |
148 | - kfree(info); | |
270 | + kfree(data); | |
149 | 271 | return ret; |
150 | 272 | } |
151 | 273 | |
152 | 274 | static int adm1275_remove(struct i2c_client *client) |
153 | 275 | { |
154 | 276 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); |
155 | - int ret; | |
277 | + const struct adm1275_data *data = to_adm1275_data(info); | |
156 | 278 | |
157 | - ret = pmbus_do_remove(client); | |
158 | - kfree(info); | |
159 | - return ret; | |
279 | + pmbus_do_remove(client); | |
280 | + kfree(data); | |
281 | + return 0; | |
160 | 282 | } |
161 | 283 | |
162 | 284 | static const struct i2c_device_id adm1275_id[] = { |
163 | - {"adm1275", 0}, | |
285 | + { "adm1275", adm1275 }, | |
286 | + { "adm1276", adm1276 }, | |
164 | 287 | { } |
165 | 288 | }; |
166 | 289 | MODULE_DEVICE_TABLE(i2c, adm1275_id); |
... | ... | @@ -185,7 +308,7 @@ |
185 | 308 | } |
186 | 309 | |
187 | 310 | MODULE_AUTHOR("Guenter Roeck"); |
188 | -MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275"); | |
311 | +MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); | |
189 | 312 | MODULE_LICENSE("GPL"); |
190 | 313 | module_init(adm1275_init); |
191 | 314 | module_exit(adm1275_exit); |
drivers/hwmon/pmbus/lm25066.c
... | ... | @@ -57,7 +57,7 @@ |
57 | 57 | int ret; |
58 | 58 | |
59 | 59 | if (page > 1) |
60 | - return -EINVAL; | |
60 | + return -ENXIO; | |
61 | 61 | |
62 | 62 | /* Map READ_VAUX into READ_VOUT register on page 1 */ |
63 | 63 | if (page == 1) { |
... | ... | @@ -85,7 +85,7 @@ |
85 | 85 | break; |
86 | 86 | default: |
87 | 87 | /* No other valid registers on page 1 */ |
88 | - ret = -EINVAL; | |
88 | + ret = -ENXIO; | |
89 | 89 | break; |
90 | 90 | } |
91 | 91 | goto done; |
... | ... | @@ -138,7 +138,7 @@ |
138 | 138 | int ret; |
139 | 139 | |
140 | 140 | if (page > 1) |
141 | - return -EINVAL; | |
141 | + return -ENXIO; | |
142 | 142 | |
143 | 143 | switch (reg) { |
144 | 144 | case PMBUS_IIN_OC_WARN_LIMIT: |
145 | 145 | |
... | ... | @@ -164,10 +164,10 @@ |
164 | 164 | static int lm25066_write_byte(struct i2c_client *client, int page, u8 value) |
165 | 165 | { |
166 | 166 | if (page > 1) |
167 | - return -EINVAL; | |
167 | + return -ENXIO; | |
168 | 168 | |
169 | - if (page == 0) | |
170 | - return pmbus_write_byte(client, 0, value); | |
169 | + if (page <= 0) | |
170 | + return pmbus_write_byte(client, page, value); | |
171 | 171 | |
172 | 172 | return 0; |
173 | 173 | } |
174 | 174 | |
175 | 175 | |
... | ... | @@ -309,11 +309,10 @@ |
309 | 309 | { |
310 | 310 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); |
311 | 311 | const struct lm25066_data *data = to_lm25066_data(info); |
312 | - int ret; | |
313 | 312 | |
314 | - ret = pmbus_do_remove(client); | |
313 | + pmbus_do_remove(client); | |
315 | 314 | kfree(data); |
316 | - return ret; | |
315 | + return 0; | |
317 | 316 | } |
318 | 317 | |
319 | 318 | static const struct i2c_device_id lm25066_id[] = { |
drivers/hwmon/pmbus/ltc2978.c
1 | +/* | |
2 | + * Hardware monitoring driver for LTC2978 and LTC3880 | |
3 | + * | |
4 | + * Copyright (c) 2011 Ericsson AB. | |
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 | + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | + | |
21 | +#include <linux/kernel.h> | |
22 | +#include <linux/module.h> | |
23 | +#include <linux/init.h> | |
24 | +#include <linux/err.h> | |
25 | +#include <linux/slab.h> | |
26 | +#include <linux/i2c.h> | |
27 | +#include "pmbus.h" | |
28 | + | |
29 | +enum chips { ltc2978, ltc3880 }; | |
30 | + | |
31 | +/* LTC2978 and LTC3880 */ | |
32 | +#define LTC2978_MFR_VOUT_PEAK 0xdd | |
33 | +#define LTC2978_MFR_VIN_PEAK 0xde | |
34 | +#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf | |
35 | +#define LTC2978_MFR_SPECIAL_ID 0xe7 | |
36 | + | |
37 | +/* LTC2978 only */ | |
38 | +#define LTC2978_MFR_VOUT_MIN 0xfb | |
39 | +#define LTC2978_MFR_VIN_MIN 0xfc | |
40 | +#define LTC2978_MFR_TEMPERATURE_MIN 0xfd | |
41 | + | |
42 | +/* LTC3880 only */ | |
43 | +#define LTC3880_MFR_IOUT_PEAK 0xd7 | |
44 | +#define LTC3880_MFR_CLEAR_PEAKS 0xe3 | |
45 | +#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 | |
46 | + | |
47 | +#define LTC2978_ID_REV1 0x0121 | |
48 | +#define LTC2978_ID_REV2 0x0122 | |
49 | +#define LTC3880_ID 0x4000 | |
50 | +#define LTC3880_ID_MASK 0xff00 | |
51 | + | |
52 | +/* | |
53 | + * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which | |
54 | + * happens pretty much each time chip data is updated. Raw peak data therefore | |
55 | + * does not provide much value. To be able to provide useful peak data, keep an | |
56 | + * internal cache of measured peak data, which is only cleared if an explicit | |
57 | + * "clear peak" command is executed for the sensor in question. | |
58 | + */ | |
59 | +struct ltc2978_data { | |
60 | + enum chips id; | |
61 | + int vin_min, vin_max; | |
62 | + int temp_min, temp_max; | |
63 | + int vout_min[8], vout_max[8]; | |
64 | + int iout_max[2]; | |
65 | + int temp2_max[2]; | |
66 | + struct pmbus_driver_info info; | |
67 | +}; | |
68 | + | |
69 | +#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info) | |
70 | + | |
71 | +static inline int lin11_to_val(int data) | |
72 | +{ | |
73 | + s16 e = ((s16)data) >> 11; | |
74 | + s32 m = (((s16)(data << 5)) >> 5); | |
75 | + | |
76 | + /* | |
77 | + * mantissa is 10 bit + sign, exponent adds up to 15 bit. | |
78 | + * Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31). | |
79 | + */ | |
80 | + e += 6; | |
81 | + return (e < 0 ? m >> -e : m << e); | |
82 | +} | |
83 | + | |
84 | +static int ltc2978_read_word_data_common(struct i2c_client *client, int page, | |
85 | + int reg) | |
86 | +{ | |
87 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
88 | + struct ltc2978_data *data = to_ltc2978_data(info); | |
89 | + int ret; | |
90 | + | |
91 | + switch (reg) { | |
92 | + case PMBUS_VIRT_READ_VIN_MAX: | |
93 | + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK); | |
94 | + if (ret >= 0) { | |
95 | + if (lin11_to_val(ret) > lin11_to_val(data->vin_max)) | |
96 | + data->vin_max = ret; | |
97 | + ret = data->vin_max; | |
98 | + } | |
99 | + break; | |
100 | + case PMBUS_VIRT_READ_VOUT_MAX: | |
101 | + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); | |
102 | + if (ret >= 0) { | |
103 | + /* | |
104 | + * VOUT is 16 bit unsigned with fixed exponent, | |
105 | + * so we can compare it directly | |
106 | + */ | |
107 | + if (ret > data->vout_max[page]) | |
108 | + data->vout_max[page] = ret; | |
109 | + ret = data->vout_max[page]; | |
110 | + } | |
111 | + break; | |
112 | + case PMBUS_VIRT_READ_TEMP_MAX: | |
113 | + ret = pmbus_read_word_data(client, page, | |
114 | + LTC2978_MFR_TEMPERATURE_PEAK); | |
115 | + if (ret >= 0) { | |
116 | + if (lin11_to_val(ret) > lin11_to_val(data->temp_max)) | |
117 | + data->temp_max = ret; | |
118 | + ret = data->temp_max; | |
119 | + } | |
120 | + break; | |
121 | + case PMBUS_VIRT_RESET_VOUT_HISTORY: | |
122 | + case PMBUS_VIRT_RESET_VIN_HISTORY: | |
123 | + case PMBUS_VIRT_RESET_TEMP_HISTORY: | |
124 | + ret = 0; | |
125 | + break; | |
126 | + default: | |
127 | + ret = -ENODATA; | |
128 | + break; | |
129 | + } | |
130 | + return ret; | |
131 | +} | |
132 | + | |
133 | +static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) | |
134 | +{ | |
135 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
136 | + struct ltc2978_data *data = to_ltc2978_data(info); | |
137 | + int ret; | |
138 | + | |
139 | + switch (reg) { | |
140 | + case PMBUS_VIRT_READ_VIN_MIN: | |
141 | + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN); | |
142 | + if (ret >= 0) { | |
143 | + if (lin11_to_val(ret) < lin11_to_val(data->vin_min)) | |
144 | + data->vin_min = ret; | |
145 | + ret = data->vin_min; | |
146 | + } | |
147 | + break; | |
148 | + case PMBUS_VIRT_READ_VOUT_MIN: | |
149 | + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); | |
150 | + if (ret >= 0) { | |
151 | + /* | |
152 | + * VOUT_MIN is known to not be supported on some lots | |
153 | + * of LTC2978 revision 1, and will return the maximum | |
154 | + * possible voltage if read. If VOUT_MAX is valid and | |
155 | + * lower than the reading of VOUT_MIN, use it instead. | |
156 | + */ | |
157 | + if (data->vout_max[page] && ret > data->vout_max[page]) | |
158 | + ret = data->vout_max[page]; | |
159 | + if (ret < data->vout_min[page]) | |
160 | + data->vout_min[page] = ret; | |
161 | + ret = data->vout_min[page]; | |
162 | + } | |
163 | + break; | |
164 | + case PMBUS_VIRT_READ_TEMP_MIN: | |
165 | + ret = pmbus_read_word_data(client, page, | |
166 | + LTC2978_MFR_TEMPERATURE_MIN); | |
167 | + if (ret >= 0) { | |
168 | + if (lin11_to_val(ret) | |
169 | + < lin11_to_val(data->temp_min)) | |
170 | + data->temp_min = ret; | |
171 | + ret = data->temp_min; | |
172 | + } | |
173 | + break; | |
174 | + case PMBUS_VIRT_READ_IOUT_MAX: | |
175 | + case PMBUS_VIRT_RESET_IOUT_HISTORY: | |
176 | + case PMBUS_VIRT_READ_TEMP2_MAX: | |
177 | + case PMBUS_VIRT_RESET_TEMP2_HISTORY: | |
178 | + ret = -ENXIO; | |
179 | + break; | |
180 | + default: | |
181 | + ret = ltc2978_read_word_data_common(client, page, reg); | |
182 | + break; | |
183 | + } | |
184 | + return ret; | |
185 | +} | |
186 | + | |
187 | +static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) | |
188 | +{ | |
189 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
190 | + struct ltc2978_data *data = to_ltc2978_data(info); | |
191 | + int ret; | |
192 | + | |
193 | + switch (reg) { | |
194 | + case PMBUS_VIRT_READ_IOUT_MAX: | |
195 | + ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK); | |
196 | + if (ret >= 0) { | |
197 | + if (lin11_to_val(ret) | |
198 | + > lin11_to_val(data->iout_max[page])) | |
199 | + data->iout_max[page] = ret; | |
200 | + ret = data->iout_max[page]; | |
201 | + } | |
202 | + break; | |
203 | + case PMBUS_VIRT_READ_TEMP2_MAX: | |
204 | + ret = pmbus_read_word_data(client, page, | |
205 | + LTC3880_MFR_TEMPERATURE2_PEAK); | |
206 | + if (ret >= 0) { | |
207 | + if (lin11_to_val(ret) | |
208 | + > lin11_to_val(data->temp2_max[page])) | |
209 | + data->temp2_max[page] = ret; | |
210 | + ret = data->temp2_max[page]; | |
211 | + } | |
212 | + break; | |
213 | + case PMBUS_VIRT_READ_VIN_MIN: | |
214 | + case PMBUS_VIRT_READ_VOUT_MIN: | |
215 | + case PMBUS_VIRT_READ_TEMP_MIN: | |
216 | + ret = -ENXIO; | |
217 | + break; | |
218 | + case PMBUS_VIRT_RESET_IOUT_HISTORY: | |
219 | + case PMBUS_VIRT_RESET_TEMP2_HISTORY: | |
220 | + ret = 0; | |
221 | + break; | |
222 | + default: | |
223 | + ret = ltc2978_read_word_data_common(client, page, reg); | |
224 | + break; | |
225 | + } | |
226 | + return ret; | |
227 | +} | |
228 | + | |
229 | +static int ltc2978_clear_peaks(struct i2c_client *client, int page, | |
230 | + enum chips id) | |
231 | +{ | |
232 | + int ret; | |
233 | + | |
234 | + if (id == ltc2978) | |
235 | + ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); | |
236 | + else | |
237 | + ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); | |
238 | + | |
239 | + return ret; | |
240 | +} | |
241 | + | |
242 | +static int ltc2978_write_word_data(struct i2c_client *client, int page, | |
243 | + int reg, u16 word) | |
244 | +{ | |
245 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
246 | + struct ltc2978_data *data = to_ltc2978_data(info); | |
247 | + int ret; | |
248 | + | |
249 | + switch (reg) { | |
250 | + case PMBUS_VIRT_RESET_IOUT_HISTORY: | |
251 | + data->iout_max[page] = 0x7fff; | |
252 | + ret = ltc2978_clear_peaks(client, page, data->id); | |
253 | + break; | |
254 | + case PMBUS_VIRT_RESET_TEMP2_HISTORY: | |
255 | + data->temp2_max[page] = 0x7fff; | |
256 | + ret = ltc2978_clear_peaks(client, page, data->id); | |
257 | + break; | |
258 | + case PMBUS_VIRT_RESET_VOUT_HISTORY: | |
259 | + data->vout_min[page] = 0xffff; | |
260 | + data->vout_max[page] = 0; | |
261 | + ret = ltc2978_clear_peaks(client, page, data->id); | |
262 | + break; | |
263 | + case PMBUS_VIRT_RESET_VIN_HISTORY: | |
264 | + data->vin_min = 0x7bff; | |
265 | + data->vin_max = 0; | |
266 | + ret = ltc2978_clear_peaks(client, page, data->id); | |
267 | + break; | |
268 | + case PMBUS_VIRT_RESET_TEMP_HISTORY: | |
269 | + data->temp_min = 0x7bff; | |
270 | + data->temp_max = 0x7fff; | |
271 | + ret = ltc2978_clear_peaks(client, page, data->id); | |
272 | + break; | |
273 | + default: | |
274 | + ret = -ENODATA; | |
275 | + break; | |
276 | + } | |
277 | + return ret; | |
278 | +} | |
279 | + | |
280 | +static const struct i2c_device_id ltc2978_id[] = { | |
281 | + {"ltc2978", ltc2978}, | |
282 | + {"ltc3880", ltc3880}, | |
283 | + {} | |
284 | +}; | |
285 | +MODULE_DEVICE_TABLE(i2c, ltc2978_id); | |
286 | + | |
287 | +static int ltc2978_probe(struct i2c_client *client, | |
288 | + const struct i2c_device_id *id) | |
289 | +{ | |
290 | + int chip_id, ret, i; | |
291 | + struct ltc2978_data *data; | |
292 | + struct pmbus_driver_info *info; | |
293 | + | |
294 | + if (!i2c_check_functionality(client->adapter, | |
295 | + I2C_FUNC_SMBUS_READ_WORD_DATA)) | |
296 | + return -ENODEV; | |
297 | + | |
298 | + data = kzalloc(sizeof(struct ltc2978_data), GFP_KERNEL); | |
299 | + if (!data) | |
300 | + return -ENOMEM; | |
301 | + | |
302 | + chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID); | |
303 | + if (chip_id < 0) { | |
304 | + ret = chip_id; | |
305 | + goto err_mem; | |
306 | + } | |
307 | + | |
308 | + if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { | |
309 | + data->id = ltc2978; | |
310 | + } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { | |
311 | + data->id = ltc3880; | |
312 | + } else { | |
313 | + dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); | |
314 | + ret = -ENODEV; | |
315 | + goto err_mem; | |
316 | + } | |
317 | + if (data->id != id->driver_data) | |
318 | + dev_warn(&client->dev, | |
319 | + "Device mismatch: Configured %s, detected %s\n", | |
320 | + id->name, | |
321 | + ltc2978_id[data->id].name); | |
322 | + | |
323 | + info = &data->info; | |
324 | + info->write_word_data = ltc2978_write_word_data; | |
325 | + | |
326 | + data->vout_min[0] = 0xffff; | |
327 | + data->vin_min = 0x7bff; | |
328 | + data->temp_min = 0x7bff; | |
329 | + data->temp_max = 0x7fff; | |
330 | + | |
331 | + switch (id->driver_data) { | |
332 | + case ltc2978: | |
333 | + info->read_word_data = ltc2978_read_word_data; | |
334 | + info->pages = 8; | |
335 | + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | |
336 | + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
337 | + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; | |
338 | + for (i = 1; i < 8; i++) { | |
339 | + info->func[i] = PMBUS_HAVE_VOUT | |
340 | + | PMBUS_HAVE_STATUS_VOUT; | |
341 | + data->vout_min[i] = 0xffff; | |
342 | + } | |
343 | + break; | |
344 | + case ltc3880: | |
345 | + info->read_word_data = ltc3880_read_word_data; | |
346 | + info->pages = 2; | |
347 | + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | |
348 | + | PMBUS_HAVE_STATUS_INPUT | |
349 | + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
350 | + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
351 | + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | |
352 | + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; | |
353 | + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
354 | + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
355 | + | PMBUS_HAVE_POUT | |
356 | + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; | |
357 | + data->vout_min[1] = 0xffff; | |
358 | + break; | |
359 | + default: | |
360 | + ret = -ENODEV; | |
361 | + goto err_mem; | |
362 | + } | |
363 | + | |
364 | + ret = pmbus_do_probe(client, id, info); | |
365 | + if (ret) | |
366 | + goto err_mem; | |
367 | + return 0; | |
368 | + | |
369 | +err_mem: | |
370 | + kfree(data); | |
371 | + return ret; | |
372 | +} | |
373 | + | |
374 | +static int ltc2978_remove(struct i2c_client *client) | |
375 | +{ | |
376 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
377 | + const struct ltc2978_data *data = to_ltc2978_data(info); | |
378 | + | |
379 | + pmbus_do_remove(client); | |
380 | + kfree(data); | |
381 | + return 0; | |
382 | +} | |
383 | + | |
384 | +/* This is the driver that will be inserted */ | |
385 | +static struct i2c_driver ltc2978_driver = { | |
386 | + .driver = { | |
387 | + .name = "ltc2978", | |
388 | + }, | |
389 | + .probe = ltc2978_probe, | |
390 | + .remove = ltc2978_remove, | |
391 | + .id_table = ltc2978_id, | |
392 | +}; | |
393 | + | |
394 | +static int __init ltc2978_init(void) | |
395 | +{ | |
396 | + return i2c_add_driver(<c2978_driver); | |
397 | +} | |
398 | + | |
399 | +static void __exit ltc2978_exit(void) | |
400 | +{ | |
401 | + i2c_del_driver(<c2978_driver); | |
402 | +} | |
403 | + | |
404 | +MODULE_AUTHOR("Guenter Roeck"); | |
405 | +MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880"); | |
406 | +MODULE_LICENSE("GPL"); | |
407 | +module_init(ltc2978_init); | |
408 | +module_exit(ltc2978_exit); |
drivers/hwmon/pmbus/max16064.c
drivers/hwmon/pmbus/max34440.c
... | ... | @@ -93,12 +93,14 @@ |
93 | 93 | |
94 | 94 | static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) |
95 | 95 | { |
96 | - int ret; | |
96 | + int ret = 0; | |
97 | 97 | int mfg_status; |
98 | 98 | |
99 | - ret = pmbus_set_page(client, page); | |
100 | - if (ret < 0) | |
101 | - return ret; | |
99 | + if (page >= 0) { | |
100 | + ret = pmbus_set_page(client, page); | |
101 | + if (ret < 0) | |
102 | + return ret; | |
103 | + } | |
102 | 104 | |
103 | 105 | switch (reg) { |
104 | 106 | case PMBUS_STATUS_IOUT: |
... | ... | @@ -224,7 +226,8 @@ |
224 | 226 | |
225 | 227 | static int max34440_remove(struct i2c_client *client) |
226 | 228 | { |
227 | - return pmbus_do_remove(client); | |
229 | + pmbus_do_remove(client); | |
230 | + return 0; | |
228 | 231 | } |
229 | 232 | |
230 | 233 | static const struct i2c_device_id max34440_id[] = { |
drivers/hwmon/pmbus/max8688.c
... | ... | @@ -45,7 +45,7 @@ |
45 | 45 | int ret; |
46 | 46 | |
47 | 47 | if (page) |
48 | - return -EINVAL; | |
48 | + return -ENXIO; | |
49 | 49 | |
50 | 50 | switch (reg) { |
51 | 51 | case PMBUS_VIRT_READ_VOUT_MAX: |
... | ... | @@ -101,8 +101,8 @@ |
101 | 101 | int ret = 0; |
102 | 102 | int mfg_status; |
103 | 103 | |
104 | - if (page) | |
105 | - return -EINVAL; | |
104 | + if (page > 0) | |
105 | + return -ENXIO; | |
106 | 106 | |
107 | 107 | switch (reg) { |
108 | 108 | case PMBUS_STATUS_VOUT: |
... | ... | @@ -182,7 +182,8 @@ |
182 | 182 | |
183 | 183 | static int max8688_remove(struct i2c_client *client) |
184 | 184 | { |
185 | - return pmbus_do_remove(client); | |
185 | + pmbus_do_remove(client); | |
186 | + return 0; | |
186 | 187 | } |
187 | 188 | |
188 | 189 | static const struct i2c_device_id max8688_id[] = { |
drivers/hwmon/pmbus/pmbus.c
... | ... | @@ -187,13 +187,12 @@ |
187 | 187 | |
188 | 188 | static int pmbus_remove(struct i2c_client *client) |
189 | 189 | { |
190 | - int ret; | |
191 | 190 | const struct pmbus_driver_info *info; |
192 | 191 | |
193 | 192 | info = pmbus_get_driver_info(client); |
194 | - ret = pmbus_do_remove(client); | |
193 | + pmbus_do_remove(client); | |
195 | 194 | kfree(info); |
196 | - return ret; | |
195 | + return 0; | |
197 | 196 | } |
198 | 197 | |
199 | 198 | /* |
200 | 199 | |
201 | 200 | |
... | ... | @@ -205,10 +204,13 @@ |
205 | 204 | {"bmr451", 1}, |
206 | 205 | {"bmr453", 1}, |
207 | 206 | {"bmr454", 1}, |
208 | - {"ltc2978", 8}, | |
209 | 207 | {"ncp4200", 1}, |
210 | 208 | {"ncp4208", 1}, |
209 | + {"pdt003", 1}, | |
210 | + {"pdt006", 1}, | |
211 | + {"pdt012", 1}, | |
211 | 212 | {"pmbus", 0}, |
213 | + {"udt020", 1}, | |
212 | 214 | {} |
213 | 215 | }; |
214 | 216 |
drivers/hwmon/pmbus/pmbus.h
... | ... | @@ -134,8 +134,16 @@ |
134 | 134 | * Semantics: |
135 | 135 | * Virtual registers are all word size. |
136 | 136 | * READ registers are read-only; writes are either ignored or return an error. |
137 | - * RESET registers are read/write. Reading returns zero (used for detection), | |
138 | - * writing any value causes the associated history to be reset. | |
137 | + * RESET registers are read/write. Reading reset registers returns zero | |
138 | + * (used for detection), writing any value causes the associated history to be | |
139 | + * reset. | |
140 | + * Virtual registers have to be handled in device specific driver code. Chip | |
141 | + * driver code returns non-negative register values if a virtual register is | |
142 | + * supported, or a negative error code if not. The chip driver may return | |
143 | + * -ENODATA or any other error code in this case, though an error code other | |
144 | + * than -ENODATA is handled more efficiently and thus preferred. Either case, | |
145 | + * the calling PMBus core code will abort if the chip driver returns an error | |
146 | + * code when reading or writing virtual registers. | |
139 | 147 | */ |
140 | 148 | #define PMBUS_VIRT_BASE 0x100 |
141 | 149 | #define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 0) |
... | ... | @@ -160,6 +168,9 @@ |
160 | 168 | #define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 19) |
161 | 169 | #define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 20) |
162 | 170 | #define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 21) |
171 | +#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 22) | |
172 | +#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 23) | |
173 | +#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 24) | |
163 | 174 | |
164 | 175 | /* |
165 | 176 | * CAPABILITY |
... | ... | @@ -320,6 +331,12 @@ |
320 | 331 | * The following functions map manufacturing specific register values |
321 | 332 | * to PMBus standard register values. Specify only if mapping is |
322 | 333 | * necessary. |
334 | + * Functions return the register value (read) or zero (write) if | |
335 | + * successful. A return value of -ENODATA indicates that there is no | |
336 | + * manufacturer specific register, but that a standard PMBus register | |
337 | + * may exist. Any other negative return value indicates that the | |
338 | + * register does not exist, and that no attempt should be made to read | |
339 | + * the standard register. | |
323 | 340 | */ |
324 | 341 | int (*read_byte_data)(struct i2c_client *client, int page, int reg); |
325 | 342 | int (*read_word_data)(struct i2c_client *client, int page, int reg); |
... | ... | @@ -347,7 +364,7 @@ |
347 | 364 | bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); |
348 | 365 | int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, |
349 | 366 | struct pmbus_driver_info *info); |
350 | -int pmbus_do_remove(struct i2c_client *client); | |
367 | +void pmbus_do_remove(struct i2c_client *client); | |
351 | 368 | const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client |
352 | 369 | *client); |
353 | 370 |
drivers/hwmon/pmbus/pmbus_core.c
... | ... | @@ -160,7 +160,7 @@ |
160 | 160 | rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); |
161 | 161 | newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); |
162 | 162 | if (newpage != page) |
163 | - rv = -EINVAL; | |
163 | + rv = -EIO; | |
164 | 164 | else |
165 | 165 | data->currpage = page; |
166 | 166 | } |
... | ... | @@ -229,7 +229,7 @@ |
229 | 229 | return status; |
230 | 230 | } |
231 | 231 | if (reg >= PMBUS_VIRT_BASE) |
232 | - return -EINVAL; | |
232 | + return -ENXIO; | |
233 | 233 | return pmbus_write_word_data(client, page, reg, word); |
234 | 234 | } |
235 | 235 | |
... | ... | @@ -261,7 +261,7 @@ |
261 | 261 | return status; |
262 | 262 | } |
263 | 263 | if (reg >= PMBUS_VIRT_BASE) |
264 | - return -EINVAL; | |
264 | + return -ENXIO; | |
265 | 265 | return pmbus_read_word_data(client, page, reg); |
266 | 266 | } |
267 | 267 | |
268 | 268 | |
269 | 269 | |
... | ... | @@ -316,11 +316,11 @@ |
316 | 316 | { |
317 | 317 | int status, status2; |
318 | 318 | |
319 | - status = pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE); | |
319 | + status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE); | |
320 | 320 | if (status < 0 || (status & PB_STATUS_CML)) { |
321 | - status2 = pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); | |
321 | + status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); | |
322 | 322 | if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) |
323 | - return -EINVAL; | |
323 | + return -EIO; | |
324 | 324 | } |
325 | 325 | return 0; |
326 | 326 | } |
... | ... | @@ -371,8 +371,8 @@ |
371 | 371 | |
372 | 372 | for (i = 0; i < info->pages; i++) |
373 | 373 | data->status[PB_STATUS_BASE + i] |
374 | - = pmbus_read_byte_data(client, i, | |
375 | - PMBUS_STATUS_BYTE); | |
374 | + = _pmbus_read_byte_data(client, i, | |
375 | + PMBUS_STATUS_BYTE); | |
376 | 376 | for (i = 0; i < info->pages; i++) { |
377 | 377 | if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT)) |
378 | 378 | continue; |
... | ... | @@ -445,13 +445,8 @@ |
445 | 445 | exponent = data->exponent; |
446 | 446 | mantissa = (u16) sensor->data; |
447 | 447 | } else { /* LINEAR11 */ |
448 | - exponent = (sensor->data >> 11) & 0x001f; | |
449 | - mantissa = sensor->data & 0x07ff; | |
450 | - | |
451 | - if (exponent > 0x0f) | |
452 | - exponent |= 0xffe0; /* sign extend exponent */ | |
453 | - if (mantissa > 0x03ff) | |
454 | - mantissa |= 0xfffff800; /* sign extend mantissa */ | |
448 | + exponent = ((s16)sensor->data) >> 11; | |
449 | + mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; | |
455 | 450 | } |
456 | 451 | |
457 | 452 | val = mantissa; |
... | ... | @@ -1401,7 +1396,7 @@ |
1401 | 1396 | } |
1402 | 1397 | }; |
1403 | 1398 | |
1404 | -static const struct pmbus_limit_attr temp_limit_attrs23[] = { | |
1399 | +static const struct pmbus_limit_attr temp_limit_attrs2[] = { | |
1405 | 1400 | { |
1406 | 1401 | .reg = PMBUS_UT_WARN_LIMIT, |
1407 | 1402 | .low = true, |
1408 | 1403 | |
... | ... | @@ -1424,9 +1419,44 @@ |
1424 | 1419 | .attr = "crit", |
1425 | 1420 | .alarm = "crit_alarm", |
1426 | 1421 | .sbit = PB_TEMP_OT_FAULT, |
1422 | + }, { | |
1423 | + .reg = PMBUS_VIRT_READ_TEMP2_MIN, | |
1424 | + .attr = "lowest", | |
1425 | + }, { | |
1426 | + .reg = PMBUS_VIRT_READ_TEMP2_MAX, | |
1427 | + .attr = "highest", | |
1428 | + }, { | |
1429 | + .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, | |
1430 | + .attr = "reset_history", | |
1427 | 1431 | } |
1428 | 1432 | }; |
1429 | 1433 | |
1434 | +static const struct pmbus_limit_attr temp_limit_attrs3[] = { | |
1435 | + { | |
1436 | + .reg = PMBUS_UT_WARN_LIMIT, | |
1437 | + .low = true, | |
1438 | + .attr = "min", | |
1439 | + .alarm = "min_alarm", | |
1440 | + .sbit = PB_TEMP_UT_WARNING, | |
1441 | + }, { | |
1442 | + .reg = PMBUS_UT_FAULT_LIMIT, | |
1443 | + .low = true, | |
1444 | + .attr = "lcrit", | |
1445 | + .alarm = "lcrit_alarm", | |
1446 | + .sbit = PB_TEMP_UT_FAULT, | |
1447 | + }, { | |
1448 | + .reg = PMBUS_OT_WARN_LIMIT, | |
1449 | + .attr = "max", | |
1450 | + .alarm = "max_alarm", | |
1451 | + .sbit = PB_TEMP_OT_WARNING, | |
1452 | + }, { | |
1453 | + .reg = PMBUS_OT_FAULT_LIMIT, | |
1454 | + .attr = "crit", | |
1455 | + .alarm = "crit_alarm", | |
1456 | + .sbit = PB_TEMP_OT_FAULT, | |
1457 | + } | |
1458 | +}; | |
1459 | + | |
1430 | 1460 | static const struct pmbus_sensor_attr temp_attributes[] = { |
1431 | 1461 | { |
1432 | 1462 | .reg = PMBUS_READ_TEMPERATURE_1, |
... | ... | @@ -1450,8 +1480,8 @@ |
1450 | 1480 | .sfunc = PMBUS_HAVE_STATUS_TEMP, |
1451 | 1481 | .sbase = PB_STATUS_TEMP_BASE, |
1452 | 1482 | .gbit = PB_STATUS_TEMPERATURE, |
1453 | - .limit = temp_limit_attrs23, | |
1454 | - .nlimit = ARRAY_SIZE(temp_limit_attrs23), | |
1483 | + .limit = temp_limit_attrs2, | |
1484 | + .nlimit = ARRAY_SIZE(temp_limit_attrs2), | |
1455 | 1485 | }, { |
1456 | 1486 | .reg = PMBUS_READ_TEMPERATURE_3, |
1457 | 1487 | .class = PSC_TEMPERATURE, |
... | ... | @@ -1462,8 +1492,8 @@ |
1462 | 1492 | .sfunc = PMBUS_HAVE_STATUS_TEMP, |
1463 | 1493 | .sbase = PB_STATUS_TEMP_BASE, |
1464 | 1494 | .gbit = PB_STATUS_TEMPERATURE, |
1465 | - .limit = temp_limit_attrs23, | |
1466 | - .nlimit = ARRAY_SIZE(temp_limit_attrs23), | |
1495 | + .limit = temp_limit_attrs3, | |
1496 | + .nlimit = ARRAY_SIZE(temp_limit_attrs3), | |
1467 | 1497 | } |
1468 | 1498 | }; |
1469 | 1499 | |
1470 | 1500 | |
... | ... | @@ -1593,10 +1623,10 @@ |
1593 | 1623 | static int pmbus_identify_common(struct i2c_client *client, |
1594 | 1624 | struct pmbus_data *data) |
1595 | 1625 | { |
1596 | - int vout_mode = -1, exponent; | |
1626 | + int vout_mode = -1; | |
1597 | 1627 | |
1598 | 1628 | if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) |
1599 | - vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); | |
1629 | + vout_mode = _pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); | |
1600 | 1630 | if (vout_mode >= 0 && vout_mode != 0xff) { |
1601 | 1631 | /* |
1602 | 1632 | * Not all chips support the VOUT_MODE command, |
... | ... | @@ -1607,11 +1637,7 @@ |
1607 | 1637 | if (data->info->format[PSC_VOLTAGE_OUT] != linear) |
1608 | 1638 | return -ENODEV; |
1609 | 1639 | |
1610 | - exponent = vout_mode & 0x1f; | |
1611 | - /* and sign-extend it */ | |
1612 | - if (exponent & 0x10) | |
1613 | - exponent |= ~0x1f; | |
1614 | - data->exponent = exponent; | |
1640 | + data->exponent = ((s8)(vout_mode << 3)) >> 3; | |
1615 | 1641 | break; |
1616 | 1642 | case 1: /* VID mode */ |
1617 | 1643 | if (data->info->format[PSC_VOLTAGE_OUT] != vid) |
... | ... | @@ -1682,7 +1708,7 @@ |
1682 | 1708 | if (info->pages <= 0 || info->pages > PMBUS_PAGES) { |
1683 | 1709 | dev_err(&client->dev, "Bad number of PMBus pages: %d\n", |
1684 | 1710 | info->pages); |
1685 | - ret = -EINVAL; | |
1711 | + ret = -ENODEV; | |
1686 | 1712 | goto out_data; |
1687 | 1713 | } |
1688 | 1714 | |
... | ... | @@ -1764,7 +1790,7 @@ |
1764 | 1790 | } |
1765 | 1791 | EXPORT_SYMBOL_GPL(pmbus_do_probe); |
1766 | 1792 | |
1767 | -int pmbus_do_remove(struct i2c_client *client) | |
1793 | +void pmbus_do_remove(struct i2c_client *client) | |
1768 | 1794 | { |
1769 | 1795 | struct pmbus_data *data = i2c_get_clientdata(client); |
1770 | 1796 | hwmon_device_unregister(data->hwmon_dev); |
... | ... | @@ -1774,7 +1800,6 @@ |
1774 | 1800 | kfree(data->booleans); |
1775 | 1801 | kfree(data->sensors); |
1776 | 1802 | kfree(data); |
1777 | - return 0; | |
1778 | 1803 | } |
1779 | 1804 | EXPORT_SYMBOL_GPL(pmbus_do_remove); |
1780 | 1805 |
drivers/hwmon/pmbus/ucd9000.c
... | ... | @@ -74,8 +74,8 @@ |
74 | 74 | |
75 | 75 | switch (reg) { |
76 | 76 | case PMBUS_FAN_CONFIG_12: |
77 | - if (page) | |
78 | - return -EINVAL; | |
77 | + if (page > 0) | |
78 | + return -ENXIO; | |
79 | 79 | |
80 | 80 | ret = ucd9000_get_fan_config(client, 0); |
81 | 81 | if (ret < 0) |
... | ... | @@ -88,8 +88,8 @@ |
88 | 88 | ret = fan_config; |
89 | 89 | break; |
90 | 90 | case PMBUS_FAN_CONFIG_34: |
91 | - if (page) | |
92 | - return -EINVAL; | |
91 | + if (page > 0) | |
92 | + return -ENXIO; | |
93 | 93 | |
94 | 94 | ret = ucd9000_get_fan_config(client, 2); |
95 | 95 | if (ret < 0) |
96 | 96 | |
97 | 97 | |
... | ... | @@ -239,13 +239,12 @@ |
239 | 239 | |
240 | 240 | static int ucd9000_remove(struct i2c_client *client) |
241 | 241 | { |
242 | - int ret; | |
243 | 242 | struct ucd9000_data *data; |
244 | 243 | |
245 | 244 | data = to_ucd9000_data(pmbus_get_driver_info(client)); |
246 | - ret = pmbus_do_remove(client); | |
245 | + pmbus_do_remove(client); | |
247 | 246 | kfree(data); |
248 | - return ret; | |
247 | + return 0; | |
249 | 248 | } |
250 | 249 | |
251 | 250 |
drivers/hwmon/pmbus/ucd9200.c
... | ... | @@ -171,13 +171,12 @@ |
171 | 171 | |
172 | 172 | static int ucd9200_remove(struct i2c_client *client) |
173 | 173 | { |
174 | - int ret; | |
175 | 174 | const struct pmbus_driver_info *info; |
176 | 175 | |
177 | 176 | info = pmbus_get_driver_info(client); |
178 | - ret = pmbus_do_remove(client); | |
177 | + pmbus_do_remove(client); | |
179 | 178 | kfree(info); |
180 | - return ret; | |
179 | + return 0; | |
181 | 180 | } |
182 | 181 | |
183 | 182 |
drivers/hwmon/pmbus/zl6100.c
1 | +/* | |
2 | + * Hardware monitoring driver for ZL6100 and compatibles | |
3 | + * | |
4 | + * Copyright (c) 2011 Ericsson AB. | |
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 | + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | + | |
21 | +#include <linux/kernel.h> | |
22 | +#include <linux/module.h> | |
23 | +#include <linux/init.h> | |
24 | +#include <linux/err.h> | |
25 | +#include <linux/slab.h> | |
26 | +#include <linux/i2c.h> | |
27 | +#include <linux/ktime.h> | |
28 | +#include <linux/delay.h> | |
29 | +#include "pmbus.h" | |
30 | + | |
31 | +enum chips { zl2004, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105 }; | |
32 | + | |
33 | +struct zl6100_data { | |
34 | + int id; | |
35 | + ktime_t access; /* chip access time */ | |
36 | + struct pmbus_driver_info info; | |
37 | +}; | |
38 | + | |
39 | +#define to_zl6100_data(x) container_of(x, struct zl6100_data, info) | |
40 | + | |
41 | +#define ZL6100_DEVICE_ID 0xe4 | |
42 | + | |
43 | +#define ZL6100_WAIT_TIME 1000 /* uS */ | |
44 | + | |
45 | +static ushort delay = ZL6100_WAIT_TIME; | |
46 | +module_param(delay, ushort, 0644); | |
47 | +MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); | |
48 | + | |
49 | +/* Some chips need a delay between accesses */ | |
50 | +static inline void zl6100_wait(const struct zl6100_data *data) | |
51 | +{ | |
52 | + if (delay) { | |
53 | + s64 delta = ktime_us_delta(ktime_get(), data->access); | |
54 | + if (delta < delay) | |
55 | + udelay(delay - delta); | |
56 | + } | |
57 | +} | |
58 | + | |
59 | +static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) | |
60 | +{ | |
61 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
62 | + struct zl6100_data *data = to_zl6100_data(info); | |
63 | + int ret; | |
64 | + | |
65 | + if (page || reg >= PMBUS_VIRT_BASE) | |
66 | + return -ENXIO; | |
67 | + | |
68 | + zl6100_wait(data); | |
69 | + ret = pmbus_read_word_data(client, page, reg); | |
70 | + data->access = ktime_get(); | |
71 | + | |
72 | + return ret; | |
73 | +} | |
74 | + | |
75 | +static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) | |
76 | +{ | |
77 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
78 | + struct zl6100_data *data = to_zl6100_data(info); | |
79 | + int ret; | |
80 | + | |
81 | + if (page > 0) | |
82 | + return -ENXIO; | |
83 | + | |
84 | + zl6100_wait(data); | |
85 | + ret = pmbus_read_byte_data(client, page, reg); | |
86 | + data->access = ktime_get(); | |
87 | + | |
88 | + return ret; | |
89 | +} | |
90 | + | |
91 | +static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, | |
92 | + u16 word) | |
93 | +{ | |
94 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
95 | + struct zl6100_data *data = to_zl6100_data(info); | |
96 | + int ret; | |
97 | + | |
98 | + if (page || reg >= PMBUS_VIRT_BASE) | |
99 | + return -ENXIO; | |
100 | + | |
101 | + zl6100_wait(data); | |
102 | + ret = pmbus_write_word_data(client, page, reg, word); | |
103 | + data->access = ktime_get(); | |
104 | + | |
105 | + return ret; | |
106 | +} | |
107 | + | |
108 | +static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) | |
109 | +{ | |
110 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
111 | + struct zl6100_data *data = to_zl6100_data(info); | |
112 | + int ret; | |
113 | + | |
114 | + if (page > 0) | |
115 | + return -ENXIO; | |
116 | + | |
117 | + zl6100_wait(data); | |
118 | + ret = pmbus_write_byte(client, page, value); | |
119 | + data->access = ktime_get(); | |
120 | + | |
121 | + return ret; | |
122 | +} | |
123 | + | |
124 | +static const struct i2c_device_id zl6100_id[] = { | |
125 | + {"zl2004", zl2004}, | |
126 | + {"zl2006", zl2006}, | |
127 | + {"zl2008", zl2008}, | |
128 | + {"zl2105", zl2105}, | |
129 | + {"zl2106", zl2106}, | |
130 | + {"zl6100", zl6100}, | |
131 | + {"zl6105", zl6105}, | |
132 | + { } | |
133 | +}; | |
134 | +MODULE_DEVICE_TABLE(i2c, zl6100_id); | |
135 | + | |
136 | +static int zl6100_probe(struct i2c_client *client, | |
137 | + const struct i2c_device_id *id) | |
138 | +{ | |
139 | + int ret; | |
140 | + struct zl6100_data *data; | |
141 | + struct pmbus_driver_info *info; | |
142 | + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; | |
143 | + const struct i2c_device_id *mid; | |
144 | + | |
145 | + if (!i2c_check_functionality(client->adapter, | |
146 | + I2C_FUNC_SMBUS_READ_BYTE_DATA | |
147 | + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) | |
148 | + return -ENODEV; | |
149 | + | |
150 | + ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID, | |
151 | + device_id); | |
152 | + if (ret < 0) { | |
153 | + dev_err(&client->dev, "Failed to read device ID\n"); | |
154 | + return ret; | |
155 | + } | |
156 | + device_id[ret] = '\0'; | |
157 | + dev_info(&client->dev, "Device ID %s\n", device_id); | |
158 | + | |
159 | + mid = NULL; | |
160 | + for (mid = zl6100_id; mid->name[0]; mid++) { | |
161 | + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) | |
162 | + break; | |
163 | + } | |
164 | + if (!mid->name[0]) { | |
165 | + dev_err(&client->dev, "Unsupported device\n"); | |
166 | + return -ENODEV; | |
167 | + } | |
168 | + if (id->driver_data != mid->driver_data) | |
169 | + dev_notice(&client->dev, | |
170 | + "Device mismatch: Configured %s, detected %s\n", | |
171 | + id->name, mid->name); | |
172 | + | |
173 | + data = kzalloc(sizeof(struct zl6100_data), GFP_KERNEL); | |
174 | + if (!data) | |
175 | + return -ENOMEM; | |
176 | + | |
177 | + data->id = mid->driver_data; | |
178 | + | |
179 | + /* | |
180 | + * ZL2008, ZL2105, and ZL6100 are known to require a wait time | |
181 | + * between I2C accesses. ZL2004 and ZL6105 are known to be safe. | |
182 | + * | |
183 | + * Only clear the wait time for chips known to be safe. The wait time | |
184 | + * can be cleared later for additional chips if tests show that it | |
185 | + * is not needed (in other words, better be safe than sorry). | |
186 | + */ | |
187 | + if (data->id == zl2004 || data->id == zl6105) | |
188 | + delay = 0; | |
189 | + | |
190 | + /* | |
191 | + * Since there was a direct I2C device access above, wait before | |
192 | + * accessing the chip again. | |
193 | + * Set the timestamp, wait, then set it again. This should provide | |
194 | + * enough buffer time to be safe. | |
195 | + */ | |
196 | + data->access = ktime_get(); | |
197 | + zl6100_wait(data); | |
198 | + data->access = ktime_get(); | |
199 | + | |
200 | + info = &data->info; | |
201 | + | |
202 | + info->pages = 1; | |
203 | + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | |
204 | + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
205 | + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
206 | + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; | |
207 | + | |
208 | + info->read_word_data = zl6100_read_word_data; | |
209 | + info->read_byte_data = zl6100_read_byte_data; | |
210 | + info->write_word_data = zl6100_write_word_data; | |
211 | + info->write_byte = zl6100_write_byte; | |
212 | + | |
213 | + ret = pmbus_do_probe(client, mid, info); | |
214 | + if (ret) | |
215 | + goto err_mem; | |
216 | + return 0; | |
217 | + | |
218 | +err_mem: | |
219 | + kfree(data); | |
220 | + return ret; | |
221 | +} | |
222 | + | |
223 | +static int zl6100_remove(struct i2c_client *client) | |
224 | +{ | |
225 | + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
226 | + const struct zl6100_data *data = to_zl6100_data(info); | |
227 | + | |
228 | + pmbus_do_remove(client); | |
229 | + kfree(data); | |
230 | + return 0; | |
231 | +} | |
232 | + | |
233 | +static struct i2c_driver zl6100_driver = { | |
234 | + .driver = { | |
235 | + .name = "zl6100", | |
236 | + }, | |
237 | + .probe = zl6100_probe, | |
238 | + .remove = zl6100_remove, | |
239 | + .id_table = zl6100_id, | |
240 | +}; | |
241 | + | |
242 | +static int __init zl6100_init(void) | |
243 | +{ | |
244 | + return i2c_add_driver(&zl6100_driver); | |
245 | +} | |
246 | + | |
247 | +static void __exit zl6100_exit(void) | |
248 | +{ | |
249 | + i2c_del_driver(&zl6100_driver); | |
250 | +} | |
251 | + | |
252 | +MODULE_AUTHOR("Guenter Roeck"); | |
253 | +MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); | |
254 | +MODULE_LICENSE("GPL"); | |
255 | +module_init(zl6100_init); | |
256 | +module_exit(zl6100_exit); |
drivers/hwmon/w83627ehf.c
... | ... | @@ -197,6 +197,9 @@ |
197 | 197 | #define W83627EHF_REG_ALARM2 0x45A |
198 | 198 | #define W83627EHF_REG_ALARM3 0x45B |
199 | 199 | |
200 | +#define W83627EHF_REG_CASEOPEN_DET 0x42 /* SMI STATUS #2 */ | |
201 | +#define W83627EHF_REG_CASEOPEN_CLR 0x46 /* SMI MASK #3 */ | |
202 | + | |
200 | 203 | /* SmartFan registers */ |
201 | 204 | #define W83627EHF_REG_FAN_STEPUP_TIME 0x0f |
202 | 205 | #define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e |
... | ... | @@ -316,7 +319,7 @@ |
316 | 319 | |
317 | 320 | #define NUM_REG_TEMP ARRAY_SIZE(NCT6775_REG_TEMP) |
318 | 321 | |
319 | -static inline int is_word_sized(u16 reg) | |
322 | +static int is_word_sized(u16 reg) | |
320 | 323 | { |
321 | 324 | return ((((reg & 0xff00) == 0x100 |
322 | 325 | || (reg & 0xff00) == 0x200) |
... | ... | @@ -385,23 +388,6 @@ |
385 | 388 | return 1 << reg; |
386 | 389 | } |
387 | 390 | |
388 | -static inline int | |
389 | -temp_from_reg(u16 reg, s16 regval) | |
390 | -{ | |
391 | - if (is_word_sized(reg)) | |
392 | - return LM75_TEMP_FROM_REG(regval); | |
393 | - return ((s8)regval) * 1000; | |
394 | -} | |
395 | - | |
396 | -static inline u16 | |
397 | -temp_to_reg(u16 reg, long temp) | |
398 | -{ | |
399 | - if (is_word_sized(reg)) | |
400 | - return LM75_TEMP_TO_REG(temp); | |
401 | - return (s8)DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000), | |
402 | - 1000); | |
403 | -} | |
404 | - | |
405 | 391 | /* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ |
406 | 392 | |
407 | 393 | static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 }; |
... | ... | @@ -469,6 +455,7 @@ |
469 | 455 | s16 temp_max[9]; |
470 | 456 | s16 temp_max_hyst[9]; |
471 | 457 | u32 alarms; |
458 | + u8 caseopen; | |
472 | 459 | |
473 | 460 | u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ |
474 | 461 | u8 pwm_enable[4]; /* 1->manual |
... | ... | @@ -557,6 +544,26 @@ |
557 | 544 | return 0; |
558 | 545 | } |
559 | 546 | |
547 | +/* We left-align 8-bit temperature values to make the code simpler */ | |
548 | +static u16 w83627ehf_read_temp(struct w83627ehf_data *data, u16 reg) | |
549 | +{ | |
550 | + u16 res; | |
551 | + | |
552 | + res = w83627ehf_read_value(data, reg); | |
553 | + if (!is_word_sized(reg)) | |
554 | + res <<= 8; | |
555 | + | |
556 | + return res; | |
557 | +} | |
558 | + | |
559 | +static int w83627ehf_write_temp(struct w83627ehf_data *data, u16 reg, | |
560 | + u16 value) | |
561 | +{ | |
562 | + if (!is_word_sized(reg)) | |
563 | + value >>= 8; | |
564 | + return w83627ehf_write_value(data, reg, value); | |
565 | +} | |
566 | + | |
560 | 567 | /* This function assumes that the caller holds data->update_lock */ |
561 | 568 | static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr) |
562 | 569 | { |
... | ... | @@ -771,6 +778,9 @@ |
771 | 778 | |
772 | 779 | /* Measured voltages and limits */ |
773 | 780 | for (i = 0; i < data->in_num; i++) { |
781 | + if ((i == 6) && data->in6_skip) | |
782 | + continue; | |
783 | + | |
774 | 784 | data->in[i] = w83627ehf_read_value(data, |
775 | 785 | W83627EHF_REG_IN(i)); |
776 | 786 | data->in_min[i] = w83627ehf_read_value(data, |
777 | 787 | |
778 | 788 | |
... | ... | @@ -855,15 +865,15 @@ |
855 | 865 | for (i = 0; i < NUM_REG_TEMP; i++) { |
856 | 866 | if (!(data->have_temp & (1 << i))) |
857 | 867 | continue; |
858 | - data->temp[i] = w83627ehf_read_value(data, | |
868 | + data->temp[i] = w83627ehf_read_temp(data, | |
859 | 869 | data->reg_temp[i]); |
860 | 870 | if (data->reg_temp_over[i]) |
861 | 871 | data->temp_max[i] |
862 | - = w83627ehf_read_value(data, | |
872 | + = w83627ehf_read_temp(data, | |
863 | 873 | data->reg_temp_over[i]); |
864 | 874 | if (data->reg_temp_hyst[i]) |
865 | 875 | data->temp_max_hyst[i] |
866 | - = w83627ehf_read_value(data, | |
876 | + = w83627ehf_read_temp(data, | |
867 | 877 | data->reg_temp_hyst[i]); |
868 | 878 | } |
869 | 879 | |
... | ... | @@ -874,6 +884,9 @@ |
874 | 884 | (w83627ehf_read_value(data, |
875 | 885 | W83627EHF_REG_ALARM3) << 16); |
876 | 886 | |
887 | + data->caseopen = w83627ehf_read_value(data, | |
888 | + W83627EHF_REG_CASEOPEN_DET); | |
889 | + | |
877 | 890 | data->last_updated = jiffies; |
878 | 891 | data->valid = 1; |
879 | 892 | } |
... | ... | @@ -1156,8 +1169,7 @@ |
1156 | 1169 | struct sensor_device_attribute *sensor_attr = \ |
1157 | 1170 | to_sensor_dev_attr(attr); \ |
1158 | 1171 | int nr = sensor_attr->index; \ |
1159 | - return sprintf(buf, "%d\n", \ | |
1160 | - temp_from_reg(data->addr[nr], data->reg[nr])); \ | |
1172 | + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->reg[nr])); \ | |
1161 | 1173 | } |
1162 | 1174 | show_temp_reg(reg_temp, temp); |
1163 | 1175 | show_temp_reg(reg_temp_over, temp_max); |
... | ... | @@ -1178,9 +1190,8 @@ |
1178 | 1190 | if (err < 0) \ |
1179 | 1191 | return err; \ |
1180 | 1192 | mutex_lock(&data->update_lock); \ |
1181 | - data->reg[nr] = temp_to_reg(data->addr[nr], val); \ | |
1182 | - w83627ehf_write_value(data, data->addr[nr], \ | |
1183 | - data->reg[nr]); \ | |
1193 | + data->reg[nr] = LM75_TEMP_TO_REG(val); \ | |
1194 | + w83627ehf_write_temp(data, data->addr[nr], data->reg[nr]); \ | |
1184 | 1195 | mutex_unlock(&data->update_lock); \ |
1185 | 1196 | return count; \ |
1186 | 1197 | } |
... | ... | @@ -1655,6 +1666,48 @@ |
1655 | 1666 | } |
1656 | 1667 | static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); |
1657 | 1668 | |
1669 | + | |
1670 | +/* Case open detection */ | |
1671 | + | |
1672 | +static ssize_t | |
1673 | +show_caseopen(struct device *dev, struct device_attribute *attr, char *buf) | |
1674 | +{ | |
1675 | + struct w83627ehf_data *data = w83627ehf_update_device(dev); | |
1676 | + | |
1677 | + return sprintf(buf, "%d\n", | |
1678 | + !!(data->caseopen & to_sensor_dev_attr_2(attr)->index)); | |
1679 | +} | |
1680 | + | |
1681 | +static ssize_t | |
1682 | +clear_caseopen(struct device *dev, struct device_attribute *attr, | |
1683 | + const char *buf, size_t count) | |
1684 | +{ | |
1685 | + struct w83627ehf_data *data = dev_get_drvdata(dev); | |
1686 | + unsigned long val; | |
1687 | + u16 reg, mask; | |
1688 | + | |
1689 | + if (strict_strtoul(buf, 10, &val) || val != 0) | |
1690 | + return -EINVAL; | |
1691 | + | |
1692 | + mask = to_sensor_dev_attr_2(attr)->nr; | |
1693 | + | |
1694 | + mutex_lock(&data->update_lock); | |
1695 | + reg = w83627ehf_read_value(data, W83627EHF_REG_CASEOPEN_CLR); | |
1696 | + w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg | mask); | |
1697 | + w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg & ~mask); | |
1698 | + data->valid = 0; /* Force cache refresh */ | |
1699 | + mutex_unlock(&data->update_lock); | |
1700 | + | |
1701 | + return count; | |
1702 | +} | |
1703 | + | |
1704 | +static struct sensor_device_attribute_2 sda_caseopen[] = { | |
1705 | + SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen, | |
1706 | + clear_caseopen, 0x80, 0x10), | |
1707 | + SENSOR_ATTR_2(intrusion1_alarm, S_IWUSR | S_IRUGO, show_caseopen, | |
1708 | + clear_caseopen, 0x40, 0x40), | |
1709 | +}; | |
1710 | + | |
1658 | 1711 | /* |
1659 | 1712 | * Driver and device management |
1660 | 1713 | */ |
... | ... | @@ -1711,6 +1764,9 @@ |
1711 | 1764 | device_remove_file(dev, &sda_temp_type[i].dev_attr); |
1712 | 1765 | } |
1713 | 1766 | |
1767 | + device_remove_file(dev, &sda_caseopen[0].dev_attr); | |
1768 | + device_remove_file(dev, &sda_caseopen[1].dev_attr); | |
1769 | + | |
1714 | 1770 | device_remove_file(dev, &dev_attr_name); |
1715 | 1771 | device_remove_file(dev, &dev_attr_cpu0_vid); |
1716 | 1772 | } |
1717 | 1773 | |
... | ... | @@ -1789,13 +1845,78 @@ |
1789 | 1845 | data->reg_temp_config[r2] = tmp; |
1790 | 1846 | } |
1791 | 1847 | |
1848 | +static void __devinit | |
1849 | +w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, | |
1850 | + struct w83627ehf_data *data) | |
1851 | +{ | |
1852 | + int fan3pin, fan4pin, fan4min, fan5pin, regval; | |
1853 | + | |
1854 | + superio_enter(sio_data->sioreg); | |
1855 | + | |
1856 | + /* fan4 and fan5 share some pins with the GPIO and serial flash */ | |
1857 | + if (sio_data->kind == nct6775) { | |
1858 | + /* On NCT6775, fan4 shares pins with the fdc interface */ | |
1859 | + fan3pin = 1; | |
1860 | + fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); | |
1861 | + fan4min = 0; | |
1862 | + fan5pin = 0; | |
1863 | + } else if (sio_data->kind == nct6776) { | |
1864 | + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); | |
1865 | + fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); | |
1866 | + fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); | |
1867 | + fan4min = fan4pin; | |
1868 | + } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { | |
1869 | + fan3pin = 1; | |
1870 | + fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; | |
1871 | + fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; | |
1872 | + fan4min = fan4pin; | |
1873 | + } else { | |
1874 | + fan3pin = 1; | |
1875 | + fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); | |
1876 | + fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); | |
1877 | + fan4min = fan4pin; | |
1878 | + } | |
1879 | + | |
1880 | + superio_exit(sio_data->sioreg); | |
1881 | + | |
1882 | + data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ | |
1883 | + data->has_fan |= (fan3pin << 2); | |
1884 | + data->has_fan_min |= (fan3pin << 2); | |
1885 | + | |
1886 | + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { | |
1887 | + /* | |
1888 | + * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 | |
1889 | + * register | |
1890 | + */ | |
1891 | + data->has_fan |= (fan4pin << 3) | (fan5pin << 4); | |
1892 | + data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); | |
1893 | + } else { | |
1894 | + /* | |
1895 | + * It looks like fan4 and fan5 pins can be alternatively used | |
1896 | + * as fan on/off switches, but fan5 control is write only :/ | |
1897 | + * We assume that if the serial interface is disabled, designers | |
1898 | + * connected fan5 as input unless they are emitting log 1, which | |
1899 | + * is not the default. | |
1900 | + */ | |
1901 | + regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); | |
1902 | + if ((regval & (1 << 2)) && fan4pin) { | |
1903 | + data->has_fan |= (1 << 3); | |
1904 | + data->has_fan_min |= (1 << 3); | |
1905 | + } | |
1906 | + if (!(regval & (1 << 1)) && fan5pin) { | |
1907 | + data->has_fan |= (1 << 4); | |
1908 | + data->has_fan_min |= (1 << 4); | |
1909 | + } | |
1910 | + } | |
1911 | +} | |
1912 | + | |
1792 | 1913 | static int __devinit w83627ehf_probe(struct platform_device *pdev) |
1793 | 1914 | { |
1794 | 1915 | struct device *dev = &pdev->dev; |
1795 | 1916 | struct w83627ehf_sio_data *sio_data = dev->platform_data; |
1796 | 1917 | struct w83627ehf_data *data; |
1797 | 1918 | struct resource *res; |
1798 | - u8 fan3pin, fan4pin, fan4min, fan5pin, en_vrm10; | |
1919 | + u8 en_vrm10; | |
1799 | 1920 | int i, err = 0; |
1800 | 1921 | |
1801 | 1922 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
... | ... | @@ -2080,30 +2201,6 @@ |
2080 | 2201 | } |
2081 | 2202 | } |
2082 | 2203 | |
2083 | - /* fan4 and fan5 share some pins with the GPIO and serial flash */ | |
2084 | - if (sio_data->kind == nct6775) { | |
2085 | - /* On NCT6775, fan4 shares pins with the fdc interface */ | |
2086 | - fan3pin = 1; | |
2087 | - fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); | |
2088 | - fan4min = 0; | |
2089 | - fan5pin = 0; | |
2090 | - } else if (sio_data->kind == nct6776) { | |
2091 | - fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); | |
2092 | - fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); | |
2093 | - fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); | |
2094 | - fan4min = fan4pin; | |
2095 | - } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { | |
2096 | - fan3pin = 1; | |
2097 | - fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; | |
2098 | - fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; | |
2099 | - fan4min = fan4pin; | |
2100 | - } else { | |
2101 | - fan3pin = 1; | |
2102 | - fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); | |
2103 | - fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); | |
2104 | - fan4min = fan4pin; | |
2105 | - } | |
2106 | - | |
2107 | 2204 | if (fan_debounce && |
2108 | 2205 | (sio_data->kind == nct6775 || sio_data->kind == nct6776)) { |
2109 | 2206 | u8 tmp; |
2110 | 2207 | |
... | ... | @@ -2121,35 +2218,8 @@ |
2121 | 2218 | |
2122 | 2219 | superio_exit(sio_data->sioreg); |
2123 | 2220 | |
2124 | - /* It looks like fan4 and fan5 pins can be alternatively used | |
2125 | - as fan on/off switches, but fan5 control is write only :/ | |
2126 | - We assume that if the serial interface is disabled, designers | |
2127 | - connected fan5 as input unless they are emitting log 1, which | |
2128 | - is not the default. */ | |
2221 | + w83627ehf_check_fan_inputs(sio_data, data); | |
2129 | 2222 | |
2130 | - data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ | |
2131 | - | |
2132 | - data->has_fan |= (fan3pin << 2); | |
2133 | - data->has_fan_min |= (fan3pin << 2); | |
2134 | - | |
2135 | - /* | |
2136 | - * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 register | |
2137 | - */ | |
2138 | - if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { | |
2139 | - data->has_fan |= (fan4pin << 3) | (fan5pin << 4); | |
2140 | - data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); | |
2141 | - } else { | |
2142 | - i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); | |
2143 | - if ((i & (1 << 2)) && fan4pin) { | |
2144 | - data->has_fan |= (1 << 3); | |
2145 | - data->has_fan_min |= (1 << 3); | |
2146 | - } | |
2147 | - if (!(i & (1 << 1)) && fan5pin) { | |
2148 | - data->has_fan |= (1 << 4); | |
2149 | - data->has_fan_min |= (1 << 4); | |
2150 | - } | |
2151 | - } | |
2152 | - | |
2153 | 2223 | /* Read fan clock dividers immediately */ |
2154 | 2224 | w83627ehf_update_fan_div_common(dev, data); |
2155 | 2225 | |
... | ... | @@ -2266,6 +2336,16 @@ |
2266 | 2336 | &sda_temp_alarm[i].dev_attr)) |
2267 | 2337 | || (err = device_create_file(dev, |
2268 | 2338 | &sda_temp_type[i].dev_attr))) |
2339 | + goto exit_remove; | |
2340 | + } | |
2341 | + | |
2342 | + err = device_create_file(dev, &sda_caseopen[0].dev_attr); | |
2343 | + if (err) | |
2344 | + goto exit_remove; | |
2345 | + | |
2346 | + if (sio_data->kind == nct6776) { | |
2347 | + err = device_create_file(dev, &sda_caseopen[1].dev_attr); | |
2348 | + if (err) | |
2269 | 2349 | goto exit_remove; |
2270 | 2350 | } |
2271 | 2351 |
include/linux/platform_data/exynos4_tmu.h
1 | +/* | |
2 | + * exynos4_tmu.h - Samsung EXYNOS4 TMU (Thermal Management Unit) | |
3 | + * | |
4 | + * Copyright (C) 2011 Samsung Electronics | |
5 | + * Donggeun Kim <dg77.kim@samsung.com> | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | + */ | |
21 | + | |
22 | +#ifndef _LINUX_EXYNOS4_TMU_H | |
23 | +#define _LINUX_EXYNOS4_TMU_H | |
24 | + | |
25 | +enum calibration_type { | |
26 | + TYPE_ONE_POINT_TRIMMING, | |
27 | + TYPE_TWO_POINT_TRIMMING, | |
28 | + TYPE_NONE, | |
29 | +}; | |
30 | + | |
31 | +/** | |
32 | + * struct exynos4_tmu_platform_data | |
33 | + * @threshold: basic temperature for generating interrupt | |
34 | + * 25 <= threshold <= 125 [unit: degree Celsius] | |
35 | + * @trigger_levels: array for each interrupt levels | |
36 | + * [unit: degree Celsius] | |
37 | + * 0: temperature for trigger_level0 interrupt | |
38 | + * condition for trigger_level0 interrupt: | |
39 | + * current temperature > threshold + trigger_levels[0] | |
40 | + * 1: temperature for trigger_level1 interrupt | |
41 | + * condition for trigger_level1 interrupt: | |
42 | + * current temperature > threshold + trigger_levels[1] | |
43 | + * 2: temperature for trigger_level2 interrupt | |
44 | + * condition for trigger_level2 interrupt: | |
45 | + * current temperature > threshold + trigger_levels[2] | |
46 | + * 3: temperature for trigger_level3 interrupt | |
47 | + * condition for trigger_level3 interrupt: | |
48 | + * current temperature > threshold + trigger_levels[3] | |
49 | + * @trigger_level0_en: | |
50 | + * 1 = enable trigger_level0 interrupt, | |
51 | + * 0 = disable trigger_level0 interrupt | |
52 | + * @trigger_level1_en: | |
53 | + * 1 = enable trigger_level1 interrupt, | |
54 | + * 0 = disable trigger_level1 interrupt | |
55 | + * @trigger_level2_en: | |
56 | + * 1 = enable trigger_level2 interrupt, | |
57 | + * 0 = disable trigger_level2 interrupt | |
58 | + * @trigger_level3_en: | |
59 | + * 1 = enable trigger_level3 interrupt, | |
60 | + * 0 = disable trigger_level3 interrupt | |
61 | + * @gain: gain of amplifier in the positive-TC generator block | |
62 | + * 0 <= gain <= 15 | |
63 | + * @reference_voltage: reference voltage of amplifier | |
64 | + * in the positive-TC generator block | |
65 | + * 0 <= reference_voltage <= 31 | |
66 | + * @cal_type: calibration type for temperature | |
67 | + * | |
68 | + * This structure is required for configuration of exynos4_tmu driver. | |
69 | + */ | |
70 | +struct exynos4_tmu_platform_data { | |
71 | + u8 threshold; | |
72 | + u8 trigger_levels[4]; | |
73 | + bool trigger_level0_en; | |
74 | + bool trigger_level1_en; | |
75 | + bool trigger_level2_en; | |
76 | + bool trigger_level3_en; | |
77 | + | |
78 | + u8 gain; | |
79 | + u8 reference_voltage; | |
80 | + | |
81 | + enum calibration_type cal_type; | |
82 | +}; | |
83 | +#endif /* _LINUX_EXYNOS4_TMU_H */ |