mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
hwmon updates for v4.14
- New driver for Lantiq CPU temperature sensor - New driver for IBM CFF power supply - New PMBus driver for TPS53679 - Add support for LM5066I lm25066 PMBus driver - Add support for Intel VID protocol VR13 to PMBus drivers - Add support for CAT34TS02C, GT30TS00, GT34TS02, and CAT34TS04 to jc42 driver - Cleanup and minor improvements in several drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZrJttAAoJEMsfJm/On5mBwmsP/2fC8Khuc7cGtGYpUmOLDP7y QjLe0Bf310r2Zs02W3+a9WExymYk/1NpireFlbsganuCCk3ooKxhvrP83Y/09N30 mhKDenVyq8gTL1YdXimUwZZZa9X/yMNf5xhdaTd3393hLYdb1dwgwjQu7V9VALFY i2Zwz0zqjZeMkvbI06LaoTvRHtPbFw8u4/fPPEoipzw19RfM7T+1hofSVjGYQ5U+ amr4lZzPI3UkOYjfKZwq4vokZVybZfvQWl+LxY/TLQLmrqcKKNV05hmindg6g5mc rpBGyM54higGSyyWby8OpoeFJ5rEfLQzTDBlGIZqlhOz6Drbd3KBj5W+Er97+l+h 9NYdJwmRU25at+BIDbZ/FrTY+gc+xdTX/N5TJWfIoxmlcZwFLIUKIX9+5p0SBJj4 JPpDCiR1u4rJPB2B5HQFxGkhQpnVOqtN9hCmj+fXg9mkigjImHuxErAOABcOoJpZ 9CMlLuteg3PlD01T4qQXJKvxsx68f7bl4HFyHC3leoD3AiWF4B4kwaft14Infiqh f1Zk3QUUxqRdPaDNCX8my0XEtfqmpTU+qow8yaKz7YCaWeu4MDBY8nkJhrE5mma4 YjmfH5A4Gvm6cLJTBQDUC09Q46G+j780SWQYyJxKSnyv5u5mvmz6m69kubZNvsjX xKsJjrdo5bBK3hWzznCv =DZJP -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: - new drivers: - Lantiq CPU temperature sensor - IBM CFF power supply - TPS53679 PMBus driver - new support: - LM5066I (lm25066 PMBus driver) - Intel VID protocol VR13 (PMBus drivers) - CAT34TS02C, GT30TS00, GT34TS02, and CAT34TS04 (jc42 driver) - cleanup and minor improvements in several drivers * tag 'hwmon-for-linus-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (36 commits) hwmon: (ltq-cputemp) add cpu temp sensor driver hwmon: (ltq-cputemp) add devicetree bindings documentation hwmon: (pmbus) Add support for Texas Instruments tps53679 device hwmon: (asc7621) make several arrays static const hwmon: (pmbus/lm25066) Add support for TI LM5066I hwmon: (pmbus/lm25066) Offset coefficient depends on CL hwmon: (pmbus) Add support for Intel VID protocol VR13 Documentation: hwmon: Document the IBM CFF power supply hwmon: (pmbus) Add IBM Common Form Factor (CFF) power supply driver dt-bindings: hwmon: Document the IBM CCF power supply version 1 hwmon: (ftsteutates) constify i2c_device_id hwmon: da9052: Add support for TSI channel mfd: da9052: Make touchscreen registration optional hwmon: da9052: Replace S_IRUGO with 0444 mfd: da9052: Add register details for TSI hwmon: (aspeed-pwm) add THERMAL dependency hwmon: (pmbus) Add debugfs for status registers hwmon: (aspeed-pwm-tacho) cooling device support. Documentation: dt-bindings: aspeed-pwm-tacho cooling device. hwmon: (pmbus): Add generic alarm bit for iin and pin ...
This commit is contained in:
commit
fe91f28138
@ -11,6 +11,8 @@ Required properties for pwm-tacho node:
|
|||||||
|
|
||||||
- #size-cells : should be 1.
|
- #size-cells : should be 1.
|
||||||
|
|
||||||
|
- #cooling-cells: should be 2.
|
||||||
|
|
||||||
- reg : address and length of the register set for the device.
|
- reg : address and length of the register set for the device.
|
||||||
|
|
||||||
- pinctrl-names : a pinctrl state named "default" must be defined.
|
- pinctrl-names : a pinctrl state named "default" must be defined.
|
||||||
@ -28,12 +30,17 @@ fan subnode format:
|
|||||||
Under fan subnode there can upto 8 child nodes, with each child node
|
Under fan subnode there can upto 8 child nodes, with each child node
|
||||||
representing a fan. If there are 8 fans each fan can have one PWM port and
|
representing a fan. If there are 8 fans each fan can have one PWM port and
|
||||||
one/two Fan tach inputs.
|
one/two Fan tach inputs.
|
||||||
|
For PWM port can be configured cooling-levels to create cooling device.
|
||||||
|
Cooling device could be bound to a thermal zone for the thermal control.
|
||||||
|
|
||||||
Required properties for each child node:
|
Required properties for each child node:
|
||||||
- reg : should specify PWM source port.
|
- reg : should specify PWM source port.
|
||||||
integer value in the range 0 to 7 with 0 indicating PWM port A and
|
integer value in the range 0 to 7 with 0 indicating PWM port A and
|
||||||
7 indicating PWM port H.
|
7 indicating PWM port H.
|
||||||
|
|
||||||
|
- cooling-levels: PWM duty cycle values in a range from 0 to 255
|
||||||
|
which correspond to thermal cooling states.
|
||||||
|
|
||||||
- aspeed,fan-tach-ch : should specify the Fan tach input channel.
|
- aspeed,fan-tach-ch : should specify the Fan tach input channel.
|
||||||
integer value in the range 0 through 15, with 0 indicating
|
integer value in the range 0 through 15, with 0 indicating
|
||||||
Fan tach channel 0 and 15 indicating Fan tach channel 15.
|
Fan tach channel 0 and 15 indicating Fan tach channel 15.
|
||||||
@ -50,6 +57,7 @@ pwm_tacho_fixed_clk: fixedclk {
|
|||||||
pwm_tacho: pwmtachocontroller@1e786000 {
|
pwm_tacho: pwmtachocontroller@1e786000 {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <1>;
|
#size-cells = <1>;
|
||||||
|
#cooling-cells = <2>;
|
||||||
reg = <0x1E786000 0x1000>;
|
reg = <0x1E786000 0x1000>;
|
||||||
compatible = "aspeed,ast2500-pwm-tacho";
|
compatible = "aspeed,ast2500-pwm-tacho";
|
||||||
clocks = <&pwm_tacho_fixed_clk>;
|
clocks = <&pwm_tacho_fixed_clk>;
|
||||||
@ -58,6 +66,7 @@ pwm_tacho: pwmtachocontroller@1e786000 {
|
|||||||
|
|
||||||
fan@0 {
|
fan@0 {
|
||||||
reg = <0x00>;
|
reg = <0x00>;
|
||||||
|
cooling-levels = /bits/ 8 <125 151 177 203 229 255>;
|
||||||
aspeed,fan-tach-ch = /bits/ 8 <0x00>;
|
aspeed,fan-tach-ch = /bits/ 8 <0x00>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
21
Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
Normal file
21
Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Device-tree bindings for IBM Common Form Factor Power Supply Version 1
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible = "ibm,cffps1";
|
||||||
|
- reg = < I2C bus address >; : Address of the power supply on the
|
||||||
|
I2C bus.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
i2c-bus@100 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
< more properties >
|
||||||
|
|
||||||
|
power-supply@68 {
|
||||||
|
compatible = "ibm,cffps1";
|
||||||
|
reg = <0x68>;
|
||||||
|
};
|
||||||
|
};
|
10
Documentation/devicetree/bindings/hwmon/ltq-cputemp.txt
Normal file
10
Documentation/devicetree/bindings/hwmon/ltq-cputemp.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Lantiq cpu temperatur sensor
|
||||||
|
|
||||||
|
Requires node properties:
|
||||||
|
- compatible value :
|
||||||
|
"lantiq,cputemp"
|
||||||
|
|
||||||
|
Example:
|
||||||
|
cputemp@0 {
|
||||||
|
compatible = "lantiq,cputemp";
|
||||||
|
};
|
@ -18,6 +18,10 @@ enhancements. It can monitor up to 4 voltages, 16 temperatures and
|
|||||||
8 fans. It also contains an integrated watchdog which is currently
|
8 fans. It also contains an integrated watchdog which is currently
|
||||||
implemented in this driver.
|
implemented in this driver.
|
||||||
|
|
||||||
|
To clear a temperature or fan alarm, execute the following command with the
|
||||||
|
correct path to the alarm file:
|
||||||
|
echo 0 >XXXX_alarm
|
||||||
|
|
||||||
Specification of the chip can be found here:
|
Specification of the chip can be found here:
|
||||||
ftp://ftp.ts.fujitsu.com/pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/BMC-Teutates_Specification_V1.21.pdf
|
ftp://ftp.ts.fujitsu.com/pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/BMC-Teutates_Specification_V1.21.pdf
|
||||||
ftp://ftp.ts.fujitsu.com/pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/Fujitsu_mainboards-1-Sensors_HowTo-en-US.pdf
|
ftp://ftp.ts.fujitsu.com/pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/Fujitsu_mainboards-1-Sensors_HowTo-en-US.pdf
|
||||||
|
54
Documentation/hwmon/ibm-cffps
Normal file
54
Documentation/hwmon/ibm-cffps
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
Kernel driver ibm-cffps
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Supported chips:
|
||||||
|
* IBM Common Form Factor power supply
|
||||||
|
|
||||||
|
Author: Eddie James <eajames@us.ibm.com>
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This driver supports IBM Common Form Factor (CFF) power supplies. This driver
|
||||||
|
is a client to the core PMBus driver.
|
||||||
|
|
||||||
|
Usage Notes
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This driver does not auto-detect devices. You will have to instantiate the
|
||||||
|
devices explicitly. Please see Documentation/i2c/instantiating-devices for
|
||||||
|
details.
|
||||||
|
|
||||||
|
Sysfs entries
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The following attributes are supported:
|
||||||
|
|
||||||
|
curr1_alarm Output current over-current alarm.
|
||||||
|
curr1_input Measured output current in mA.
|
||||||
|
curr1_label "iout1"
|
||||||
|
|
||||||
|
fan1_alarm Fan 1 warning.
|
||||||
|
fan1_fault Fan 1 fault.
|
||||||
|
fan1_input Fan 1 speed in RPM.
|
||||||
|
fan2_alarm Fan 2 warning.
|
||||||
|
fan2_fault Fan 2 fault.
|
||||||
|
fan2_input Fan 2 speed in RPM.
|
||||||
|
|
||||||
|
in1_alarm Input voltage under-voltage alarm.
|
||||||
|
in1_input Measured input voltage in mV.
|
||||||
|
in1_label "vin"
|
||||||
|
in2_alarm Output voltage over-voltage alarm.
|
||||||
|
in2_input Measured output voltage in mV.
|
||||||
|
in2_label "vout1"
|
||||||
|
|
||||||
|
power1_alarm Input fault or alarm.
|
||||||
|
power1_input Measured input power in uW.
|
||||||
|
power1_label "pin"
|
||||||
|
|
||||||
|
temp1_alarm PSU inlet ambient temperature over-temperature alarm.
|
||||||
|
temp1_input Measured PSU inlet ambient temp in millidegrees C.
|
||||||
|
temp2_alarm Secondary rectifier temp over-temperature alarm.
|
||||||
|
temp2_input Measured secondary rectifier temp in millidegrees C.
|
||||||
|
temp3_alarm ORing FET temperature over-temperature alarm.
|
||||||
|
temp3_input Measured ORing FET temperature in millidegrees C.
|
@ -29,6 +29,11 @@ Supported chips:
|
|||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
Datasheet:
|
Datasheet:
|
||||||
http://www.national.com/pf/LM/LM5066.html
|
http://www.national.com/pf/LM/LM5066.html
|
||||||
|
* Texas Instruments LM5066I
|
||||||
|
Prefix: 'lm5066i'
|
||||||
|
Addresses scanned: -
|
||||||
|
Datasheet:
|
||||||
|
http://www.ti.com/product/LM5066I
|
||||||
|
|
||||||
Author: Guenter Roeck <linux@roeck-us.net>
|
Author: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
|
||||||
@ -37,8 +42,8 @@ Description
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
This driver supports hardware monitoring for National Semiconductor / TI LM25056,
|
This driver supports hardware monitoring for National Semiconductor / TI LM25056,
|
||||||
LM25063, LM25066, LM5064, and LM5066 Power Management, Monitoring, Control, and
|
LM25063, LM25066, LM5064, and LM5066/LM5066I Power Management, Monitoring,
|
||||||
Protection ICs.
|
Control, and Protection ICs.
|
||||||
|
|
||||||
The driver is a client driver to the core PMBus driver. Please see
|
The driver is a client driver to the core PMBus driver. Please see
|
||||||
Documentation/hwmon/pmbus for details on PMBus client drivers.
|
Documentation/hwmon/pmbus for details on PMBus client drivers.
|
||||||
|
@ -343,6 +343,7 @@ config SENSORS_ASB100
|
|||||||
|
|
||||||
config SENSORS_ASPEED
|
config SENSORS_ASPEED
|
||||||
tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver"
|
tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver"
|
||||||
|
depends on THERMAL || THERMAL=n
|
||||||
select REGMAP
|
select REGMAP
|
||||||
help
|
help
|
||||||
This driver provides support for ASPEED AST2400/AST2500 PWM
|
This driver provides support for ASPEED AST2400/AST2500 PWM
|
||||||
@ -790,6 +791,13 @@ config SENSORS_LTC4261
|
|||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called ltc4261.
|
be called ltc4261.
|
||||||
|
|
||||||
|
config SENSORS_LTQ_CPUTEMP
|
||||||
|
bool "Lantiq cpu temperature sensor driver"
|
||||||
|
depends on LANTIQ
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the temperature
|
||||||
|
sensor inside your CPU.
|
||||||
|
|
||||||
config SENSORS_MAX1111
|
config SENSORS_MAX1111
|
||||||
tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
|
tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
|
||||||
depends on SPI_MASTER
|
depends on SPI_MASTER
|
||||||
|
@ -110,6 +110,7 @@ obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o
|
|||||||
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
|
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
|
||||||
obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o
|
obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o
|
||||||
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
|
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
|
||||||
|
obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o
|
||||||
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
|
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
|
||||||
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
|
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
|
||||||
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
|
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
|
||||||
|
@ -384,7 +384,7 @@ static struct attribute *adc128_attrs[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct attribute_group adc128_group = {
|
static const struct attribute_group adc128_group = {
|
||||||
.attrs = adc128_attrs,
|
.attrs = adc128_attrs,
|
||||||
.is_visible = adc128_is_visible,
|
.is_visible = adc128_is_visible,
|
||||||
};
|
};
|
||||||
|
@ -191,24 +191,23 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
|
|||||||
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
|
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
|
||||||
|
|
||||||
if (of_property_read_u32(node, "reg", &pval)) {
|
if (of_property_read_u32(node, "reg", &pval)) {
|
||||||
dev_err(&client->dev, "invalid reg on %s\n",
|
dev_err(&client->dev, "invalid reg on %pOF\n", node);
|
||||||
node->full_name);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel = pval;
|
channel = pval;
|
||||||
if (channel >= ADS1015_CHANNELS) {
|
if (channel >= ADS1015_CHANNELS) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"invalid channel index %d on %s\n",
|
"invalid channel index %d on %pOF\n",
|
||||||
channel, node->full_name);
|
channel, node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_property_read_u32(node, "ti,gain", &pval)) {
|
if (!of_property_read_u32(node, "ti,gain", &pval)) {
|
||||||
pga = pval;
|
pga = pval;
|
||||||
if (pga > 6) {
|
if (pga > 6) {
|
||||||
dev_err(&client->dev, "invalid gain on %s\n",
|
dev_err(&client->dev, "invalid gain on %pOF\n",
|
||||||
node->full_name);
|
node);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,8 +216,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
|
|||||||
data_rate = pval;
|
data_rate = pval;
|
||||||
if (data_rate > 7) {
|
if (data_rate > 7) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"invalid data_rate on %s\n",
|
"invalid data_rate on %pOF\n", node);
|
||||||
node->full_name);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1319,14 +1319,14 @@ static struct attribute *vid_attrs[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
|
static const struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
|
||||||
static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs };
|
static const struct attribute_group fan4_attr_group = { .attrs = fan4_attrs };
|
||||||
static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs };
|
static const struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs };
|
||||||
static struct attribute_group in0_attr_group = { .attrs = in0_attrs };
|
static const struct attribute_group in0_attr_group = { .attrs = in0_attrs };
|
||||||
static struct attribute_group in3_attr_group = { .attrs = in3_attrs };
|
static const struct attribute_group in3_attr_group = { .attrs = in3_attrs };
|
||||||
static struct attribute_group in4_attr_group = { .attrs = in4_attrs };
|
static const struct attribute_group in4_attr_group = { .attrs = in4_attrs };
|
||||||
static struct attribute_group in5_attr_group = { .attrs = in5_attrs };
|
static const struct attribute_group in5_attr_group = { .attrs = in5_attrs };
|
||||||
static struct attribute_group vid_attr_group = { .attrs = vid_attrs };
|
static const struct attribute_group vid_attr_group = { .attrs = vid_attrs };
|
||||||
|
|
||||||
static int adt7475_detect(struct i2c_client *client,
|
static int adt7475_detect(struct i2c_client *client,
|
||||||
struct i2c_board_info *info)
|
struct i2c_board_info *info)
|
||||||
|
@ -512,7 +512,7 @@ static ssize_t show_pwm_ac(struct device *dev,
|
|||||||
{
|
{
|
||||||
SETUP_SHOW_DATA_PARAM(dev, attr);
|
SETUP_SHOW_DATA_PARAM(dev, attr);
|
||||||
u8 config, altbit, regval;
|
u8 config, altbit, regval;
|
||||||
const u8 map[] = {
|
static const u8 map[] = {
|
||||||
0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
|
0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
|
||||||
0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f
|
0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f
|
||||||
};
|
};
|
||||||
@ -533,7 +533,7 @@ static ssize_t store_pwm_ac(struct device *dev,
|
|||||||
SETUP_STORE_DATA_PARAM(dev, attr);
|
SETUP_STORE_DATA_PARAM(dev, attr);
|
||||||
unsigned long reqval;
|
unsigned long reqval;
|
||||||
u8 currval, config, altbit, newval;
|
u8 currval, config, altbit, newval;
|
||||||
const u16 map[] = {
|
static const u16 map[] = {
|
||||||
0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06,
|
0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06,
|
||||||
0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
|
0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
|
||||||
0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/thermal.h>
|
||||||
|
|
||||||
/* ASPEED PWM & FAN Tach Register Definition */
|
/* ASPEED PWM & FAN Tach Register Definition */
|
||||||
#define ASPEED_PTCR_CTRL 0x00
|
#define ASPEED_PTCR_CTRL 0x00
|
||||||
@ -166,6 +167,18 @@
|
|||||||
/* How long we sleep in us while waiting for an RPM result. */
|
/* How long we sleep in us while waiting for an RPM result. */
|
||||||
#define ASPEED_RPM_STATUS_SLEEP_USEC 500
|
#define ASPEED_RPM_STATUS_SLEEP_USEC 500
|
||||||
|
|
||||||
|
#define MAX_CDEV_NAME_LEN 16
|
||||||
|
|
||||||
|
struct aspeed_cooling_device {
|
||||||
|
char name[16];
|
||||||
|
struct aspeed_pwm_tacho_data *priv;
|
||||||
|
struct thermal_cooling_device *tcdev;
|
||||||
|
int pwm_port;
|
||||||
|
u8 *cooling_levels;
|
||||||
|
u8 max_state;
|
||||||
|
u8 cur_state;
|
||||||
|
};
|
||||||
|
|
||||||
struct aspeed_pwm_tacho_data {
|
struct aspeed_pwm_tacho_data {
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned long clk_freq;
|
unsigned long clk_freq;
|
||||||
@ -180,6 +193,7 @@ struct aspeed_pwm_tacho_data {
|
|||||||
u8 pwm_port_type[8];
|
u8 pwm_port_type[8];
|
||||||
u8 pwm_port_fan_ctrl[8];
|
u8 pwm_port_fan_ctrl[8];
|
||||||
u8 fan_tach_ch_source[16];
|
u8 fan_tach_ch_source[16];
|
||||||
|
struct aspeed_cooling_device *cdev[8];
|
||||||
const struct attribute_group *groups[3];
|
const struct attribute_group *groups[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -765,6 +779,94 @@ static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tacho_data *priv,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
|
||||||
|
unsigned long *state)
|
||||||
|
{
|
||||||
|
struct aspeed_cooling_device *cdev = tcdev->devdata;
|
||||||
|
|
||||||
|
*state = cdev->max_state;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
|
||||||
|
unsigned long *state)
|
||||||
|
{
|
||||||
|
struct aspeed_cooling_device *cdev = tcdev->devdata;
|
||||||
|
|
||||||
|
*state = cdev->cur_state;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
|
||||||
|
unsigned long state)
|
||||||
|
{
|
||||||
|
struct aspeed_cooling_device *cdev = tcdev->devdata;
|
||||||
|
|
||||||
|
if (state > cdev->max_state)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
cdev->cur_state = state;
|
||||||
|
cdev->priv->pwm_port_fan_ctrl[cdev->pwm_port] =
|
||||||
|
cdev->cooling_levels[cdev->cur_state];
|
||||||
|
aspeed_set_pwm_port_fan_ctrl(cdev->priv, cdev->pwm_port,
|
||||||
|
cdev->cooling_levels[cdev->cur_state]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = {
|
||||||
|
.get_max_state = aspeed_pwm_cz_get_max_state,
|
||||||
|
.get_cur_state = aspeed_pwm_cz_get_cur_state,
|
||||||
|
.set_cur_state = aspeed_pwm_cz_set_cur_state,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int aspeed_create_pwm_cooling(struct device *dev,
|
||||||
|
struct device_node *child,
|
||||||
|
struct aspeed_pwm_tacho_data *priv,
|
||||||
|
u32 pwm_port, u8 num_levels)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct aspeed_cooling_device *cdev;
|
||||||
|
|
||||||
|
cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (!cdev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
|
||||||
|
if (!cdev->cooling_levels)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cdev->max_state = num_levels - 1;
|
||||||
|
ret = of_property_read_u8_array(child, "cooling-levels",
|
||||||
|
cdev->cooling_levels,
|
||||||
|
num_levels);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_port);
|
||||||
|
|
||||||
|
cdev->tcdev = thermal_of_cooling_device_register(child,
|
||||||
|
cdev->name,
|
||||||
|
cdev,
|
||||||
|
&aspeed_pwm_cool_ops);
|
||||||
|
if (IS_ERR(cdev->tcdev))
|
||||||
|
return PTR_ERR(cdev->tcdev);
|
||||||
|
|
||||||
|
cdev->priv = priv;
|
||||||
|
cdev->pwm_port = pwm_port;
|
||||||
|
|
||||||
|
priv->cdev[pwm_port] = cdev;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int aspeed_create_fan(struct device *dev,
|
static int aspeed_create_fan(struct device *dev,
|
||||||
struct device_node *child,
|
struct device_node *child,
|
||||||
struct aspeed_pwm_tacho_data *priv)
|
struct aspeed_pwm_tacho_data *priv)
|
||||||
@ -778,6 +880,15 @@ static int aspeed_create_fan(struct device *dev,
|
|||||||
return ret;
|
return ret;
|
||||||
aspeed_create_pwm_port(priv, (u8)pwm_port);
|
aspeed_create_pwm_port(priv, (u8)pwm_port);
|
||||||
|
|
||||||
|
ret = of_property_count_u8_elems(child, "cooling-levels");
|
||||||
|
|
||||||
|
if (ret > 0) {
|
||||||
|
ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_port,
|
||||||
|
ret);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch");
|
count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch");
|
||||||
if (count < 1)
|
if (count < 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -834,9 +945,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
for_each_child_of_node(np, child) {
|
for_each_child_of_node(np, child) {
|
||||||
ret = aspeed_create_fan(dev, child, priv);
|
ret = aspeed_create_fan(dev, child, priv);
|
||||||
of_node_put(child);
|
if (ret) {
|
||||||
if (ret)
|
of_node_put(child);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->groups[0] = &pwm_dev_group;
|
priv->groups[0] = &pwm_dev_group;
|
||||||
|
@ -20,13 +20,19 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/property.h>
|
||||||
|
|
||||||
#include <linux/mfd/da9052/da9052.h>
|
#include <linux/mfd/da9052/da9052.h>
|
||||||
#include <linux/mfd/da9052/reg.h>
|
#include <linux/mfd/da9052/reg.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
struct da9052_hwmon {
|
struct da9052_hwmon {
|
||||||
struct da9052 *da9052;
|
struct da9052 *da9052;
|
||||||
struct mutex hwmon_lock;
|
struct mutex hwmon_lock;
|
||||||
|
bool tsi_as_adc;
|
||||||
|
int tsiref_mv;
|
||||||
|
struct regulator *tsiref;
|
||||||
|
struct completion tsidone;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char * const input_names[] = {
|
static const char * const input_names[] = {
|
||||||
@ -37,6 +43,10 @@ static const char * const input_names[] = {
|
|||||||
[DA9052_ADC_IN4] = "ADC IN4",
|
[DA9052_ADC_IN4] = "ADC IN4",
|
||||||
[DA9052_ADC_IN5] = "ADC IN5",
|
[DA9052_ADC_IN5] = "ADC IN5",
|
||||||
[DA9052_ADC_IN6] = "ADC IN6",
|
[DA9052_ADC_IN6] = "ADC IN6",
|
||||||
|
[DA9052_ADC_TSI_XP] = "ADC TS X+",
|
||||||
|
[DA9052_ADC_TSI_YP] = "ADC TS Y+",
|
||||||
|
[DA9052_ADC_TSI_XN] = "ADC TS X-",
|
||||||
|
[DA9052_ADC_TSI_YN] = "ADC TS Y-",
|
||||||
[DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP",
|
[DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP",
|
||||||
[DA9052_ADC_VBBAT] = "BACK-UP BATTERY VOLTAGE",
|
[DA9052_ADC_VBBAT] = "BACK-UP BATTERY VOLTAGE",
|
||||||
};
|
};
|
||||||
@ -59,6 +69,11 @@ static inline int vbbat_reg_to_mv(int value)
|
|||||||
return DIV_ROUND_CLOSEST(value * 5000, 1023);
|
return DIV_ROUND_CLOSEST(value * 5000, 1023);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int input_tsireg_to_mv(struct da9052_hwmon *hwmon, int value)
|
||||||
|
{
|
||||||
|
return DIV_ROUND_CLOSEST(value * hwmon->tsiref_mv, 1023);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int da9052_enable_vddout_channel(struct da9052 *da9052)
|
static inline int da9052_enable_vddout_channel(struct da9052 *da9052)
|
||||||
{
|
{
|
||||||
return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
|
return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
|
||||||
@ -154,6 +169,97 @@ static ssize_t da9052_read_misc_channel(struct device *dev,
|
|||||||
return sprintf(buf, "%d\n", input_reg_to_mv(ret));
|
return sprintf(buf, "%d\n", input_reg_to_mv(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int da9052_request_tsi_read(struct da9052_hwmon *hwmon, int channel)
|
||||||
|
{
|
||||||
|
u8 val = DA9052_TSICONTB_TSIMAN;
|
||||||
|
|
||||||
|
switch (channel) {
|
||||||
|
case DA9052_ADC_TSI_XP:
|
||||||
|
val |= DA9052_TSICONTB_TSIMUX_XP;
|
||||||
|
break;
|
||||||
|
case DA9052_ADC_TSI_YP:
|
||||||
|
val |= DA9052_TSICONTB_TSIMUX_YP;
|
||||||
|
break;
|
||||||
|
case DA9052_ADC_TSI_XN:
|
||||||
|
val |= DA9052_TSICONTB_TSIMUX_XN;
|
||||||
|
break;
|
||||||
|
case DA9052_ADC_TSI_YN:
|
||||||
|
val |= DA9052_TSICONTB_TSIMUX_YN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return da9052_reg_write(hwmon->da9052, DA9052_TSI_CONT_B_REG, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9052_get_tsi_result(struct da9052_hwmon *hwmon, int channel)
|
||||||
|
{
|
||||||
|
u8 regs[3];
|
||||||
|
int msb, lsb, err;
|
||||||
|
|
||||||
|
/* block read to avoid separation of MSB and LSB */
|
||||||
|
err = da9052_group_read(hwmon->da9052, DA9052_TSI_X_MSB_REG,
|
||||||
|
ARRAY_SIZE(regs), regs);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
switch (channel) {
|
||||||
|
case DA9052_ADC_TSI_XP:
|
||||||
|
case DA9052_ADC_TSI_XN:
|
||||||
|
msb = regs[0] << DA9052_TSILSB_TSIXL_BITS;
|
||||||
|
lsb = regs[2] & DA9052_TSILSB_TSIXL;
|
||||||
|
lsb >>= DA9052_TSILSB_TSIXL_SHIFT;
|
||||||
|
break;
|
||||||
|
case DA9052_ADC_TSI_YP:
|
||||||
|
case DA9052_ADC_TSI_YN:
|
||||||
|
msb = regs[1] << DA9052_TSILSB_TSIYL_BITS;
|
||||||
|
lsb = regs[2] & DA9052_TSILSB_TSIYL;
|
||||||
|
lsb >>= DA9052_TSILSB_TSIYL_SHIFT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return msb | lsb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ssize_t __da9052_read_tsi(struct device *dev, int channel)
|
||||||
|
{
|
||||||
|
struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
reinit_completion(&hwmon->tsidone);
|
||||||
|
|
||||||
|
ret = da9052_request_tsi_read(hwmon, channel);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Wait for an conversion done interrupt */
|
||||||
|
if (!wait_for_completion_timeout(&hwmon->tsidone,
|
||||||
|
msecs_to_jiffies(500)))
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
return da9052_get_tsi_result(hwmon, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t da9052_read_tsi(struct device *dev,
|
||||||
|
struct device_attribute *devattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
|
||||||
|
int channel = to_sensor_dev_attr(devattr)->index;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&hwmon->hwmon_lock);
|
||||||
|
ret = __da9052_read_tsi(dev, channel);
|
||||||
|
mutex_unlock(&hwmon->hwmon_lock);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
else
|
||||||
|
return sprintf(buf, "%d\n", input_tsireg_to_mv(hwmon, ret));
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t da9052_read_tjunc(struct device *dev,
|
static ssize_t da9052_read_tjunc(struct device *dev,
|
||||||
struct device_attribute *devattr, char *buf)
|
struct device_attribute *devattr, char *buf)
|
||||||
{
|
{
|
||||||
@ -196,43 +302,82 @@ static ssize_t show_label(struct device *dev,
|
|||||||
input_names[to_sensor_dev_attr(devattr)->index]);
|
input_names[to_sensor_dev_attr(devattr)->index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9052_read_vddout, NULL,
|
static umode_t da9052_channel_is_visible(struct kobject *kobj,
|
||||||
|
struct attribute *attr, int index)
|
||||||
|
{
|
||||||
|
struct device *dev = container_of(kobj, struct device, kobj);
|
||||||
|
struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
|
||||||
|
struct device_attribute *dattr = container_of(attr,
|
||||||
|
struct device_attribute, attr);
|
||||||
|
struct sensor_device_attribute *sattr = to_sensor_dev_attr(dattr);
|
||||||
|
|
||||||
|
if (!hwmon->tsi_as_adc) {
|
||||||
|
switch (sattr->index) {
|
||||||
|
case DA9052_ADC_TSI_XP:
|
||||||
|
case DA9052_ADC_TSI_YP:
|
||||||
|
case DA9052_ADC_TSI_XN:
|
||||||
|
case DA9052_ADC_TSI_YN:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr->mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SENSOR_DEVICE_ATTR(in0_input, 0444, da9052_read_vddout, NULL,
|
||||||
DA9052_ADC_VDDOUT);
|
DA9052_ADC_VDDOUT);
|
||||||
static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(in0_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_VDDOUT);
|
DA9052_ADC_VDDOUT);
|
||||||
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9052_read_vbat, NULL,
|
static SENSOR_DEVICE_ATTR(in3_input, 0444, da9052_read_vbat, NULL,
|
||||||
DA9052_ADC_VBAT);
|
DA9052_ADC_VBAT);
|
||||||
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(in3_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_VBAT);
|
DA9052_ADC_VBAT);
|
||||||
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_read_misc_channel, NULL,
|
static SENSOR_DEVICE_ATTR(in4_input, 0444, da9052_read_misc_channel, NULL,
|
||||||
DA9052_ADC_IN4);
|
DA9052_ADC_IN4);
|
||||||
static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(in4_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_IN4);
|
DA9052_ADC_IN4);
|
||||||
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_read_misc_channel, NULL,
|
static SENSOR_DEVICE_ATTR(in5_input, 0444, da9052_read_misc_channel, NULL,
|
||||||
DA9052_ADC_IN5);
|
DA9052_ADC_IN5);
|
||||||
static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(in5_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_IN5);
|
DA9052_ADC_IN5);
|
||||||
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_read_misc_channel, NULL,
|
static SENSOR_DEVICE_ATTR(in6_input, 0444, da9052_read_misc_channel, NULL,
|
||||||
DA9052_ADC_IN6);
|
DA9052_ADC_IN6);
|
||||||
static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(in6_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_IN6);
|
DA9052_ADC_IN6);
|
||||||
static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, da9052_read_vbbat, NULL,
|
static SENSOR_DEVICE_ATTR(in9_input, 0444, da9052_read_vbbat, NULL,
|
||||||
DA9052_ADC_VBBAT);
|
DA9052_ADC_VBBAT);
|
||||||
static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(in9_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_VBBAT);
|
DA9052_ADC_VBBAT);
|
||||||
|
|
||||||
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, da9052_read_ich, NULL,
|
static SENSOR_DEVICE_ATTR(in70_input, 0444, da9052_read_tsi, NULL,
|
||||||
|
DA9052_ADC_TSI_XP);
|
||||||
|
static SENSOR_DEVICE_ATTR(in70_label, 0444, show_label, NULL,
|
||||||
|
DA9052_ADC_TSI_XP);
|
||||||
|
static SENSOR_DEVICE_ATTR(in71_input, 0444, da9052_read_tsi, NULL,
|
||||||
|
DA9052_ADC_TSI_XN);
|
||||||
|
static SENSOR_DEVICE_ATTR(in71_label, 0444, show_label, NULL,
|
||||||
|
DA9052_ADC_TSI_XN);
|
||||||
|
static SENSOR_DEVICE_ATTR(in72_input, 0444, da9052_read_tsi, NULL,
|
||||||
|
DA9052_ADC_TSI_YP);
|
||||||
|
static SENSOR_DEVICE_ATTR(in72_label, 0444, show_label, NULL,
|
||||||
|
DA9052_ADC_TSI_YP);
|
||||||
|
static SENSOR_DEVICE_ATTR(in73_input, 0444, da9052_read_tsi, NULL,
|
||||||
|
DA9052_ADC_TSI_YN);
|
||||||
|
static SENSOR_DEVICE_ATTR(in73_label, 0444, show_label, NULL,
|
||||||
|
DA9052_ADC_TSI_YN);
|
||||||
|
|
||||||
|
static SENSOR_DEVICE_ATTR(curr1_input, 0444, da9052_read_ich, NULL,
|
||||||
DA9052_ADC_ICH);
|
DA9052_ADC_ICH);
|
||||||
static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(curr1_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_ICH);
|
DA9052_ADC_ICH);
|
||||||
|
|
||||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, da9052_read_tbat, NULL,
|
static SENSOR_DEVICE_ATTR(temp2_input, 0444, da9052_read_tbat, NULL,
|
||||||
DA9052_ADC_TBAT);
|
DA9052_ADC_TBAT);
|
||||||
static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(temp2_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_TBAT);
|
DA9052_ADC_TBAT);
|
||||||
static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL,
|
static SENSOR_DEVICE_ATTR(temp8_input, 0444, da9052_read_tjunc, NULL,
|
||||||
DA9052_ADC_TJUNC);
|
DA9052_ADC_TJUNC);
|
||||||
static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL,
|
static SENSOR_DEVICE_ATTR(temp8_label, 0444, show_label, NULL,
|
||||||
DA9052_ADC_TJUNC);
|
DA9052_ADC_TJUNC);
|
||||||
|
|
||||||
static struct attribute *da9052_attrs[] = {
|
static struct attribute *da9052_attrs[] = {
|
||||||
@ -246,6 +391,14 @@ static struct attribute *da9052_attrs[] = {
|
|||||||
&sensor_dev_attr_in5_label.dev_attr.attr,
|
&sensor_dev_attr_in5_label.dev_attr.attr,
|
||||||
&sensor_dev_attr_in6_input.dev_attr.attr,
|
&sensor_dev_attr_in6_input.dev_attr.attr,
|
||||||
&sensor_dev_attr_in6_label.dev_attr.attr,
|
&sensor_dev_attr_in6_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in70_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in70_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in71_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in71_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in72_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in72_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in73_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in73_label.dev_attr.attr,
|
||||||
&sensor_dev_attr_in9_input.dev_attr.attr,
|
&sensor_dev_attr_in9_input.dev_attr.attr,
|
||||||
&sensor_dev_attr_in9_label.dev_attr.attr,
|
&sensor_dev_attr_in9_label.dev_attr.attr,
|
||||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||||
@ -257,29 +410,117 @@ static struct attribute *da9052_attrs[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
ATTRIBUTE_GROUPS(da9052);
|
static const struct attribute_group da9052_group = {
|
||||||
|
.attrs = da9052_attrs,
|
||||||
|
.is_visible = da9052_channel_is_visible,
|
||||||
|
};
|
||||||
|
__ATTRIBUTE_GROUPS(da9052);
|
||||||
|
|
||||||
|
static irqreturn_t da9052_tsi_datardy_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct da9052_hwmon *hwmon = data;
|
||||||
|
|
||||||
|
complete(&hwmon->tsidone);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static int da9052_hwmon_probe(struct platform_device *pdev)
|
static int da9052_hwmon_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct da9052_hwmon *hwmon;
|
struct da9052_hwmon *hwmon;
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
hwmon = devm_kzalloc(dev, sizeof(struct da9052_hwmon), GFP_KERNEL);
|
hwmon = devm_kzalloc(dev, sizeof(struct da9052_hwmon), GFP_KERNEL);
|
||||||
if (!hwmon)
|
if (!hwmon)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, hwmon);
|
||||||
|
|
||||||
mutex_init(&hwmon->hwmon_lock);
|
mutex_init(&hwmon->hwmon_lock);
|
||||||
hwmon->da9052 = dev_get_drvdata(pdev->dev.parent);
|
hwmon->da9052 = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
|
||||||
|
init_completion(&hwmon->tsidone);
|
||||||
|
|
||||||
|
hwmon->tsi_as_adc =
|
||||||
|
device_property_read_bool(pdev->dev.parent, "dlg,tsi-as-adc");
|
||||||
|
|
||||||
|
if (hwmon->tsi_as_adc) {
|
||||||
|
hwmon->tsiref = devm_regulator_get(pdev->dev.parent, "tsiref");
|
||||||
|
if (IS_ERR(hwmon->tsiref)) {
|
||||||
|
err = PTR_ERR(hwmon->tsiref);
|
||||||
|
dev_err(&pdev->dev, "failed to get tsiref: %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = regulator_enable(hwmon->tsiref);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
hwmon->tsiref_mv = regulator_get_voltage(hwmon->tsiref);
|
||||||
|
if (hwmon->tsiref_mv < 0) {
|
||||||
|
err = hwmon->tsiref_mv;
|
||||||
|
goto exit_regulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert from microvolt (DT) to millivolt (hwmon) */
|
||||||
|
hwmon->tsiref_mv /= 1000;
|
||||||
|
|
||||||
|
/* TSIREF limits from datasheet */
|
||||||
|
if (hwmon->tsiref_mv < 1800 || hwmon->tsiref_mv > 2600) {
|
||||||
|
dev_err(hwmon->da9052->dev, "invalid TSIREF voltage: %d",
|
||||||
|
hwmon->tsiref_mv);
|
||||||
|
err = -ENXIO;
|
||||||
|
goto exit_regulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disable touchscreen features */
|
||||||
|
da9052_reg_write(hwmon->da9052, DA9052_TSI_CONT_A_REG, 0x00);
|
||||||
|
|
||||||
|
err = da9052_request_irq(hwmon->da9052, DA9052_IRQ_TSIREADY,
|
||||||
|
"tsiready-irq", da9052_tsi_datardy_irq,
|
||||||
|
hwmon);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register TSIRDY IRQ: %d",
|
||||||
|
err);
|
||||||
|
goto exit_regulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9052",
|
hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9052",
|
||||||
hwmon,
|
hwmon,
|
||||||
da9052_groups);
|
da9052_groups);
|
||||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
err = PTR_ERR_OR_ZERO(hwmon_dev);
|
||||||
|
if (err)
|
||||||
|
goto exit_irq;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_irq:
|
||||||
|
if (hwmon->tsi_as_adc)
|
||||||
|
da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon);
|
||||||
|
exit_regulator:
|
||||||
|
if (hwmon->tsiref)
|
||||||
|
regulator_disable(hwmon->tsiref);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9052_hwmon_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct da9052_hwmon *hwmon = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (hwmon->tsi_as_adc) {
|
||||||
|
da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon);
|
||||||
|
regulator_disable(hwmon->tsiref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver da9052_hwmon_driver = {
|
static struct platform_driver da9052_hwmon_driver = {
|
||||||
.probe = da9052_hwmon_probe,
|
.probe = da9052_hwmon_probe,
|
||||||
|
.remove = da9052_hwmon_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "da9052-hwmon",
|
.name = "da9052-hwmon",
|
||||||
},
|
},
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
|
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
|
||||||
|
|
||||||
static struct i2c_device_id fts_id[] = {
|
static const struct i2c_device_id fts_id[] = {
|
||||||
{ "ftsteutates", 0 },
|
{ "ftsteutates", 0 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
@ -435,6 +435,7 @@ clear_temp_alarm(struct device *dev, struct device_attribute *devattr,
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
data->valid = false;
|
data->valid = false;
|
||||||
|
ret = count;
|
||||||
error:
|
error:
|
||||||
mutex_unlock(&data->update_lock);
|
mutex_unlock(&data->update_lock);
|
||||||
return ret;
|
return ret;
|
||||||
@ -508,6 +509,7 @@ clear_fan_alarm(struct device *dev, struct device_attribute *devattr,
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
data->valid = false;
|
data->valid = false;
|
||||||
|
ret = count;
|
||||||
error:
|
error:
|
||||||
mutex_unlock(&data->update_lock);
|
mutex_unlock(&data->update_lock);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -85,7 +85,7 @@ static umode_t hwmon_dev_name_is_visible(struct kobject *kobj,
|
|||||||
return attr->mode;
|
return attr->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute_group hwmon_dev_attr_group = {
|
static const struct attribute_group hwmon_dev_attr_group = {
|
||||||
.attrs = hwmon_dev_attrs,
|
.attrs = hwmon_dev_attrs,
|
||||||
.is_visible = hwmon_dev_name_is_visible,
|
.is_visible = hwmon_dev_name_is_visible,
|
||||||
};
|
};
|
||||||
@ -135,7 +135,7 @@ static int hwmon_thermal_get_temp(void *data, int *temp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct thermal_zone_of_device_ops hwmon_thermal_ops = {
|
static const struct thermal_zone_of_device_ops hwmon_thermal_ops = {
|
||||||
.get_temp = hwmon_thermal_get_temp,
|
.get_temp = hwmon_thermal_get_temp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -495,7 +495,7 @@ static struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef MODULE
|
#ifdef MODULE
|
||||||
static struct pci_device_id i5k_amb_ids[] = {
|
static const struct pci_device_id i5k_amb_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
|
||||||
{ 0, }
|
{ 0, }
|
||||||
|
@ -497,12 +497,14 @@ static const struct it87_devices it87_devices[] = {
|
|||||||
#define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V)
|
#define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V)
|
||||||
|
|
||||||
struct it87_sio_data {
|
struct it87_sio_data {
|
||||||
|
int sioaddr;
|
||||||
enum chips type;
|
enum chips type;
|
||||||
/* Values read from Super-I/O config space */
|
/* Values read from Super-I/O config space */
|
||||||
u8 revision;
|
u8 revision;
|
||||||
u8 vid_value;
|
u8 vid_value;
|
||||||
u8 beep_pin;
|
u8 beep_pin;
|
||||||
u8 internal; /* Internal sensors can be labeled */
|
u8 internal; /* Internal sensors can be labeled */
|
||||||
|
bool need_in7_reroute;
|
||||||
/* Features skipped based on config or DMI */
|
/* Features skipped based on config or DMI */
|
||||||
u16 skip_in;
|
u16 skip_in;
|
||||||
u8 skip_vid;
|
u8 skip_vid;
|
||||||
@ -517,6 +519,7 @@ struct it87_sio_data {
|
|||||||
*/
|
*/
|
||||||
struct it87_data {
|
struct it87_data {
|
||||||
const struct attribute_group *groups[7];
|
const struct attribute_group *groups[7];
|
||||||
|
int sioaddr;
|
||||||
enum chips type;
|
enum chips type;
|
||||||
u32 features;
|
u32 features;
|
||||||
u8 peci_mask;
|
u8 peci_mask;
|
||||||
@ -532,6 +535,7 @@ struct it87_data {
|
|||||||
u16 in_internal; /* Bitfield, internal sensors (for labels) */
|
u16 in_internal; /* Bitfield, internal sensors (for labels) */
|
||||||
u16 has_in; /* Bitfield, voltage sensors enabled */
|
u16 has_in; /* Bitfield, voltage sensors enabled */
|
||||||
u8 in[NUM_VIN][3]; /* [nr][0]=in, [1]=min, [2]=max */
|
u8 in[NUM_VIN][3]; /* [nr][0]=in, [1]=min, [2]=max */
|
||||||
|
bool need_in7_reroute;
|
||||||
u8 has_fan; /* Bitfield, fans enabled */
|
u8 has_fan; /* Bitfield, fans enabled */
|
||||||
u16 fan[NUM_FAN][2]; /* Register values, [nr][0]=fan, [1]=min */
|
u16 fan[NUM_FAN][2]; /* Register values, [nr][0]=fan, [1]=min */
|
||||||
u8 has_temp; /* Bitfield, temp sensors enabled */
|
u8 has_temp; /* Bitfield, temp sensors enabled */
|
||||||
@ -2487,6 +2491,7 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
|
sio_data->sioaddr = sioaddr;
|
||||||
sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f;
|
sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f;
|
||||||
pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type,
|
pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type,
|
||||||
it87_devices[sio_data->type].suffix,
|
it87_devices[sio_data->type].suffix,
|
||||||
@ -2575,6 +2580,7 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||||||
reg2c |= BIT(1);
|
reg2c |= BIT(1);
|
||||||
superio_outb(sioaddr, IT87_SIO_PINX2_REG,
|
superio_outb(sioaddr, IT87_SIO_PINX2_REG,
|
||||||
reg2c);
|
reg2c);
|
||||||
|
sio_data->need_in7_reroute = true;
|
||||||
pr_notice("Routing internal VCCH5V to in7.\n");
|
pr_notice("Routing internal VCCH5V to in7.\n");
|
||||||
}
|
}
|
||||||
pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
|
pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
|
||||||
@ -2761,13 +2767,13 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||||||
uart6 = sio_data->type == it8782 && (reg & BIT(2));
|
uart6 = sio_data->type == it8782 && (reg & BIT(2));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The IT8720F has no VIN7 pin, so VCCH should always be
|
* The IT8720F has no VIN7 pin, so VCCH5V should always be
|
||||||
* routed internally to VIN7 with an internal divider.
|
* routed internally to VIN7 with an internal divider.
|
||||||
* Curiously, there still is a configuration bit to control
|
* Curiously, there still is a configuration bit to control
|
||||||
* this, which means it can be set incorrectly. And even
|
* this, which means it can be set incorrectly. And even
|
||||||
* more curiously, many boards out there are improperly
|
* more curiously, many boards out there are improperly
|
||||||
* configured, even though the IT8720F datasheet claims
|
* configured, even though the IT8720F datasheet claims
|
||||||
* that the internal routing of VCCH to VIN7 is the default
|
* that the internal routing of VCCH5V to VIN7 is the default
|
||||||
* setting. So we force the internal routing in this case.
|
* setting. So we force the internal routing in this case.
|
||||||
*
|
*
|
||||||
* On IT8782F, VIN7 is multiplexed with one of the UART6 pins.
|
* On IT8782F, VIN7 is multiplexed with one of the UART6 pins.
|
||||||
@ -2777,7 +2783,8 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||||||
if ((sio_data->type == it8720 || uart6) && !(reg & BIT(1))) {
|
if ((sio_data->type == it8720 || uart6) && !(reg & BIT(1))) {
|
||||||
reg |= BIT(1);
|
reg |= BIT(1);
|
||||||
superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg);
|
superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg);
|
||||||
pr_notice("Routing internal VCCH to in7\n");
|
sio_data->need_in7_reroute = true;
|
||||||
|
pr_notice("Routing internal VCCH5V to in7\n");
|
||||||
}
|
}
|
||||||
if (reg & BIT(0))
|
if (reg & BIT(0))
|
||||||
sio_data->internal |= BIT(0);
|
sio_data->internal |= BIT(0);
|
||||||
@ -2828,13 +2835,89 @@ exit:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some chips seem to have default value 0xff for all limit
|
||||||
|
* registers. For low voltage limits it makes no sense and triggers
|
||||||
|
* alarms, so change to 0 instead. For high temperature limits, it
|
||||||
|
* means -1 degree C, which surprisingly doesn't trigger an alarm,
|
||||||
|
* but is still confusing, so change to 127 degrees C.
|
||||||
|
*/
|
||||||
|
static void it87_check_limit_regs(struct it87_data *data)
|
||||||
|
{
|
||||||
|
int i, reg;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_VIN_LIMIT; i++) {
|
||||||
|
reg = it87_read_value(data, IT87_REG_VIN_MIN(i));
|
||||||
|
if (reg == 0xff)
|
||||||
|
it87_write_value(data, IT87_REG_VIN_MIN(i), 0);
|
||||||
|
}
|
||||||
|
for (i = 0; i < NUM_TEMP_LIMIT; i++) {
|
||||||
|
reg = it87_read_value(data, IT87_REG_TEMP_HIGH(i));
|
||||||
|
if (reg == 0xff)
|
||||||
|
it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if voltage monitors are reset manually or by some reason */
|
||||||
|
static void it87_check_voltage_monitors_reset(struct it87_data *data)
|
||||||
|
{
|
||||||
|
int reg;
|
||||||
|
|
||||||
|
reg = it87_read_value(data, IT87_REG_VIN_ENABLE);
|
||||||
|
if ((reg & 0xff) == 0) {
|
||||||
|
/* Enable all voltage monitors */
|
||||||
|
it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if tachometers are reset manually or by some reason */
|
||||||
|
static void it87_check_tachometers_reset(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
|
||||||
|
struct it87_data *data = platform_get_drvdata(pdev);
|
||||||
|
u8 mask, fan_main_ctrl;
|
||||||
|
|
||||||
|
mask = 0x70 & ~(sio_data->skip_fan << 4);
|
||||||
|
fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL);
|
||||||
|
if ((fan_main_ctrl & mask) == 0) {
|
||||||
|
/* Enable all fan tachometers */
|
||||||
|
fan_main_ctrl |= mask;
|
||||||
|
it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
|
||||||
|
fan_main_ctrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set tachometers to 16-bit mode if needed */
|
||||||
|
static void it87_check_tachometers_16bit_mode(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct it87_data *data = platform_get_drvdata(pdev);
|
||||||
|
int reg;
|
||||||
|
|
||||||
|
if (!has_fan16_config(data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
reg = it87_read_value(data, IT87_REG_FAN_16BIT);
|
||||||
|
if (~reg & 0x07 & data->has_fan) {
|
||||||
|
dev_dbg(&pdev->dev,
|
||||||
|
"Setting fan1-3 to 16-bit mode\n");
|
||||||
|
it87_write_value(data, IT87_REG_FAN_16BIT,
|
||||||
|
reg | 0x07);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void it87_start_monitoring(struct it87_data *data)
|
||||||
|
{
|
||||||
|
it87_write_value(data, IT87_REG_CONFIG,
|
||||||
|
(it87_read_value(data, IT87_REG_CONFIG) & 0x3e)
|
||||||
|
| (update_vbat ? 0x41 : 0x01));
|
||||||
|
}
|
||||||
|
|
||||||
/* Called when we have found a new IT87. */
|
/* Called when we have found a new IT87. */
|
||||||
static void it87_init_device(struct platform_device *pdev)
|
static void it87_init_device(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
|
struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
|
||||||
struct it87_data *data = platform_get_drvdata(pdev);
|
struct it87_data *data = platform_get_drvdata(pdev);
|
||||||
int tmp, i;
|
int tmp, i;
|
||||||
u8 mask;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For each PWM channel:
|
* For each PWM channel:
|
||||||
@ -2855,23 +2938,7 @@ static void it87_init_device(struct platform_device *pdev)
|
|||||||
data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */
|
data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
it87_check_limit_regs(data);
|
||||||
* Some chips seem to have default value 0xff for all limit
|
|
||||||
* registers. For low voltage limits it makes no sense and triggers
|
|
||||||
* alarms, so change to 0 instead. For high temperature limits, it
|
|
||||||
* means -1 degree C, which surprisingly doesn't trigger an alarm,
|
|
||||||
* but is still confusing, so change to 127 degrees C.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < NUM_VIN_LIMIT; i++) {
|
|
||||||
tmp = it87_read_value(data, IT87_REG_VIN_MIN(i));
|
|
||||||
if (tmp == 0xff)
|
|
||||||
it87_write_value(data, IT87_REG_VIN_MIN(i), 0);
|
|
||||||
}
|
|
||||||
for (i = 0; i < NUM_TEMP_LIMIT; i++) {
|
|
||||||
tmp = it87_read_value(data, IT87_REG_TEMP_HIGH(i));
|
|
||||||
if (tmp == 0xff)
|
|
||||||
it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Temperature channels are not forcibly enabled, as they can be
|
* Temperature channels are not forcibly enabled, as they can be
|
||||||
@ -2880,38 +2947,19 @@ static void it87_init_device(struct platform_device *pdev)
|
|||||||
* run-time through the temp{1-3}_type sysfs accessors if needed.
|
* run-time through the temp{1-3}_type sysfs accessors if needed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Check if voltage monitors are reset manually or by some reason */
|
it87_check_voltage_monitors_reset(data);
|
||||||
tmp = it87_read_value(data, IT87_REG_VIN_ENABLE);
|
|
||||||
if ((tmp & 0xff) == 0) {
|
it87_check_tachometers_reset(pdev);
|
||||||
/* Enable all voltage monitors */
|
|
||||||
it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if tachometers are reset manually or by some reason */
|
|
||||||
mask = 0x70 & ~(sio_data->skip_fan << 4);
|
|
||||||
data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL);
|
data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL);
|
||||||
if ((data->fan_main_ctrl & mask) == 0) {
|
|
||||||
/* Enable all fan tachometers */
|
|
||||||
data->fan_main_ctrl |= mask;
|
|
||||||
it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
|
|
||||||
data->fan_main_ctrl);
|
|
||||||
}
|
|
||||||
data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
|
data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
|
||||||
|
|
||||||
tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
|
it87_check_tachometers_16bit_mode(pdev);
|
||||||
|
|
||||||
/* Set tachometers to 16-bit mode if needed */
|
|
||||||
if (has_fan16_config(data)) {
|
|
||||||
if (~tmp & 0x07 & data->has_fan) {
|
|
||||||
dev_dbg(&pdev->dev,
|
|
||||||
"Setting fan1-3 to 16-bit mode\n");
|
|
||||||
it87_write_value(data, IT87_REG_FAN_16BIT,
|
|
||||||
tmp | 0x07);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for additional fans */
|
/* Check for additional fans */
|
||||||
if (has_five_fans(data)) {
|
if (has_five_fans(data)) {
|
||||||
|
tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
|
||||||
|
|
||||||
if (tmp & BIT(4))
|
if (tmp & BIT(4))
|
||||||
data->has_fan |= BIT(3); /* fan4 enabled */
|
data->has_fan |= BIT(3); /* fan4 enabled */
|
||||||
if (tmp & BIT(5))
|
if (tmp & BIT(5))
|
||||||
@ -2933,10 +2981,7 @@ static void it87_init_device(struct platform_device *pdev)
|
|||||||
sio_data->skip_pwm |= BIT(5);
|
sio_data->skip_pwm |= BIT(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start monitoring */
|
it87_start_monitoring(data);
|
||||||
it87_write_value(data, IT87_REG_CONFIG,
|
|
||||||
(it87_read_value(data, IT87_REG_CONFIG) & 0x3e)
|
|
||||||
| (update_vbat ? 0x41 : 0x01));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return 1 if and only if the PWM interface is safe to use */
|
/* Return 1 if and only if the PWM interface is safe to use */
|
||||||
@ -2986,8 +3031,6 @@ static int it87_check_pwm(struct device *dev)
|
|||||||
"PWM configuration is too broken to be fixed\n");
|
"PWM configuration is too broken to be fixed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev,
|
|
||||||
"Detected broken BIOS defaults, disabling PWM interface\n");
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (fix_pwm_polarity) {
|
} else if (fix_pwm_polarity) {
|
||||||
dev_info(dev,
|
dev_info(dev,
|
||||||
@ -3020,6 +3063,7 @@ static int it87_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
data->addr = res->start;
|
data->addr = res->start;
|
||||||
|
data->sioaddr = sio_data->sioaddr;
|
||||||
data->type = sio_data->type;
|
data->type = sio_data->type;
|
||||||
data->features = it87_devices[sio_data->type].features;
|
data->features = it87_devices[sio_data->type].features;
|
||||||
data->peci_mask = it87_devices[sio_data->type].peci_mask;
|
data->peci_mask = it87_devices[sio_data->type].peci_mask;
|
||||||
@ -3058,6 +3102,9 @@ static int it87_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* Check PWM configuration */
|
/* Check PWM configuration */
|
||||||
enable_pwm_interface = it87_check_pwm(dev);
|
enable_pwm_interface = it87_check_pwm(dev);
|
||||||
|
if (!enable_pwm_interface)
|
||||||
|
dev_info(dev,
|
||||||
|
"Detected broken BIOS defaults, disabling PWM interface\n");
|
||||||
|
|
||||||
/* Starting with IT8721F, we handle scaling of internal voltages */
|
/* Starting with IT8721F, we handle scaling of internal voltages */
|
||||||
if (has_12mv_adc(data)) {
|
if (has_12mv_adc(data)) {
|
||||||
@ -3085,6 +3132,7 @@ static int it87_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
data->in_internal = sio_data->internal;
|
data->in_internal = sio_data->internal;
|
||||||
|
data->need_in7_reroute = sio_data->need_in7_reroute;
|
||||||
data->has_in = 0x3ff & ~sio_data->skip_in;
|
data->has_in = 0x3ff & ~sio_data->skip_in;
|
||||||
|
|
||||||
if (has_six_temp(data)) {
|
if (has_six_temp(data)) {
|
||||||
@ -3140,9 +3188,71 @@ static int it87_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __maybe_unused it87_resume_sio(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct it87_data *data = dev_get_drvdata(&pdev->dev);
|
||||||
|
int err;
|
||||||
|
int reg2c;
|
||||||
|
|
||||||
|
if (!data->need_in7_reroute)
|
||||||
|
return;
|
||||||
|
|
||||||
|
err = superio_enter(data->sioaddr);
|
||||||
|
if (err) {
|
||||||
|
dev_warn(&pdev->dev,
|
||||||
|
"Unable to enter Super I/O to reroute in7 (%d)",
|
||||||
|
err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
superio_select(data->sioaddr, GPIO);
|
||||||
|
|
||||||
|
reg2c = superio_inb(data->sioaddr, IT87_SIO_PINX2_REG);
|
||||||
|
if (!(reg2c & BIT(1))) {
|
||||||
|
dev_dbg(&pdev->dev,
|
||||||
|
"Routing internal VCCH5V to in7 again");
|
||||||
|
|
||||||
|
reg2c |= BIT(1);
|
||||||
|
superio_outb(data->sioaddr, IT87_SIO_PINX2_REG,
|
||||||
|
reg2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
superio_exit(data->sioaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused it87_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct it87_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
it87_resume_sio(pdev);
|
||||||
|
|
||||||
|
mutex_lock(&data->update_lock);
|
||||||
|
|
||||||
|
it87_check_pwm(dev);
|
||||||
|
it87_check_limit_regs(data);
|
||||||
|
it87_check_voltage_monitors_reset(data);
|
||||||
|
it87_check_tachometers_reset(pdev);
|
||||||
|
it87_check_tachometers_16bit_mode(pdev);
|
||||||
|
|
||||||
|
it87_start_monitoring(data);
|
||||||
|
|
||||||
|
/* force update */
|
||||||
|
data->valid = 0;
|
||||||
|
|
||||||
|
mutex_unlock(&data->update_lock);
|
||||||
|
|
||||||
|
it87_update_device(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume);
|
||||||
|
|
||||||
static struct platform_driver it87_driver = {
|
static struct platform_driver it87_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRVNAME,
|
.name = DRVNAME,
|
||||||
|
.pm = &it87_dev_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = it87_probe,
|
.probe = it87_probe,
|
||||||
};
|
};
|
||||||
|
@ -72,6 +72,8 @@ static const unsigned short normal_i2c[] = {
|
|||||||
#define NXP_MANID 0x1131 /* NXP Semiconductors */
|
#define NXP_MANID 0x1131 /* NXP Semiconductors */
|
||||||
#define ONS_MANID 0x1b09 /* ON Semiconductor */
|
#define ONS_MANID 0x1b09 /* ON Semiconductor */
|
||||||
#define STM_MANID 0x104a /* ST Microelectronics */
|
#define STM_MANID 0x104a /* ST Microelectronics */
|
||||||
|
#define GT_MANID 0x1c68 /* Giantec */
|
||||||
|
#define GT_MANID2 0x132d /* Giantec, 2nd mfg ID */
|
||||||
|
|
||||||
/* Supported chips */
|
/* Supported chips */
|
||||||
|
|
||||||
@ -86,6 +88,13 @@ static const unsigned short normal_i2c[] = {
|
|||||||
#define AT30TSE004_DEVID 0x2200
|
#define AT30TSE004_DEVID 0x2200
|
||||||
#define AT30TSE004_DEVID_MASK 0xffff
|
#define AT30TSE004_DEVID_MASK 0xffff
|
||||||
|
|
||||||
|
/* Giantec */
|
||||||
|
#define GT30TS00_DEVID 0x2200
|
||||||
|
#define GT30TS00_DEVID_MASK 0xff00
|
||||||
|
|
||||||
|
#define GT34TS02_DEVID 0x3300
|
||||||
|
#define GT34TS02_DEVID_MASK 0xff00
|
||||||
|
|
||||||
/* IDT */
|
/* IDT */
|
||||||
#define TSE2004_DEVID 0x2200
|
#define TSE2004_DEVID 0x2200
|
||||||
#define TSE2004_DEVID_MASK 0xff00
|
#define TSE2004_DEVID_MASK 0xff00
|
||||||
@ -130,6 +139,12 @@ static const unsigned short normal_i2c[] = {
|
|||||||
#define CAT6095_DEVID 0x0800 /* Also matches CAT34TS02 */
|
#define CAT6095_DEVID 0x0800 /* Also matches CAT34TS02 */
|
||||||
#define CAT6095_DEVID_MASK 0xffe0
|
#define CAT6095_DEVID_MASK 0xffe0
|
||||||
|
|
||||||
|
#define CAT34TS02C_DEVID 0x0a00
|
||||||
|
#define CAT34TS02C_DEVID_MASK 0xfff0
|
||||||
|
|
||||||
|
#define CAT34TS04_DEVID 0x2200
|
||||||
|
#define CAT34TS04_DEVID_MASK 0xfff0
|
||||||
|
|
||||||
/* ST Microelectronics */
|
/* ST Microelectronics */
|
||||||
#define STTS424_DEVID 0x0101
|
#define STTS424_DEVID 0x0101
|
||||||
#define STTS424_DEVID_MASK 0xffff
|
#define STTS424_DEVID_MASK 0xffff
|
||||||
@ -158,6 +173,8 @@ static struct jc42_chips jc42_chips[] = {
|
|||||||
{ ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
|
{ ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
|
||||||
{ ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK },
|
{ ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK },
|
||||||
{ ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK },
|
{ ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK },
|
||||||
|
{ GT_MANID, GT30TS00_DEVID, GT30TS00_DEVID_MASK },
|
||||||
|
{ GT_MANID2, GT34TS02_DEVID, GT34TS02_DEVID_MASK },
|
||||||
{ IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
|
{ IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
|
||||||
{ IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK },
|
{ IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK },
|
||||||
{ IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK },
|
{ IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK },
|
||||||
@ -170,6 +187,8 @@ static struct jc42_chips jc42_chips[] = {
|
|||||||
{ MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK },
|
{ MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK },
|
||||||
{ NXP_MANID, SE97_DEVID, SE97_DEVID_MASK },
|
{ NXP_MANID, SE97_DEVID, SE97_DEVID_MASK },
|
||||||
{ ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK },
|
{ ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK },
|
||||||
|
{ ONS_MANID, CAT34TS02C_DEVID, CAT34TS02C_DEVID_MASK },
|
||||||
|
{ ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK },
|
||||||
{ NXP_MANID, SE98_DEVID, SE98_DEVID_MASK },
|
{ NXP_MANID, SE98_DEVID, SE98_DEVID_MASK },
|
||||||
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
|
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
|
||||||
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
|
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
|
||||||
|
163
drivers/hwmon/ltq-cputemp.c
Normal file
163
drivers/hwmon/ltq-cputemp.c
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/* Lantiq cpu temperature sensor driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Florian Eckert <fe@dev.tdt.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/hwmon-sysfs.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
|
||||||
|
#include <lantiq_soc.h>
|
||||||
|
|
||||||
|
/* gphy1 configuration register contains cpu temperature */
|
||||||
|
#define CGU_GPHY1_CR 0x0040
|
||||||
|
#define CGU_TEMP_PD BIT(19)
|
||||||
|
|
||||||
|
static void ltq_cputemp_enable(void)
|
||||||
|
{
|
||||||
|
ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) | CGU_TEMP_PD, CGU_GPHY1_CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ltq_cputemp_disable(void *data)
|
||||||
|
{
|
||||||
|
ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) & ~CGU_TEMP_PD, CGU_GPHY1_CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ltq_read(struct device *dev, enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel, long *temp)
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
|
||||||
|
switch (attr) {
|
||||||
|
case hwmon_temp_input:
|
||||||
|
/* get the temperature including one decimal place */
|
||||||
|
value = (ltq_cgu_r32(CGU_GPHY1_CR) >> 9) & 0x01FF;
|
||||||
|
value = value * 5;
|
||||||
|
/* range -38 to +154 °C, register value zero is -38.0 °C */
|
||||||
|
value -= 380;
|
||||||
|
/* scale temp to millidegree */
|
||||||
|
value = value * 100;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
*temp = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static umode_t ltq_is_visible(const void *_data, enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel)
|
||||||
|
{
|
||||||
|
if (type != hwmon_temp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (attr) {
|
||||||
|
case hwmon_temp_input:
|
||||||
|
return 0444;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const u32 ltq_chip_config[] = {
|
||||||
|
HWMON_C_REGISTER_TZ,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_channel_info ltq_chip = {
|
||||||
|
.type = hwmon_chip,
|
||||||
|
.config = ltq_chip_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u32 ltq_temp_config[] = {
|
||||||
|
HWMON_T_INPUT,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_channel_info ltq_temp = {
|
||||||
|
.type = hwmon_temp,
|
||||||
|
.config = ltq_temp_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_channel_info *ltq_info[] = {
|
||||||
|
<q_chip,
|
||||||
|
<q_temp,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_ops ltq_hwmon_ops = {
|
||||||
|
.is_visible = ltq_is_visible,
|
||||||
|
.read = ltq_read,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_chip_info ltq_chip_info = {
|
||||||
|
.ops = <q_hwmon_ops,
|
||||||
|
.info = ltq_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ltq_cputemp_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *hwmon_dev;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* available on vr9 v1.2 SoCs only */
|
||||||
|
if (ltq_soc_type() != SOC_TYPE_VR9_2)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
err = devm_add_action(&pdev->dev, ltq_cputemp_disable, NULL);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ltq_cputemp_enable();
|
||||||
|
|
||||||
|
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
|
||||||
|
"ltq_cputemp",
|
||||||
|
NULL,
|
||||||
|
<q_chip_info,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (IS_ERR(hwmon_dev)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register as hwmon device");
|
||||||
|
return PTR_ERR(hwmon_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct of_device_id ltq_cputemp_match[] = {
|
||||||
|
{ .compatible = "lantiq,cputemp" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, ltq_cputemp_match);
|
||||||
|
|
||||||
|
static struct platform_driver ltq_cputemp_driver = {
|
||||||
|
.probe = ltq_cputemp_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "ltq-cputemp",
|
||||||
|
.of_match_table = ltq_cputemp_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(ltq_cputemp_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Florian Eckert <fe@dev.tdt.de>");
|
||||||
|
MODULE_DESCRIPTION("Lantiq cpu temperature sensor driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -704,7 +704,7 @@ static umode_t nct7802_temp_is_visible(struct kobject *kobj,
|
|||||||
return attr->mode;
|
return attr->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute_group nct7802_temp_group = {
|
static const struct attribute_group nct7802_temp_group = {
|
||||||
.attrs = nct7802_temp_attrs,
|
.attrs = nct7802_temp_attrs,
|
||||||
.is_visible = nct7802_temp_is_visible,
|
.is_visible = nct7802_temp_is_visible,
|
||||||
};
|
};
|
||||||
@ -802,7 +802,7 @@ static umode_t nct7802_in_is_visible(struct kobject *kobj,
|
|||||||
return attr->mode;
|
return attr->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute_group nct7802_in_group = {
|
static const struct attribute_group nct7802_in_group = {
|
||||||
.attrs = nct7802_in_attrs,
|
.attrs = nct7802_in_attrs,
|
||||||
.is_visible = nct7802_in_is_visible,
|
.is_visible = nct7802_in_is_visible,
|
||||||
};
|
};
|
||||||
@ -880,7 +880,7 @@ static umode_t nct7802_fan_is_visible(struct kobject *kobj,
|
|||||||
return attr->mode;
|
return attr->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute_group nct7802_fan_group = {
|
static const struct attribute_group nct7802_fan_group = {
|
||||||
.attrs = nct7802_fan_attrs,
|
.attrs = nct7802_fan_attrs,
|
||||||
.is_visible = nct7802_fan_is_visible,
|
.is_visible = nct7802_fan_is_visible,
|
||||||
};
|
};
|
||||||
@ -898,7 +898,7 @@ static struct attribute *nct7802_pwm_attrs[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct attribute_group nct7802_pwm_group = {
|
static const struct attribute_group nct7802_pwm_group = {
|
||||||
.attrs = nct7802_pwm_attrs,
|
.attrs = nct7802_pwm_attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1011,7 +1011,7 @@ static struct attribute *nct7802_auto_point_attrs[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct attribute_group nct7802_auto_point_group = {
|
static const struct attribute_group nct7802_auto_point_group = {
|
||||||
.attrs = nct7802_auto_point_attrs,
|
.attrs = nct7802_auto_point_attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,6 +37,15 @@ config SENSORS_ADM1275
|
|||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called adm1275.
|
be called adm1275.
|
||||||
|
|
||||||
|
config SENSORS_IBM_CFFPS
|
||||||
|
tristate "IBM Common Form Factor Power Supply"
|
||||||
|
help
|
||||||
|
If you say yes here you get hardware monitoring support for the IBM
|
||||||
|
Common Form Factor power supply.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will
|
||||||
|
be called ibm-cffps.
|
||||||
|
|
||||||
config SENSORS_IR35221
|
config SENSORS_IR35221
|
||||||
tristate "Infineon IR35221"
|
tristate "Infineon IR35221"
|
||||||
default n
|
default n
|
||||||
@ -135,6 +144,15 @@ config SENSORS_TPS40422
|
|||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called tps40422.
|
be called tps40422.
|
||||||
|
|
||||||
|
config SENSORS_TPS53679
|
||||||
|
tristate "TI TPS53679"
|
||||||
|
help
|
||||||
|
If you say yes here you get hardware monitoring support for TI
|
||||||
|
TPS53679.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will
|
||||||
|
be called tps53679.
|
||||||
|
|
||||||
config SENSORS_UCD9000
|
config SENSORS_UCD9000
|
||||||
tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910"
|
tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910"
|
||||||
default n
|
default n
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
obj-$(CONFIG_PMBUS) += pmbus_core.o
|
obj-$(CONFIG_PMBUS) += pmbus_core.o
|
||||||
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
|
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
|
||||||
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
|
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
|
||||||
|
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
|
||||||
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
|
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
|
||||||
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
||||||
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||||
@ -14,6 +15,7 @@ obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
|
|||||||
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
||||||
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
||||||
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
||||||
|
obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
|
||||||
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
||||||
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
||||||
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
|
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
|
||||||
|
151
drivers/hwmon/pmbus/ibm-cffps.c
Normal file
151
drivers/hwmon/pmbus/ibm-cffps.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 IBM Corp.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#include "pmbus.h"
|
||||||
|
|
||||||
|
/* STATUS_MFR_SPECIFIC bits */
|
||||||
|
#define CFFPS_MFR_FAN_FAULT BIT(0)
|
||||||
|
#define CFFPS_MFR_THERMAL_FAULT BIT(1)
|
||||||
|
#define CFFPS_MFR_OV_FAULT BIT(2)
|
||||||
|
#define CFFPS_MFR_UV_FAULT BIT(3)
|
||||||
|
#define CFFPS_MFR_PS_KILL BIT(4)
|
||||||
|
#define CFFPS_MFR_OC_FAULT BIT(5)
|
||||||
|
#define CFFPS_MFR_VAUX_FAULT BIT(6)
|
||||||
|
#define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7)
|
||||||
|
|
||||||
|
static int ibm_cffps_read_byte_data(struct i2c_client *client, int page,
|
||||||
|
int reg)
|
||||||
|
{
|
||||||
|
int rc, mfr;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_STATUS_VOUT:
|
||||||
|
case PMBUS_STATUS_IOUT:
|
||||||
|
case PMBUS_STATUS_TEMPERATURE:
|
||||||
|
case PMBUS_STATUS_FAN_12:
|
||||||
|
rc = pmbus_read_byte_data(client, page, reg);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
mfr = pmbus_read_byte_data(client, page,
|
||||||
|
PMBUS_STATUS_MFR_SPECIFIC);
|
||||||
|
if (mfr < 0)
|
||||||
|
/*
|
||||||
|
* Return the status register instead of an error,
|
||||||
|
* since we successfully read status.
|
||||||
|
*/
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Add MFR_SPECIFIC bits to the standard pmbus status regs. */
|
||||||
|
if (reg == PMBUS_STATUS_FAN_12) {
|
||||||
|
if (mfr & CFFPS_MFR_FAN_FAULT)
|
||||||
|
rc |= PB_FAN_FAN1_FAULT;
|
||||||
|
} else if (reg == PMBUS_STATUS_TEMPERATURE) {
|
||||||
|
if (mfr & CFFPS_MFR_THERMAL_FAULT)
|
||||||
|
rc |= PB_TEMP_OT_FAULT;
|
||||||
|
} else if (reg == PMBUS_STATUS_VOUT) {
|
||||||
|
if (mfr & (CFFPS_MFR_OV_FAULT | CFFPS_MFR_VAUX_FAULT))
|
||||||
|
rc |= PB_VOLTAGE_OV_FAULT;
|
||||||
|
if (mfr & CFFPS_MFR_UV_FAULT)
|
||||||
|
rc |= PB_VOLTAGE_UV_FAULT;
|
||||||
|
} else if (reg == PMBUS_STATUS_IOUT) {
|
||||||
|
if (mfr & CFFPS_MFR_OC_FAULT)
|
||||||
|
rc |= PB_IOUT_OC_FAULT;
|
||||||
|
if (mfr & CFFPS_MFR_CURRENT_SHARE_WARNING)
|
||||||
|
rc |= PB_CURRENT_SHARE_FAULT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rc = -ENODATA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ibm_cffps_read_word_data(struct i2c_client *client, int page,
|
||||||
|
int reg)
|
||||||
|
{
|
||||||
|
int rc, mfr;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_STATUS_WORD:
|
||||||
|
rc = pmbus_read_word_data(client, page, reg);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
mfr = pmbus_read_byte_data(client, page,
|
||||||
|
PMBUS_STATUS_MFR_SPECIFIC);
|
||||||
|
if (mfr < 0)
|
||||||
|
/*
|
||||||
|
* Return the status register instead of an error,
|
||||||
|
* since we successfully read status.
|
||||||
|
*/
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (mfr & CFFPS_MFR_PS_KILL)
|
||||||
|
rc |= PB_STATUS_OFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rc = -ENODATA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pmbus_driver_info ibm_cffps_info = {
|
||||||
|
.pages = 1,
|
||||||
|
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
|
||||||
|
PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
|
||||||
|
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
|
||||||
|
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
|
||||||
|
PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12,
|
||||||
|
.read_byte_data = ibm_cffps_read_byte_data,
|
||||||
|
.read_word_data = ibm_cffps_read_word_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ibm_cffps_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
return pmbus_do_probe(client, id, &ibm_cffps_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id ibm_cffps_id[] = {
|
||||||
|
{ "ibm_cffps1", 1 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
|
||||||
|
|
||||||
|
static const struct of_device_id ibm_cffps_of_match[] = {
|
||||||
|
{ .compatible = "ibm,cffps1" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
|
||||||
|
|
||||||
|
static struct i2c_driver ibm_cffps_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "ibm-cffps",
|
||||||
|
.of_match_table = ibm_cffps_of_match,
|
||||||
|
},
|
||||||
|
.probe = ibm_cffps_probe,
|
||||||
|
.remove = pmbus_do_remove,
|
||||||
|
.id_table = ibm_cffps_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(ibm_cffps_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Eddie James");
|
||||||
|
MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -28,7 +28,7 @@
|
|||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include "pmbus.h"
|
#include "pmbus.h"
|
||||||
|
|
||||||
enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 };
|
enum chips { lm25056, lm25063, lm25066, lm5064, lm5066, lm5066i };
|
||||||
|
|
||||||
#define LM25066_READ_VAUX 0xd0
|
#define LM25066_READ_VAUX 0xd0
|
||||||
#define LM25066_MFR_READ_IIN 0xd1
|
#define LM25066_MFR_READ_IIN 0xd1
|
||||||
@ -65,7 +65,7 @@ struct __coeff {
|
|||||||
#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES)
|
#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES)
|
||||||
#define PSC_POWER_L (PSC_NUM_CLASSES + 1)
|
#define PSC_POWER_L (PSC_NUM_CLASSES + 1)
|
||||||
|
|
||||||
static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = {
|
static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = {
|
||||||
[lm25056] = {
|
[lm25056] = {
|
||||||
[PSC_VOLTAGE_IN] = {
|
[PSC_VOLTAGE_IN] = {
|
||||||
.m = 16296,
|
.m = 16296,
|
||||||
@ -210,6 +210,41 @@ static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = {
|
|||||||
.m = 16,
|
.m = 16,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[lm5066i] = {
|
||||||
|
[PSC_VOLTAGE_IN] = {
|
||||||
|
.m = 4617,
|
||||||
|
.b = -140,
|
||||||
|
.R = -2,
|
||||||
|
},
|
||||||
|
[PSC_VOLTAGE_OUT] = {
|
||||||
|
.m = 4602,
|
||||||
|
.b = 500,
|
||||||
|
.R = -2,
|
||||||
|
},
|
||||||
|
[PSC_CURRENT_IN] = {
|
||||||
|
.m = 15076,
|
||||||
|
.b = -504,
|
||||||
|
.R = -2,
|
||||||
|
},
|
||||||
|
[PSC_CURRENT_IN_L] = {
|
||||||
|
.m = 7645,
|
||||||
|
.b = 100,
|
||||||
|
.R = -2,
|
||||||
|
},
|
||||||
|
[PSC_POWER] = {
|
||||||
|
.m = 1701,
|
||||||
|
.b = -4000,
|
||||||
|
.R = -3,
|
||||||
|
},
|
||||||
|
[PSC_POWER_L] = {
|
||||||
|
.m = 861,
|
||||||
|
.b = -965,
|
||||||
|
.R = -3,
|
||||||
|
},
|
||||||
|
[PSC_TEMPERATURE] = {
|
||||||
|
.m = 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lm25066_data {
|
struct lm25066_data {
|
||||||
@ -250,6 +285,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
|
|||||||
ret = DIV_ROUND_CLOSEST(ret * 70, 453);
|
ret = DIV_ROUND_CLOSEST(ret * 70, 453);
|
||||||
break;
|
break;
|
||||||
case lm5066:
|
case lm5066:
|
||||||
|
case lm5066i:
|
||||||
/* VIN: 2.18 mV VAUX: 725 uV LSB */
|
/* VIN: 2.18 mV VAUX: 725 uV LSB */
|
||||||
ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
|
ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
|
||||||
break;
|
break;
|
||||||
@ -488,16 +524,18 @@ static int lm25066_probe(struct i2c_client *client,
|
|||||||
info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m;
|
info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m;
|
||||||
info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b;
|
info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b;
|
||||||
info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R;
|
info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R;
|
||||||
info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b;
|
|
||||||
info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R;
|
info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R;
|
||||||
info->b[PSC_POWER] = coeff[PSC_POWER].b;
|
|
||||||
info->R[PSC_POWER] = coeff[PSC_POWER].R;
|
info->R[PSC_POWER] = coeff[PSC_POWER].R;
|
||||||
if (config & LM25066_DEV_SETUP_CL) {
|
if (config & LM25066_DEV_SETUP_CL) {
|
||||||
info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m;
|
info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m;
|
||||||
|
info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].b;
|
||||||
info->m[PSC_POWER] = coeff[PSC_POWER_L].m;
|
info->m[PSC_POWER] = coeff[PSC_POWER_L].m;
|
||||||
|
info->b[PSC_POWER] = coeff[PSC_POWER_L].b;
|
||||||
} else {
|
} else {
|
||||||
info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m;
|
info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m;
|
||||||
|
info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b;
|
||||||
info->m[PSC_POWER] = coeff[PSC_POWER].m;
|
info->m[PSC_POWER] = coeff[PSC_POWER].m;
|
||||||
|
info->b[PSC_POWER] = coeff[PSC_POWER].b;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pmbus_do_probe(client, id, info);
|
return pmbus_do_probe(client, id, info);
|
||||||
@ -509,6 +547,7 @@ static const struct i2c_device_id lm25066_id[] = {
|
|||||||
{"lm25066", lm25066},
|
{"lm25066", lm25066},
|
||||||
{"lm5064", lm5064},
|
{"lm5064", lm5064},
|
||||||
{"lm5066", lm5066},
|
{"lm5066", lm5066},
|
||||||
|
{"lm5066i", lm5066i},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ enum pmbus_sensor_classes {
|
|||||||
#define PMBUS_HAVE_STATUS_VMON BIT(19)
|
#define PMBUS_HAVE_STATUS_VMON BIT(19)
|
||||||
|
|
||||||
enum pmbus_data_format { linear = 0, direct, vid };
|
enum pmbus_data_format { linear = 0, direct, vid };
|
||||||
enum vrm_version { vr11 = 0, vr12 };
|
enum vrm_version { vr11 = 0, vr12, vr13 };
|
||||||
|
|
||||||
struct pmbus_driver_info {
|
struct pmbus_driver_info {
|
||||||
int pages; /* Total number of pages */
|
int pages; /* Total number of pages */
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -101,6 +102,7 @@ struct pmbus_data {
|
|||||||
int num_attributes;
|
int num_attributes;
|
||||||
struct attribute_group group;
|
struct attribute_group group;
|
||||||
const struct attribute_group *groups[2];
|
const struct attribute_group *groups[2];
|
||||||
|
struct dentry *debugfs; /* debugfs device directory */
|
||||||
|
|
||||||
struct pmbus_sensor *sensors;
|
struct pmbus_sensor *sensors;
|
||||||
|
|
||||||
@ -112,12 +114,20 @@ struct pmbus_data {
|
|||||||
* A single status register covers multiple attributes,
|
* A single status register covers multiple attributes,
|
||||||
* so we keep them all together.
|
* so we keep them all together.
|
||||||
*/
|
*/
|
||||||
u8 status[PB_NUM_STATUS_REG];
|
u16 status[PB_NUM_STATUS_REG];
|
||||||
u8 status_register;
|
|
||||||
|
bool has_status_word; /* device uses STATUS_WORD register */
|
||||||
|
int (*read_status)(struct i2c_client *client, int page);
|
||||||
|
|
||||||
u8 currpage;
|
u8 currpage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pmbus_debugfs_entry {
|
||||||
|
struct i2c_client *client;
|
||||||
|
u8 page;
|
||||||
|
u8 reg;
|
||||||
|
};
|
||||||
|
|
||||||
void pmbus_clear_cache(struct i2c_client *client)
|
void pmbus_clear_cache(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||||
@ -324,7 +334,7 @@ static int pmbus_check_status_cml(struct i2c_client *client)
|
|||||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||||
int status, status2;
|
int status, status2;
|
||||||
|
|
||||||
status = _pmbus_read_byte_data(client, -1, data->status_register);
|
status = data->read_status(client, -1);
|
||||||
if (status < 0 || (status & PB_STATUS_CML)) {
|
if (status < 0 || (status & PB_STATUS_CML)) {
|
||||||
status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
|
status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
|
||||||
if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))
|
if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))
|
||||||
@ -348,6 +358,23 @@ static bool pmbus_check_register(struct i2c_client *client,
|
|||||||
return rv >= 0;
|
return rv >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool pmbus_check_status_register(struct i2c_client *client, int page)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
status = data->read_status(client, page);
|
||||||
|
if (status >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK) &&
|
||||||
|
(status & PB_STATUS_CML)) {
|
||||||
|
status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
|
||||||
|
if (status < 0 || (status & PB_CML_FAULT_INVALID_COMMAND))
|
||||||
|
status = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmbus_clear_fault_page(client, -1);
|
||||||
|
return status >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
|
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
|
||||||
{
|
{
|
||||||
return pmbus_check_register(client, _pmbus_read_byte_data, page, reg);
|
return pmbus_check_register(client, _pmbus_read_byte_data, page, reg);
|
||||||
@ -394,8 +421,7 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
|
|||||||
|
|
||||||
for (i = 0; i < info->pages; i++) {
|
for (i = 0; i < info->pages; i++) {
|
||||||
data->status[PB_STATUS_BASE + i]
|
data->status[PB_STATUS_BASE + i]
|
||||||
= _pmbus_read_byte_data(client, i,
|
= data->read_status(client, i);
|
||||||
data->status_register);
|
|
||||||
for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) {
|
for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) {
|
||||||
struct _pmbus_status *s = &pmbus_status[j];
|
struct _pmbus_status *s = &pmbus_status[j];
|
||||||
|
|
||||||
@ -531,6 +557,10 @@ static long pmbus_reg2data_vid(struct pmbus_data *data,
|
|||||||
if (val >= 0x01)
|
if (val >= 0x01)
|
||||||
rv = 250 + (val - 1) * 5;
|
rv = 250 + (val - 1) * 5;
|
||||||
break;
|
break;
|
||||||
|
case vr13:
|
||||||
|
if (val >= 0x01)
|
||||||
|
rv = 500 + (val - 1) * 10;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@ -716,10 +746,10 @@ static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b,
|
|||||||
{
|
{
|
||||||
struct pmbus_sensor *s1 = b->s1;
|
struct pmbus_sensor *s1 = b->s1;
|
||||||
struct pmbus_sensor *s2 = b->s2;
|
struct pmbus_sensor *s2 = b->s2;
|
||||||
u16 reg = (index >> 8) & 0xffff;
|
u16 reg = (index >> 16) & 0xffff;
|
||||||
u8 mask = index & 0xff;
|
u16 mask = index & 0xffff;
|
||||||
int ret, status;
|
int ret, status;
|
||||||
u8 regval;
|
u16 regval;
|
||||||
|
|
||||||
status = data->status[reg];
|
status = data->status[reg];
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
@ -860,7 +890,7 @@ static int pmbus_add_boolean(struct pmbus_data *data,
|
|||||||
const char *name, const char *type, int seq,
|
const char *name, const char *type, int seq,
|
||||||
struct pmbus_sensor *s1,
|
struct pmbus_sensor *s1,
|
||||||
struct pmbus_sensor *s2,
|
struct pmbus_sensor *s2,
|
||||||
u16 reg, u8 mask)
|
u16 reg, u16 mask)
|
||||||
{
|
{
|
||||||
struct pmbus_boolean *boolean;
|
struct pmbus_boolean *boolean;
|
||||||
struct sensor_device_attribute *a;
|
struct sensor_device_attribute *a;
|
||||||
@ -876,7 +906,7 @@ static int pmbus_add_boolean(struct pmbus_data *data,
|
|||||||
boolean->s1 = s1;
|
boolean->s1 = s1;
|
||||||
boolean->s2 = s2;
|
boolean->s2 = s2;
|
||||||
pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL,
|
pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL,
|
||||||
(reg << 8) | mask);
|
(reg << 16) | mask);
|
||||||
|
|
||||||
return pmbus_add_attribute(data, &a->dev_attr.attr);
|
return pmbus_add_attribute(data, &a->dev_attr.attr);
|
||||||
}
|
}
|
||||||
@ -962,7 +992,7 @@ struct pmbus_limit_attr {
|
|||||||
*/
|
*/
|
||||||
struct pmbus_sensor_attr {
|
struct pmbus_sensor_attr {
|
||||||
u16 reg; /* sensor register */
|
u16 reg; /* sensor register */
|
||||||
u8 gbit; /* generic status bit */
|
u16 gbit; /* generic status bit */
|
||||||
u8 nlimit; /* # of limit registers */
|
u8 nlimit; /* # of limit registers */
|
||||||
enum pmbus_sensor_classes class;/* sensor class */
|
enum pmbus_sensor_classes class;/* sensor class */
|
||||||
const char *label; /* sensor label */
|
const char *label; /* sensor label */
|
||||||
@ -1028,6 +1058,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
|
|||||||
const struct pmbus_sensor_attr *attr)
|
const struct pmbus_sensor_attr *attr)
|
||||||
{
|
{
|
||||||
struct pmbus_sensor *base;
|
struct pmbus_sensor *base;
|
||||||
|
bool upper = !!(attr->gbit & 0xff00); /* need to check STATUS_WORD */
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (attr->label) {
|
if (attr->label) {
|
||||||
@ -1048,11 +1079,12 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
|
|||||||
/*
|
/*
|
||||||
* Add generic alarm attribute only if there are no individual
|
* Add generic alarm attribute only if there are no individual
|
||||||
* alarm attributes, if there is a global alarm bit, and if
|
* alarm attributes, if there is a global alarm bit, and if
|
||||||
* the generic status register for this page is accessible.
|
* the generic status register (word or byte, depending on
|
||||||
|
* which global bit is set) for this page is accessible.
|
||||||
*/
|
*/
|
||||||
if (!ret && attr->gbit &&
|
if (!ret && attr->gbit &&
|
||||||
pmbus_check_byte_register(client, page,
|
(!upper || (upper && data->has_status_word)) &&
|
||||||
data->status_register)) {
|
pmbus_check_status_register(client, page)) {
|
||||||
ret = pmbus_add_boolean(data, name, "alarm", index,
|
ret = pmbus_add_boolean(data, name, "alarm", index,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
PB_STATUS_BASE + page,
|
PB_STATUS_BASE + page,
|
||||||
@ -1308,6 +1340,7 @@ static const struct pmbus_sensor_attr current_attributes[] = {
|
|||||||
.func = PMBUS_HAVE_IIN,
|
.func = PMBUS_HAVE_IIN,
|
||||||
.sfunc = PMBUS_HAVE_STATUS_INPUT,
|
.sfunc = PMBUS_HAVE_STATUS_INPUT,
|
||||||
.sbase = PB_STATUS_INPUT_BASE,
|
.sbase = PB_STATUS_INPUT_BASE,
|
||||||
|
.gbit = PB_STATUS_INPUT,
|
||||||
.limit = iin_limit_attrs,
|
.limit = iin_limit_attrs,
|
||||||
.nlimit = ARRAY_SIZE(iin_limit_attrs),
|
.nlimit = ARRAY_SIZE(iin_limit_attrs),
|
||||||
}, {
|
}, {
|
||||||
@ -1392,6 +1425,7 @@ static const struct pmbus_sensor_attr power_attributes[] = {
|
|||||||
.func = PMBUS_HAVE_PIN,
|
.func = PMBUS_HAVE_PIN,
|
||||||
.sfunc = PMBUS_HAVE_STATUS_INPUT,
|
.sfunc = PMBUS_HAVE_STATUS_INPUT,
|
||||||
.sbase = PB_STATUS_INPUT_BASE,
|
.sbase = PB_STATUS_INPUT_BASE,
|
||||||
|
.gbit = PB_STATUS_INPUT,
|
||||||
.limit = pin_limit_attrs,
|
.limit = pin_limit_attrs,
|
||||||
.nlimit = ARRAY_SIZE(pin_limit_attrs),
|
.nlimit = ARRAY_SIZE(pin_limit_attrs),
|
||||||
}, {
|
}, {
|
||||||
@ -1729,6 +1763,16 @@ static int pmbus_identify_common(struct i2c_client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pmbus_read_status_byte(struct i2c_client *client, int page)
|
||||||
|
{
|
||||||
|
return _pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pmbus_read_status_word(struct i2c_client *client, int page)
|
||||||
|
{
|
||||||
|
return _pmbus_read_word_data(client, page, PMBUS_STATUS_WORD);
|
||||||
|
}
|
||||||
|
|
||||||
static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
||||||
struct pmbus_driver_info *info)
|
struct pmbus_driver_info *info)
|
||||||
{
|
{
|
||||||
@ -1736,19 +1780,21 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
|||||||
int page, ret;
|
int page, ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some PMBus chips don't support PMBUS_STATUS_BYTE, so try
|
* Some PMBus chips don't support PMBUS_STATUS_WORD, so try
|
||||||
* to use PMBUS_STATUS_WORD instead if that is the case.
|
* to use PMBUS_STATUS_BYTE instead if that is the case.
|
||||||
* Bail out if both registers are not supported.
|
* Bail out if both registers are not supported.
|
||||||
*/
|
*/
|
||||||
data->status_register = PMBUS_STATUS_BYTE;
|
data->read_status = pmbus_read_status_word;
|
||||||
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
|
ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
|
||||||
if (ret < 0 || ret == 0xff) {
|
if (ret < 0 || ret == 0xffff) {
|
||||||
data->status_register = PMBUS_STATUS_WORD;
|
data->read_status = pmbus_read_status_byte;
|
||||||
ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
|
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
|
||||||
if (ret < 0 || ret == 0xffff) {
|
if (ret < 0 || ret == 0xff) {
|
||||||
dev_err(dev, "PMBus status register not found\n");
|
dev_err(dev, "PMBus status register not found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
data->has_status_word = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable PEC if the controller supports it */
|
/* Enable PEC if the controller supports it */
|
||||||
@ -1859,6 +1905,184 @@ static int pmbus_regulator_register(struct pmbus_data *data)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||||
|
static int pmbus_debugfs_get(void *data, u64 *val)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct pmbus_debugfs_entry *entry = data;
|
||||||
|
|
||||||
|
rc = _pmbus_read_byte_data(entry->client, entry->page, entry->reg);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
*val = rc;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL,
|
||||||
|
"0x%02llx\n");
|
||||||
|
|
||||||
|
static int pmbus_debugfs_get_status(void *data, u64 *val)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct pmbus_debugfs_entry *entry = data;
|
||||||
|
struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
|
||||||
|
|
||||||
|
rc = pdata->read_status(entry->client, entry->page);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
*val = rc;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
|
||||||
|
NULL, "0x%04llx\n");
|
||||||
|
|
||||||
|
static int pmbus_init_debugfs(struct i2c_client *client,
|
||||||
|
struct pmbus_data *data)
|
||||||
|
{
|
||||||
|
int i, idx = 0;
|
||||||
|
char name[PMBUS_NAME_SIZE];
|
||||||
|
struct pmbus_debugfs_entry *entries;
|
||||||
|
|
||||||
|
if (!pmbus_debugfs_dir)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the debugfs directory for this device. Use the hwmon device
|
||||||
|
* name to avoid conflicts (hwmon numbers are globally unique).
|
||||||
|
*/
|
||||||
|
data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev),
|
||||||
|
pmbus_debugfs_dir);
|
||||||
|
if (IS_ERR_OR_NULL(data->debugfs)) {
|
||||||
|
data->debugfs = NULL;
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate the max possible entries we need. */
|
||||||
|
entries = devm_kzalloc(data->dev,
|
||||||
|
sizeof(*entries) * (data->info->pages * 10),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!entries)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < data->info->pages; ++i) {
|
||||||
|
/* Check accessibility of status register if it's not page 0 */
|
||||||
|
if (!i || pmbus_check_status_register(client, i)) {
|
||||||
|
/* No need to set reg as we have special read op. */
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_VOUT;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_IOUT;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_INPUT;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_TEMPERATURE;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_CML;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_OTHER;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pmbus_check_byte_register(client, i,
|
||||||
|
PMBUS_STATUS_MFR_SPECIFIC)) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_FAN_12;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) {
|
||||||
|
entries[idx].client = client;
|
||||||
|
entries[idx].page = i;
|
||||||
|
entries[idx].reg = PMBUS_STATUS_FAN_34;
|
||||||
|
scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i);
|
||||||
|
debugfs_create_file(name, 0444, data->debugfs,
|
||||||
|
&entries[idx++],
|
||||||
|
&pmbus_debugfs_ops);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int pmbus_init_debugfs(struct i2c_client *client,
|
||||||
|
struct pmbus_data *data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
|
||||||
|
|
||||||
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
|
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
|
||||||
struct pmbus_driver_info *info)
|
struct pmbus_driver_info *info)
|
||||||
{
|
{
|
||||||
@ -1918,6 +2142,10 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out_unregister;
|
goto out_unregister;
|
||||||
|
|
||||||
|
ret = pmbus_init_debugfs(client, data);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to register debugfs\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_unregister:
|
out_unregister:
|
||||||
@ -1931,12 +2159,32 @@ EXPORT_SYMBOL_GPL(pmbus_do_probe);
|
|||||||
int pmbus_do_remove(struct i2c_client *client)
|
int pmbus_do_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
debugfs_remove_recursive(data->debugfs);
|
||||||
|
|
||||||
hwmon_device_unregister(data->hwmon_dev);
|
hwmon_device_unregister(data->hwmon_dev);
|
||||||
kfree(data->group.attrs);
|
kfree(data->group.attrs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pmbus_do_remove);
|
EXPORT_SYMBOL_GPL(pmbus_do_remove);
|
||||||
|
|
||||||
|
static int __init pmbus_core_init(void)
|
||||||
|
{
|
||||||
|
pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL);
|
||||||
|
if (IS_ERR(pmbus_debugfs_dir))
|
||||||
|
pmbus_debugfs_dir = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit pmbus_core_exit(void)
|
||||||
|
{
|
||||||
|
debugfs_remove_recursive(pmbus_debugfs_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(pmbus_core_init);
|
||||||
|
module_exit(pmbus_core_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Guenter Roeck");
|
MODULE_AUTHOR("Guenter Roeck");
|
||||||
MODULE_DESCRIPTION("PMBus core driver");
|
MODULE_DESCRIPTION("PMBus core driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
113
drivers/hwmon/pmbus/tps53679.c
Normal file
113
drivers/hwmon/pmbus/tps53679.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Hardware monitoring driver for Texas Instruments TPS53679
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
|
||||||
|
* Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include "pmbus.h"
|
||||||
|
|
||||||
|
#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */
|
||||||
|
#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */
|
||||||
|
#define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */
|
||||||
|
#define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */
|
||||||
|
#define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */
|
||||||
|
#define TPS53679_PAGE_NUM 2
|
||||||
|
|
||||||
|
static int tps53679_identify(struct i2c_client *client,
|
||||||
|
struct pmbus_driver_info *info)
|
||||||
|
{
|
||||||
|
u8 vout_params;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Read the register with VOUT scaling value.*/
|
||||||
|
ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
vout_params = ret & GENMASK(4, 0);
|
||||||
|
|
||||||
|
switch (vout_params) {
|
||||||
|
case TPS53679_PROT_VR13_10MV:
|
||||||
|
case TPS53679_PROT_VR12_5_10MV:
|
||||||
|
info->vrm_version = vr13;
|
||||||
|
break;
|
||||||
|
case TPS53679_PROT_VR13_5MV:
|
||||||
|
case TPS53679_PROT_VR12_5MV:
|
||||||
|
case TPS53679_PROT_IMVP8_5MV:
|
||||||
|
info->vrm_version = vr12;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pmbus_driver_info tps53679_info = {
|
||||||
|
.pages = TPS53679_PAGE_NUM,
|
||||||
|
.format[PSC_VOLTAGE_IN] = linear,
|
||||||
|
.format[PSC_VOLTAGE_OUT] = vid,
|
||||||
|
.format[PSC_TEMPERATURE] = linear,
|
||||||
|
.format[PSC_CURRENT_OUT] = linear,
|
||||||
|
.format[PSC_POWER] = linear,
|
||||||
|
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
|
||||||
|
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||||
|
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
|
||||||
|
PMBUS_HAVE_POUT,
|
||||||
|
.func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
|
||||||
|
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||||
|
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
|
||||||
|
PMBUS_HAVE_POUT,
|
||||||
|
.identify = tps53679_identify,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tps53679_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
return pmbus_do_probe(client, id, &tps53679_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id tps53679_id[] = {
|
||||||
|
{"tps53679", 0},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(i2c, tps53679_id);
|
||||||
|
|
||||||
|
static const struct of_device_id tps53679_of_match[] = {
|
||||||
|
{.compatible = "ti,tps53679"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, tps53679_of_match);
|
||||||
|
|
||||||
|
static struct i2c_driver tps53679_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "tps53679",
|
||||||
|
.of_match_table = of_match_ptr(tps53679_of_match),
|
||||||
|
},
|
||||||
|
.probe = tps53679_probe,
|
||||||
|
.remove = pmbus_do_remove,
|
||||||
|
.id_table = tps53679_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(tps53679_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
|
||||||
|
MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -120,7 +120,7 @@ scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
|
|||||||
return sprintf(buf, "%s\n", sensor->info.name);
|
return sprintf(buf, "%s\n", sensor->info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct thermal_zone_of_device_ops scpi_sensor_ops = {
|
static const struct thermal_zone_of_device_ops scpi_sensor_ops = {
|
||||||
.get_temp = scpi_read_temp,
|
.get_temp = scpi_read_temp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -718,6 +718,10 @@ static int stts751_read_chip_config(struct stts751_priv *priv)
|
|||||||
ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_RATE);
|
ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_RATE);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
if (ret >= ARRAY_SIZE(stts751_intervals)) {
|
||||||
|
dev_err(priv->dev, "Unrecognized conversion rate 0x%x\n", ret);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
priv->interval = ret;
|
priv->interval = ret;
|
||||||
|
|
||||||
ret = stts751_read_reg16(priv, &priv->event_max,
|
ret = stts751_read_reg16(priv, &priv->event_max,
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/property.h>
|
||||||
|
|
||||||
#include <linux/mfd/da9052/da9052.h>
|
#include <linux/mfd/da9052/da9052.h>
|
||||||
#include <linux/mfd/da9052/pdata.h>
|
#include <linux/mfd/da9052/pdata.h>
|
||||||
@ -518,9 +519,6 @@ static const struct mfd_cell da9052_subdev_info[] = {
|
|||||||
{
|
{
|
||||||
.name = "da9052-wled3",
|
.name = "da9052-wled3",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.name = "da9052-tsi",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.name = "da9052-bat",
|
.name = "da9052-bat",
|
||||||
},
|
},
|
||||||
@ -529,6 +527,10 @@ static const struct mfd_cell da9052_subdev_info[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct mfd_cell da9052_tsi_subdev_info[] = {
|
||||||
|
{ .name = "da9052-tsi" },
|
||||||
|
};
|
||||||
|
|
||||||
const struct regmap_config da9052_regmap_config = {
|
const struct regmap_config da9052_regmap_config = {
|
||||||
.reg_bits = 8,
|
.reg_bits = 8,
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
@ -619,9 +621,27 @@ int da9052_device_init(struct da9052 *da9052, u8 chip_id)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if touchscreen pins are used are analogue input instead
|
||||||
|
* of having a touchscreen connected to them. The analogue input
|
||||||
|
* functionality will be provided by hwmon driver (if enabled).
|
||||||
|
*/
|
||||||
|
if (!device_property_read_bool(da9052->dev, "dlg,tsi-as-adc")) {
|
||||||
|
ret = mfd_add_devices(da9052->dev, PLATFORM_DEVID_AUTO,
|
||||||
|
da9052_tsi_subdev_info,
|
||||||
|
ARRAY_SIZE(da9052_tsi_subdev_info),
|
||||||
|
NULL, 0, NULL);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(da9052->dev, "failed to add TSI subdev: %d\n",
|
||||||
|
ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
mfd_remove_devices(da9052->dev);
|
||||||
da9052_irq_exit(da9052);
|
da9052_irq_exit(da9052);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -45,6 +45,12 @@
|
|||||||
#define DA9052_ADC_TJUNC 8
|
#define DA9052_ADC_TJUNC 8
|
||||||
#define DA9052_ADC_VBBAT 9
|
#define DA9052_ADC_VBBAT 9
|
||||||
|
|
||||||
|
/* TSI channel has its own 4 channel mux */
|
||||||
|
#define DA9052_ADC_TSI_XP 70
|
||||||
|
#define DA9052_ADC_TSI_XN 71
|
||||||
|
#define DA9052_ADC_TSI_YP 72
|
||||||
|
#define DA9052_ADC_TSI_YN 73
|
||||||
|
|
||||||
#define DA9052_IRQ_DCIN 0
|
#define DA9052_IRQ_DCIN 0
|
||||||
#define DA9052_IRQ_VBUS 1
|
#define DA9052_IRQ_VBUS 1
|
||||||
#define DA9052_IRQ_DCINREM 2
|
#define DA9052_IRQ_DCINREM 2
|
||||||
|
@ -690,7 +690,10 @@
|
|||||||
/* TSI CONTROL REGISTER B BITS */
|
/* TSI CONTROL REGISTER B BITS */
|
||||||
#define DA9052_TSICONTB_ADCREF 0X80
|
#define DA9052_TSICONTB_ADCREF 0X80
|
||||||
#define DA9052_TSICONTB_TSIMAN 0X40
|
#define DA9052_TSICONTB_TSIMAN 0X40
|
||||||
#define DA9052_TSICONTB_TSIMUX 0X30
|
#define DA9052_TSICONTB_TSIMUX_XP 0X00
|
||||||
|
#define DA9052_TSICONTB_TSIMUX_YP 0X10
|
||||||
|
#define DA9052_TSICONTB_TSIMUX_XN 0X20
|
||||||
|
#define DA9052_TSICONTB_TSIMUX_YN 0X30
|
||||||
#define DA9052_TSICONTB_TSISEL3 0X08
|
#define DA9052_TSICONTB_TSISEL3 0X08
|
||||||
#define DA9052_TSICONTB_TSISEL2 0X04
|
#define DA9052_TSICONTB_TSISEL2 0X04
|
||||||
#define DA9052_TSICONTB_TSISEL1 0X02
|
#define DA9052_TSICONTB_TSISEL1 0X02
|
||||||
@ -705,8 +708,14 @@
|
|||||||
/* TSI CO-ORDINATE LSB RESULT REGISTER BITS */
|
/* TSI CO-ORDINATE LSB RESULT REGISTER BITS */
|
||||||
#define DA9052_TSILSB_PENDOWN 0X40
|
#define DA9052_TSILSB_PENDOWN 0X40
|
||||||
#define DA9052_TSILSB_TSIZL 0X30
|
#define DA9052_TSILSB_TSIZL 0X30
|
||||||
|
#define DA9052_TSILSB_TSIZL_SHIFT 4
|
||||||
|
#define DA9052_TSILSB_TSIZL_BITS 2
|
||||||
#define DA9052_TSILSB_TSIYL 0X0C
|
#define DA9052_TSILSB_TSIYL 0X0C
|
||||||
|
#define DA9052_TSILSB_TSIYL_SHIFT 2
|
||||||
|
#define DA9052_TSILSB_TSIYL_BITS 2
|
||||||
#define DA9052_TSILSB_TSIXL 0X03
|
#define DA9052_TSILSB_TSIXL 0X03
|
||||||
|
#define DA9052_TSILSB_TSIXL_SHIFT 0
|
||||||
|
#define DA9052_TSILSB_TSIXL_BITS 2
|
||||||
|
|
||||||
/* TSI Z MEASUREMENT MSB RESULT REGISTER BIT */
|
/* TSI Z MEASUREMENT MSB RESULT REGISTER BIT */
|
||||||
#define DA9052_TSIZMSB_TSIZM 0XFF
|
#define DA9052_TSIZMSB_TSIZM 0XFF
|
||||||
|
Loading…
Reference in New Issue
Block a user