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.
|
||||
|
||||
- #cooling-cells: should be 2.
|
||||
|
||||
- reg : address and length of the register set for the device.
|
||||
|
||||
- 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
|
||||
representing a fan. If there are 8 fans each fan can have one PWM port and
|
||||
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:
|
||||
- reg : should specify PWM source port.
|
||||
integer value in the range 0 to 7 with 0 indicating PWM port A and
|
||||
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.
|
||||
integer value in the range 0 through 15, with 0 indicating
|
||||
Fan tach channel 0 and 15 indicating Fan tach channel 15.
|
||||
|
@ -50,6 +57,7 @@ pwm_tacho_fixed_clk: fixedclk {
|
|||
pwm_tacho: pwmtachocontroller@1e786000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
#cooling-cells = <2>;
|
||||
reg = <0x1E786000 0x1000>;
|
||||
compatible = "aspeed,ast2500-pwm-tacho";
|
||||
clocks = <&pwm_tacho_fixed_clk>;
|
||||
|
@ -58,6 +66,7 @@ pwm_tacho: pwmtachocontroller@1e786000 {
|
|||
|
||||
fan@0 {
|
||||
reg = <0x00>;
|
||||
cooling-levels = /bits/ 8 <125 151 177 203 229 255>;
|
||||
aspeed,fan-tach-ch = /bits/ 8 <0x00>;
|
||||
};
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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
|
||||
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:
|
||||
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
|
||||
|
|
|
@ -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: -
|
||||
Datasheet:
|
||||
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>
|
||||
|
||||
|
@ -37,8 +42,8 @@ Description
|
|||
-----------
|
||||
|
||||
This driver supports hardware monitoring for National Semiconductor / TI LM25056,
|
||||
LM25063, LM25066, LM5064, and LM5066 Power Management, Monitoring, Control, and
|
||||
Protection ICs.
|
||||
LM25063, LM25066, LM5064, and LM5066/LM5066I Power Management, Monitoring,
|
||||
Control, and Protection ICs.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus for details on PMBus client drivers.
|
||||
|
|
|
@ -343,6 +343,7 @@ config SENSORS_ASB100
|
|||
|
||||
config SENSORS_ASPEED
|
||||
tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver"
|
||||
depends on THERMAL || THERMAL=n
|
||||
select REGMAP
|
||||
help
|
||||
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
|
||||
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
|
||||
tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
|
||||
depends on SPI_MASTER
|
||||
|
|
|
@ -110,6 +110,7 @@ obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o
|
|||
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
|
||||
obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o
|
||||
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
|
||||
obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o
|
||||
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
|
||||
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
|
||||
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
|
||||
|
|
|
@ -384,7 +384,7 @@ static struct attribute *adc128_attrs[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group adc128_group = {
|
||||
static const struct attribute_group adc128_group = {
|
||||
.attrs = adc128_attrs,
|
||||
.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;
|
||||
|
||||
if (of_property_read_u32(node, "reg", &pval)) {
|
||||
dev_err(&client->dev, "invalid reg on %s\n",
|
||||
node->full_name);
|
||||
dev_err(&client->dev, "invalid reg on %pOF\n", node);
|
||||
continue;
|
||||
}
|
||||
|
||||
channel = pval;
|
||||
if (channel >= ADS1015_CHANNELS) {
|
||||
dev_err(&client->dev,
|
||||
"invalid channel index %d on %s\n",
|
||||
channel, node->full_name);
|
||||
"invalid channel index %d on %pOF\n",
|
||||
channel, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(node, "ti,gain", &pval)) {
|
||||
pga = pval;
|
||||
if (pga > 6) {
|
||||
dev_err(&client->dev, "invalid gain on %s\n",
|
||||
node->full_name);
|
||||
dev_err(&client->dev, "invalid gain on %pOF\n",
|
||||
node);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
@ -217,8 +216,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
|
|||
data_rate = pval;
|
||||
if (data_rate > 7) {
|
||||
dev_err(&client->dev,
|
||||
"invalid data_rate on %s\n",
|
||||
node->full_name);
|
||||
"invalid data_rate on %pOF\n", node);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1319,14 +1319,14 @@ static struct attribute *vid_attrs[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
|
||||
static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs };
|
||||
static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs };
|
||||
static struct attribute_group in0_attr_group = { .attrs = in0_attrs };
|
||||
static struct attribute_group in3_attr_group = { .attrs = in3_attrs };
|
||||
static struct attribute_group in4_attr_group = { .attrs = in4_attrs };
|
||||
static struct attribute_group in5_attr_group = { .attrs = in5_attrs };
|
||||
static struct attribute_group vid_attr_group = { .attrs = vid_attrs };
|
||||
static const struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
|
||||
static const struct attribute_group fan4_attr_group = { .attrs = fan4_attrs };
|
||||
static const struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs };
|
||||
static const struct attribute_group in0_attr_group = { .attrs = in0_attrs };
|
||||
static const struct attribute_group in3_attr_group = { .attrs = in3_attrs };
|
||||
static const struct attribute_group in4_attr_group = { .attrs = in4_attrs };
|
||||
static const struct attribute_group in5_attr_group = { .attrs = in5_attrs };
|
||||
static const struct attribute_group vid_attr_group = { .attrs = vid_attrs };
|
||||
|
||||
static int adt7475_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
|
|
|
@ -512,7 +512,7 @@ static ssize_t show_pwm_ac(struct device *dev,
|
|||
{
|
||||
SETUP_SHOW_DATA_PARAM(dev, attr);
|
||||
u8 config, altbit, regval;
|
||||
const u8 map[] = {
|
||||
static const u8 map[] = {
|
||||
0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
|
||||
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);
|
||||
unsigned long reqval;
|
||||
u8 currval, config, altbit, newval;
|
||||
const u16 map[] = {
|
||||
static const u16 map[] = {
|
||||
0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06,
|
||||
0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
|
||||
0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
/* ASPEED PWM & FAN Tach Register Definition */
|
||||
#define ASPEED_PTCR_CTRL 0x00
|
||||
|
@ -166,6 +167,18 @@
|
|||
/* How long we sleep in us while waiting for an RPM result. */
|
||||
#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 regmap *regmap;
|
||||
unsigned long clk_freq;
|
||||
|
@ -180,6 +193,7 @@ struct aspeed_pwm_tacho_data {
|
|||
u8 pwm_port_type[8];
|
||||
u8 pwm_port_fan_ctrl[8];
|
||||
u8 fan_tach_ch_source[16];
|
||||
struct aspeed_cooling_device *cdev[8];
|
||||
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,
|
||||
struct device_node *child,
|
||||
struct aspeed_pwm_tacho_data *priv)
|
||||
|
@ -778,6 +880,15 @@ static int aspeed_create_fan(struct device *dev,
|
|||
return ret;
|
||||
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");
|
||||
if (count < 1)
|
||||
return -EINVAL;
|
||||
|
@ -834,9 +945,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
|
|||
|
||||
for_each_child_of_node(np, child) {
|
||||
ret = aspeed_create_fan(dev, child, priv);
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
priv->groups[0] = &pwm_dev_group;
|
||||
|
|
|
@ -20,13 +20,19 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/mfd/da9052/da9052.h>
|
||||
#include <linux/mfd/da9052/reg.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct da9052_hwmon {
|
||||
struct da9052 *da9052;
|
||||
struct mutex hwmon_lock;
|
||||
struct da9052 *da9052;
|
||||
struct mutex hwmon_lock;
|
||||
bool tsi_as_adc;
|
||||
int tsiref_mv;
|
||||
struct regulator *tsiref;
|
||||
struct completion tsidone;
|
||||
};
|
||||
|
||||
static const char * const input_names[] = {
|
||||
|
@ -37,6 +43,10 @@ static const char * const input_names[] = {
|
|||
[DA9052_ADC_IN4] = "ADC IN4",
|
||||
[DA9052_ADC_IN5] = "ADC IN5",
|
||||
[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_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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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,
|
||||
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]);
|
||||
}
|
||||
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(in0_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(in3_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(in4_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(in5_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(in6_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(in9_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(curr1_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(temp2_label, 0444, show_label, NULL,
|
||||
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);
|
||||
static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL,
|
||||
static SENSOR_DEVICE_ATTR(temp8_label, 0444, show_label, NULL,
|
||||
DA9052_ADC_TJUNC);
|
||||
|
||||
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_in6_input.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_label.dev_attr.attr,
|
||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||
|
@ -257,29 +410,117 @@ static struct attribute *da9052_attrs[] = {
|
|||
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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct da9052_hwmon *hwmon;
|
||||
struct device *hwmon_dev;
|
||||
int err;
|
||||
|
||||
hwmon = devm_kzalloc(dev, sizeof(struct da9052_hwmon), GFP_KERNEL);
|
||||
if (!hwmon)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, hwmon);
|
||||
|
||||
mutex_init(&hwmon->hwmon_lock);
|
||||
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,
|
||||
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 = {
|
||||
.probe = da9052_hwmon_probe,
|
||||
.remove = da9052_hwmon_remove,
|
||||
.driver = {
|
||||
.name = "da9052-hwmon",
|
||||
},
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
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 },
|
||||
{ }
|
||||
};
|
||||
|
@ -435,6 +435,7 @@ clear_temp_alarm(struct device *dev, struct device_attribute *devattr,
|
|||
goto error;
|
||||
|
||||
data->valid = false;
|
||||
ret = count;
|
||||
error:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
|
@ -508,6 +509,7 @@ clear_fan_alarm(struct device *dev, struct device_attribute *devattr,
|
|||
goto error;
|
||||
|
||||
data->valid = false;
|
||||
ret = count;
|
||||
error:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
|
|
|
@ -85,7 +85,7 @@ static umode_t hwmon_dev_name_is_visible(struct kobject *kobj,
|
|||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group hwmon_dev_attr_group = {
|
||||
static const struct attribute_group hwmon_dev_attr_group = {
|
||||
.attrs = hwmon_dev_attrs,
|
||||
.is_visible = hwmon_dev_name_is_visible,
|
||||
};
|
||||
|
@ -135,7 +135,7 @@ static int hwmon_thermal_get_temp(void *data, int *temp)
|
|||
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,
|
||||
};
|
||||
|
||||
|
|
|
@ -495,7 +495,7 @@ static struct {
|
|||
};
|
||||
|
||||
#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_5400_ERR) },
|
||||
{ 0, }
|
||||
|
|
|
@ -497,12 +497,14 @@ static const struct it87_devices it87_devices[] = {
|
|||
#define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V)
|
||||
|
||||
struct it87_sio_data {
|
||||
int sioaddr;
|
||||
enum chips type;
|
||||
/* Values read from Super-I/O config space */
|
||||
u8 revision;
|
||||
u8 vid_value;
|
||||
u8 beep_pin;
|
||||
u8 internal; /* Internal sensors can be labeled */
|
||||
bool need_in7_reroute;
|
||||
/* Features skipped based on config or DMI */
|
||||
u16 skip_in;
|
||||
u8 skip_vid;
|
||||
|
@ -517,6 +519,7 @@ struct it87_sio_data {
|
|||
*/
|
||||
struct it87_data {
|
||||
const struct attribute_group *groups[7];
|
||||
int sioaddr;
|
||||
enum chips type;
|
||||
u32 features;
|
||||
u8 peci_mask;
|
||||
|
@ -532,6 +535,7 @@ struct it87_data {
|
|||
u16 in_internal; /* Bitfield, internal sensors (for labels) */
|
||||
u16 has_in; /* Bitfield, voltage sensors enabled */
|
||||
u8 in[NUM_VIN][3]; /* [nr][0]=in, [1]=min, [2]=max */
|
||||
bool need_in7_reroute;
|
||||
u8 has_fan; /* Bitfield, fans enabled */
|
||||
u16 fan[NUM_FAN][2]; /* Register values, [nr][0]=fan, [1]=min */
|
||||
u8 has_temp; /* Bitfield, temp sensors enabled */
|
||||
|
@ -2487,6 +2491,7 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||
}
|
||||
|
||||
err = 0;
|
||||
sio_data->sioaddr = sioaddr;
|
||||
sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f;
|
||||
pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type,
|
||||
it87_devices[sio_data->type].suffix,
|
||||
|
@ -2575,6 +2580,7 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||
reg2c |= BIT(1);
|
||||
superio_outb(sioaddr, IT87_SIO_PINX2_REG,
|
||||
reg2c);
|
||||
sio_data->need_in7_reroute = true;
|
||||
pr_notice("Routing internal VCCH5V to in7.\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));
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Curiously, there still is a configuration bit to control
|
||||
* this, which means it can be set incorrectly. And even
|
||||
* more curiously, many boards out there are improperly
|
||||
* 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.
|
||||
*
|
||||
* 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))) {
|
||||
reg |= BIT(1);
|
||||
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))
|
||||
sio_data->internal |= BIT(0);
|
||||
|
@ -2828,13 +2835,89 @@ exit:
|
|||
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. */
|
||||
static void it87_init_device(struct platform_device *pdev)
|
||||
{
|
||||
struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
|
||||
struct it87_data *data = platform_get_drvdata(pdev);
|
||||
int tmp, i;
|
||||
u8 mask;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
it87_check_limit_regs(data);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Check if voltage monitors are reset manually or by some reason */
|
||||
tmp = it87_read_value(data, IT87_REG_VIN_ENABLE);
|
||||
if ((tmp & 0xff) == 0) {
|
||||
/* Enable all voltage monitors */
|
||||
it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff);
|
||||
}
|
||||
it87_check_voltage_monitors_reset(data);
|
||||
|
||||
it87_check_tachometers_reset(pdev);
|
||||
|
||||
/* 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);
|
||||
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;
|
||||
|
||||
tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
it87_check_tachometers_16bit_mode(pdev);
|
||||
|
||||
/* Check for additional fans */
|
||||
if (has_five_fans(data)) {
|
||||
tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
|
||||
|
||||
if (tmp & BIT(4))
|
||||
data->has_fan |= BIT(3); /* fan4 enabled */
|
||||
if (tmp & BIT(5))
|
||||
|
@ -2933,10 +2981,7 @@ static void it87_init_device(struct platform_device *pdev)
|
|||
sio_data->skip_pwm |= BIT(5);
|
||||
}
|
||||
|
||||
/* Start monitoring */
|
||||
it87_write_value(data, IT87_REG_CONFIG,
|
||||
(it87_read_value(data, IT87_REG_CONFIG) & 0x3e)
|
||||
| (update_vbat ? 0x41 : 0x01));
|
||||
it87_start_monitoring(data);
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
dev_info(dev,
|
||||
"Detected broken BIOS defaults, disabling PWM interface\n");
|
||||
return 0;
|
||||
} else if (fix_pwm_polarity) {
|
||||
dev_info(dev,
|
||||
|
@ -3020,6 +3063,7 @@ static int it87_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
data->addr = res->start;
|
||||
data->sioaddr = sio_data->sioaddr;
|
||||
data->type = sio_data->type;
|
||||
data->features = it87_devices[sio_data->type].features;
|
||||
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 */
|
||||
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 */
|
||||
if (has_12mv_adc(data)) {
|
||||
|
@ -3085,6 +3132,7 @@ static int it87_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
data->in_internal = sio_data->internal;
|
||||
data->need_in7_reroute = sio_data->need_in7_reroute;
|
||||
data->has_in = 0x3ff & ~sio_data->skip_in;
|
||||
|
||||
if (has_six_temp(data)) {
|
||||
|
@ -3140,9 +3188,71 @@ static int it87_probe(struct platform_device *pdev)
|
|||
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 = {
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.pm = &it87_dev_pm_ops,
|
||||
},
|
||||
.probe = it87_probe,
|
||||
};
|
||||
|
|
|
@ -72,6 +72,8 @@ static const unsigned short normal_i2c[] = {
|
|||
#define NXP_MANID 0x1131 /* NXP Semiconductors */
|
||||
#define ONS_MANID 0x1b09 /* ON Semiconductor */
|
||||
#define STM_MANID 0x104a /* ST Microelectronics */
|
||||
#define GT_MANID 0x1c68 /* Giantec */
|
||||
#define GT_MANID2 0x132d /* Giantec, 2nd mfg ID */
|
||||
|
||||
/* Supported chips */
|
||||
|
||||
|
@ -86,6 +88,13 @@ static const unsigned short normal_i2c[] = {
|
|||
#define AT30TSE004_DEVID 0x2200
|
||||
#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 */
|
||||
#define TSE2004_DEVID 0x2200
|
||||
#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_MASK 0xffe0
|
||||
|
||||
#define CAT34TS02C_DEVID 0x0a00
|
||||
#define CAT34TS02C_DEVID_MASK 0xfff0
|
||||
|
||||
#define CAT34TS04_DEVID 0x2200
|
||||
#define CAT34TS04_DEVID_MASK 0xfff0
|
||||
|
||||
/* ST Microelectronics */
|
||||
#define STTS424_DEVID 0x0101
|
||||
#define STTS424_DEVID_MASK 0xffff
|
||||
|
@ -158,6 +173,8 @@ static struct jc42_chips jc42_chips[] = {
|
|||
{ ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
|
||||
{ ATMEL_MANID, AT30TS00_DEVID, AT30TS00_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, TS3000_DEVID, TS3000_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 },
|
||||
{ NXP_MANID, SE97_DEVID, SE97_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 },
|
||||
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
|
||||
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
static struct attribute_group nct7802_temp_group = {
|
||||
static const struct attribute_group nct7802_temp_group = {
|
||||
.attrs = nct7802_temp_attrs,
|
||||
.is_visible = nct7802_temp_is_visible,
|
||||
};
|
||||
|
@ -802,7 +802,7 @@ static umode_t nct7802_in_is_visible(struct kobject *kobj,
|
|||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group nct7802_in_group = {
|
||||
static const struct attribute_group nct7802_in_group = {
|
||||
.attrs = nct7802_in_attrs,
|
||||
.is_visible = nct7802_in_is_visible,
|
||||
};
|
||||
|
@ -880,7 +880,7 @@ static umode_t nct7802_fan_is_visible(struct kobject *kobj,
|
|||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group nct7802_fan_group = {
|
||||
static const struct attribute_group nct7802_fan_group = {
|
||||
.attrs = nct7802_fan_attrs,
|
||||
.is_visible = nct7802_fan_is_visible,
|
||||
};
|
||||
|
@ -898,7 +898,7 @@ static struct attribute *nct7802_pwm_attrs[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group nct7802_pwm_group = {
|
||||
static const struct attribute_group nct7802_pwm_group = {
|
||||
.attrs = nct7802_pwm_attrs,
|
||||
};
|
||||
|
||||
|
@ -1011,7 +1011,7 @@ static struct attribute *nct7802_auto_point_attrs[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group nct7802_auto_point_group = {
|
||||
static const struct attribute_group nct7802_auto_point_group = {
|
||||
.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
|
||||
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
|
||||
tristate "Infineon IR35221"
|
||||
default n
|
||||
|
@ -135,6 +144,15 @@ config SENSORS_TPS40422
|
|||
This driver can also be built as a module. If so, the module will
|
||||
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
|
||||
tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910"
|
||||
default n
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
obj-$(CONFIG_PMBUS) += pmbus_core.o
|
||||
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
|
||||
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
|
||||
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
|
||||
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
|
||||
obj-$(CONFIG_SENSORS_LM25066) += lm25066.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_MAX8688) += max8688.o
|
||||
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
||||
obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
|
||||
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
||||
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
||||
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
|
||||
|
|
|
@ -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 "pmbus.h"
|
||||
|
||||
enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 };
|
||||
enum chips { lm25056, lm25063, lm25066, lm5064, lm5066, lm5066i };
|
||||
|
||||
#define LM25066_READ_VAUX 0xd0
|
||||
#define LM25066_MFR_READ_IIN 0xd1
|
||||
|
@ -65,7 +65,7 @@ struct __coeff {
|
|||
#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES)
|
||||
#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] = {
|
||||
[PSC_VOLTAGE_IN] = {
|
||||
.m = 16296,
|
||||
|
@ -210,6 +210,41 @@ static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = {
|
|||
.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 {
|
||||
|
@ -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);
|
||||
break;
|
||||
case lm5066:
|
||||
case lm5066i:
|
||||
/* VIN: 2.18 mV VAUX: 725 uV LSB */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
|
||||
break;
|
||||
|
@ -488,16 +524,18 @@ static int lm25066_probe(struct i2c_client *client,
|
|||
info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m;
|
||||
info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b;
|
||||
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->b[PSC_POWER] = coeff[PSC_POWER].b;
|
||||
info->R[PSC_POWER] = coeff[PSC_POWER].R;
|
||||
if (config & LM25066_DEV_SETUP_CL) {
|
||||
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->b[PSC_POWER] = coeff[PSC_POWER_L].b;
|
||||
} else {
|
||||
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->b[PSC_POWER] = coeff[PSC_POWER].b;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, id, info);
|
||||
|
@ -509,6 +547,7 @@ static const struct i2c_device_id lm25066_id[] = {
|
|||
{"lm25066", lm25066},
|
||||
{"lm5064", lm5064},
|
||||
{"lm5066", lm5066},
|
||||
{"lm5066i", lm5066i},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ enum pmbus_sensor_classes {
|
|||
#define PMBUS_HAVE_STATUS_VMON BIT(19)
|
||||
|
||||
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 {
|
||||
int pages; /* Total number of pages */
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -101,6 +102,7 @@ struct pmbus_data {
|
|||
int num_attributes;
|
||||
struct attribute_group group;
|
||||
const struct attribute_group *groups[2];
|
||||
struct dentry *debugfs; /* debugfs device directory */
|
||||
|
||||
struct pmbus_sensor *sensors;
|
||||
|
||||
|
@ -112,12 +114,20 @@ struct pmbus_data {
|
|||
* A single status register covers multiple attributes,
|
||||
* so we keep them all together.
|
||||
*/
|
||||
u8 status[PB_NUM_STATUS_REG];
|
||||
u8 status_register;
|
||||
u16 status[PB_NUM_STATUS_REG];
|
||||
|
||||
bool has_status_word; /* device uses STATUS_WORD register */
|
||||
int (*read_status)(struct i2c_client *client, int page);
|
||||
|
||||
u8 currpage;
|
||||
};
|
||||
|
||||
struct pmbus_debugfs_entry {
|
||||
struct i2c_client *client;
|
||||
u8 page;
|
||||
u8 reg;
|
||||
};
|
||||
|
||||
void pmbus_clear_cache(struct i2c_client *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);
|
||||
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)) {
|
||||
status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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++) {
|
||||
data->status[PB_STATUS_BASE + i]
|
||||
= _pmbus_read_byte_data(client, i,
|
||||
data->status_register);
|
||||
= data->read_status(client, i);
|
||||
for (j = 0; j < ARRAY_SIZE(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)
|
||||
rv = 250 + (val - 1) * 5;
|
||||
break;
|
||||
case vr13:
|
||||
if (val >= 0x01)
|
||||
rv = 500 + (val - 1) * 10;
|
||||
break;
|
||||
}
|
||||
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 *s2 = b->s2;
|
||||
u16 reg = (index >> 8) & 0xffff;
|
||||
u8 mask = index & 0xff;
|
||||
u16 reg = (index >> 16) & 0xffff;
|
||||
u16 mask = index & 0xffff;
|
||||
int ret, status;
|
||||
u8 regval;
|
||||
u16 regval;
|
||||
|
||||
status = data->status[reg];
|
||||
if (status < 0)
|
||||
|
@ -860,7 +890,7 @@ static int pmbus_add_boolean(struct pmbus_data *data,
|
|||
const char *name, const char *type, int seq,
|
||||
struct pmbus_sensor *s1,
|
||||
struct pmbus_sensor *s2,
|
||||
u16 reg, u8 mask)
|
||||
u16 reg, u16 mask)
|
||||
{
|
||||
struct pmbus_boolean *boolean;
|
||||
struct sensor_device_attribute *a;
|
||||
|
@ -876,7 +906,7 @@ static int pmbus_add_boolean(struct pmbus_data *data,
|
|||
boolean->s1 = s1;
|
||||
boolean->s2 = s2;
|
||||
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);
|
||||
}
|
||||
|
@ -962,7 +992,7 @@ struct pmbus_limit_attr {
|
|||
*/
|
||||
struct pmbus_sensor_attr {
|
||||
u16 reg; /* sensor register */
|
||||
u8 gbit; /* generic status bit */
|
||||
u16 gbit; /* generic status bit */
|
||||
u8 nlimit; /* # of limit registers */
|
||||
enum pmbus_sensor_classes class;/* sensor class */
|
||||
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)
|
||||
{
|
||||
struct pmbus_sensor *base;
|
||||
bool upper = !!(attr->gbit & 0xff00); /* need to check STATUS_WORD */
|
||||
int ret;
|
||||
|
||||
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
|
||||
* 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 &&
|
||||
pmbus_check_byte_register(client, page,
|
||||
data->status_register)) {
|
||||
(!upper || (upper && data->has_status_word)) &&
|
||||
pmbus_check_status_register(client, page)) {
|
||||
ret = pmbus_add_boolean(data, name, "alarm", index,
|
||||
NULL, NULL,
|
||||
PB_STATUS_BASE + page,
|
||||
|
@ -1308,6 +1340,7 @@ static const struct pmbus_sensor_attr current_attributes[] = {
|
|||
.func = PMBUS_HAVE_IIN,
|
||||
.sfunc = PMBUS_HAVE_STATUS_INPUT,
|
||||
.sbase = PB_STATUS_INPUT_BASE,
|
||||
.gbit = PB_STATUS_INPUT,
|
||||
.limit = 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,
|
||||
.sfunc = PMBUS_HAVE_STATUS_INPUT,
|
||||
.sbase = PB_STATUS_INPUT_BASE,
|
||||
.gbit = PB_STATUS_INPUT,
|
||||
.limit = pin_limit_attrs,
|
||||
.nlimit = ARRAY_SIZE(pin_limit_attrs),
|
||||
}, {
|
||||
|
@ -1729,6 +1763,16 @@ static int pmbus_identify_common(struct i2c_client *client,
|
|||
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,
|
||||
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;
|
||||
|
||||
/*
|
||||
* Some PMBus chips don't support PMBUS_STATUS_BYTE, so try
|
||||
* to use PMBUS_STATUS_WORD instead if that is the case.
|
||||
* Some PMBus chips don't support PMBUS_STATUS_WORD, so try
|
||||
* to use PMBUS_STATUS_BYTE instead if that is the case.
|
||||
* Bail out if both registers are not supported.
|
||||
*/
|
||||
data->status_register = PMBUS_STATUS_BYTE;
|
||||
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
|
||||
if (ret < 0 || ret == 0xff) {
|
||||
data->status_register = PMBUS_STATUS_WORD;
|
||||
ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
|
||||
if (ret < 0 || ret == 0xffff) {
|
||||
data->read_status = pmbus_read_status_word;
|
||||
ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
|
||||
if (ret < 0 || ret == 0xffff) {
|
||||
data->read_status = pmbus_read_status_byte;
|
||||
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
|
||||
if (ret < 0 || ret == 0xff) {
|
||||
dev_err(dev, "PMBus status register not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else {
|
||||
data->has_status_word = true;
|
||||
}
|
||||
|
||||
/* Enable PEC if the controller supports it */
|
||||
|
@ -1859,6 +1905,184 @@ static int pmbus_regulator_register(struct pmbus_data *data)
|
|||
}
|
||||
#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,
|
||||
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)
|
||||
goto out_unregister;
|
||||
|
||||
ret = pmbus_init_debugfs(client, data);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to register debugfs\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister:
|
||||
|
@ -1931,12 +2159,32 @@ EXPORT_SYMBOL_GPL(pmbus_do_probe);
|
|||
int pmbus_do_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
|
||||
debugfs_remove_recursive(data->debugfs);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
kfree(data->group.attrs);
|
||||
return 0;
|
||||
}
|
||||
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_DESCRIPTION("PMBus core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret >= ARRAY_SIZE(stts751_intervals)) {
|
||||
dev_err(priv->dev, "Unrecognized conversion rate 0x%x\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
priv->interval = ret;
|
||||
|
||||
ret = stts751_read_reg16(priv, &priv->event_max,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/mfd/core.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/mfd/da9052/da9052.h>
|
||||
#include <linux/mfd/da9052/pdata.h>
|
||||
|
@ -518,9 +519,6 @@ static const struct mfd_cell da9052_subdev_info[] = {
|
|||
{
|
||||
.name = "da9052-wled3",
|
||||
},
|
||||
{
|
||||
.name = "da9052-tsi",
|
||||
},
|
||||
{
|
||||
.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 = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
@ -619,9 +621,27 @@ int da9052_device_init(struct da9052 *da9052, u8 chip_id)
|
|||
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;
|
||||
|
||||
err:
|
||||
mfd_remove_devices(da9052->dev);
|
||||
da9052_irq_exit(da9052);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -45,6 +45,12 @@
|
|||
#define DA9052_ADC_TJUNC 8
|
||||
#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_VBUS 1
|
||||
#define DA9052_IRQ_DCINREM 2
|
||||
|
|
|
@ -690,7 +690,10 @@
|
|||
/* TSI CONTROL REGISTER B BITS */
|
||||
#define DA9052_TSICONTB_ADCREF 0X80
|
||||
#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_TSISEL2 0X04
|
||||
#define DA9052_TSICONTB_TSISEL1 0X02
|
||||
|
@ -705,8 +708,14 @@
|
|||
/* TSI CO-ORDINATE LSB RESULT REGISTER BITS */
|
||||
#define DA9052_TSILSB_PENDOWN 0X40
|
||||
#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_SHIFT 2
|
||||
#define DA9052_TSILSB_TSIYL_BITS 2
|
||||
#define DA9052_TSILSB_TSIXL 0X03
|
||||
#define DA9052_TSILSB_TSIXL_SHIFT 0
|
||||
#define DA9052_TSILSB_TSIXL_BITS 2
|
||||
|
||||
/* TSI Z MEASUREMENT MSB RESULT REGISTER BIT */
|
||||
#define DA9052_TSIZMSB_TSIZM 0XFF
|
||||
|
|
Loading…
Reference in New Issue