summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-10-23 00:58:12 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2018-10-23 00:58:12 +0100
commitb8e445b6895cfe76c5959a7135a3216703fe32d4 (patch)
tree285797138e87a67c25aa173de48a4cf3d18d6a61
parentcff229491af5df946781edfbeafd43e9cf66a3b4 (diff)
parente7c6a55606b5c46b449d76588968b4d8caae903f (diff)
downloadlinux-b8e445b6895cfe76c5959a7135a3216703fe32d4.tar.gz
linux-b8e445b6895cfe76c5959a7135a3216703fe32d4.tar.bz2
linux-b8e445b6895cfe76c5959a7135a3216703fe32d4.zip
Merge tag 'hwmon-for-v4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: - Add support for trace events to hwmon core - Add support for NCT6797D, NCT6798D, MAX31725/6, LTM4686 - Support all AMD Family 15h Model 6xh and Model 7xh processors in k10temp driver - Convert ina3221 driver to _info API - Fixes, cleanups, and improvements in various drivers * tag 'hwmon-for-v4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (46 commits) hwmon: (pmbus) Fix page count auto-detection. hwmon: (pmbus) remove redundant 'default n' from Kconfig hwmon: (core) Add trace events to _attr_show/store functions hwmon: (ina3221) Use _info API to register hwmon device hwmon: (npcm-750-pwm-fan) Change initial pwm target to 255 hwmon: (ina3221) Validate shunt resistor value from DT hwmon: (tmp421) make const array 'names' static hwmon: (core) Add hwmon_in_enable attribute hwmon: (ina3221) mark PM functions as __maybe_unused hwmon: (ina3221) Read channel input source info from DT dt-bindings: hwmon: Add ina3221 documentation hwmon: (ina3221) Add suspend and resume functions hwmon: (ina3221) Fix INA3221_CONFIG_MODE macros hwmon: (ina3221) Add INA3221_CONFIG to volatile_table MAINTAINERS: Update PMBUS maintainer entry hwmon: (pwm-fan) Set fan speed to 0 on suspend hwmon: (pwm-fan) Silence error on probe deferral hwmon: (scpi-hwmon) remove redundant continue hwmon: (nct6775) Add support for NCT6798D hwmon: (nct6775) Add support for NCT6797D ...
-rw-r--r--Documentation/devicetree/bindings/hwmon/ina3221.txt44
-rw-r--r--Documentation/devicetree/bindings/hwmon/ltc2978.txt2
-rw-r--r--Documentation/hwmon/ina32212
-rw-r--r--Documentation/hwmon/lm756
-rw-r--r--Documentation/hwmon/ltc29785
-rw-r--r--Documentation/hwmon/mc13783-adc8
-rw-r--r--MAINTAINERS27
-rw-r--r--drivers/hwmon/aspeed-pwm-tacho.c2
-rw-r--r--drivers/hwmon/asus_atk0110.c4
-rw-r--r--drivers/hwmon/hwmon.c28
-rw-r--r--drivers/hwmon/ibmpowernv.c10
-rw-r--r--drivers/hwmon/iio_hwmon.c16
-rw-r--r--drivers/hwmon/ina3221.c592
-rw-r--r--drivers/hwmon/k10temp.c5
-rw-r--r--drivers/hwmon/lm75.c18
-rw-r--r--drivers/hwmon/lm92.c14
-rw-r--r--drivers/hwmon/mc13783-adc.c14
-rw-r--r--drivers/hwmon/nct6775.c302
-rw-r--r--drivers/hwmon/npcm750-pwm-fan.c4
-rw-r--r--drivers/hwmon/pmbus/Kconfig18
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c9
-rw-r--r--drivers/hwmon/pmbus/pmbus.c2
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c5
-rw-r--r--drivers/hwmon/pwm-fan.c20
-rw-r--r--drivers/hwmon/scmi-hwmon.c2
-rw-r--r--drivers/hwmon/scpi-hwmon.c4
-rw-r--r--drivers/hwmon/sht15.c5
-rw-r--r--drivers/hwmon/tmp421.c6
-rw-r--r--include/linux/hwmon.h2
-rw-r--r--include/trace/events/hwmon.h71
30 files changed, 878 insertions, 369 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/ina3221.txt b/Documentation/devicetree/bindings/hwmon/ina3221.txt
new file mode 100644
index 000000000000..a7b25caa2b8e
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ina3221.txt
@@ -0,0 +1,44 @@
+Texas Instruments INA3221 Device Tree Bindings
+
+1) ina3221 node
+ Required properties:
+ - compatible: Must be "ti,ina3221"
+ - reg: I2C address
+
+ Optional properties:
+ = The node contains optional child nodes for three channels =
+ = Each child node describes the information of input source =
+
+ - #address-cells: Required only if a child node is present. Must be 1.
+ - #size-cells: Required only if a child node is present. Must be 0.
+
+2) child nodes
+ Required properties:
+ - reg: Must be 0, 1 or 2, corresponding to IN1, IN2 or IN3 port of INA3221
+
+ Optional properties:
+ - label: Name of the input source
+ - shunt-resistor-micro-ohms: Shunt resistor value in micro-Ohm
+
+Example:
+
+ina3221@40 {
+ compatible = "ti,ina3221";
+ reg = <0x40>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ input@0 {
+ reg = <0x0>;
+ status = "disabled";
+ };
+ input@1 {
+ reg = <0x1>;
+ shunt-resistor-micro-ohms = <5000>;
+ };
+ input@2 {
+ reg = <0x2>;
+ label = "VDD_5V";
+ shunt-resistor-micro-ohms = <5000>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/hwmon/ltc2978.txt b/Documentation/devicetree/bindings/hwmon/ltc2978.txt
index bf2a47bbdc58..b428a70a7cc0 100644
--- a/Documentation/devicetree/bindings/hwmon/ltc2978.txt
+++ b/Documentation/devicetree/bindings/hwmon/ltc2978.txt
@@ -15,6 +15,7 @@ Required properties:
* "lltc,ltm2987"
* "lltc,ltm4675"
* "lltc,ltm4676"
+ * "lltc,ltm4686"
- reg: I2C slave address
Optional properties:
@@ -30,6 +31,7 @@ Valid names of regulators depend on number of supplies supported per device:
* ltc3880, ltc3882, ltc3886 : vout0 - vout1
* ltc3883 : vout0
* ltm4676 : vout0 - vout1
+ * ltm4686 : vout0 - vout1
Example:
ltc2978@5e {
diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221
index 0ff74854cb2e..4b82cbfb551c 100644
--- a/Documentation/hwmon/ina3221
+++ b/Documentation/hwmon/ina3221
@@ -21,6 +21,8 @@ and power are calculated host-side from these.
Sysfs entries
-------------
+in[123]_label Voltage channel labels
+in[123]_enable Voltage channel enable controls
in[123]_input Bus voltage(mV) channels
curr[123]_input Current(mA) measurement channels
shunt[123]_resistor Shunt resistance(uOhm) channels
diff --git a/Documentation/hwmon/lm75 b/Documentation/hwmon/lm75
index ac95edfcd907..2f1120f88c16 100644
--- a/Documentation/hwmon/lm75
+++ b/Documentation/hwmon/lm75
@@ -17,8 +17,8 @@ Supported chips:
Addresses scanned: none
Datasheet: Publicly available at the Maxim website
http://www.maximintegrated.com/
- * Maxim MAX6625, MAX6626
- Prefixes: 'max6625', 'max6626'
+ * Maxim MAX6625, MAX6626, MAX31725, MAX31726
+ Prefixes: 'max6625', 'max6626', 'max31725', 'max31726'
Addresses scanned: none
Datasheet: Publicly available at the Maxim website
http://www.maxim-ic.com/
@@ -86,7 +86,7 @@ The LM75 is essentially an industry standard; there may be other
LM75 clones not listed here, with or without various enhancements,
that are supported. The clones are not detected by the driver, unless
they reproduce the exact register tricks of the original LM75, and must
-therefore be instantiated explicitly. Higher resolution up to 12-bit
+therefore be instantiated explicitly. Higher resolution up to 16-bit
is supported by this driver, other specific enhancements are not.
The LM77 is not supported, contrary to what we pretended for a long time.
diff --git a/Documentation/hwmon/ltc2978 b/Documentation/hwmon/ltc2978
index 9a49d3c90cd1..dfb2caa401d9 100644
--- a/Documentation/hwmon/ltc2978
+++ b/Documentation/hwmon/ltc2978
@@ -55,6 +55,10 @@ Supported chips:
Prefix: 'ltm4676'
Addresses scanned: -
Datasheet: http://www.linear.com/product/ltm4676
+ * Analog Devices LTM4686
+ Prefix: 'ltm4686'
+ Addresses scanned: -
+ Datasheet: http://www.analog.com/ltm4686
Author: Guenter Roeck <linux@roeck-us.net>
@@ -76,6 +80,7 @@ additional components on a single die. The chip is instantiated and reported
as two separate chips on two different I2C bus addresses.
LTM4675 is a dual 9A or single 18A μModule regulator
LTM4676 is a dual 13A or single 26A uModule regulator.
+LTM4686 is a dual 10A or single 20A uModule regulator.
Usage Notes
diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc
index d0e7b3fa9e75..05ccc9f159f1 100644
--- a/Documentation/hwmon/mc13783-adc
+++ b/Documentation/hwmon/mc13783-adc
@@ -2,12 +2,12 @@ Kernel driver mc13783-adc
=========================
Supported chips:
- * Freescale Atlas MC13783
+ * Freescale MC13783
Prefix: 'mc13783'
- Datasheet: http://www.freescale.com/files/rf_if/doc/data_sheet/MC13783.pdf?fsrch=1
- * Freescale Atlas MC13892
+ Datasheet: https://www.nxp.com/docs/en/data-sheet/MC13783.pdf
+ * Freescale MC13892
Prefix: 'mc13892'
- Datasheet: http://cache.freescale.com/files/analog/doc/data_sheet/MC13892.pdf?fsrch=1&sr=1
+ Datasheet: https://www.nxp.com/docs/en/data-sheet/MC13892.pdf
Authors:
Sascha Hauer <s.hauer@pengutronix.de>
diff --git a/MAINTAINERS b/MAINTAINERS
index c393746b0209..c742c517f95c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6468,6 +6468,7 @@ F: Documentation/devicetree/bindings/hwmon/
F: Documentation/hwmon/
F: drivers/hwmon/
F: include/linux/hwmon*.h
+F: include/trace/events/hwmon*.h
HARDWARE RANDOM NUMBER GENERATOR CORE
M: Matt Mackall <mpm@selenic.com>
@@ -8860,13 +8861,6 @@ S: Maintained
F: Documentation/hwmon/max16065
F: drivers/hwmon/max16065.c
-MAX20751 HARDWARE MONITOR DRIVER
-M: Guenter Roeck <linux@roeck-us.net>
-L: linux-hwmon@vger.kernel.org
-S: Maintained
-F: Documentation/hwmon/max20751
-F: drivers/hwmon/max20751.c
-
MAX2175 SDR TUNER DRIVER
M: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
L: linux-media@vger.kernel.org
@@ -11601,7 +11595,26 @@ W: http://hwmon.wiki.kernel.org/
W: http://www.roeck-us.net/linux/drivers/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
S: Maintained
+F: Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
+F: Documentation/devicetree/bindings/hwmon/max31785.txt
+F: Documentation/devicetree/bindings/hwmon/ltc2978.txt
+F: Documentation/hwmon/adm1275
+F: Documentation/hwmon/ibm-cffps
+F: Documentation/hwmon/ir35221
+F: Documentation/hwmon/lm25066
+F: Documentation/hwmon/ltc2978
+F: Documentation/hwmon/ltc3815
+F: Documentation/hwmon/max16064
+F: Documentation/hwmon/max20751
+F: Documentation/hwmon/max31785
+F: Documentation/hwmon/max34440
+F: Documentation/hwmon/max8688
F: Documentation/hwmon/pmbus
+F: Documentation/hwmon/pmbus-core
+F: Documentation/hwmon/tps40422
+F: Documentation/hwmon/ucd9000
+F: Documentation/hwmon/ucd9200
+F: Documentation/hwmon/zl6100
F: drivers/hwmon/pmbus/
F: include/linux/pmbus.h
diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
index 5e449eac788a..92de8139d398 100644
--- a/drivers/hwmon/aspeed-pwm-tacho.c
+++ b/drivers/hwmon/aspeed-pwm-tacho.c
@@ -852,7 +852,7 @@ static int aspeed_create_pwm_cooling(struct device *dev,
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);
+ snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port);
cdev->tcdev = thermal_of_cooling_device_register(child,
cdev->name,
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c
index a6636fe42189..a7cf00885c5d 100644
--- a/drivers/hwmon/asus_atk0110.c
+++ b/drivers/hwmon/asus_atk0110.c
@@ -1210,10 +1210,8 @@ static int atk_register_hwmon(struct atk_data *data)
data->hwmon_dev = hwmon_device_register_with_groups(dev, "atk0110",
data,
data->attr_groups);
- if (IS_ERR(data->hwmon_dev))
- return PTR_ERR(data->hwmon_dev);
- return 0;
+ return PTR_ERR_OR_ZERO(data->hwmon_dev);
}
static int atk_probe_if(struct atk_data *data)
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 33d51281272b..975c95169884 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -24,6 +24,9 @@
#include <linux/string.h>
#include <linux/thermal.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/hwmon.h>
+
#define HWMON_ID_PREFIX "hwmon"
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
@@ -171,6 +174,13 @@ static int hwmon_thermal_add_sensor(struct device *dev,
}
#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
+static int hwmon_attr_base(enum hwmon_sensor_types type)
+{
+ if (type == hwmon_in)
+ return 0;
+ return 1;
+}
+
/* sysfs attribute management */
static ssize_t hwmon_attr_show(struct device *dev,
@@ -185,6 +195,9 @@ static ssize_t hwmon_attr_show(struct device *dev,
if (ret < 0)
return ret;
+ trace_hwmon_attr_show(hattr->index + hwmon_attr_base(hattr->type),
+ hattr->name, val);
+
return sprintf(buf, "%ld\n", val);
}
@@ -193,6 +206,7 @@ static ssize_t hwmon_attr_show_string(struct device *dev,
char *buf)
{
struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ enum hwmon_sensor_types type = hattr->type;
const char *s;
int ret;
@@ -201,6 +215,9 @@ static ssize_t hwmon_attr_show_string(struct device *dev,
if (ret < 0)
return ret;
+ trace_hwmon_attr_show_string(hattr->index + hwmon_attr_base(type),
+ hattr->name, s);
+
return sprintf(buf, "%s\n", s);
}
@@ -221,14 +238,10 @@ static ssize_t hwmon_attr_store(struct device *dev,
if (ret < 0)
return ret;
- return count;
-}
+ trace_hwmon_attr_store(hattr->index + hwmon_attr_base(hattr->type),
+ hattr->name, val);
-static int hwmon_attr_base(enum hwmon_sensor_types type)
-{
- if (type == hwmon_in)
- return 0;
- return 1;
+ return count;
}
static bool is_string_attr(enum hwmon_sensor_types type, u32 attr)
@@ -356,6 +369,7 @@ static const char * const hwmon_in_attr_templates[] = {
[hwmon_in_max_alarm] = "in%d_max_alarm",
[hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm",
[hwmon_in_crit_alarm] = "in%d_crit_alarm",
+ [hwmon_in_enable] = "in%d_enable",
};
static const char * const hwmon_curr_attr_templates[] = {
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
index 83472808c816..0ccca87f5271 100644
--- a/drivers/hwmon/ibmpowernv.c
+++ b/drivers/hwmon/ibmpowernv.c
@@ -458,9 +458,6 @@ static int populate_attr_groups(struct platform_device *pdev)
for_each_child_of_node(opal, np) {
const char *label;
- if (np->name == NULL)
- continue;
-
type = get_sensor_type(np);
if (type == MAX_SENSOR_TYPE)
continue;
@@ -589,9 +586,6 @@ static int create_device_attrs(struct platform_device *pdev)
const char *label;
enum sensors type;
- if (np->name == NULL)
- continue;
-
type = get_sensor_type(np);
if (type == MAX_SENSOR_TYPE)
continue;
@@ -603,8 +597,8 @@ static int create_device_attrs(struct platform_device *pdev)
if (of_property_read_u32(np, "sensor-id", &sensor_id) &&
of_property_read_u32(np, "sensor-data", &sensor_id)) {
dev_info(&pdev->dev,
- "'sensor-id' missing in the node '%s'\n",
- np->name);
+ "'sensor-id' missing in the node '%pOFn'\n",
+ np);
continue;
}
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
index 2f3f875c06ac..eed66e533ee2 100644
--- a/drivers/hwmon/iio_hwmon.c
+++ b/drivers/hwmon/iio_hwmon.c
@@ -65,13 +65,9 @@ static int iio_hwmon_probe(struct platform_device *pdev)
int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1;
enum iio_chan_type type;
struct iio_channel *channels;
- const char *name = "iio_hwmon";
struct device *hwmon_dev;
char *sname;
- if (dev->of_node && dev->of_node->name)
- name = dev->of_node->name;
-
channels = devm_iio_channel_get_all(dev);
if (IS_ERR(channels)) {
if (PTR_ERR(channels) == -ENODEV)
@@ -141,11 +137,15 @@ static int iio_hwmon_probe(struct platform_device *pdev)
st->attr_group.attrs = st->attrs;
st->groups[0] = &st->attr_group;
- sname = devm_kstrdup(dev, name, GFP_KERNEL);
- if (!sname)
- return -ENOMEM;
+ if (dev->of_node) {
+ sname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
+ if (!sname)
+ return -ENOMEM;
+ strreplace(sname, '-', '_');
+ } else {
+ sname = "iio_hwmon";
+ }
- strreplace(sname, '-', '_');
hwmon_dev = devm_hwmon_device_register_with_groups(dev, sname, st,
st->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index e6b49500c52a..d61688f04594 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -38,9 +38,12 @@
#define INA3221_WARN3 0x0c
#define INA3221_MASK_ENABLE 0x0f
-#define INA3221_CONFIG_MODE_SHUNT BIT(1)
-#define INA3221_CONFIG_MODE_BUS BIT(2)
-#define INA3221_CONFIG_MODE_CONTINUOUS BIT(3)
+#define INA3221_CONFIG_MODE_MASK GENMASK(2, 0)
+#define INA3221_CONFIG_MODE_POWERDOWN 0
+#define INA3221_CONFIG_MODE_SHUNT BIT(0)
+#define INA3221_CONFIG_MODE_BUS BIT(1)
+#define INA3221_CONFIG_MODE_CONTINUOUS BIT(2)
+#define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x))
#define INA3221_RSHUNT_DEFAULT 10000
@@ -74,30 +77,37 @@ enum ina3221_channels {
INA3221_NUM_CHANNELS
};
-static const unsigned int register_channel[] = {
- [INA3221_SHUNT1] = INA3221_CHANNEL1,
- [INA3221_SHUNT2] = INA3221_CHANNEL2,
- [INA3221_SHUNT3] = INA3221_CHANNEL3,
- [INA3221_CRIT1] = INA3221_CHANNEL1,
- [INA3221_CRIT2] = INA3221_CHANNEL2,
- [INA3221_CRIT3] = INA3221_CHANNEL3,
- [INA3221_WARN1] = INA3221_CHANNEL1,
- [INA3221_WARN2] = INA3221_CHANNEL2,
- [INA3221_WARN3] = INA3221_CHANNEL3,
+/**
+ * struct ina3221_input - channel input source specific information
+ * @label: label of channel input source
+ * @shunt_resistor: shunt resistor value of channel input source
+ * @disconnected: connection status of channel input source
+ */
+struct ina3221_input {
+ const char *label;
+ int shunt_resistor;
+ bool disconnected;
};
/**
* struct ina3221_data - device specific information
* @regmap: Register map of the device
* @fields: Register fields of the device
- * @shunt_resistors: Array of resistor values per channel
+ * @inputs: Array of channel input source specific structures
+ * @reg_config: Register value of INA3221_CONFIG
*/
struct ina3221_data {
struct regmap *regmap;
struct regmap_field *fields[F_MAX_FIELDS];
- int shunt_resistors[INA3221_NUM_CHANNELS];
+ struct ina3221_input inputs[INA3221_NUM_CHANNELS];
+ u32 reg_config;
};
+static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
+{
+ return ina->reg_config & INA3221_CONFIG_CHx_EN(channel);
+}
+
static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
int *val)
{
@@ -113,107 +123,284 @@ static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
return 0;
}
-static ssize_t ina3221_show_bus_voltage(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static const u8 ina3221_in_reg[] = {
+ INA3221_BUS1,
+ INA3221_BUS2,
+ INA3221_BUS3,
+ INA3221_SHUNT1,
+ INA3221_SHUNT2,
+ INA3221_SHUNT3,
+};
+
+static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
+ const bool is_shunt = channel > INA3221_CHANNEL3;
struct ina3221_data *ina = dev_get_drvdata(dev);
- unsigned int reg = sd_attr->index;
- int val, voltage_mv, ret;
-
- ret = ina3221_read_value(ina, reg, &val);
- if (ret)
- return ret;
+ u8 reg = ina3221_in_reg[channel];
+ int regval, ret;
+
+ /* Translate shunt channel index to sensor channel index */
+ channel %= INA3221_NUM_CHANNELS;
+
+ switch (attr) {
+ case hwmon_in_input:
+ if (!ina3221_is_enabled(ina, channel))
+ return -ENODATA;
+
+ ret = ina3221_read_value(ina, reg, &regval);
+ if (ret)
+ return ret;
+
+ /*
+ * Scale of shunt voltage (uV): LSB is 40uV
+ * Scale of bus voltage (mV): LSB is 8mV
+ */
+ *val = regval * (is_shunt ? 40 : 8);
+ return 0;
+ case hwmon_in_enable:
+ *val = ina3221_is_enabled(ina, channel);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
- voltage_mv = val * 8;
+static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = {
+ [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 },
+ [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 },
+ [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 },
+ [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 },
+ [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 },
+};
- return snprintf(buf, PAGE_SIZE, "%d\n", voltage_mv);
+static int ina3221_read_curr(struct device *dev, u32 attr,
+ int channel, long *val)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ struct ina3221_input *input = &ina->inputs[channel];
+ int resistance_uo = input->shunt_resistor;
+ u8 reg = ina3221_curr_reg[attr][channel];
+ int regval, voltage_nv, ret;
+
+ switch (attr) {
+ case hwmon_curr_input:
+ if (!ina3221_is_enabled(ina, channel))
+ return -ENODATA;
+ /* fall through */
+ case hwmon_curr_crit:
+ case hwmon_curr_max:
+ ret = ina3221_read_value(ina, reg, &regval);
+ if (ret)
+ return ret;
+
+ /* Scale of shunt voltage: LSB is 40uV (40000nV) */
+ voltage_nv = regval * 40000;
+ /* Return current in mA */
+ *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
+ return 0;
+ case hwmon_curr_crit_alarm:
+ case hwmon_curr_max_alarm:
+ ret = regmap_field_read(ina->fields[reg], &regval);
+ if (ret)
+ return ret;
+ *val = regval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t ina3221_show_shunt_voltage(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int ina3221_write_curr(struct device *dev, u32 attr,
+ int channel, long val)
{
- struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
- unsigned int reg = sd_attr->index;
- int val, voltage_uv, ret;
+ struct ina3221_input *input = &ina->inputs[channel];
+ int resistance_uo = input->shunt_resistor;
+ u8 reg = ina3221_curr_reg[attr][channel];
+ int regval, current_ma, voltage_uv;
- ret = ina3221_read_value(ina, reg, &val);
- if (ret)
- return ret;
- voltage_uv = val * 40;
+ /* clamp current */
+ current_ma = clamp_val(val,
+ INT_MIN / resistance_uo,
+ INT_MAX / resistance_uo);
+
+ voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000);
- return snprintf(buf, PAGE_SIZE, "%d\n", voltage_uv);
+ /* clamp voltage */
+ voltage_uv = clamp_val(voltage_uv, -163800, 163800);
+
+ /* 1 / 40uV(scale) << 3(register shift) = 5 */
+ regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
+
+ return regmap_write(ina->regmap, reg, regval);
}
-static ssize_t ina3221_show_current(struct device *dev,
- struct device_attribute *attr, char *buf)
+static int ina3221_write_enable(struct device *dev, int channel, bool enable)
{
- struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
- unsigned int reg = sd_attr->index;
- unsigned int channel = register_channel[reg];
- int resistance_uo = ina->shunt_resistors[channel];
- int val, current_ma, voltage_nv, ret;
+ u16 config, mask = INA3221_CONFIG_CHx_EN(channel);
+ int ret;
- ret = ina3221_read_value(ina, reg, &val);
+ config = enable ? mask : 0;
+
+ /* Enable or disable the channel */
+ ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config);
if (ret)
return ret;
- voltage_nv = val * 40000;
- current_ma = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
+ /* Cache the latest config register value */
+ ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
+ if (ret)
+ return ret;
- return snprintf(buf, PAGE_SIZE, "%d\n", current_ma);
+ return 0;
}
-static ssize_t ina3221_set_current(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int ina3221_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_in:
+ /* 0-align channel ID */
+ return ina3221_read_in(dev, attr, channel - 1, val);
+ case hwmon_curr:
+ return ina3221_read_curr(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina3221_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_in:
+ /* 0-align channel ID */
+ return ina3221_write_enable(dev, channel - 1, val);
+ case hwmon_curr:
+ return ina3221_write_curr(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
{
- struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
- unsigned int reg = sd_attr->index;
- unsigned int channel = register_channel[reg];
- int resistance_uo = ina->shunt_resistors[channel];
- int val, current_ma, voltage_uv, ret;
+ int index = channel - 1;
- ret = kstrtoint(buf, 0, &current_ma);
- if (ret)
- return ret;
+ *str = ina->inputs[index].label;
- /* clamp current */
- current_ma = clamp_val(current_ma,
- INT_MIN / resistance_uo,
- INT_MAX / resistance_uo);
+ return 0;
+}
- voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000);
+static umode_t ina3221_is_visible(const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct ina3221_data *ina = drvdata;
+ const struct ina3221_input *input = NULL;
+
+ switch (type) {
+ case hwmon_in:
+ /* Ignore in0_ */
+ if (channel == 0)
+ return 0;
+
+ switch (attr) {
+ case hwmon_in_label:
+ if (channel - 1 <= INA3221_CHANNEL3)
+ input = &ina->inputs[channel - 1];
+ /* Hide label node if label is not provided */
+ return (input && input->label) ? 0444 : 0;
+ case hwmon_in_input:
+ return 0444;
+ case hwmon_in_enable:
+ return 0644;
+ default:
+ return 0;
+ }
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_crit_alarm:
+ case hwmon_curr_max_alarm:
+ return 0444;
+ case hwmon_curr_crit:
+ case hwmon_curr_max:
+ return 0644;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
- /* clamp voltage */
- voltage_uv = clamp_val(voltage_uv, -163800, 163800);
+static const u32 ina3221_in_config[] = {
+ /* 0: dummy, skipped in is_visible */
+ HWMON_I_INPUT,
+ /* 1-3: input voltage Channels */
+ HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
+ /* 4-6: shunt voltage Channels */
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ 0
+};
- /* 1 / 40uV(scale) << 3(register shift) = 5 */
- val = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
+static const struct hwmon_channel_info ina3221_in = {
+ .type = hwmon_in,
+ .config = ina3221_in_config,
+};
- ret = regmap_write(ina->regmap, reg, val);
- if (ret)
- return ret;
+#define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \
+ HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \
+ HWMON_C_MAX | HWMON_C_MAX_ALARM)
- return count;
-}
+static const u32 ina3221_curr_config[] = {
+ INA3221_HWMON_CURR_CONFIG,
+ INA3221_HWMON_CURR_CONFIG,
+ INA3221_HWMON_CURR_CONFIG,
+ 0
+};
+
+static const struct hwmon_channel_info ina3221_curr = {
+ .type = hwmon_curr,
+ .config = ina3221_curr_config,
+};
+
+static const struct hwmon_channel_info *ina3221_info[] = {
+ &ina3221_in,
+ &ina3221_curr,
+ NULL
+};
+
+static const struct hwmon_ops ina3221_hwmon_ops = {
+ .is_visible = ina3221_is_visible,
+ .read_string = ina3221_read_string,
+ .read = ina3221_read,
+ .write = ina3221_write,
+};
+static const struct hwmon_chip_info ina3221_chip_info = {
+ .ops = &ina3221_hwmon_ops,
+ .info = ina3221_info,
+};
+
+/* Extra attribute groups */
static ssize_t ina3221_show_shunt(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int channel = sd_attr->index;
- unsigned int resistance_uo;
-
- resistance_uo = ina->shunt_resistors[channel];
+ struct ina3221_input *input = &ina->inputs[channel];
<