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:
Linus Torvalds 2017-09-03 18:43:20 -07:00
commit fe91f28138
33 changed files with 1505 additions and 138 deletions

View File

@ -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>;
}; };

View 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>;
};
};

View File

@ -0,0 +1,10 @@
Lantiq cpu temperatur sensor
Requires node properties:
- compatible value :
"lantiq,cputemp"
Example:
cputemp@0 {
compatible = "lantiq,cputemp";
};

View File

@ -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

View 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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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,
}; };

View File

@ -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;
} }
} }

View File

@ -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)

View File

@ -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,

View File

@ -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;

View File

@ -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",
}, },

View File

@ -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;

View File

@ -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,
}; };

View File

@ -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, }

View File

@ -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,
}; };

View File

@ -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
View 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[] = {
&ltq_chip,
&ltq_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 = &ltq_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,
&ltq_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");

View File

@ -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,
}; };

View File

@ -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

View File

@ -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

View 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");

View File

@ -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},
{ } { }
}; };

View File

@ -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 */

View File

@ -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");

View 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");

View File

@ -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,
}; };

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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