linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion
@ 2016-11-17 12:10 Tom Levens
  2016-11-17 12:10 ` [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding Tom Levens
                   ` (2 more replies)
  0 siblings, 3 replies; 29+ messages in thread
From: Tom Levens @ 2016-11-17 12:10 UTC (permalink / raw)
  To: linux
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon,
	devicetree, Tom Levens

Conversion from raw values to signed integers has been refactored using
the macros in bitops.h.

Signed-off-by: Tom Levens <tom.levens@cern.ch>
---
 drivers/hwmon/ltc2990.c |   27 ++++++++++-----------------
 1 files changed, 10 insertions(+), 17 deletions(-)

diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
index 8f8fe05..0ec4102 100644
--- a/drivers/hwmon/ltc2990.c
+++ b/drivers/hwmon/ltc2990.c
@@ -9,8 +9,12 @@
  * This driver assumes the chip is wired as a dual current monitor, and
  * reports the voltage drop across two series resistors. It also reports
  * the chip's internal temperature and Vcc power supply voltage.
+ *
+ * Value conversion refactored
+ * by Tom Levens <tom.levens@cern.ch>
  */
 
+#include <linux/bitops.h>
 #include <linux/err.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
@@ -34,19 +38,10 @@
 #define LTC2990_CONTROL_MODE_CURRENT	0x06
 #define LTC2990_CONTROL_MODE_VOLTAGE	0x07
 
-/* convert raw register value to sign-extended integer in 16-bit range */
-static int ltc2990_voltage_to_int(int raw)
-{
-	if (raw & BIT(14))
-		return -(0x4000 - (raw & 0x3FFF)) << 2;
-	else
-		return (raw & 0x3FFF) << 2;
-}
-
 /* Return the converted value from the given register in uV or mC */
-static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result)
+static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
 {
-	int val;
+	s32 val;
 
 	val = i2c_smbus_read_word_swapped(i2c, reg);
 	if (unlikely(val < 0))
@@ -55,18 +50,16 @@ static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result)
 	switch (reg) {
 	case LTC2990_TINT_MSB:
 		/* internal temp, 0.0625 degrees/LSB, 13-bit  */
-		val = (val & 0x1FFF) << 3;
-		*result = (val * 1000) >> 7;
+		*result = sign_extend32(val, 12) * 1000 / 16;
 		break;
 	case LTC2990_V1_MSB:
 	case LTC2990_V3_MSB:
 		 /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
-		*result = ltc2990_voltage_to_int(val) * 1942 / (4 * 100);
+		*result = sign_extend32(val, 14) * 1942 / 100;
 		break;
 	case LTC2990_VCC_MSB:
 		/* Vcc, 305.18μV/LSB, 2.5V offset */
-		*result = (ltc2990_voltage_to_int(val) * 30518 /
-			   (4 * 100 * 1000)) + 2500;
+		*result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
 		break;
 	default:
 		return -EINVAL; /* won't happen, keep compiler happy */
@@ -79,7 +72,7 @@ static ssize_t ltc2990_show_value(struct device *dev,
 				  struct device_attribute *da, char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	int value;
+	s32 value;
 	int ret;
 
 	ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding
  2016-11-17 12:10 [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Tom Levens
@ 2016-11-17 12:10 ` Tom Levens
  2016-11-18 14:50   ` Rob Herring
  2016-11-17 12:10 ` [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes Tom Levens
  2016-11-17 15:06 ` [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Guenter Roeck
  2 siblings, 1 reply; 29+ messages in thread
From: Tom Levens @ 2016-11-17 12:10 UTC (permalink / raw)
  To: linux
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon,
	devicetree, Tom Levens

Add a devicetree binding for the ltc2990 hwmon driver.

Signed-off-by: Tom Levens <tom.levens@cern.ch>
---
 .../devicetree/bindings/hwmon/ltc2990.txt          |   28 ++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/hwmon/ltc2990.txt

diff --git a/Documentation/devicetree/bindings/hwmon/ltc2990.txt b/Documentation/devicetree/bindings/hwmon/ltc2990.txt
new file mode 100644
index 0000000..e4040e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ltc2990.txt
@@ -0,0 +1,28 @@
+ltc2990
+
+Required properties:
+- compatible: Must be "lltc,ltc2990"
+- reg: I2C slave address
+
+Optional properties:
+- lltc,mode:
+	Sets the chip's measurement mode, defaults to <6> if unset.
+
+	The following measurements are available in each mode:
+
+	0: V1, V2, TR2
+	1: V1-V2, TR2
+	2: V1-V2, V3, V4
+	3: TR1, V3, V4
+	4: TR1, V3-V4
+	5: TR1, TR2
+	6: V1-V2, V3-V4
+	7: V1, V2, V3, V4
+
+Example:
+
+ltc2990@4c {
+	compatible = "lltc,ltc2990";
+	reg = <0x4c>;
+	lltc,mode = <7>;
+};
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 12:10 [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Tom Levens
  2016-11-17 12:10 ` [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding Tom Levens
@ 2016-11-17 12:10 ` Tom Levens
  2016-11-17 16:56   ` Guenter Roeck
  2016-11-17 15:06 ` [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Guenter Roeck
  2 siblings, 1 reply; 29+ messages in thread
From: Tom Levens @ 2016-11-17 12:10 UTC (permalink / raw)
  To: linux
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon,
	devicetree, Tom Levens

Updated version of the ltc2990 driver which supports all measurement
modes available in the chip. The mode can be set through a devicetree
attribute.

Signed-off-by: Tom Levens <tom.levens@cern.ch>
---

Changes since v1:
  * Refactored value conversion (patch 1/3)
  * Split the devicetree binding into separate patch (patch 2/3)
  * Specifying an invalid mode now returns -EINVAL, previously this
    only issued a warning and used the default value
  * Removed the "mode" sysfs attribute, as the mode of the chip is
    hardware specific and should not be user configurable. This allows much
    simpler code as a result.

 Documentation/hwmon/ltc2990 |   24 ++++---
 drivers/hwmon/Kconfig       |    7 +--
 drivers/hwmon/ltc2990.c     |  167 ++++++++++++++++++++++++++++++++++++-------
 3 files changed, 159 insertions(+), 39 deletions(-)

diff --git a/Documentation/hwmon/ltc2990 b/Documentation/hwmon/ltc2990
index c25211e..3ed68f6 100644
--- a/Documentation/hwmon/ltc2990
+++ b/Documentation/hwmon/ltc2990
@@ -8,6 +8,7 @@ Supported chips:
     Datasheet: http://www.linear.com/product/ltc2990
 
 Author: Mike Looijmans <mike.looijmans@topic.nl>
+        Tom Levens <tom.levens@cern.ch>
 
 
 Description
@@ -16,10 +17,8 @@ Description
 LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
 The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4)
 can be combined to measure a differential voltage, which is typically used to
-measure current through a series resistor, or a temperature.
-
-This driver currently uses the 2x differential mode only. In order to support
-other modes, the driver will need to be expanded.
+measure current through a series resistor, or a temperature with an external
+diode.
 
 
 Usage Notes
@@ -32,12 +31,19 @@ devices explicitly.
 Sysfs attributes
 ----------------
 
+in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
+temp1_input   Internal chip temperature in millidegrees Celcius
+
+A subset of the following attributes are visible, depending on the measurement
+mode of the chip.
+
+in[1-4]_input Voltage at V[1-4] pin in millivolt
+temp2_input   External temperature sensor TR1 in millidegrees Celcius
+temp3_input   External temperature sensor TR2 in millidegrees Celcius
+curr1_input   Current in mA across V1-V2 assuming a 1mOhm sense resistor
+curr2_input   Current in mA across V3-V4 assuming a 1mOhm sense resistor
+
 The "curr*_input" measurements actually report the voltage drop across the
 input pins in microvolts. This is equivalent to the current through a 1mOhm
 sense resistor. Divide the reported value by the actual sense resistor value
 in mOhm to get the actual value.
-
-in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
-temp1_input   Internal chip temperature in millidegrees Celcius
-curr1_input   Current in mA across v1-v2 assuming a 1mOhm sense resistor.
-curr2_input   Current in mA across v3-v4 assuming a 1mOhm sense resistor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 45cef3d..f7096ca 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -699,15 +699,12 @@ config SENSORS_LTC2945
 	  be called ltc2945.
 
 config SENSORS_LTC2990
-	tristate "Linear Technology LTC2990 (current monitoring mode only)"
+	tristate "Linear Technology LTC2990"
 	depends on I2C
 	help
 	  If you say yes here you get support for Linear Technology LTC2990
 	  I2C System Monitor. The LTC2990 supports a combination of voltage,
-	  current and temperature monitoring, but in addition to the Vcc supply
-	  voltage and chip temperature, this driver currently only supports
-	  reading two currents by measuring two differential voltages across
-	  series resistors.
+	  current and temperature monitoring.
 
 	  This driver can also be built as a module. If so, the module will
 	  be called ltc2990.
diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
index 0ec4102..e8d36f5 100644
--- a/drivers/hwmon/ltc2990.c
+++ b/drivers/hwmon/ltc2990.c
@@ -6,11 +6,7 @@
  *
  * License: GPLv2
  *
- * This driver assumes the chip is wired as a dual current monitor, and
- * reports the voltage drop across two series resistors. It also reports
- * the chip's internal temperature and Vcc power supply voltage.
- *
- * Value conversion refactored
+ * Value conversion refactored and support for all measurement modes added
  * by Tom Levens <tom.levens@cern.ch>
  */
 
@@ -21,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
 #define LTC2990_STATUS	0x00
 #define LTC2990_CONTROL	0x01
@@ -35,32 +32,96 @@
 #define LTC2990_CONTROL_KELVIN		BIT(7)
 #define LTC2990_CONTROL_SINGLE		BIT(6)
 #define LTC2990_CONTROL_MEASURE_ALL	(0x3 << 3)
-#define LTC2990_CONTROL_MODE_CURRENT	0x06
-#define LTC2990_CONTROL_MODE_VOLTAGE	0x07
+#define LTC2990_CONTROL_MODE_DEFAULT	0x06
+#define LTC2990_CONTROL_MODE_MAX	0x07
+
+#define LTC2990_IN0	BIT(0)
+#define LTC2990_IN1	BIT(1)
+#define LTC2990_IN2	BIT(2)
+#define LTC2990_IN3	BIT(3)
+#define LTC2990_IN4	BIT(4)
+#define LTC2990_CURR1	BIT(5)
+#define LTC2990_CURR2	BIT(6)
+#define LTC2990_TEMP1	BIT(7)
+#define LTC2990_TEMP2	BIT(8)
+#define LTC2990_TEMP3	BIT(9)
+
+static const int ltc2990_attrs_ena[] = {
+	LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
+	LTC2990_CURR1 | LTC2990_TEMP3,
+	LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
+	LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
+	LTC2990_TEMP2 | LTC2990_CURR2,
+	LTC2990_TEMP2 | LTC2990_TEMP3,
+	LTC2990_CURR1 | LTC2990_CURR2,
+	LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
+};
+
+struct ltc2990_data {
+	struct i2c_client *i2c;
+	u32 mode;
+};
 
 /* Return the converted value from the given register in uV or mC */
-static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
+static int ltc2990_get_value(struct i2c_client *i2c, int index, s32 *result)
 {
 	s32 val;
+	u8 reg;
+
+	switch (index) {
+	case LTC2990_IN0:
+		reg = LTC2990_VCC_MSB;
+		break;
+	case LTC2990_IN1:
+	case LTC2990_CURR1:
+	case LTC2990_TEMP2:
+		reg = LTC2990_V1_MSB;
+		break;
+	case LTC2990_IN2:
+		reg = LTC2990_V2_MSB;
+		break;
+	case LTC2990_IN3:
+	case LTC2990_CURR2:
+	case LTC2990_TEMP3:
+		reg = LTC2990_V3_MSB;
+		break;
+	case LTC2990_IN4:
+		reg = LTC2990_V4_MSB;
+		break;
+	case LTC2990_TEMP1:
+		reg = LTC2990_TINT_MSB;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	val = i2c_smbus_read_word_swapped(i2c, reg);
 	if (unlikely(val < 0))
 		return val;
 
-	switch (reg) {
-	case LTC2990_TINT_MSB:
-		/* internal temp, 0.0625 degrees/LSB, 13-bit  */
+	switch (index) {
+	case LTC2990_TEMP1:
+	case LTC2990_TEMP2:
+	case LTC2990_TEMP3:
+		/* temp, 0.0625 degrees/LSB, 13-bit  */
 		*result = sign_extend32(val, 12) * 1000 / 16;
 		break;
-	case LTC2990_V1_MSB:
-	case LTC2990_V3_MSB:
-		 /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
+	case LTC2990_CURR1:
+	case LTC2990_CURR2:
+		 /* Vx-Vy, 19.42uV/LSB */
 		*result = sign_extend32(val, 14) * 1942 / 100;
 		break;
-	case LTC2990_VCC_MSB:
-		/* Vcc, 305.18μV/LSB, 2.5V offset */
+	case LTC2990_IN0:
+		/* Vcc, 305.18uV/LSB, 2.5V offset */
 		*result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
 		break;
+	case LTC2990_IN1:
+	case LTC2990_IN2:
+	case LTC2990_IN3:
+	case LTC2990_IN4:
+		/* Vx: 305.18uV/LSB */
+		*result = sign_extend32(val, 14) * 30518 / (100 * 1000);
+		break;
 	default:
 		return -EINVAL; /* won't happen, keep compiler happy */
 	}
@@ -72,48 +133,104 @@ static ssize_t ltc2990_show_value(struct device *dev,
 				  struct device_attribute *da, char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct ltc2990_data *data = dev_get_drvdata(dev);
 	s32 value;
 	int ret;
 
-	ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
+	ret = ltc2990_get_value(data->i2c, attr->index, &value);
 	if (unlikely(ret < 0))
 		return ret;
 
 	return snprintf(buf, PAGE_SIZE, "%d\n", value);
 }
 
+static umode_t ltc2990_attrs_visible(struct kobject *kobj,
+				     struct attribute *a, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct ltc2990_data *data = dev_get_drvdata(dev);
+	struct device_attribute *da =
+			container_of(a, struct device_attribute, attr);
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+
+	if (attr->index == LTC2990_TEMP1 ||
+	    attr->index == LTC2990_IN0 ||
+	    attr->index & ltc2990_attrs_ena[data->mode])
+		return a->mode;
+	else
+		return 0;
+}
+
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
-			  LTC2990_TINT_MSB);
+			  LTC2990_TEMP1);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, ltc2990_show_value, NULL,
+			  LTC2990_TEMP2);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, ltc2990_show_value, NULL,
+			  LTC2990_TEMP3);
 static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
-			  LTC2990_V1_MSB);
+			  LTC2990_CURR1);
 static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
-			  LTC2990_V3_MSB);
+			  LTC2990_CURR2);
 static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
-			  LTC2990_VCC_MSB);
+			  LTC2990_IN0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2990_show_value, NULL,
+			  LTC2990_IN1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2990_show_value, NULL,
+			  LTC2990_IN2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc2990_show_value, NULL,
+			  LTC2990_IN3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc2990_show_value, NULL,
+			  LTC2990_IN4);
 
 static struct attribute *ltc2990_attrs[] = {
 	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp2_input.dev_attr.attr,
+	&sensor_dev_attr_temp3_input.dev_attr.attr,
 	&sensor_dev_attr_curr1_input.dev_attr.attr,
 	&sensor_dev_attr_curr2_input.dev_attr.attr,
 	&sensor_dev_attr_in0_input.dev_attr.attr,
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_in2_input.dev_attr.attr,
+	&sensor_dev_attr_in3_input.dev_attr.attr,
+	&sensor_dev_attr_in4_input.dev_attr.attr,
 	NULL,
 };
-ATTRIBUTE_GROUPS(ltc2990);
+
+static const struct attribute_group ltc2990_group = {
+	.attrs = ltc2990_attrs,
+	.is_visible = ltc2990_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(ltc2990);
 
 static int ltc2990_i2c_probe(struct i2c_client *i2c,
 			     const struct i2c_device_id *id)
 {
 	int ret;
 	struct device *hwmon_dev;
+	struct ltc2990_data *data;
+	struct device_node *of_node = i2c->dev.of_node;
 
 	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
 				     I2C_FUNC_SMBUS_WORD_DATA))
 		return -ENODEV;
 
-	/* Setup continuous mode, current monitor */
+	data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data), GFP_KERNEL);
+	if (unlikely(!data))
+		return -ENOMEM;
+	data->i2c = i2c;
+
+	if (!of_node || of_property_read_u32(of_node, "lltc,mode", &data->mode))
+		data->mode = LTC2990_CONTROL_MODE_DEFAULT;
+
+	if (data->mode > LTC2990_CONTROL_MODE_MAX) {
+		dev_err(&i2c->dev, "Error: Invalid mode %d.\n", data->mode);
+		return -EINVAL;
+	}
+
+	/* Setup continuous mode */
 	ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
 					LTC2990_CONTROL_MEASURE_ALL |
-					LTC2990_CONTROL_MODE_CURRENT);
+					data->mode);
 	if (ret < 0) {
 		dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
 		return ret;
@@ -127,7 +244,7 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
 
 	hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
 							   i2c->name,
-							   i2c,
+							   data,
 							   ltc2990_groups);
 
 	return PTR_ERR_OR_ZERO(hwmon_dev);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion
  2016-11-17 12:10 [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Tom Levens
  2016-11-17 12:10 ` [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding Tom Levens
  2016-11-17 12:10 ` [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes Tom Levens
@ 2016-11-17 15:06 ` Guenter Roeck
  2016-11-17 16:23   ` Tom Levens
  2 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2016-11-17 15:06 UTC (permalink / raw)
  To: Tom Levens
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

Hi Tom,

On 11/17/2016 04:10 AM, Tom Levens wrote:
> Conversion from raw values to signed integers has been refactored using
> the macros in bitops.h.
>
Please also mention that this fixes a bug in negative temperature conversions.

> Signed-off-by: Tom Levens <tom.levens@cern.ch>
> ---
>  drivers/hwmon/ltc2990.c |   27 ++++++++++-----------------
>  1 files changed, 10 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
> index 8f8fe05..0ec4102 100644
> --- a/drivers/hwmon/ltc2990.c
> +++ b/drivers/hwmon/ltc2990.c
> @@ -9,8 +9,12 @@
>   * This driver assumes the chip is wired as a dual current monitor, and
>   * reports the voltage drop across two series resistors. It also reports
>   * the chip's internal temperature and Vcc power supply voltage.
> + *
> + * Value conversion refactored
> + * by Tom Levens <tom.levens@cern.ch>

Kind of unusual to do that for minor changes like this. Imagine if everyone would do that.
The commit log is what gives you credit.

>   */
>
> +#include <linux/bitops.h>
>  #include <linux/err.h>
>  #include <linux/hwmon.h>
>  #include <linux/hwmon-sysfs.h>
> @@ -34,19 +38,10 @@
>  #define LTC2990_CONTROL_MODE_CURRENT	0x06
>  #define LTC2990_CONTROL_MODE_VOLTAGE	0x07
>
> -/* convert raw register value to sign-extended integer in 16-bit range */
> -static int ltc2990_voltage_to_int(int raw)
> -{
> -	if (raw & BIT(14))
> -		return -(0x4000 - (raw & 0x3FFF)) << 2;
> -	else
> -		return (raw & 0x3FFF) << 2;
> -}
> -
>  /* Return the converted value from the given register in uV or mC */
> -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result)
> +static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
>  {
> -	int val;
> +	s32 val;

Please just leave the variable type alone. it is also used for the return value
from i2c_smbus_read_word_swapped(), which is an int, and changing it to s32 doesn't
really make the code better.

Can you send me a register map for the chip ? I would like to write a module test.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion
  2016-11-17 15:06 ` [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Guenter Roeck
@ 2016-11-17 16:23   ` Tom Levens
  2016-11-17 16:36     ` Guenter Roeck
  0 siblings, 1 reply; 29+ messages in thread
From: Tom Levens @ 2016-11-17 16:23 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Tom Levens, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

Hi Guenter,

Thanks for taking the time to review the patch.

On Thu, 17 Nov 2016, Guenter Roeck wrote:

> Hi Tom,
>
> On 11/17/2016 04:10 AM, Tom Levens wrote:
>>  Conversion from raw values to signed integers has been refactored using
>>  the macros in bitops.h.
>> 
> Please also mention that this fixes a bug in negative temperature 
> conversions.

Yes, of course, I will include the information in v3.

>
>>  Signed-off-by: Tom Levens <tom.levens@cern.ch>
>>  ---
>>   drivers/hwmon/ltc2990.c |   27 ++++++++++-----------------
>>   1 files changed, 10 insertions(+), 17 deletions(-)
>>
>>  diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
>>  index 8f8fe05..0ec4102 100644
>>  --- a/drivers/hwmon/ltc2990.c
>>  +++ b/drivers/hwmon/ltc2990.c
>>  @@ -9,8 +9,12 @@
>>    * This driver assumes the chip is wired as a dual current monitor, and
>>    * reports the voltage drop across two series resistors. It also reports
>>    * the chip's internal temperature and Vcc power supply voltage.
>>  + *
>>  + * Value conversion refactored
>>  + * by Tom Levens <tom.levens@cern.ch>
>
> Kind of unusual to do that for minor changes like this. Imagine if everyone would do that.
> The commit log is what gives you credit.

Good point, thanks for the hint. I will remove it from v3.

>>    */
>>
>>  +#include <linux/bitops.h>
>>   #include <linux/err.h>
>>   #include <linux/hwmon.h>
>>   #include <linux/hwmon-sysfs.h>
>>  @@ -34,19 +38,10 @@
>>   #define LTC2990_CONTROL_MODE_CURRENT	0x06
>>   #define LTC2990_CONTROL_MODE_VOLTAGE	0x07
>>
>>  -/* convert raw register value to sign-extended integer in 16-bit range */
>>  -static int ltc2990_voltage_to_int(int raw)
>>  -{
>>  -	if (raw & BIT(14))
>>  -		return -(0x4000 - (raw & 0x3FFF)) << 2;
>>  -	else
>>  -		return (raw & 0x3FFF) << 2;
>>  -}
>>  -
>>  /* Return the converted value from the given register in uV or mC */
>>  -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result)
>>  +static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
>>  {
>>  -	int val;
>>  +	s32 val;
>
> Please just leave the variable type alone. it is also used for the return value
> from i2c_smbus_read_word_swapped(), which is an int, and changing it to s32 
> doesn't really make the code better.

According to i2c.h the return type for i2c_smbus_read_word_swapped() is 
s32, which is why I modified it here. But it could be changed back if you 
think it is better to leave it as an int.

> Can you send me a register map for the chip ? I would like to write a module test.

Here is an example register dump:

00 00 00 00
01 90 07 d0
2c cd 7d 80
7c 29 20 00

The expected values in this case are:

in0_input 	5000
in1_input	610
in2_input	3500
in3_input	-195
in4_input	-299
temp1_input 	25000
temp2_input 	125000
temp3_input	-40000
curr1_input	38840
curr2_input	-12428

Testing with lltc,mode set to <5>, <6> and <7> should give you all 
measurements.

> Thanks,
> Guenter

Cheers,

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion
  2016-11-17 16:23   ` Tom Levens
@ 2016-11-17 16:36     ` Guenter Roeck
  2016-11-18  8:18       ` Tom Levens
  0 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2016-11-17 16:36 UTC (permalink / raw)
  To: Tom Levens
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

On 11/17/2016 08:23 AM, Tom Levens wrote:
> Hi Guenter,
>
> Thanks for taking the time to review the patch.
>
> On Thu, 17 Nov 2016, Guenter Roeck wrote:
>
>> Hi Tom,
>>
>> On 11/17/2016 04:10 AM, Tom Levens wrote:
>>>  Conversion from raw values to signed integers has been refactored using
>>>  the macros in bitops.h.
>>>
>> Please also mention that this fixes a bug in negative temperature conversions.
>
> Yes, of course, I will include the information in v3.
>
>>
>>>  Signed-off-by: Tom Levens <tom.levens@cern.ch>
>>>  ---
>>>   drivers/hwmon/ltc2990.c |   27 ++++++++++-----------------
>>>   1 files changed, 10 insertions(+), 17 deletions(-)
>>>
>>>  diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
>>>  index 8f8fe05..0ec4102 100644
>>>  --- a/drivers/hwmon/ltc2990.c
>>>  +++ b/drivers/hwmon/ltc2990.c
>>>  @@ -9,8 +9,12 @@
>>>    * This driver assumes the chip is wired as a dual current monitor, and
>>>    * reports the voltage drop across two series resistors. It also reports
>>>    * the chip's internal temperature and Vcc power supply voltage.
>>>  + *
>>>  + * Value conversion refactored
>>>  + * by Tom Levens <tom.levens@cern.ch>
>>
>> Kind of unusual to do that for minor changes like this. Imagine if everyone would do that.
>> The commit log is what gives you credit.
>
> Good point, thanks for the hint. I will remove it from v3.
>
>>>    */
>>>
>>>  +#include <linux/bitops.h>
>>>   #include <linux/err.h>
>>>   #include <linux/hwmon.h>
>>>   #include <linux/hwmon-sysfs.h>
>>>  @@ -34,19 +38,10 @@
>>>   #define LTC2990_CONTROL_MODE_CURRENT    0x06
>>>   #define LTC2990_CONTROL_MODE_VOLTAGE    0x07
>>>
>>>  -/* convert raw register value to sign-extended integer in 16-bit range */
>>>  -static int ltc2990_voltage_to_int(int raw)
>>>  -{
>>>  -    if (raw & BIT(14))
>>>  -        return -(0x4000 - (raw & 0x3FFF)) << 2;
>>>  -    else
>>>  -        return (raw & 0x3FFF) << 2;
>>>  -}
>>>  -
>>>  /* Return the converted value from the given register in uV or mC */
>>>  -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result)
>>>  +static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
>>>  {
>>>  -    int val;
>>>  +    s32 val;
>>
>> Please just leave the variable type alone. it is also used for the return value
>> from i2c_smbus_read_word_swapped(), which is an int, and changing it to s32 doesn't really make the code better.
>
> According to i2c.h the return type for i2c_smbus_read_word_swapped() is s32, which is why I modified it here. But it could be changed back if you think it is better to leave it as an int.
>
Ah, ok. Good to know. Please leave it anyway, reason being that there is no real
reason to change it. Effectively those are just whitespace changes (unlike the rest,
which is part bug fix, part cleanup).

>> Can you send me a register map for the chip ? I would like to write a module test.
>
> Here is an example register dump:

I meant the output of i2cdump (something like "i2cdump -y -f <bus> <i2c-address> w").

Thanks,
Guenter

>
> 00 00 00 00
> 01 90 07 d0
> 2c cd 7d 80
> 7c 29 20 00
>
> The expected values in this case are:
>
> in0_input     5000
> in1_input    610
> in2_input    3500
> in3_input    -195
> in4_input    -299
> temp1_input     25000
> temp2_input     125000
> temp3_input    -40000
> curr1_input    38840
> curr2_input    -12428
>
> Testing with lltc,mode set to <5>, <6> and <7> should give you all measurements.
>
>> Thanks,
>> Guenter
>
> Cheers,
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 12:10 ` [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes Tom Levens
@ 2016-11-17 16:56   ` Guenter Roeck
  2016-11-17 17:40     ` Mike Looijmans
  2017-06-28 14:24     ` Mike Looijmans
  0 siblings, 2 replies; 29+ messages in thread
From: Guenter Roeck @ 2016-11-17 16:56 UTC (permalink / raw)
  To: Tom Levens
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon,
	devicetree, Mike Looijmans

On 11/17/2016 04:10 AM, Tom Levens wrote:
> Updated version of the ltc2990 driver which supports all measurement
> modes available in the chip. The mode can be set through a devicetree
> attribute.

property

>
> Signed-off-by: Tom Levens <tom.levens@cern.ch>
> ---
>
> Changes since v1:
>   * Refactored value conversion (patch 1/3)
>   * Split the devicetree binding into separate patch (patch 2/3)
>   * Specifying an invalid mode now returns -EINVAL, previously this
>     only issued a warning and used the default value
>   * Removed the "mode" sysfs attribute, as the mode of the chip is
>     hardware specific and should not be user configurable. This allows much
>     simpler code as a result.
>
>  Documentation/hwmon/ltc2990 |   24 ++++---
>  drivers/hwmon/Kconfig       |    7 +--
>  drivers/hwmon/ltc2990.c     |  167 ++++++++++++++++++++++++++++++++++++-------
>  3 files changed, 159 insertions(+), 39 deletions(-)
>
> diff --git a/Documentation/hwmon/ltc2990 b/Documentation/hwmon/ltc2990
> index c25211e..3ed68f6 100644
> --- a/Documentation/hwmon/ltc2990
> +++ b/Documentation/hwmon/ltc2990
> @@ -8,6 +8,7 @@ Supported chips:
>      Datasheet: http://www.linear.com/product/ltc2990
>
>  Author: Mike Looijmans <mike.looijmans@topic.nl>
> +        Tom Levens <tom.levens@cern.ch>
>
>
>  Description
> @@ -16,10 +17,8 @@ Description
>  LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
>  The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4)
>  can be combined to measure a differential voltage, which is typically used to
> -measure current through a series resistor, or a temperature.
> -
> -This driver currently uses the 2x differential mode only. In order to support
> -other modes, the driver will need to be expanded.
> +measure current through a series resistor, or a temperature with an external
> +diode.
>
>
>  Usage Notes
> @@ -32,12 +31,19 @@ devices explicitly.
>  Sysfs attributes
>  ----------------
>
> +in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
> +temp1_input   Internal chip temperature in millidegrees Celcius
> +
> +A subset of the following attributes are visible, depending on the measurement
> +mode of the chip.
> +
> +in[1-4]_input Voltage at V[1-4] pin in millivolt
> +temp2_input   External temperature sensor TR1 in millidegrees Celcius
> +temp3_input   External temperature sensor TR2 in millidegrees Celcius
> +curr1_input   Current in mA across V1-V2 assuming a 1mOhm sense resistor
> +curr2_input   Current in mA across V3-V4 assuming a 1mOhm sense resistor
> +
>  The "curr*_input" measurements actually report the voltage drop across the
>  input pins in microvolts. This is equivalent to the current through a 1mOhm
>  sense resistor. Divide the reported value by the actual sense resistor value
>  in mOhm to get the actual value.
> -
> -in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
> -temp1_input   Internal chip temperature in millidegrees Celcius
> -curr1_input   Current in mA across v1-v2 assuming a 1mOhm sense resistor.
> -curr2_input   Current in mA across v3-v4 assuming a 1mOhm sense resistor.
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 45cef3d..f7096ca 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -699,15 +699,12 @@ config SENSORS_LTC2945
>  	  be called ltc2945.
>
>  config SENSORS_LTC2990
> -	tristate "Linear Technology LTC2990 (current monitoring mode only)"
> +	tristate "Linear Technology LTC2990"
>  	depends on I2C
>  	help
>  	  If you say yes here you get support for Linear Technology LTC2990
>  	  I2C System Monitor. The LTC2990 supports a combination of voltage,
> -	  current and temperature monitoring, but in addition to the Vcc supply
> -	  voltage and chip temperature, this driver currently only supports
> -	  reading two currents by measuring two differential voltages across
> -	  series resistors.
> +	  current and temperature monitoring.
>
>  	  This driver can also be built as a module. If so, the module will
>  	  be called ltc2990.
> diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
> index 0ec4102..e8d36f5 100644
> --- a/drivers/hwmon/ltc2990.c
> +++ b/drivers/hwmon/ltc2990.c
> @@ -6,11 +6,7 @@
>   *
>   * License: GPLv2
>   *
> - * This driver assumes the chip is wired as a dual current monitor, and
> - * reports the voltage drop across two series resistors. It also reports
> - * the chip's internal temperature and Vcc power supply voltage.
> - *
> - * Value conversion refactored
> + * Value conversion refactored and support for all measurement modes added
>   * by Tom Levens <tom.levens@cern.ch>
>   */
>
> @@ -21,6 +17,7 @@
>  #include <linux/i2c.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
>
>  #define LTC2990_STATUS	0x00
>  #define LTC2990_CONTROL	0x01
> @@ -35,32 +32,96 @@
>  #define LTC2990_CONTROL_KELVIN		BIT(7)
>  #define LTC2990_CONTROL_SINGLE		BIT(6)
>  #define LTC2990_CONTROL_MEASURE_ALL	(0x3 << 3)
> -#define LTC2990_CONTROL_MODE_CURRENT	0x06
> -#define LTC2990_CONTROL_MODE_VOLTAGE	0x07
> +#define LTC2990_CONTROL_MODE_DEFAULT	0x06

I think LTC2990_CONTROL_MODE_CURRENT was better - it describes the actual mode.
Changing the define to _DEFAULT does not really improve code readability.

> +#define LTC2990_CONTROL_MODE_MAX	0x07
> +
> +#define LTC2990_IN0	BIT(0)
> +#define LTC2990_IN1	BIT(1)
> +#define LTC2990_IN2	BIT(2)
> +#define LTC2990_IN3	BIT(3)
> +#define LTC2990_IN4	BIT(4)
> +#define LTC2990_CURR1	BIT(5)
> +#define LTC2990_CURR2	BIT(6)
> +#define LTC2990_TEMP1	BIT(7)
> +#define LTC2990_TEMP2	BIT(8)
> +#define LTC2990_TEMP3	BIT(9)
> +
> +static const int ltc2990_attrs_ena[] = {
> +	LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
> +	LTC2990_CURR1 | LTC2990_TEMP3,
> +	LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
> +	LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
> +	LTC2990_TEMP2 | LTC2990_CURR2,
> +	LTC2990_TEMP2 | LTC2990_TEMP3,
> +	LTC2990_CURR1 | LTC2990_CURR2,
> +	LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
> +};
> +
> +struct ltc2990_data {
> +	struct i2c_client *i2c;
> +	u32 mode;
> +};
>
>  /* Return the converted value from the given register in uV or mC */
> -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
> +static int ltc2990_get_value(struct i2c_client *i2c, int index, s32 *result)
>  {
>  	s32 val;
> +	u8 reg;
> +
> +	switch (index) {
> +	case LTC2990_IN0:
> +		reg = LTC2990_VCC_MSB;
> +		break;
> +	case LTC2990_IN1:
> +	case LTC2990_CURR1:
> +	case LTC2990_TEMP2:
> +		reg = LTC2990_V1_MSB;
> +		break;
> +	case LTC2990_IN2:
> +		reg = LTC2990_V2_MSB;
> +		break;
> +	case LTC2990_IN3:
> +	case LTC2990_CURR2:
> +	case LTC2990_TEMP3:
> +		reg = LTC2990_V3_MSB;
> +		break;
> +	case LTC2990_IN4:
> +		reg = LTC2990_V4_MSB;
> +		break;
> +	case LTC2990_TEMP1:
> +		reg = LTC2990_TINT_MSB;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
>
>  	val = i2c_smbus_read_word_swapped(i2c, reg);
>  	if (unlikely(val < 0))
>  		return val;
>
> -	switch (reg) {
> -	case LTC2990_TINT_MSB:
> -		/* internal temp, 0.0625 degrees/LSB, 13-bit  */
> +	switch (index) {
> +	case LTC2990_TEMP1:
> +	case LTC2990_TEMP2:
> +	case LTC2990_TEMP3:
> +		/* temp, 0.0625 degrees/LSB, 13-bit  */
>  		*result = sign_extend32(val, 12) * 1000 / 16;
>  		break;
> -	case LTC2990_V1_MSB:
> -	case LTC2990_V3_MSB:
> -		 /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
> +	case LTC2990_CURR1:
> +	case LTC2990_CURR2:
> +		 /* Vx-Vy, 19.42uV/LSB */
>  		*result = sign_extend32(val, 14) * 1942 / 100;
>  		break;
> -	case LTC2990_VCC_MSB:
> -		/* Vcc, 305.18μV/LSB, 2.5V offset */
> +	case LTC2990_IN0:
> +		/* Vcc, 305.18uV/LSB, 2.5V offset */
>  		*result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
>  		break;
> +	case LTC2990_IN1:
> +	case LTC2990_IN2:
> +	case LTC2990_IN3:
> +	case LTC2990_IN4:
> +		/* Vx: 305.18uV/LSB */
> +		*result = sign_extend32(val, 14) * 30518 / (100 * 1000);
> +		break;
>  	default:
>  		return -EINVAL; /* won't happen, keep compiler happy */
>  	}
> @@ -72,48 +133,104 @@ static ssize_t ltc2990_show_value(struct device *dev,
>  				  struct device_attribute *da, char *buf)
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ltc2990_data *data = dev_get_drvdata(dev);
>  	s32 value;
>  	int ret;
>
> -	ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
> +	ret = ltc2990_get_value(data->i2c, attr->index, &value);
>  	if (unlikely(ret < 0))
>  		return ret;
>
>  	return snprintf(buf, PAGE_SIZE, "%d\n", value);
>  }
>
> +static umode_t ltc2990_attrs_visible(struct kobject *kobj,
> +				     struct attribute *a, int n)
> +{
> +	struct device *dev = container_of(kobj, struct device, kobj);
> +	struct ltc2990_data *data = dev_get_drvdata(dev);
> +	struct device_attribute *da =
> +			container_of(a, struct device_attribute, attr);
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +
> +	if (attr->index == LTC2990_TEMP1 ||
> +	    attr->index == LTC2990_IN0 ||
> +	    attr->index & ltc2990_attrs_ena[data->mode])
> +		return a->mode;
> +	else

Unnecessary else

> +		return 0;
> +}
> +
>  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
> -			  LTC2990_TINT_MSB);
> +			  LTC2990_TEMP1);
> +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, ltc2990_show_value, NULL,
> +			  LTC2990_TEMP2);
> +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, ltc2990_show_value, NULL,
> +			  LTC2990_TEMP3);
>  static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
> -			  LTC2990_V1_MSB);
> +			  LTC2990_CURR1);
>  static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
> -			  LTC2990_V3_MSB);
> +			  LTC2990_CURR2);
>  static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
> -			  LTC2990_VCC_MSB);
> +			  LTC2990_IN0);
> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2990_show_value, NULL,
> +			  LTC2990_IN1);
> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2990_show_value, NULL,
> +			  LTC2990_IN2);
> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc2990_show_value, NULL,
> +			  LTC2990_IN3);
> +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc2990_show_value, NULL,
> +			  LTC2990_IN4);
>
>  static struct attribute *ltc2990_attrs[] = {
>  	&sensor_dev_attr_temp1_input.dev_attr.attr,
> +	&sensor_dev_attr_temp2_input.dev_attr.attr,
> +	&sensor_dev_attr_temp3_input.dev_attr.attr,
>  	&sensor_dev_attr_curr1_input.dev_attr.attr,
>  	&sensor_dev_attr_curr2_input.dev_attr.attr,
>  	&sensor_dev_attr_in0_input.dev_attr.attr,
> +	&sensor_dev_attr_in1_input.dev_attr.attr,
> +	&sensor_dev_attr_in2_input.dev_attr.attr,
> +	&sensor_dev_attr_in3_input.dev_attr.attr,
> +	&sensor_dev_attr_in4_input.dev_attr.attr,
>  	NULL,
>  };
> -ATTRIBUTE_GROUPS(ltc2990);
> +
> +static const struct attribute_group ltc2990_group = {
> +	.attrs = ltc2990_attrs,
> +	.is_visible = ltc2990_attrs_visible,
> +};
> +__ATTRIBUTE_GROUPS(ltc2990);
>
>  static int ltc2990_i2c_probe(struct i2c_client *i2c,
>  			     const struct i2c_device_id *id)
>  {
>  	int ret;
>  	struct device *hwmon_dev;
> +	struct ltc2990_data *data;
> +	struct device_node *of_node = i2c->dev.of_node;
>
>  	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
>  				     I2C_FUNC_SMBUS_WORD_DATA))
>  		return -ENODEV;
>
> -	/* Setup continuous mode, current monitor */
> +	data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data), GFP_KERNEL);
> +	if (unlikely(!data))
> +		return -ENOMEM;
> +	data->i2c = i2c;
> +
> +	if (!of_node || of_property_read_u32(of_node, "lltc,mode", &data->mode))
> +		data->mode = LTC2990_CONTROL_MODE_DEFAULT;

Iam arguing with myself if we should still do this or if we should read the mode
from the chip instead if it isn't provided (after all, it may have been initialized
by the BIOS/ROMMON).

Mike, would that break your application, or can you specify the mode in devicetree ?

Thanks,
Guenter

> +
> +	if (data->mode > LTC2990_CONTROL_MODE_MAX) {
> +		dev_err(&i2c->dev, "Error: Invalid mode %d.\n", data->mode);
> +		return -EINVAL;
> +	}
> +
> +	/* Setup continuous mode */
>  	ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
>  					LTC2990_CONTROL_MEASURE_ALL |
> -					LTC2990_CONTROL_MODE_CURRENT);
> +					data->mode);
>  	if (ret < 0) {
>  		dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
>  		return ret;
> @@ -127,7 +244,7 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
>
>  	hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
>  							   i2c->name,
> -							   i2c,
> +							   data,
>  							   ltc2990_groups);
>
>  	return PTR_ERR_OR_ZERO(hwmon_dev);
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 16:56   ` Guenter Roeck
@ 2016-11-17 17:40     ` Mike Looijmans
  2016-11-17 18:56       ` Guenter Roeck
  2017-06-28 14:24     ` Mike Looijmans
  1 sibling, 1 reply; 29+ messages in thread
From: Mike Looijmans @ 2016-11-17 17:40 UTC (permalink / raw)
  To: Guenter Roeck, Tom Levens
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

On 17-11-16 17:56, Guenter Roeck wrote:
> On 11/17/2016 04:10 AM, Tom Levens wrote:
>> Updated version of the ltc2990 driver which supports all measurement
>> modes available in the chip. The mode can be set through a devicetree
>> attribute.
>
> property
>
>>
>> Signed-off-by: Tom Levens <tom.levens@cern.ch>
>> ---
>>
>> Changes since v1:
>>   * Refactored value conversion (patch 1/3)
>>   * Split the devicetree binding into separate patch (patch 2/3)
>>   * Specifying an invalid mode now returns -EINVAL, previously this
>>     only issued a warning and used the default value
>>   * Removed the "mode" sysfs attribute, as the mode of the chip is
>>     hardware specific and should not be user configurable. This allows
>> much
>>     simpler code as a result.
>>
>>  Documentation/hwmon/ltc2990 |   24 ++++---
>>  drivers/hwmon/Kconfig       |    7 +--
>>  drivers/hwmon/ltc2990.c     |  167
>> ++++++++++++++++++++++++++++++++++++-------
>>  3 files changed, 159 insertions(+), 39 deletions(-)
>>
>> diff --git a/Documentation/hwmon/ltc2990 b/Documentation/hwmon/ltc2990
>> index c25211e..3ed68f6 100644
>> --- a/Documentation/hwmon/ltc2990
>> +++ b/Documentation/hwmon/ltc2990
>> @@ -8,6 +8,7 @@ Supported chips:
>>      Datasheet: http://www.linear.com/product/ltc2990
>>
>>  Author: Mike Looijmans <mike.looijmans@topic.nl>
>> +        Tom Levens <tom.levens@cern.ch>
>>
>>
>>  Description
>> @@ -16,10 +17,8 @@ Description
>>  LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
>>  The chip's inputs can measure 4 voltages, or two inputs together (1+2
>> and 3+4)
>>  can be combined to measure a differential voltage, which is typically
>> used to
>> -measure current through a series resistor, or a temperature.
>> -
>> -This driver currently uses the 2x differential mode only. In order to
>> support
>> -other modes, the driver will need to be expanded.
>> +measure current through a series resistor, or a temperature with an
>> external
>> +diode.
>>
>>
>>  Usage Notes
>> @@ -32,12 +31,19 @@ devices explicitly.
>>  Sysfs attributes
>>  ----------------
>>
>> +in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
>> +temp1_input   Internal chip temperature in millidegrees Celcius
>> +
>> +A subset of the following attributes are visible, depending on the
>> measurement
>> +mode of the chip.
>> +
>> +in[1-4]_input Voltage at V[1-4] pin in millivolt
>> +temp2_input   External temperature sensor TR1 in millidegrees Celcius
>> +temp3_input   External temperature sensor TR2 in millidegrees Celcius
>> +curr1_input   Current in mA across V1-V2 assuming a 1mOhm sense resistor
>> +curr2_input   Current in mA across V3-V4 assuming a 1mOhm sense resistor
>> +
>>  The "curr*_input" measurements actually report the voltage drop
>> across the
>>  input pins in microvolts. This is equivalent to the current through a
>> 1mOhm
>>  sense resistor. Divide the reported value by the actual sense
>> resistor value
>>  in mOhm to get the actual value.
>> -
>> -in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
>> -temp1_input   Internal chip temperature in millidegrees Celcius
>> -curr1_input   Current in mA across v1-v2 assuming a 1mOhm sense
>> resistor.
>> -curr2_input   Current in mA across v3-v4 assuming a 1mOhm sense
>> resistor.
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 45cef3d..f7096ca 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -699,15 +699,12 @@ config SENSORS_LTC2945
>>        be called ltc2945.
>>
>>  config SENSORS_LTC2990
>> -    tristate "Linear Technology LTC2990 (current monitoring mode only)"
>> +    tristate "Linear Technology LTC2990"
>>      depends on I2C
>>      help
>>        If you say yes here you get support for Linear Technology LTC2990
>>        I2C System Monitor. The LTC2990 supports a combination of voltage,
>> -      current and temperature monitoring, but in addition to the Vcc
>> supply
>> -      voltage and chip temperature, this driver currently only supports
>> -      reading two currents by measuring two differential voltages across
>> -      series resistors.
>> +      current and temperature monitoring.
>>
>>        This driver can also be built as a module. If so, the module will
>>        be called ltc2990.
>> diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
>> index 0ec4102..e8d36f5 100644
>> --- a/drivers/hwmon/ltc2990.c
>> +++ b/drivers/hwmon/ltc2990.c
>> @@ -6,11 +6,7 @@
>>   *
>>   * License: GPLv2
>>   *
>> - * This driver assumes the chip is wired as a dual current monitor, and
>> - * reports the voltage drop across two series resistors. It also reports
>> - * the chip's internal temperature and Vcc power supply voltage.
>> - *
>> - * Value conversion refactored
>> + * Value conversion refactored and support for all measurement modes
>> added
>>   * by Tom Levens <tom.levens@cern.ch>
>>   */
>>
>> @@ -21,6 +17,7 @@
>>  #include <linux/i2c.h>
>>  #include <linux/kernel.h>
>>  #include <linux/module.h>
>> +#include <linux/of.h>
>>
>>  #define LTC2990_STATUS    0x00
>>  #define LTC2990_CONTROL    0x01
>> @@ -35,32 +32,96 @@
>>  #define LTC2990_CONTROL_KELVIN        BIT(7)
>>  #define LTC2990_CONTROL_SINGLE        BIT(6)
>>  #define LTC2990_CONTROL_MEASURE_ALL    (0x3 << 3)
>> -#define LTC2990_CONTROL_MODE_CURRENT    0x06
>> -#define LTC2990_CONTROL_MODE_VOLTAGE    0x07
>> +#define LTC2990_CONTROL_MODE_DEFAULT    0x06
>
> I think LTC2990_CONTROL_MODE_CURRENT was better - it describes the
> actual mode.
> Changing the define to _DEFAULT does not really improve code readability.

I think I understand what the author intended - the mode becomes an 
index into the array.

But I think the define can be dropped altogether, see below.

>
>> +#define LTC2990_CONTROL_MODE_MAX    0x07
>> +
>> +#define LTC2990_IN0    BIT(0)
>> +#define LTC2990_IN1    BIT(1)
>> +#define LTC2990_IN2    BIT(2)
>> +#define LTC2990_IN3    BIT(3)
>> +#define LTC2990_IN4    BIT(4)
>> +#define LTC2990_CURR1    BIT(5)
>> +#define LTC2990_CURR2    BIT(6)
>> +#define LTC2990_TEMP1    BIT(7)
>> +#define LTC2990_TEMP2    BIT(8)
>> +#define LTC2990_TEMP3    BIT(9)
>> +
>> +static const int ltc2990_attrs_ena[] = {
>> +    LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
>> +    LTC2990_CURR1 | LTC2990_TEMP3,
>> +    LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
>> +    LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
>> +    LTC2990_TEMP2 | LTC2990_CURR2,
>> +    LTC2990_TEMP2 | LTC2990_TEMP3,
>> +    LTC2990_CURR1 | LTC2990_CURR2,
>> +    LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
>> +};
>> +
>> +struct ltc2990_data {
>> +    struct i2c_client *i2c;
>> +    u32 mode;
>> +};
>>
>>  /* Return the converted value from the given register in uV or mC */
>> -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32
>> *result)
>> +static int ltc2990_get_value(struct i2c_client *i2c, int index, s32
>> *result)
>>  {
>>      s32 val;
>> +    u8 reg;
>> +
>> +    switch (index) {
>> +    case LTC2990_IN0:
>> +        reg = LTC2990_VCC_MSB;
>> +        break;
>> +    case LTC2990_IN1:
>> +    case LTC2990_CURR1:
>> +    case LTC2990_TEMP2:
>> +        reg = LTC2990_V1_MSB;
>> +        break;
>> +    case LTC2990_IN2:
>> +        reg = LTC2990_V2_MSB;
>> +        break;
>> +    case LTC2990_IN3:
>> +    case LTC2990_CURR2:
>> +    case LTC2990_TEMP3:
>> +        reg = LTC2990_V3_MSB;
>> +        break;
>> +    case LTC2990_IN4:
>> +        reg = LTC2990_V4_MSB;
>> +        break;
>> +    case LTC2990_TEMP1:
>> +        reg = LTC2990_TINT_MSB;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>>
>>      val = i2c_smbus_read_word_swapped(i2c, reg);
>>      if (unlikely(val < 0))
>>          return val;
>>
>> -    switch (reg) {
>> -    case LTC2990_TINT_MSB:
>> -        /* internal temp, 0.0625 degrees/LSB, 13-bit  */
>> +    switch (index) {
>> +    case LTC2990_TEMP1:
>> +    case LTC2990_TEMP2:
>> +    case LTC2990_TEMP3:
>> +        /* temp, 0.0625 degrees/LSB, 13-bit  */
>>          *result = sign_extend32(val, 12) * 1000 / 16;
>>          break;
>> -    case LTC2990_V1_MSB:
>> -    case LTC2990_V3_MSB:
>> -         /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
>> +    case LTC2990_CURR1:
>> +    case LTC2990_CURR2:
>> +         /* Vx-Vy, 19.42uV/LSB */
>>          *result = sign_extend32(val, 14) * 1942 / 100;
>>          break;
>> -    case LTC2990_VCC_MSB:
>> -        /* Vcc, 305.18μV/LSB, 2.5V offset */
>> +    case LTC2990_IN0:
>> +        /* Vcc, 305.18uV/LSB, 2.5V offset */
>>          *result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
>>          break;
>> +    case LTC2990_IN1:
>> +    case LTC2990_IN2:
>> +    case LTC2990_IN3:
>> +    case LTC2990_IN4:
>> +        /* Vx: 305.18uV/LSB */
>> +        *result = sign_extend32(val, 14) * 30518 / (100 * 1000);
>> +        break;
>>      default:
>>          return -EINVAL; /* won't happen, keep compiler happy */
>>      }
>> @@ -72,48 +133,104 @@ static ssize_t ltc2990_show_value(struct device
>> *dev,
>>                    struct device_attribute *da, char *buf)
>>  {
>>      struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> +    struct ltc2990_data *data = dev_get_drvdata(dev);
>>      s32 value;
>>      int ret;
>>
>> -    ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
>> +    ret = ltc2990_get_value(data->i2c, attr->index, &value);
>>      if (unlikely(ret < 0))
>>          return ret;
>>
>>      return snprintf(buf, PAGE_SIZE, "%d\n", value);
>>  }
>>
>> +static umode_t ltc2990_attrs_visible(struct kobject *kobj,
>> +                     struct attribute *a, int n)
>> +{
>> +    struct device *dev = container_of(kobj, struct device, kobj);
>> +    struct ltc2990_data *data = dev_get_drvdata(dev);
>> +    struct device_attribute *da =
>> +            container_of(a, struct device_attribute, attr);
>> +    struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> +
>> +    if (attr->index == LTC2990_TEMP1 ||
>> +        attr->index == LTC2990_IN0 ||
>> +        attr->index & ltc2990_attrs_ena[data->mode])
>> +        return a->mode;
>> +    else
>
> Unnecessary else
>
>> +        return 0;
>> +}
>> +
>>  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value,
>> NULL,
>> -              LTC2990_TINT_MSB);
>> +              LTC2990_TEMP1);
>> +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, ltc2990_show_value,
>> NULL,
>> +              LTC2990_TEMP2);
>> +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, ltc2990_show_value,
>> NULL,
>> +              LTC2990_TEMP3);
>>  static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value,
>> NULL,
>> -              LTC2990_V1_MSB);
>> +              LTC2990_CURR1);
>>  static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value,
>> NULL,
>> -              LTC2990_V3_MSB);
>> +              LTC2990_CURR2);
>>  static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
>> -              LTC2990_VCC_MSB);
>> +              LTC2990_IN0);
>> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN1);
>> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN2);
>> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN3);
>> +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN4);
>>
>>  static struct attribute *ltc2990_attrs[] = {
>>      &sensor_dev_attr_temp1_input.dev_attr.attr,
>> +    &sensor_dev_attr_temp2_input.dev_attr.attr,
>> +    &sensor_dev_attr_temp3_input.dev_attr.attr,
>>      &sensor_dev_attr_curr1_input.dev_attr.attr,
>>      &sensor_dev_attr_curr2_input.dev_attr.attr,
>>      &sensor_dev_attr_in0_input.dev_attr.attr,
>> +    &sensor_dev_attr_in1_input.dev_attr.attr,
>> +    &sensor_dev_attr_in2_input.dev_attr.attr,
>> +    &sensor_dev_attr_in3_input.dev_attr.attr,
>> +    &sensor_dev_attr_in4_input.dev_attr.attr,
>>      NULL,
>>  };
>> -ATTRIBUTE_GROUPS(ltc2990);
>> +
>> +static const struct attribute_group ltc2990_group = {
>> +    .attrs = ltc2990_attrs,
>> +    .is_visible = ltc2990_attrs_visible,
>> +};
>> +__ATTRIBUTE_GROUPS(ltc2990);
>>
>>  static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>                   const struct i2c_device_id *id)
>>  {
>>      int ret;
>>      struct device *hwmon_dev;
>> +    struct ltc2990_data *data;
>> +    struct device_node *of_node = i2c->dev.of_node;
>>
>>      if (!i2c_check_functionality(i2c->adapter,
>> I2C_FUNC_SMBUS_BYTE_DATA |
>>                       I2C_FUNC_SMBUS_WORD_DATA))
>>          return -ENODEV;
>>
>> -    /* Setup continuous mode, current monitor */
>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
>> GFP_KERNEL);
>> +    if (unlikely(!data))
>> +        return -ENOMEM;
>> +    data->i2c = i2c;
>> +
>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
>> &data->mode))
>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
>
> Iam arguing with myself if we should still do this or if we should read
> the mode
> from the chip instead if it isn't provided (after all, it may have been
> initialized
> by the BIOS/ROMMON).

I think the mode should be explicitly set, without default. There's no 
way to tell whether the BIOS or bootloader has really set it up or 
whether the chip is just reporting whatever it happened to default to. 
And given the chip's function, it's unlikely a bootloader would want to 
initialize it.

My advice would be to make it a required property. If not set, display 
an error and bail out.

> Mike, would that break your application, or can you specify the mode in
> devicetree ?

I'm fine with specifying this in the devicetree. It will break things 
for me, but I've been warned and willing to bow for the greater good :)

>
> Thanks,
> Guenter
>
>> +
>> +    if (data->mode > LTC2990_CONTROL_MODE_MAX) {
>> +        dev_err(&i2c->dev, "Error: Invalid mode %d.\n", data->mode);
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Setup continuous mode */
>>      ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
>>                      LTC2990_CONTROL_MEASURE_ALL |
>> -                    LTC2990_CONTROL_MODE_CURRENT);
>> +                    data->mode);
>>      if (ret < 0) {
>>          dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
>>          return ret;
>> @@ -127,7 +244,7 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>
>>      hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
>>                                 i2c->name,
>> -                               i2c,
>> +                               data,
>>                                 ltc2990_groups);
>>
>>      return PTR_ERR_OR_ZERO(hwmon_dev);
>>
>


-- 
Mike Looijmans


Kind regards,

Mike Looijmans
System Expert

TOPIC Products
Materiaalweg 4, NL-5681 RJ Best
Postbus 440, NL-5680 AK Best
Telefoon: +31 (0) 499 33 69 79
E-mail: mike.looijmans@topicproducts.com
Website: www.topicproducts.com

Please consider the environment before printing this e-mail

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 17:40     ` Mike Looijmans
@ 2016-11-17 18:56       ` Guenter Roeck
  2016-11-17 19:52         ` Mike Looijmans
  0 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2016-11-17 18:56 UTC (permalink / raw)
  To: Mike Looijmans
  Cc: Tom Levens, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On Thu, Nov 17, 2016 at 06:40:17PM +0100, Mike Looijmans wrote:
> On 17-11-16 17:56, Guenter Roeck wrote:
> >On 11/17/2016 04:10 AM, Tom Levens wrote:
> >>Updated version of the ltc2990 driver which supports all measurement
> >>modes available in the chip. The mode can be set through a devicetree
> >>attribute.
> >
[ ... ] 

> >>
> >> static int ltc2990_i2c_probe(struct i2c_client *i2c,
> >>                  const struct i2c_device_id *id)
> >> {
> >>     int ret;
> >>     struct device *hwmon_dev;
> >>+    struct ltc2990_data *data;
> >>+    struct device_node *of_node = i2c->dev.of_node;
> >>
> >>     if (!i2c_check_functionality(i2c->adapter,
> >>I2C_FUNC_SMBUS_BYTE_DATA |
> >>                      I2C_FUNC_SMBUS_WORD_DATA))
> >>         return -ENODEV;
> >>
> >>-    /* Setup continuous mode, current monitor */
> >>+    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
> >>GFP_KERNEL);
> >>+    if (unlikely(!data))
> >>+        return -ENOMEM;
> >>+    data->i2c = i2c;
> >>+
> >>+    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
> >>&data->mode))
> >>+        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
> >
> >Iam arguing with myself if we should still do this or if we should read
> >the mode
> >from the chip instead if it isn't provided (after all, it may have been
> >initialized
> >by the BIOS/ROMMON).
> 
> I think the mode should be explicitly set, without default. There's no way
> to tell whether the BIOS or bootloader has really set it up or whether the
> chip is just reporting whatever it happened to default to. And given the
> chip's function, it's unlikely a bootloader would want to initialize it.
> 
Unlikely but possible. Even if we all agree that the chip should be configured
by the driver, I don't like imposing that view on everyone else.

> My advice would be to make it a required property. If not set, display an
> error and bail out.
> 
It is not that easy, unfortunately. It also has to work on a non-devicetree
system. I would not object to making the property mandatory, but we would
still need to provide non-DT support.

My "use case" for taking the current mode from the chip if not specified
is that it would enable me to run a module test with all modes. I consider
this extremely valuable.

> >Mike, would that break your application, or can you specify the mode in
> >devicetree ?
> 
> I'm fine with specifying this in the devicetree. It will break things for
> me, but I've been warned and willing to bow for the greater good :)
> 
I should have asked if your system uses devicetree. If it does, the problem
should be easy to fix for you. If not, we'll need to find a solution
for your use case.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 18:56       ` Guenter Roeck
@ 2016-11-17 19:52         ` Mike Looijmans
  2016-11-17 21:54           ` Guenter Roeck
  0 siblings, 1 reply; 29+ messages in thread
From: Mike Looijmans @ 2016-11-17 19:52 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Tom Levens, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On 17-11-2016 19:56, Guenter Roeck wrote:
> On Thu, Nov 17, 2016 at 06:40:17PM +0100, Mike Looijmans wrote:
>> On 17-11-16 17:56, Guenter Roeck wrote:
>>> On 11/17/2016 04:10 AM, Tom Levens wrote:
>>>> Updated version of the ltc2990 driver which supports all measurement
>>>> modes available in the chip. The mode can be set through a devicetree
>>>> attribute.
>>>
> [ ... ]
>
>>>>
>>>> static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>>>                   const struct i2c_device_id *id)
>>>> {
>>>>      int ret;
>>>>      struct device *hwmon_dev;
>>>> +    struct ltc2990_data *data;
>>>> +    struct device_node *of_node = i2c->dev.of_node;
>>>>
>>>>      if (!i2c_check_functionality(i2c->adapter,
>>>> I2C_FUNC_SMBUS_BYTE_DATA |
>>>>                       I2C_FUNC_SMBUS_WORD_DATA))
>>>>          return -ENODEV;
>>>>
>>>> -    /* Setup continuous mode, current monitor */
>>>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
>>>> GFP_KERNEL);
>>>> +    if (unlikely(!data))
>>>> +        return -ENOMEM;
>>>> +    data->i2c = i2c;
>>>> +
>>>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
>>>> &data->mode))
>>>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
>>>
>>> Iam arguing with myself if we should still do this or if we should read
>>> the mode
>> >from the chip instead if it isn't provided (after all, it may have been
>>> initialized
>>> by the BIOS/ROMMON).
>>
>> I think the mode should be explicitly set, without default. There's no way
>> to tell whether the BIOS or bootloader has really set it up or whether the
>> chip is just reporting whatever it happened to default to. And given the
>> chip's function, it's unlikely a bootloader would want to initialize it.
>>
> Unlikely but possible. Even if we all agree that the chip should be configured
> by the driver, I don't like imposing that view on everyone else.
>
>> My advice would be to make it a required property. If not set, display an
>> error and bail out.
>>
> It is not that easy, unfortunately. It also has to work on a non-devicetree
> system. I would not object to making the property mandatory, but we would
> still need to provide non-DT support.
>
> My "use case" for taking the current mode from the chip if not specified
> is that it would enable me to run a module test with all modes. I consider
> this extremely valuable.

Good point.

The chip defaults to measuring internal temperature only, and the mode 
defaults to "0".

Choosing a mode that doesn't match the actual circuitry could be bad for 
the chip or the board (though unlikely, it'll probably just be useless) 
since it will actively drive some of the inputs in the temperature modes 
(which is default for V3/V4 pins).

Instead of failing, one could choose to set the default mode to "7", 
which just measures the 4 voltages, which would be a harmless mode in 
all cases.

As a way to let a bootloader set things up, I think it would be a good 
check to see if CONTROL register bits 4:3 are set. If "00", the chip is 
not acquiring data at all, and probably needs configuration still. In 
that case, the mode must be provided by the devicetree (or the default "7").
If bits 4:3 are "11", it has already been set up to measure its inputs, 
and it's okay to continue doing just that and use the current value of 
2:0 register as default mode (if the devicetree didn't specify any mode 
at all).

The reason I wanted the property to be mandatory is to trigger users 
like me (probably I'm the only other user so far) to update their 
devicetree. But I'd notice quickly enough if it defaults to something 
else. So that's not very compelling.

>>> Mike, would that break your application, or can you specify the mode in
>>> devicetree ?
>>
>> I'm fine with specifying this in the devicetree. It will break things for
>> me, but I've been warned and willing to bow for the greater good :)
>>
> I should have asked if your system uses devicetree. If it does, the problem
> should be easy to fix for you. If not, we'll need to find a solution
> for your use case.

I'm using devicetree. I planned to 'mainline' the boards some time this 
year...

>
> Thanks,
> Guenter
>


-- 
Mike Looijmans


Kind regards,

Mike Looijmans
System Expert

TOPIC Products
Materiaalweg 4, NL-5681 RJ Best
Postbus 440, NL-5680 AK Best
Telefoon: +31 (0) 499 33 69 79
E-mail: mike.looijmans@topicproducts.com
Website: www.topicproducts.com

Please consider the environment before printing this e-mail

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 19:52         ` Mike Looijmans
@ 2016-11-17 21:54           ` Guenter Roeck
  2016-11-17 23:25             ` Tom Levens
  0 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2016-11-17 21:54 UTC (permalink / raw)
  To: Mike Looijmans
  Cc: Tom Levens, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On Thu, Nov 17, 2016 at 08:52:12PM +0100, Mike Looijmans wrote:
> On 17-11-2016 19:56, Guenter Roeck wrote:
> >On Thu, Nov 17, 2016 at 06:40:17PM +0100, Mike Looijmans wrote:
> >>On 17-11-16 17:56, Guenter Roeck wrote:
> >>>On 11/17/2016 04:10 AM, Tom Levens wrote:
> >>>>Updated version of the ltc2990 driver which supports all measurement
> >>>>modes available in the chip. The mode can be set through a devicetree
> >>>>attribute.
> >>>
> >[ ... ]
> >
> >>>>
> >>>>static int ltc2990_i2c_probe(struct i2c_client *i2c,
> >>>>                  const struct i2c_device_id *id)
> >>>>{
> >>>>     int ret;
> >>>>     struct device *hwmon_dev;
> >>>>+    struct ltc2990_data *data;
> >>>>+    struct device_node *of_node = i2c->dev.of_node;
> >>>>
> >>>>     if (!i2c_check_functionality(i2c->adapter,
> >>>>I2C_FUNC_SMBUS_BYTE_DATA |
> >>>>                      I2C_FUNC_SMBUS_WORD_DATA))
> >>>>         return -ENODEV;
> >>>>
> >>>>-    /* Setup continuous mode, current monitor */
> >>>>+    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
> >>>>GFP_KERNEL);
> >>>>+    if (unlikely(!data))
> >>>>+        return -ENOMEM;
> >>>>+    data->i2c = i2c;
> >>>>+
> >>>>+    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
> >>>>&data->mode))
> >>>>+        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
> >>>
> >>>Iam arguing with myself if we should still do this or if we should read
> >>>the mode
> >>>from the chip instead if it isn't provided (after all, it may have been
> >>>initialized
> >>>by the BIOS/ROMMON).
> >>
> >>I think the mode should be explicitly set, without default. There's no way
> >>to tell whether the BIOS or bootloader has really set it up or whether the
> >>chip is just reporting whatever it happened to default to. And given the
> >>chip's function, it's unlikely a bootloader would want to initialize it.
> >>
> >Unlikely but possible. Even if we all agree that the chip should be configured
> >by the driver, I don't like imposing that view on everyone else.
> >
> >>My advice would be to make it a required property. If not set, display an
> >>error and bail out.
> >>
> >It is not that easy, unfortunately. It also has to work on a non-devicetree
> >system. I would not object to making the property mandatory, but we would
> >still need to provide non-DT support.
> >
> >My "use case" for taking the current mode from the chip if not specified
> >is that it would enable me to run a module test with all modes. I consider
> >this extremely valuable.
> 
> Good point.
> 
> The chip defaults to measuring internal temperature only, and the mode
> defaults to "0".
> 
> Choosing a mode that doesn't match the actual circuitry could be bad for the
> chip or the board (though unlikely, it'll probably just be useless) since it
> will actively drive some of the inputs in the temperature modes (which is
> default for V3/V4 pins).
> 
> Instead of failing, one could choose to set the default mode to "7", which
> just measures the 4 voltages, which would be a harmless mode in all cases.
> 
> As a way to let a bootloader set things up, I think it would be a good check
> to see if CONTROL register bits 4:3 are set. If "00", the chip is not
> acquiring data at all, and probably needs configuration still. In that case,
> the mode must be provided by the devicetree (or the default "7").
> If bits 4:3 are "11", it has already been set up to measure its inputs, and
> it's okay to continue doing just that and use the current value of 2:0
> register as default mode (if the devicetree didn't specify any mode at all).
> 

At first glance, agreed, though by default b[3:4] are 00, and only the
internal temperature is measured. Actually, the 5 mode bits are all
relevant to determine what is measured. Maybe it would be better to take
all 5 bits into account instead of blindly setting b[34]:=11 and a specific
setting of b[0:2]. Sure, that would make the mode table a bit larger,
but then ltc2990_attrs_ena[] could be made an u16 array, and a table size
of 64 bytes would not be that bad.

What do you think ?

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 21:54           ` Guenter Roeck
@ 2016-11-17 23:25             ` Tom Levens
  2016-11-17 23:40               ` Guenter Roeck
  0 siblings, 1 reply; 29+ messages in thread
From: Tom Levens @ 2016-11-17 23:25 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mike Looijmans, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On 17 Nov 2016, at 22:54, Guenter Roeck <linux@roeck-us.net> wrote:
> On Thu, Nov 17, 2016 at 08:52:12PM +0100, Mike Looijmans wrote:
>> On 17-11-2016 19:56, Guenter Roeck wrote:
>>> On Thu, Nov 17, 2016 at 06:40:17PM +0100, Mike Looijmans wrote:
>>>> On 17-11-16 17:56, Guenter Roeck wrote:
>>>>> On 11/17/2016 04:10 AM, Tom Levens wrote:
>>>>>> Updated version of the ltc2990 driver which supports all measurement
>>>>>> modes available in the chip. The mode can be set through a devicetree
>>>>>> attribute.
>>>>> 
>>> [ ... ]
>>> 
>>>>>> 
>>>>>> static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>>>>>                 const struct i2c_device_id *id)
>>>>>> {
>>>>>>    int ret;
>>>>>>    struct device *hwmon_dev;
>>>>>> +    struct ltc2990_data *data;
>>>>>> +    struct device_node *of_node = i2c->dev.of_node;
>>>>>> 
>>>>>>    if (!i2c_check_functionality(i2c->adapter,
>>>>>> I2C_FUNC_SMBUS_BYTE_DATA |
>>>>>>                     I2C_FUNC_SMBUS_WORD_DATA))
>>>>>>        return -ENODEV;
>>>>>> 
>>>>>> -    /* Setup continuous mode, current monitor */
>>>>>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
>>>>>> GFP_KERNEL);
>>>>>> +    if (unlikely(!data))
>>>>>> +        return -ENOMEM;
>>>>>> +    data->i2c = i2c;
>>>>>> +
>>>>>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
>>>>>> &data->mode))
>>>>>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
>>>>> 
>>>>> Iam arguing with myself if we should still do this or if we should read
>>>>> the mode
>>>>> from the chip instead if it isn't provided (after all, it may have been
>>>>> initialized
>>>>> by the BIOS/ROMMON).
>>>> 
>>>> I think the mode should be explicitly set, without default. There's no way
>>>> to tell whether the BIOS or bootloader has really set it up or whether the
>>>> chip is just reporting whatever it happened to default to. And given the
>>>> chip's function, it's unlikely a bootloader would want to initialize it.
>>>> 
>>> Unlikely but possible. Even if we all agree that the chip should be configured
>>> by the driver, I don't like imposing that view on everyone else.
>>> 
>>>> My advice would be to make it a required property. If not set, display an
>>>> error and bail out.
>>>> 
>>> It is not that easy, unfortunately. It also has to work on a non-devicetree
>>> system. I would not object to making the property mandatory, but we would
>>> still need to provide non-DT support.
>>> 
>>> My "use case" for taking the current mode from the chip if not specified
>>> is that it would enable me to run a module test with all modes. I consider
>>> this extremely valuable.
>> 
>> Good point.
>> 
>> The chip defaults to measuring internal temperature only, and the mode
>> defaults to "0".
>> 
>> Choosing a mode that doesn't match the actual circuitry could be bad for the
>> chip or the board (though unlikely, it'll probably just be useless) since it
>> will actively drive some of the inputs in the temperature modes (which is
>> default for V3/V4 pins).
>> 
>> Instead of failing, one could choose to set the default mode to "7", which
>> just measures the 4 voltages, which would be a harmless mode in all cases.
>> 
>> As a way to let a bootloader set things up, I think it would be a good check
>> to see if CONTROL register bits 4:3 are set. If "00", the chip is not
>> acquiring data at all, and probably needs configuration still. In that case,
>> the mode must be provided by the devicetree (or the default "7").
>> If bits 4:3 are "11", it has already been set up to measure its inputs, and
>> it's okay to continue doing just that and use the current value of 2:0
>> register as default mode (if the devicetree didn't specify any mode at all).
>> 
> 
> At first glance, agreed, though by default b[3:4] are 00, and only the
> internal temperature is measured. Actually, the 5 mode bits are all
> relevant to determine what is measured. Maybe it would be better to take
> all 5 bits into account instead of blindly setting b[34]:=11 and a specific
> setting of b[0:2]. Sure, that would make the mode table a bit larger,
> but then ltc2990_attrs_ena[] could be made an u16 array, and a table size
> of 64 bytes would not be that bad.

I would tend to agree that it should be possible to configure all 5 mode
bits through the devicetree. What I would propose is as follows.

If a devicetree node exists, the mode parameter(s?) are required and the 
chip is initialised.

If a devicetree node doesn't exist, it is assumed that the chip has 
already been configured (by the BIOS, etc). The mode is read from the 
chip to set the visibility of the sysfs attributes. In the worst case, where the 
chip has not been configured by another source, it would only be possible
to measure the internal temperature -- but I think this is an acceptable
limitation.

The only case that this does not cover is if the device tree node 
exists but the chip is expected to be configured by some other source. 
Maybe I am wrong, but I would not expect this to be a terribly common
situation.

What do you think?

Cheers,
Tom

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 23:25             ` Tom Levens
@ 2016-11-17 23:40               ` Guenter Roeck
  2016-11-18 12:23                 ` Tom Levens
  0 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2016-11-17 23:40 UTC (permalink / raw)
  To: Tom Levens
  Cc: Mike Looijmans, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On Thu, Nov 17, 2016 at 11:25:30PM +0000, Tom Levens wrote:
> On 17 Nov 2016, at 22:54, Guenter Roeck <linux@roeck-us.net> wrote:
> > On Thu, Nov 17, 2016 at 08:52:12PM +0100, Mike Looijmans wrote:
> >> On 17-11-2016 19:56, Guenter Roeck wrote:
> >>> On Thu, Nov 17, 2016 at 06:40:17PM +0100, Mike Looijmans wrote:
> >>>> On 17-11-16 17:56, Guenter Roeck wrote:
> >>>>> On 11/17/2016 04:10 AM, Tom Levens wrote:
> >>>>>> Updated version of the ltc2990 driver which supports all measurement
> >>>>>> modes available in the chip. The mode can be set through a devicetree
> >>>>>> attribute.
> >>>>> 
> >>> [ ... ]
> >>> 
> >>>>>> 
> >>>>>> static int ltc2990_i2c_probe(struct i2c_client *i2c,
> >>>>>>                 const struct i2c_device_id *id)
> >>>>>> {
> >>>>>>    int ret;
> >>>>>>    struct device *hwmon_dev;
> >>>>>> +    struct ltc2990_data *data;
> >>>>>> +    struct device_node *of_node = i2c->dev.of_node;
> >>>>>> 
> >>>>>>    if (!i2c_check_functionality(i2c->adapter,
> >>>>>> I2C_FUNC_SMBUS_BYTE_DATA |
> >>>>>>                     I2C_FUNC_SMBUS_WORD_DATA))
> >>>>>>        return -ENODEV;
> >>>>>> 
> >>>>>> -    /* Setup continuous mode, current monitor */
> >>>>>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
> >>>>>> GFP_KERNEL);
> >>>>>> +    if (unlikely(!data))
> >>>>>> +        return -ENOMEM;
> >>>>>> +    data->i2c = i2c;
> >>>>>> +
> >>>>>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
> >>>>>> &data->mode))
> >>>>>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
> >>>>> 
> >>>>> Iam arguing with myself if we should still do this or if we should read
> >>>>> the mode
> >>>>> from the chip instead if it isn't provided (after all, it may have been
> >>>>> initialized
> >>>>> by the BIOS/ROMMON).
> >>>> 
> >>>> I think the mode should be explicitly set, without default. There's no way
> >>>> to tell whether the BIOS or bootloader has really set it up or whether the
> >>>> chip is just reporting whatever it happened to default to. And given the
> >>>> chip's function, it's unlikely a bootloader would want to initialize it.
> >>>> 
> >>> Unlikely but possible. Even if we all agree that the chip should be configured
> >>> by the driver, I don't like imposing that view on everyone else.
> >>> 
> >>>> My advice would be to make it a required property. If not set, display an
> >>>> error and bail out.
> >>>> 
> >>> It is not that easy, unfortunately. It also has to work on a non-devicetree
> >>> system. I would not object to making the property mandatory, but we would
> >>> still need to provide non-DT support.
> >>> 
> >>> My "use case" for taking the current mode from the chip if not specified
> >>> is that it would enable me to run a module test with all modes. I consider
> >>> this extremely valuable.
> >> 
> >> Good point.
> >> 
> >> The chip defaults to measuring internal temperature only, and the mode
> >> defaults to "0".
> >> 
> >> Choosing a mode that doesn't match the actual circuitry could be bad for the
> >> chip or the board (though unlikely, it'll probably just be useless) since it
> >> will actively drive some of the inputs in the temperature modes (which is
> >> default for V3/V4 pins).
> >> 
> >> Instead of failing, one could choose to set the default mode to "7", which
> >> just measures the 4 voltages, which would be a harmless mode in all cases.
> >> 
> >> As a way to let a bootloader set things up, I think it would be a good check
> >> to see if CONTROL register bits 4:3 are set. If "00", the chip is not
> >> acquiring data at all, and probably needs configuration still. In that case,
> >> the mode must be provided by the devicetree (or the default "7").
> >> If bits 4:3 are "11", it has already been set up to measure its inputs, and
> >> it's okay to continue doing just that and use the current value of 2:0
> >> register as default mode (if the devicetree didn't specify any mode at all).
> >> 
> > 
> > At first glance, agreed, though by default b[3:4] are 00, and only the
> > internal temperature is measured. Actually, the 5 mode bits are all
> > relevant to determine what is measured. Maybe it would be better to take
> > all 5 bits into account instead of blindly setting b[34]:=11 and a specific
> > setting of b[0:2]. Sure, that would make the mode table a bit larger,
> > but then ltc2990_attrs_ena[] could be made an u16 array, and a table size
> > of 64 bytes would not be that bad.
> 
> I would tend to agree that it should be possible to configure all 5 mode
> bits through the devicetree. What I would propose is as follows.
> 
> If a devicetree node exists, the mode parameter(s?) are required and the 
> chip is initialised.
> 
> If a devicetree node doesn't exist, it is assumed that the chip has 
> already been configured (by the BIOS, etc). The mode is read from the 
> chip to set the visibility of the sysfs attributes. In the worst case, where the 
> chip has not been configured by another source, it would only be possible
> to measure the internal temperature -- but I think this is an acceptable
> limitation.
> 
SGTM.

> The only case that this does not cover is if the device tree node 
> exists but the chip is expected to be configured by some other source. 
> Maybe I am wrong, but I would not expect this to be a terribly common
> situation.
> 
> What do you think?
> 
I would not bother about this case. Just make the mode property mandatory.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion
  2016-11-17 16:36     ` Guenter Roeck
@ 2016-11-18  8:18       ` Tom Levens
  2016-11-18 14:09         ` Guenter Roeck
  0 siblings, 1 reply; 29+ messages in thread
From: Tom Levens @ 2016-11-18  8:18 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Tom Levens, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

Hi Guenter,

On Thu, 17 Nov 2016, Guenter Roeck wrote:

> On 11/17/2016 08:23 AM, Tom Levens wrote:
>>  Hi Guenter,
>>
>>  Thanks for taking the time to review the patch.
>>
>>  On Thu, 17 Nov 2016, Guenter Roeck wrote:
>> 
>> >  Hi Tom,
>> > 
>> >  On 11/17/2016 04:10 AM, Tom Levens wrote:
>> > >   Conversion from raw values to signed integers has been refactored 
>> > >   using
>> > >   the macros in bitops.h.
>> > > 
>> >  Please also mention that this fixes a bug in negative temperature 
>> >  conversions.
>>
>>  Yes, of course, I will include the information in v3.
>> 
>> > 
>> > >   Signed-off-by: Tom Levens <tom.levens@cern.ch>
>> > >   ---
>> > >    drivers/hwmon/ltc2990.c |   27 ++++++++++-----------------
>> > >    1 files changed, 10 insertions(+), 17 deletions(-)
>> > > 
>> > >   diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
>> > >   index 8f8fe05..0ec4102 100644
>> > >   --- a/drivers/hwmon/ltc2990.c
>> > >   +++ b/drivers/hwmon/ltc2990.c
>> > >   @@ -9,8 +9,12 @@
>> > >     * This driver assumes the chip is wired as a dual current monitor, 
>> > >     and
>> > >     * reports the voltage drop across two series resistors. It also 
>> > >     reports
>> > >     * the chip's internal temperature and Vcc power supply voltage.
>> > >   + *
>> > >   + * Value conversion refactored
>> > >   + * by Tom Levens <tom.levens@cern.ch>
>> > 
>> >  Kind of unusual to do that for minor changes like this. Imagine if 
>> >  everyone would do that.
>> >  The commit log is what gives you credit.
>>
>>  Good point, thanks for the hint. I will remove it from v3.
>> 
>> > >     */
>> > > 
>> > >   +#include <linux/bitops.h>
>> > >    #include <linux/err.h>
>> > >    #include <linux/hwmon.h>
>> > >    #include <linux/hwmon-sysfs.h>
>> > >   @@ -34,19 +38,10 @@
>> > >    #define LTC2990_CONTROL_MODE_CURRENT    0x06
>> > >    #define LTC2990_CONTROL_MODE_VOLTAGE    0x07
>> > > 
>> > >   -/* convert raw register value to sign-extended integer in 16-bit 
>> > >   range */
>> > >   -static int ltc2990_voltage_to_int(int raw)
>> > >   -{
>> > >   -    if (raw & BIT(14))
>> > >   -        return -(0x4000 - (raw & 0x3FFF)) << 2;
>> > >   -    else
>> > >   -        return (raw & 0x3FFF) << 2;
>> > >   -}
>> > >   -
>> > >   /* Return the converted value from the given register in uV or mC */
>> > >   -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int 
>> > >   *result)
>> > >   +static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 
>> > >   *result)
>> > >   {
>> > >   -    int val;
>> > >   +    s32 val;
>> > 
>> >  Please just leave the variable type alone. it is also used for the 
>> >  return value
>> >  from i2c_smbus_read_word_swapped(), which is an int, and changing it to 
>> >  s32 doesn't really make the code better.
>>
>>  According to i2c.h the return type for i2c_smbus_read_word_swapped() is
>>  s32, which is why I modified it here. But it could be changed back if you
>>  think it is better to leave it as an int.
>> 
> Ah, ok. Good to know. Please leave it anyway, reason being that there is no 
> real
> reason to change it. Effectively those are just whitespace changes (unlike 
> the rest,
> which is part bug fix, part cleanup).
>
>> >  Can you send me a register map for the chip ? I would like to write a 
>> >  module test.
>>
>>  Here is an example register dump:
>
> I meant the output of i2cdump (something like "i2cdump -y -f <bus> 
> <i2c-address> w").
>

The register map wraps at 0x0F, so I only sent you the first 16 bytes. But 
the fully expanded form is:

      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
10: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
20: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
30: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
40: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
50: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
60: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
70: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
80: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
90: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
a0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
b0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
c0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
d0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
e0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
f0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00

Cheers,

> Thanks,
> Guenter
>
>>
>>  00 00 00 00
>>  01 90 07 d0
>>  2c cd 7d 80
>>  7c 29 20 00
>>
>>  The expected values in this case are:
>>
>>  in0_input     5000
>>  in1_input    610
>>  in2_input    3500
>>  in3_input    -195
>>  in4_input    -299
>>  temp1_input     25000
>>  temp2_input     125000
>>  temp3_input    -40000
>>  curr1_input    38840
>>  curr2_input    -12428
>>
>>  Testing with lltc,mode set to <5>, <6> and <7> should give you all
>>  measurements.
>> 
>> >  Thanks,
>> >  Guenter
>>
>>  Cheers,
>> 
>
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 23:40               ` Guenter Roeck
@ 2016-11-18 12:23                 ` Tom Levens
  2016-11-18 14:16                   ` Guenter Roeck
  0 siblings, 1 reply; 29+ messages in thread
From: Tom Levens @ 2016-11-18 12:23 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Tom Levens, Mike Looijmans, jdelvare, robh+dt, mark.rutland,
	linux-kernel, linux-hwmon, devicetree

[-- Attachment #1: Type: text/plain, Size: 5894 bytes --]



On Fri, 18 Nov 2016, Guenter Roeck wrote:

> On Thu, Nov 17, 2016 at 11:25:30PM +0000, Tom Levens wrote:
>> On 17 Nov 2016, at 22:54, Guenter Roeck <linux@roeck-us.net> wrote:
>>> On Thu, Nov 17, 2016 at 08:52:12PM +0100, Mike Looijmans wrote:
>>>> On 17-11-2016 19:56, Guenter Roeck wrote:
>>>>> On Thu, Nov 17, 2016 at 06:40:17PM +0100, Mike Looijmans wrote:
>>>>>> On 17-11-16 17:56, Guenter Roeck wrote:
>>>>>>> On 11/17/2016 04:10 AM, Tom Levens wrote:
>>>>>>>> Updated version of the ltc2990 driver which supports all measurement
>>>>>>>> modes available in the chip. The mode can be set through a devicetree
>>>>>>>> attribute.
>>>>>>>
>>>>> [ ... ]
>>>>>
>>>>>>>>
>>>>>>>> static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>>>>>>>                 const struct i2c_device_id *id)
>>>>>>>> {
>>>>>>>>    int ret;
>>>>>>>>    struct device *hwmon_dev;
>>>>>>>> +    struct ltc2990_data *data;
>>>>>>>> +    struct device_node *of_node = i2c->dev.of_node;
>>>>>>>>
>>>>>>>>    if (!i2c_check_functionality(i2c->adapter,
>>>>>>>> I2C_FUNC_SMBUS_BYTE_DATA |
>>>>>>>>                     I2C_FUNC_SMBUS_WORD_DATA))
>>>>>>>>        return -ENODEV;
>>>>>>>>
>>>>>>>> -    /* Setup continuous mode, current monitor */
>>>>>>>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
>>>>>>>> GFP_KERNEL);
>>>>>>>> +    if (unlikely(!data))
>>>>>>>> +        return -ENOMEM;
>>>>>>>> +    data->i2c = i2c;
>>>>>>>> +
>>>>>>>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
>>>>>>>> &data->mode))
>>>>>>>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
>>>>>>>
>>>>>>> Iam arguing with myself if we should still do this or if we should read
>>>>>>> the mode
>>>>>>> from the chip instead if it isn't provided (after all, it may have been
>>>>>>> initialized
>>>>>>> by the BIOS/ROMMON).
>>>>>>
>>>>>> I think the mode should be explicitly set, without default. There's no way
>>>>>> to tell whether the BIOS or bootloader has really set it up or whether the
>>>>>> chip is just reporting whatever it happened to default to. And given the
>>>>>> chip's function, it's unlikely a bootloader would want to initialize it.
>>>>>>
>>>>> Unlikely but possible. Even if we all agree that the chip should be configured
>>>>> by the driver, I don't like imposing that view on everyone else.
>>>>>
>>>>>> My advice would be to make it a required property. If not set, display an
>>>>>> error and bail out.
>>>>>>
>>>>> It is not that easy, unfortunately. It also has to work on a non-devicetree
>>>>> system. I would not object to making the property mandatory, but we would
>>>>> still need to provide non-DT support.
>>>>>
>>>>> My "use case" for taking the current mode from the chip if not specified
>>>>> is that it would enable me to run a module test with all modes. I consider
>>>>> this extremely valuable.
>>>>
>>>> Good point.
>>>>
>>>> The chip defaults to measuring internal temperature only, and the mode
>>>> defaults to "0".
>>>>
>>>> Choosing a mode that doesn't match the actual circuitry could be bad for the
>>>> chip or the board (though unlikely, it'll probably just be useless) since it
>>>> will actively drive some of the inputs in the temperature modes (which is
>>>> default for V3/V4 pins).
>>>>
>>>> Instead of failing, one could choose to set the default mode to "7", which
>>>> just measures the 4 voltages, which would be a harmless mode in all cases.
>>>>
>>>> As a way to let a bootloader set things up, I think it would be a good check
>>>> to see if CONTROL register bits 4:3 are set. If "00", the chip is not
>>>> acquiring data at all, and probably needs configuration still. In that case,
>>>> the mode must be provided by the devicetree (or the default "7").
>>>> If bits 4:3 are "11", it has already been set up to measure its inputs, and
>>>> it's okay to continue doing just that and use the current value of 2:0
>>>> register as default mode (if the devicetree didn't specify any mode at all).
>>>>
>>>
>>> At first glance, agreed, though by default b[3:4] are 00, and only the
>>> internal temperature is measured. Actually, the 5 mode bits are all
>>> relevant to determine what is measured. Maybe it would be better to take
>>> all 5 bits into account instead of blindly setting b[34]:=11 and a specific
>>> setting of b[0:2]. Sure, that would make the mode table a bit larger,
>>> but then ltc2990_attrs_ena[] could be made an u16 array, and a table size
>>> of 64 bytes would not be that bad.
>>
>> I would tend to agree that it should be possible to configure all 5 mode
>> bits through the devicetree. What I would propose is as follows.
>>
>> If a devicetree node exists, the mode parameter(s?) are required and the
>> chip is initialised.
>>
>> If a devicetree node doesn't exist, it is assumed that the chip has
>> already been configured (by the BIOS, etc). The mode is read from the
>> chip to set the visibility of the sysfs attributes. In the worst case, where the
>> chip has not been configured by another source, it would only be possible
>> to measure the internal temperature -- but I think this is an acceptable
>> limitation.
>>
> SGTM.
>
>> The only case that this does not cover is if the device tree node
>> exists but the chip is expected to be configured by some other source.
>> Maybe I am wrong, but I would not expect this to be a terribly common
>> situation.
>>
>> What do you think?
>>
> I would not bother about this case. Just make the mode property mandatory.

What do you think about making the devicetree property a list of two 
integers? Something like

lltc,mode = <7 3>;

which would set mode[2..0]=7 and mode[4..3]=3.

To me, this is easier to setup from the datasheet than a single integer 
value. The other option would be to split it into two properties, but I am 
struggling to come up with suitable names for them -- the datasheet 
helpfully calls both fields "mode".

Cheers,

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion
  2016-11-18  8:18       ` Tom Levens
@ 2016-11-18 14:09         ` Guenter Roeck
  2016-11-18 14:17           ` Guenter Roeck
  0 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2016-11-18 14:09 UTC (permalink / raw)
  To: Tom Levens
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

On 11/18/2016 12:18 AM, Tom Levens wrote:
> Hi Guenter,
>
> On Thu, 17 Nov 2016, Guenter Roeck wrote:
>
>> On 11/17/2016 08:23 AM, Tom Levens wrote:
>>>  Hi Guenter,
>>>
>>>  Thanks for taking the time to review the patch.
>>>
>>>  On Thu, 17 Nov 2016, Guenter Roeck wrote:
>>>
>>> >  Hi Tom,
>>> > >  On 11/17/2016 04:10 AM, Tom Levens wrote:
>>> > >   Conversion from raw values to signed integers has been refactored > >   using
>>> > >   the macros in bitops.h.
>>> > > >  Please also mention that this fixes a bug in negative temperature >  conversions.
>>>
>>>  Yes, of course, I will include the information in v3.
>>>
>>> > > >   Signed-off-by: Tom Levens <tom.levens@cern.ch>
>>> > >   ---
>>> > >    drivers/hwmon/ltc2990.c |   27 ++++++++++-----------------
>>> > >    1 files changed, 10 insertions(+), 17 deletions(-)
>>> > > > >   diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
>>> > >   index 8f8fe05..0ec4102 100644
>>> > >   --- a/drivers/hwmon/ltc2990.c
>>> > >   +++ b/drivers/hwmon/ltc2990.c
>>> > >   @@ -9,8 +9,12 @@
>>> > >     * This driver assumes the chip is wired as a dual current monitor, > >     and
>>> > >     * reports the voltage drop across two series resistors. It also > >     reports
>>> > >     * the chip's internal temperature and Vcc power supply voltage.
>>> > >   + *
>>> > >   + * Value conversion refactored
>>> > >   + * by Tom Levens <tom.levens@cern.ch>
>>> > >  Kind of unusual to do that for minor changes like this. Imagine if >  everyone would do that.
>>> >  The commit log is what gives you credit.
>>>
>>>  Good point, thanks for the hint. I will remove it from v3.
>>>
>>> > >     */
>>> > > > >   +#include <linux/bitops.h>
>>> > >    #include <linux/err.h>
>>> > >    #include <linux/hwmon.h>
>>> > >    #include <linux/hwmon-sysfs.h>
>>> > >   @@ -34,19 +38,10 @@
>>> > >    #define LTC2990_CONTROL_MODE_CURRENT    0x06
>>> > >    #define LTC2990_CONTROL_MODE_VOLTAGE    0x07
>>> > > > >   -/* convert raw register value to sign-extended integer in 16-bit > >   range */
>>> > >   -static int ltc2990_voltage_to_int(int raw)
>>> > >   -{
>>> > >   -    if (raw & BIT(14))
>>> > >   -        return -(0x4000 - (raw & 0x3FFF)) << 2;
>>> > >   -    else
>>> > >   -        return (raw & 0x3FFF) << 2;
>>> > >   -}
>>> > >   -
>>> > >   /* Return the converted value from the given register in uV or mC */
>>> > >   -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int > >   *result)
>>> > >   +static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 > >   *result)
>>> > >   {
>>> > >   -    int val;
>>> > >   +    s32 val;
>>> > >  Please just leave the variable type alone. it is also used for the >  return value
>>> >  from i2c_smbus_read_word_swapped(), which is an int, and changing it to >  s32 doesn't really make the code better.
>>>
>>>  According to i2c.h the return type for i2c_smbus_read_word_swapped() is
>>>  s32, which is why I modified it here. But it could be changed back if you
>>>  think it is better to leave it as an int.
>>>
>> Ah, ok. Good to know. Please leave it anyway, reason being that there is no real
>> reason to change it. Effectively those are just whitespace changes (unlike the rest,
>> which is part bug fix, part cleanup).
>>
>>> >  Can you send me a register map for the chip ? I would like to write a >  module test.
>>>
>>>  Here is an example register dump:
>>
>> I meant the output of i2cdump (something like "i2cdump -y -f <bus> <i2c-address> w").
>>
>
> The register map wraps at 0x0F, so I only sent you the first 16 bytes. But the fully expanded form is:
>
>      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
> 00: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 10: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 20: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 30: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 40: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 50: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 60: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 70: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 80: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> 90: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> a0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> b0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> c0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> d0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> e0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
> f0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00

Sorry, I wasn't clear. The chip uses 16-bit registers, so the
"w" in the command would be important to report the entire
register content, not just the first 8 bit of each register.

Thanks,
Guenter

>
> Cheers,
>
>> Thanks,
>> Guenter
>>
>>>
>>>  00 00 00 00
>>>  01 90 07 d0
>>>  2c cd 7d 80
>>>  7c 29 20 00
>>>
>>>  The expected values in this case are:
>>>
>>>  in0_input     5000
>>>  in1_input    610
>>>  in2_input    3500
>>>  in3_input    -195
>>>  in4_input    -299
>>>  temp1_input     25000
>>>  temp2_input     125000
>>>  temp3_input    -40000
>>>  curr1_input    38840
>>>  curr2_input    -12428
>>>
>>>  Testing with lltc,mode set to <5>, <6> and <7> should give you all
>>>  measurements.
>>>
>>> >  Thanks,
>>> >  Guenter
>>>
>>>  Cheers,
>>>
>>
>>
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-18 12:23                 ` Tom Levens
@ 2016-11-18 14:16                   ` Guenter Roeck
  0 siblings, 0 replies; 29+ messages in thread
From: Guenter Roeck @ 2016-11-18 14:16 UTC (permalink / raw)
  To: Tom Levens
  Cc: Mike Looijmans, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On 11/18/2016 04:23 AM, Tom Levens wrote:
>
>
> On Fri, 18 Nov 2016, Guenter Roeck wrote:
>
>> On Thu, Nov 17, 2016 at 11:25:30PM +0000, Tom Levens wrote:
>>> On 17 Nov 2016, at 22:54, Guenter Roeck <linux@roeck-us.net> wrote:
>>>> On Thu, Nov 17, 2016 at 08:52:12PM +0100, Mike Looijmans wrote:
>>>>> On 17-11-2016 19:56, Guenter Roeck wrote:
>>>>>> On Thu, Nov 17, 2016 at 06:40:17PM +0100, Mike Looijmans wrote:
>>>>>>> On 17-11-16 17:56, Guenter Roeck wrote:
>>>>>>>> On 11/17/2016 04:10 AM, Tom Levens wrote:
>>>>>>>>> Updated version of the ltc2990 driver which supports all measurement
>>>>>>>>> modes available in the chip. The mode can be set through a devicetree
>>>>>>>>> attribute.
>>>>>>>>
>>>>>> [ ... ]
>>>>>>
>>>>>>>>>
>>>>>>>>> static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>>>>>>>>                 const struct i2c_device_id *id)
>>>>>>>>> {
>>>>>>>>>    int ret;
>>>>>>>>>    struct device *hwmon_dev;
>>>>>>>>> +    struct ltc2990_data *data;
>>>>>>>>> +    struct device_node *of_node = i2c->dev.of_node;
>>>>>>>>>
>>>>>>>>>    if (!i2c_check_functionality(i2c->adapter,
>>>>>>>>> I2C_FUNC_SMBUS_BYTE_DATA |
>>>>>>>>>                     I2C_FUNC_SMBUS_WORD_DATA))
>>>>>>>>>        return -ENODEV;
>>>>>>>>>
>>>>>>>>> -    /* Setup continuous mode, current monitor */
>>>>>>>>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data),
>>>>>>>>> GFP_KERNEL);
>>>>>>>>> +    if (unlikely(!data))
>>>>>>>>> +        return -ENOMEM;
>>>>>>>>> +    data->i2c = i2c;
>>>>>>>>> +
>>>>>>>>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode",
>>>>>>>>> &data->mode))
>>>>>>>>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
>>>>>>>>
>>>>>>>> Iam arguing with myself if we should still do this or if we should read
>>>>>>>> the mode
>>>>>>>> from the chip instead if it isn't provided (after all, it may have been
>>>>>>>> initialized
>>>>>>>> by the BIOS/ROMMON).
>>>>>>>
>>>>>>> I think the mode should be explicitly set, without default. There's no way
>>>>>>> to tell whether the BIOS or bootloader has really set it up or whether the
>>>>>>> chip is just reporting whatever it happened to default to. And given the
>>>>>>> chip's function, it's unlikely a bootloader would want to initialize it.
>>>>>>>
>>>>>> Unlikely but possible. Even if we all agree that the chip should be configured
>>>>>> by the driver, I don't like imposing that view on everyone else.
>>>>>>
>>>>>>> My advice would be to make it a required property. If not set, display an
>>>>>>> error and bail out.
>>>>>>>
>>>>>> It is not that easy, unfortunately. It also has to work on a non-devicetree
>>>>>> system. I would not object to making the property mandatory, but we would
>>>>>> still need to provide non-DT support.
>>>>>>
>>>>>> My "use case" for taking the current mode from the chip if not specified
>>>>>> is that it would enable me to run a module test with all modes. I consider
>>>>>> this extremely valuable.
>>>>>
>>>>> Good point.
>>>>>
>>>>> The chip defaults to measuring internal temperature only, and the mode
>>>>> defaults to "0".
>>>>>
>>>>> Choosing a mode that doesn't match the actual circuitry could be bad for the
>>>>> chip or the board (though unlikely, it'll probably just be useless) since it
>>>>> will actively drive some of the inputs in the temperature modes (which is
>>>>> default for V3/V4 pins).
>>>>>
>>>>> Instead of failing, one could choose to set the default mode to "7", which
>>>>> just measures the 4 voltages, which would be a harmless mode in all cases.
>>>>>
>>>>> As a way to let a bootloader set things up, I think it would be a good check
>>>>> to see if CONTROL register bits 4:3 are set. If "00", the chip is not
>>>>> acquiring data at all, and probably needs configuration still. In that case,
>>>>> the mode must be provided by the devicetree (or the default "7").
>>>>> If bits 4:3 are "11", it has already been set up to measure its inputs, and
>>>>> it's okay to continue doing just that and use the current value of 2:0
>>>>> register as default mode (if the devicetree didn't specify any mode at all).
>>>>>
>>>>
>>>> At first glance, agreed, though by default b[3:4] are 00, and only the
>>>> internal temperature is measured. Actually, the 5 mode bits are all
>>>> relevant to determine what is measured. Maybe it would be better to take
>>>> all 5 bits into account instead of blindly setting b[34]:=11 and a specific
>>>> setting of b[0:2]. Sure, that would make the mode table a bit larger,
>>>> but then ltc2990_attrs_ena[] could be made an u16 array, and a table size
>>>> of 64 bytes would not be that bad.
>>>
>>> I would tend to agree that it should be possible to configure all 5 mode
>>> bits through the devicetree. What I would propose is as follows.
>>>
>>> If a devicetree node exists, the mode parameter(s?) are required and the
>>> chip is initialised.
>>>
>>> If a devicetree node doesn't exist, it is assumed that the chip has
>>> already been configured (by the BIOS, etc). The mode is read from the
>>> chip to set the visibility of the sysfs attributes. In the worst case, where the
>>> chip has not been configured by another source, it would only be possible
>>> to measure the internal temperature -- but I think this is an acceptable
>>> limitation.
>>>
>> SGTM.
>>
>>> The only case that this does not cover is if the device tree node
>>> exists but the chip is expected to be configured by some other source.
>>> Maybe I am wrong, but I would not expect this to be a terribly common
>>> situation.
>>>
>>> What do you think?
>>>
>> I would not bother about this case. Just make the mode property mandatory.
>
> What do you think about making the devicetree property a list of two integers? Something like
>
> lltc,mode = <7 3>;
>
> which would set mode[2..0]=7 and mode[4..3]=3.
>
I would personally just use a single value for b[4..0]. But that is really up for bikeshedding
(eg should it be <7 3> or <3 7>. I'll leave that up to Rob to decide - he knows better than me
of what makes more sense from a DT perspective.

Guenter

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion
  2016-11-18 14:09         ` Guenter Roeck
@ 2016-11-18 14:17           ` Guenter Roeck
  0 siblings, 0 replies; 29+ messages in thread
From: Guenter Roeck @ 2016-11-18 14:17 UTC (permalink / raw)
  To: Tom Levens
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

On 11/18/2016 06:09 AM, Guenter Roeck wrote:

>>
>> The register map wraps at 0x0F, so I only sent you the first 16 bytes. But the fully expanded form is:
>>
>>      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
>> 00: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 10: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 20: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 30: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 40: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 50: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 60: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 70: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 80: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> 90: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> a0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> b0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> c0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> d0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> e0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>> f0: 00 00 00 00 01 90 07 d0 2c cd 7d 80 7c 29 20 00
>
> Sorry, I wasn't clear. The chip uses 16-bit registers, so the
> "w" in the command would be important to report the entire
> register content, not just the first 8 bit of each register.
>
Too early, sorry. the above is fine.

Guenter

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding
  2016-11-17 12:10 ` [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding Tom Levens
@ 2016-11-18 14:50   ` Rob Herring
  2016-11-18 15:36     ` Tom Levens
  0 siblings, 1 reply; 29+ messages in thread
From: Rob Herring @ 2016-11-18 14:50 UTC (permalink / raw)
  To: Tom Levens
  Cc: linux, jdelvare, mark.rutland, linux-kernel, linux-hwmon, devicetree

On Thu, Nov 17, 2016 at 01:10:15PM +0100, Tom Levens wrote:
> Add a devicetree binding for the ltc2990 hwmon driver.
> 
> Signed-off-by: Tom Levens <tom.levens@cern.ch>
> ---
>  .../devicetree/bindings/hwmon/ltc2990.txt          |   28 ++++++++++++++++++++
>  1 files changed, 28 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/hwmon/ltc2990.txt
> 
> diff --git a/Documentation/devicetree/bindings/hwmon/ltc2990.txt b/Documentation/devicetree/bindings/hwmon/ltc2990.txt
> new file mode 100644
> index 0000000..e4040e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/ltc2990.txt
> @@ -0,0 +1,28 @@
> +ltc2990
> +
> +Required properties:
> +- compatible: Must be "lltc,ltc2990"
> +- reg: I2C slave address
> +
> +Optional properties:
> +- lltc,mode:

What determines the mode? If this is something a user will want to 
control, then it should be a sysfs attr rather than DT prop. If the 
board design dictates then DT is the right place.

'mode' is a bit vague, 'lltc,meas-mode' perhaps.

> +	Sets the chip's measurement mode, defaults to <6> if unset.
> +
> +	The following measurements are available in each mode:
> +
> +	0: V1, V2, TR2
> +	1: V1-V2, TR2
> +	2: V1-V2, V3, V4
> +	3: TR1, V3, V4
> +	4: TR1, V3-V4
> +	5: TR1, TR2
> +	6: V1-V2, V3-V4
> +	7: V1, V2, V3, V4
> +
> +Example:
> +
> +ltc2990@4c {
> +	compatible = "lltc,ltc2990";
> +	reg = <0x4c>;
> +	lltc,mode = <7>;
> +};
> -- 
> 1.7.1
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding
  2016-11-18 14:50   ` Rob Herring
@ 2016-11-18 15:36     ` Tom Levens
  0 siblings, 0 replies; 29+ messages in thread
From: Tom Levens @ 2016-11-18 15:36 UTC (permalink / raw)
  To: Rob Herring
  Cc: Tom Levens, linux, jdelvare, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

Hi Rob,

Thanks for taking the time to look at this patch.

On Fri, 18 Nov 2016, Rob Herring wrote:
> On Thu, Nov 17, 2016 at 01:10:15PM +0100, Tom Levens wrote:
>> Add a devicetree binding for the ltc2990 hwmon driver.
>>
>> Signed-off-by: Tom Levens <tom.levens@cern.ch>
>> ---
>>  .../devicetree/bindings/hwmon/ltc2990.txt          |   28 ++++++++++++++++++++
>>  1 files changed, 28 insertions(+), 0 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/hwmon/ltc2990.txt
>>
>> diff --git a/Documentation/devicetree/bindings/hwmon/ltc2990.txt b/Documentation/devicetree/bindings/hwmon/ltc2990.txt
>> new file mode 100644
>> index 0000000..e4040e0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/hwmon/ltc2990.txt
>> @@ -0,0 +1,28 @@
>> +ltc2990
>> +
>> +Required properties:
>> +- compatible: Must be "lltc,ltc2990"
>> +- reg: I2C slave address
>> +
>> +Optional properties:
>> +- lltc,mode:
>
> What determines the mode? If this is something a user will want to
> control, then it should be a sysfs attr rather than DT prop. If the
> board design dictates then DT is the right place.

It is based on the external connections of the chip. So, I would say that 
it is board specific.

> 'mode' is a bit vague, 'lltc,meas-mode' perhaps.

Sure thing.

There is also one question which came up in the thread for the patch 3 of 
this patchset. The full mode for this chip is actually made of two logical 
values which are written to the bits 4..3 and 2..0 of the register. This 
version of the patch only configures one of the values, but for the next 
version we would like to configure both. While combining them into a 
single integer value would be possible, they are defined as two values in 
the datasheet. Therefore, I propose using an array, such as:

lltc,meas-mode = <7 3>;

This would set the mode[2..0]=7 and mode[4..3]=3.

What do you think of this? Or should this be split into two properties? 
However, in this case I struggle come up with names for the properties. 
The datasheet, helpfully, calls both the fields "mode" and there is no 
clear difference in their function as both control which measurements are 
available.

Thanks,

>> +A	Sets the chip's measurement mode, defaults to <6> if unset.
>> +
>> +	The following measurements are available in each mode:
>> +
>> +	0: V1, V2, TR2
>> +	1: V1-V2, TR2
>> +	2: V1-V2, V3, V4
>> +	3: TR1, V3, V4
>> +	4: TR1, V3-V4
>> +	5: TR1, TR2
>> +	6: V1-V2, V3-V4
>> +	7: V1, V2, V3, V4
>> +
>> +Example:
>> +
>> +ltc2990@4c {
>> +	compatible = "lltc,ltc2990";
>> +	reg = <0x4c>;
>> +	lltc,mode = <7>;
>> +};
>> --
>> 1.7.1
>>
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2016-11-17 16:56   ` Guenter Roeck
  2016-11-17 17:40     ` Mike Looijmans
@ 2017-06-28 14:24     ` Mike Looijmans
  2017-06-28 15:01       ` Guenter Roeck
  1 sibling, 1 reply; 29+ messages in thread
From: Mike Looijmans @ 2017-06-28 14:24 UTC (permalink / raw)
  To: Guenter Roeck, Tom Levens
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

On 17-11-16 17:56, Guenter Roeck wrote:
> On 11/17/2016 04:10 AM, Tom Levens wrote:
>> Updated version of the ltc2990 driver which supports all measurement
>> modes available in the chip. The mode can be set through a devicetree
>> attribute.
> 
> property
> 
>>
>> Signed-off-by: Tom Levens <tom.levens@cern.ch>
>> ---
>>
>> Changes since v1:
>>   * Refactored value conversion (patch 1/3)
>>   * Split the devicetree binding into separate patch (patch 2/3)
>>   * Specifying an invalid mode now returns -EINVAL, previously this
>>     only issued a warning and used the default value
>>   * Removed the "mode" sysfs attribute, as the mode of the chip is
>>     hardware specific and should not be user configurable. This allows much
>>     simpler code as a result.
>>
>>  Documentation/hwmon/ltc2990 |   24 ++++---
>>  drivers/hwmon/Kconfig       |    7 +--
>>  drivers/hwmon/ltc2990.c     |  167 ++++++++++++++++++++++++++++++++++++-------
>>  3 files changed, 159 insertions(+), 39 deletions(-)
>>
>> diff --git a/Documentation/hwmon/ltc2990 b/Documentation/hwmon/ltc2990
>> index c25211e..3ed68f6 100644
>> --- a/Documentation/hwmon/ltc2990
>> +++ b/Documentation/hwmon/ltc2990
>> @@ -8,6 +8,7 @@ Supported chips:
>>      Datasheet: http://www.linear.com/product/ltc2990
>>
>>  Author: Mike Looijmans <mike.looijmans@topic.nl>
>> +        Tom Levens <tom.levens@cern.ch>
>>
>>
>>  Description
>> @@ -16,10 +17,8 @@ Description
>>  LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
>>  The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4)
>>  can be combined to measure a differential voltage, which is typically used to
>> -measure current through a series resistor, or a temperature.
>> -
>> -This driver currently uses the 2x differential mode only. In order to support
>> -other modes, the driver will need to be expanded.
>> +measure current through a series resistor, or a temperature with an external
>> +diode.
>>
>>
>>  Usage Notes
>> @@ -32,12 +31,19 @@ devices explicitly.
>>  Sysfs attributes
>>  ----------------
>>
>> +in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
>> +temp1_input   Internal chip temperature in millidegrees Celcius
>> +
>> +A subset of the following attributes are visible, depending on the measurement
>> +mode of the chip.
>> +
>> +in[1-4]_input Voltage at V[1-4] pin in millivolt
>> +temp2_input   External temperature sensor TR1 in millidegrees Celcius
>> +temp3_input   External temperature sensor TR2 in millidegrees Celcius
>> +curr1_input   Current in mA across V1-V2 assuming a 1mOhm sense resistor
>> +curr2_input   Current in mA across V3-V4 assuming a 1mOhm sense resistor
>> +
>>  The "curr*_input" measurements actually report the voltage drop across the
>>  input pins in microvolts. This is equivalent to the current through a 1mOhm
>>  sense resistor. Divide the reported value by the actual sense resistor value
>>  in mOhm to get the actual value.
>> -
>> -in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
>> -temp1_input   Internal chip temperature in millidegrees Celcius
>> -curr1_input   Current in mA across v1-v2 assuming a 1mOhm sense resistor.
>> -curr2_input   Current in mA across v3-v4 assuming a 1mOhm sense resistor.
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 45cef3d..f7096ca 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -699,15 +699,12 @@ config SENSORS_LTC2945
>>        be called ltc2945.
>>
>>  config SENSORS_LTC2990
>> -    tristate "Linear Technology LTC2990 (current monitoring mode only)"
>> +    tristate "Linear Technology LTC2990"
>>      depends on I2C
>>      help
>>        If you say yes here you get support for Linear Technology LTC2990
>>        I2C System Monitor. The LTC2990 supports a combination of voltage,
>> -      current and temperature monitoring, but in addition to the Vcc supply
>> -      voltage and chip temperature, this driver currently only supports
>> -      reading two currents by measuring two differential voltages across
>> -      series resistors.
>> +      current and temperature monitoring.
>>
>>        This driver can also be built as a module. If so, the module will
>>        be called ltc2990.
>> diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
>> index 0ec4102..e8d36f5 100644
>> --- a/drivers/hwmon/ltc2990.c
>> +++ b/drivers/hwmon/ltc2990.c
>> @@ -6,11 +6,7 @@
>>   *
>>   * License: GPLv2
>>   *
>> - * This driver assumes the chip is wired as a dual current monitor, and
>> - * reports the voltage drop across two series resistors. It also reports
>> - * the chip's internal temperature and Vcc power supply voltage.
>> - *
>> - * Value conversion refactored
>> + * Value conversion refactored and support for all measurement modes added
>>   * by Tom Levens <tom.levens@cern.ch>
>>   */
>>
>> @@ -21,6 +17,7 @@
>>  #include <linux/i2c.h>
>>  #include <linux/kernel.h>
>>  #include <linux/module.h>
>> +#include <linux/of.h>
>>
>>  #define LTC2990_STATUS    0x00
>>  #define LTC2990_CONTROL    0x01
>> @@ -35,32 +32,96 @@
>>  #define LTC2990_CONTROL_KELVIN        BIT(7)
>>  #define LTC2990_CONTROL_SINGLE        BIT(6)
>>  #define LTC2990_CONTROL_MEASURE_ALL    (0x3 << 3)
>> -#define LTC2990_CONTROL_MODE_CURRENT    0x06
>> -#define LTC2990_CONTROL_MODE_VOLTAGE    0x07
>> +#define LTC2990_CONTROL_MODE_DEFAULT    0x06
> 
> I think LTC2990_CONTROL_MODE_CURRENT was better - it describes the actual mode.
> Changing the define to _DEFAULT does not really improve code readability.
> 
>> +#define LTC2990_CONTROL_MODE_MAX    0x07
>> +
>> +#define LTC2990_IN0    BIT(0)
>> +#define LTC2990_IN1    BIT(1)
>> +#define LTC2990_IN2    BIT(2)
>> +#define LTC2990_IN3    BIT(3)
>> +#define LTC2990_IN4    BIT(4)
>> +#define LTC2990_CURR1    BIT(5)
>> +#define LTC2990_CURR2    BIT(6)
>> +#define LTC2990_TEMP1    BIT(7)
>> +#define LTC2990_TEMP2    BIT(8)
>> +#define LTC2990_TEMP3    BIT(9)
>> +
>> +static const int ltc2990_attrs_ena[] = {
>> +    LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
>> +    LTC2990_CURR1 | LTC2990_TEMP3,
>> +    LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
>> +    LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
>> +    LTC2990_TEMP2 | LTC2990_CURR2,
>> +    LTC2990_TEMP2 | LTC2990_TEMP3,
>> +    LTC2990_CURR1 | LTC2990_CURR2,
>> +    LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
>> +};
>> +
>> +struct ltc2990_data {
>> +    struct i2c_client *i2c;
>> +    u32 mode;
>> +};
>>
>>  /* Return the converted value from the given register in uV or mC */
>> -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
>> +static int ltc2990_get_value(struct i2c_client *i2c, int index, s32 *result)
>>  {
>>      s32 val;
>> +    u8 reg;
>> +
>> +    switch (index) {
>> +    case LTC2990_IN0:
>> +        reg = LTC2990_VCC_MSB;
>> +        break;
>> +    case LTC2990_IN1:
>> +    case LTC2990_CURR1:
>> +    case LTC2990_TEMP2:
>> +        reg = LTC2990_V1_MSB;
>> +        break;
>> +    case LTC2990_IN2:
>> +        reg = LTC2990_V2_MSB;
>> +        break;
>> +    case LTC2990_IN3:
>> +    case LTC2990_CURR2:
>> +    case LTC2990_TEMP3:
>> +        reg = LTC2990_V3_MSB;
>> +        break;
>> +    case LTC2990_IN4:
>> +        reg = LTC2990_V4_MSB;
>> +        break;
>> +    case LTC2990_TEMP1:
>> +        reg = LTC2990_TINT_MSB;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>>
>>      val = i2c_smbus_read_word_swapped(i2c, reg);
>>      if (unlikely(val < 0))
>>          return val;
>>
>> -    switch (reg) {
>> -    case LTC2990_TINT_MSB:
>> -        /* internal temp, 0.0625 degrees/LSB, 13-bit  */
>> +    switch (index) {
>> +    case LTC2990_TEMP1:
>> +    case LTC2990_TEMP2:
>> +    case LTC2990_TEMP3:
>> +        /* temp, 0.0625 degrees/LSB, 13-bit  */
>>          *result = sign_extend32(val, 12) * 1000 / 16;
>>          break;
>> -    case LTC2990_V1_MSB:
>> -    case LTC2990_V3_MSB:
>> -         /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
>> +    case LTC2990_CURR1:
>> +    case LTC2990_CURR2:
>> +         /* Vx-Vy, 19.42uV/LSB */
>>          *result = sign_extend32(val, 14) * 1942 / 100;
>>          break;
>> -    case LTC2990_VCC_MSB:
>> -        /* Vcc, 305.18μV/LSB, 2.5V offset */
>> +    case LTC2990_IN0:
>> +        /* Vcc, 305.18uV/LSB, 2.5V offset */
>>          *result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
>>          break;
>> +    case LTC2990_IN1:
>> +    case LTC2990_IN2:
>> +    case LTC2990_IN3:
>> +    case LTC2990_IN4:
>> +        /* Vx: 305.18uV/LSB */
>> +        *result = sign_extend32(val, 14) * 30518 / (100 * 1000);
>> +        break;
>>      default:
>>          return -EINVAL; /* won't happen, keep compiler happy */
>>      }
>> @@ -72,48 +133,104 @@ static ssize_t ltc2990_show_value(struct device *dev,
>>                    struct device_attribute *da, char *buf)
>>  {
>>      struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> +    struct ltc2990_data *data = dev_get_drvdata(dev);
>>      s32 value;
>>      int ret;
>>
>> -    ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
>> +    ret = ltc2990_get_value(data->i2c, attr->index, &value);
>>      if (unlikely(ret < 0))
>>          return ret;
>>
>>      return snprintf(buf, PAGE_SIZE, "%d\n", value);
>>  }
>>
>> +static umode_t ltc2990_attrs_visible(struct kobject *kobj,
>> +                     struct attribute *a, int n)
>> +{
>> +    struct device *dev = container_of(kobj, struct device, kobj);
>> +    struct ltc2990_data *data = dev_get_drvdata(dev);
>> +    struct device_attribute *da =
>> +            container_of(a, struct device_attribute, attr);
>> +    struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> +
>> +    if (attr->index == LTC2990_TEMP1 ||
>> +        attr->index == LTC2990_IN0 ||
>> +        attr->index & ltc2990_attrs_ena[data->mode])
>> +        return a->mode;
>> +    else
> 
> Unnecessary else
> 
>> +        return 0;
>> +}
>> +
>>  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
>> -              LTC2990_TINT_MSB);
>> +              LTC2990_TEMP1);
>> +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_TEMP2);
>> +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_TEMP3);
>>  static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
>> -              LTC2990_V1_MSB);
>> +              LTC2990_CURR1);
>>  static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
>> -              LTC2990_V3_MSB);
>> +              LTC2990_CURR2);
>>  static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
>> -              LTC2990_VCC_MSB);
>> +              LTC2990_IN0);
>> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN1);
>> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN2);
>> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN3);
>> +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc2990_show_value, NULL,
>> +              LTC2990_IN4);
>>
>>  static struct attribute *ltc2990_attrs[] = {
>>      &sensor_dev_attr_temp1_input.dev_attr.attr,
>> +    &sensor_dev_attr_temp2_input.dev_attr.attr,
>> +    &sensor_dev_attr_temp3_input.dev_attr.attr,
>>      &sensor_dev_attr_curr1_input.dev_attr.attr,
>>      &sensor_dev_attr_curr2_input.dev_attr.attr,
>>      &sensor_dev_attr_in0_input.dev_attr.attr,
>> +    &sensor_dev_attr_in1_input.dev_attr.attr,
>> +    &sensor_dev_attr_in2_input.dev_attr.attr,
>> +    &sensor_dev_attr_in3_input.dev_attr.attr,
>> +    &sensor_dev_attr_in4_input.dev_attr.attr,
>>      NULL,
>>  };
>> -ATTRIBUTE_GROUPS(ltc2990);
>> +
>> +static const struct attribute_group ltc2990_group = {
>> +    .attrs = ltc2990_attrs,
>> +    .is_visible = ltc2990_attrs_visible,
>> +};
>> +__ATTRIBUTE_GROUPS(ltc2990);
>>
>>  static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>                   const struct i2c_device_id *id)
>>  {
>>      int ret;
>>      struct device *hwmon_dev;
>> +    struct ltc2990_data *data;
>> +    struct device_node *of_node = i2c->dev.of_node;
>>
>>      if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
>>                       I2C_FUNC_SMBUS_WORD_DATA))
>>          return -ENODEV;
>>
>> -    /* Setup continuous mode, current monitor */
>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data), GFP_KERNEL);
>> +    if (unlikely(!data))
>> +        return -ENOMEM;
>> +    data->i2c = i2c;
>> +
>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode", &data->mode))
>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
> 
> Iam arguing with myself if we should still do this or if we should read the mode
> from the chip instead if it isn't provided (after all, it may have been 
> initialized
> by the BIOS/ROMMON).
> 
> Mike, would that break your application, or can you specify the mode in 
> devicetree ?

OMFG, I just spent half the day implementing the exact same thing, and only 
after sumbmitting it, I stumbled upon this thread. I must be getting old.

A well, seems I implemented things a bit differently, so you get to pick and 
choose which patch was better.

Whatever happened to this patch though? It didn't make it to mainline, 
otherwise I'd have found it sooner...


> 
> Thanks,
> Guenter
> 
>> +
>> +    if (data->mode > LTC2990_CONTROL_MODE_MAX) {
>> +        dev_err(&i2c->dev, "Error: Invalid mode %d.\n", data->mode);
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Setup continuous mode */
>>      ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
>>                      LTC2990_CONTROL_MEASURE_ALL |
>> -                    LTC2990_CONTROL_MODE_CURRENT);
>> +                    data->mode);
>>      if (ret < 0) {
>>          dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
>>          return ret;
>> @@ -127,7 +244,7 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>
>>      hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
>>                                 i2c->name,
>> -                               i2c,
>> +                               data,
>>                                 ltc2990_groups);
>>
>>      return PTR_ERR_OR_ZERO(hwmon_dev);
>>
> 



Kind regards,

Mike Looijmans
System Expert

TOPIC Products
Materiaalweg 4, NL-5681 RJ Best
Postbus 440, NL-5680 AK Best
Telefoon: +31 (0) 499 33 69 79
E-mail: mike.looijmans@topicproducts.com
Website: www.topicproducts.com

Please consider the environment before printing this e-mail

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-28 14:24     ` Mike Looijmans
@ 2017-06-28 15:01       ` Guenter Roeck
  2017-06-28 15:29         ` Tom Levens
  0 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2017-06-28 15:01 UTC (permalink / raw)
  To: Mike Looijmans
  Cc: Tom Levens, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On Wed, Jun 28, 2017 at 04:24:03PM +0200, Mike Looijmans wrote:
> On 17-11-16 17:56, Guenter Roeck wrote:
> >On 11/17/2016 04:10 AM, Tom Levens wrote:
> >>Updated version of the ltc2990 driver which supports all measurement
> >>modes available in the chip. The mode can be set through a devicetree
> >>attribute.
> >
> >property
> >
> >>
> >>Signed-off-by: Tom Levens <tom.levens@cern.ch>
> >>---
> >>
> >>Changes since v1:
> >>  * Refactored value conversion (patch 1/3)
> >>  * Split the devicetree binding into separate patch (patch 2/3)
> >>  * Specifying an invalid mode now returns -EINVAL, previously this
> >>    only issued a warning and used the default value
> >>  * Removed the "mode" sysfs attribute, as the mode of the chip is
> >>    hardware specific and should not be user configurable. This allows much
> >>    simpler code as a result.
> >>
> >> Documentation/hwmon/ltc2990 |   24 ++++---
> >> drivers/hwmon/Kconfig       |    7 +--
> >> drivers/hwmon/ltc2990.c     |  167 ++++++++++++++++++++++++++++++++++++-------
> >> 3 files changed, 159 insertions(+), 39 deletions(-)
> >>
> >>diff --git a/Documentation/hwmon/ltc2990 b/Documentation/hwmon/ltc2990
> >>index c25211e..3ed68f6 100644
> >>--- a/Documentation/hwmon/ltc2990
> >>+++ b/Documentation/hwmon/ltc2990
> >>@@ -8,6 +8,7 @@ Supported chips:
> >>     Datasheet: http://www.linear.com/product/ltc2990
> >>
> >> Author: Mike Looijmans <mike.looijmans@topic.nl>
> >>+        Tom Levens <tom.levens@cern.ch>
> >>
> >>
> >> Description
> >>@@ -16,10 +17,8 @@ Description
> >> LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
> >> The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4)
> >> can be combined to measure a differential voltage, which is typically used to
> >>-measure current through a series resistor, or a temperature.
> >>-
> >>-This driver currently uses the 2x differential mode only. In order to support
> >>-other modes, the driver will need to be expanded.
> >>+measure current through a series resistor, or a temperature with an external
> >>+diode.
> >>
> >>
> >> Usage Notes
> >>@@ -32,12 +31,19 @@ devices explicitly.
> >> Sysfs attributes
> >> ----------------
> >>
> >>+in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
> >>+temp1_input   Internal chip temperature in millidegrees Celcius
> >>+
> >>+A subset of the following attributes are visible, depending on the measurement
> >>+mode of the chip.
> >>+
> >>+in[1-4]_input Voltage at V[1-4] pin in millivolt
> >>+temp2_input   External temperature sensor TR1 in millidegrees Celcius
> >>+temp3_input   External temperature sensor TR2 in millidegrees Celcius
> >>+curr1_input   Current in mA across V1-V2 assuming a 1mOhm sense resistor
> >>+curr2_input   Current in mA across V3-V4 assuming a 1mOhm sense resistor
> >>+
> >> The "curr*_input" measurements actually report the voltage drop across the
> >> input pins in microvolts. This is equivalent to the current through a 1mOhm
> >> sense resistor. Divide the reported value by the actual sense resistor value
> >> in mOhm to get the actual value.
> >>-
> >>-in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
> >>-temp1_input   Internal chip temperature in millidegrees Celcius
> >>-curr1_input   Current in mA across v1-v2 assuming a 1mOhm sense resistor.
> >>-curr2_input   Current in mA across v3-v4 assuming a 1mOhm sense resistor.
> >>diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> >>index 45cef3d..f7096ca 100644
> >>--- a/drivers/hwmon/Kconfig
> >>+++ b/drivers/hwmon/Kconfig
> >>@@ -699,15 +699,12 @@ config SENSORS_LTC2945
> >>       be called ltc2945.
> >>
> >> config SENSORS_LTC2990
> >>-    tristate "Linear Technology LTC2990 (current monitoring mode only)"
> >>+    tristate "Linear Technology LTC2990"
> >>     depends on I2C
> >>     help
> >>       If you say yes here you get support for Linear Technology LTC2990
> >>       I2C System Monitor. The LTC2990 supports a combination of voltage,
> >>-      current and temperature monitoring, but in addition to the Vcc supply
> >>-      voltage and chip temperature, this driver currently only supports
> >>-      reading two currents by measuring two differential voltages across
> >>-      series resistors.
> >>+      current and temperature monitoring.
> >>
> >>       This driver can also be built as a module. If so, the module will
> >>       be called ltc2990.
> >>diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
> >>index 0ec4102..e8d36f5 100644
> >>--- a/drivers/hwmon/ltc2990.c
> >>+++ b/drivers/hwmon/ltc2990.c
> >>@@ -6,11 +6,7 @@
> >>  *
> >>  * License: GPLv2
> >>  *
> >>- * This driver assumes the chip is wired as a dual current monitor, and
> >>- * reports the voltage drop across two series resistors. It also reports
> >>- * the chip's internal temperature and Vcc power supply voltage.
> >>- *
> >>- * Value conversion refactored
> >>+ * Value conversion refactored and support for all measurement modes added
> >>  * by Tom Levens <tom.levens@cern.ch>
> >>  */
> >>
> >>@@ -21,6 +17,7 @@
> >> #include <linux/i2c.h>
> >> #include <linux/kernel.h>
> >> #include <linux/module.h>
> >>+#include <linux/of.h>
> >>
> >> #define LTC2990_STATUS    0x00
> >> #define LTC2990_CONTROL    0x01
> >>@@ -35,32 +32,96 @@
> >> #define LTC2990_CONTROL_KELVIN        BIT(7)
> >> #define LTC2990_CONTROL_SINGLE        BIT(6)
> >> #define LTC2990_CONTROL_MEASURE_ALL    (0x3 << 3)
> >>-#define LTC2990_CONTROL_MODE_CURRENT    0x06
> >>-#define LTC2990_CONTROL_MODE_VOLTAGE    0x07
> >>+#define LTC2990_CONTROL_MODE_DEFAULT    0x06
> >
> >I think LTC2990_CONTROL_MODE_CURRENT was better - it describes the actual mode.
> >Changing the define to _DEFAULT does not really improve code readability.
> >
> >>+#define LTC2990_CONTROL_MODE_MAX    0x07
> >>+
> >>+#define LTC2990_IN0    BIT(0)
> >>+#define LTC2990_IN1    BIT(1)
> >>+#define LTC2990_IN2    BIT(2)
> >>+#define LTC2990_IN3    BIT(3)
> >>+#define LTC2990_IN4    BIT(4)
> >>+#define LTC2990_CURR1    BIT(5)
> >>+#define LTC2990_CURR2    BIT(6)
> >>+#define LTC2990_TEMP1    BIT(7)
> >>+#define LTC2990_TEMP2    BIT(8)
> >>+#define LTC2990_TEMP3    BIT(9)
> >>+
> >>+static const int ltc2990_attrs_ena[] = {
> >>+    LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
> >>+    LTC2990_CURR1 | LTC2990_TEMP3,
> >>+    LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
> >>+    LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
> >>+    LTC2990_TEMP2 | LTC2990_CURR2,
> >>+    LTC2990_TEMP2 | LTC2990_TEMP3,
> >>+    LTC2990_CURR1 | LTC2990_CURR2,
> >>+    LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
> >>+};
> >>+
> >>+struct ltc2990_data {
> >>+    struct i2c_client *i2c;
> >>+    u32 mode;
> >>+};
> >>
> >> /* Return the converted value from the given register in uV or mC */
> >>-static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
> >>+static int ltc2990_get_value(struct i2c_client *i2c, int index, s32 *result)
> >> {
> >>     s32 val;
> >>+    u8 reg;
> >>+
> >>+    switch (index) {
> >>+    case LTC2990_IN0:
> >>+        reg = LTC2990_VCC_MSB;
> >>+        break;
> >>+    case LTC2990_IN1:
> >>+    case LTC2990_CURR1:
> >>+    case LTC2990_TEMP2:
> >>+        reg = LTC2990_V1_MSB;
> >>+        break;
> >>+    case LTC2990_IN2:
> >>+        reg = LTC2990_V2_MSB;
> >>+        break;
> >>+    case LTC2990_IN3:
> >>+    case LTC2990_CURR2:
> >>+    case LTC2990_TEMP3:
> >>+        reg = LTC2990_V3_MSB;
> >>+        break;
> >>+    case LTC2990_IN4:
> >>+        reg = LTC2990_V4_MSB;
> >>+        break;
> >>+    case LTC2990_TEMP1:
> >>+        reg = LTC2990_TINT_MSB;
> >>+        break;
> >>+    default:
> >>+        return -EINVAL;
> >>+    }
> >>
> >>     val = i2c_smbus_read_word_swapped(i2c, reg);
> >>     if (unlikely(val < 0))
> >>         return val;
> >>
> >>-    switch (reg) {
> >>-    case LTC2990_TINT_MSB:
> >>-        /* internal temp, 0.0625 degrees/LSB, 13-bit  */
> >>+    switch (index) {
> >>+    case LTC2990_TEMP1:
> >>+    case LTC2990_TEMP2:
> >>+    case LTC2990_TEMP3:
> >>+        /* temp, 0.0625 degrees/LSB, 13-bit  */
> >>         *result = sign_extend32(val, 12) * 1000 / 16;
> >>         break;
> >>-    case LTC2990_V1_MSB:
> >>-    case LTC2990_V3_MSB:
> >>-         /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
> >>+    case LTC2990_CURR1:
> >>+    case LTC2990_CURR2:
> >>+         /* Vx-Vy, 19.42uV/LSB */
> >>         *result = sign_extend32(val, 14) * 1942 / 100;
> >>         break;
> >>-    case LTC2990_VCC_MSB:
> >>-        /* Vcc, 305.18μV/LSB, 2.5V offset */
> >>+    case LTC2990_IN0:
> >>+        /* Vcc, 305.18uV/LSB, 2.5V offset */
> >>         *result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
> >>         break;
> >>+    case LTC2990_IN1:
> >>+    case LTC2990_IN2:
> >>+    case LTC2990_IN3:
> >>+    case LTC2990_IN4:
> >>+        /* Vx: 305.18uV/LSB */
> >>+        *result = sign_extend32(val, 14) * 30518 / (100 * 1000);
> >>+        break;
> >>     default:
> >>         return -EINVAL; /* won't happen, keep compiler happy */
> >>     }
> >>@@ -72,48 +133,104 @@ static ssize_t ltc2990_show_value(struct device *dev,
> >>                   struct device_attribute *da, char *buf)
> >> {
> >>     struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> >>+    struct ltc2990_data *data = dev_get_drvdata(dev);
> >>     s32 value;
> >>     int ret;
> >>
> >>-    ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
> >>+    ret = ltc2990_get_value(data->i2c, attr->index, &value);
> >>     if (unlikely(ret < 0))
> >>         return ret;
> >>
> >>     return snprintf(buf, PAGE_SIZE, "%d\n", value);
> >> }
> >>
> >>+static umode_t ltc2990_attrs_visible(struct kobject *kobj,
> >>+                     struct attribute *a, int n)
> >>+{
> >>+    struct device *dev = container_of(kobj, struct device, kobj);
> >>+    struct ltc2990_data *data = dev_get_drvdata(dev);
> >>+    struct device_attribute *da =
> >>+            container_of(a, struct device_attribute, attr);
> >>+    struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> >>+
> >>+    if (attr->index == LTC2990_TEMP1 ||
> >>+        attr->index == LTC2990_IN0 ||
> >>+        attr->index & ltc2990_attrs_ena[data->mode])
> >>+        return a->mode;
> >>+    else
> >
> >Unnecessary else
> >
> >>+        return 0;
> >>+}
> >>+
> >> static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
> >>-              LTC2990_TINT_MSB);
> >>+              LTC2990_TEMP1);
> >>+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, ltc2990_show_value, NULL,
> >>+              LTC2990_TEMP2);
> >>+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, ltc2990_show_value, NULL,
> >>+              LTC2990_TEMP3);
> >> static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
> >>-              LTC2990_V1_MSB);
> >>+              LTC2990_CURR1);
> >> static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
> >>-              LTC2990_V3_MSB);
> >>+              LTC2990_CURR2);
> >> static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
> >>-              LTC2990_VCC_MSB);
> >>+              LTC2990_IN0);
> >>+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2990_show_value, NULL,
> >>+              LTC2990_IN1);
> >>+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2990_show_value, NULL,
> >>+              LTC2990_IN2);
> >>+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc2990_show_value, NULL,
> >>+              LTC2990_IN3);
> >>+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc2990_show_value, NULL,
> >>+              LTC2990_IN4);
> >>
> >> static struct attribute *ltc2990_attrs[] = {
> >>     &sensor_dev_attr_temp1_input.dev_attr.attr,
> >>+    &sensor_dev_attr_temp2_input.dev_attr.attr,
> >>+    &sensor_dev_attr_temp3_input.dev_attr.attr,
> >>     &sensor_dev_attr_curr1_input.dev_attr.attr,
> >>     &sensor_dev_attr_curr2_input.dev_attr.attr,
> >>     &sensor_dev_attr_in0_input.dev_attr.attr,
> >>+    &sensor_dev_attr_in1_input.dev_attr.attr,
> >>+    &sensor_dev_attr_in2_input.dev_attr.attr,
> >>+    &sensor_dev_attr_in3_input.dev_attr.attr,
> >>+    &sensor_dev_attr_in4_input.dev_attr.attr,
> >>     NULL,
> >> };
> >>-ATTRIBUTE_GROUPS(ltc2990);
> >>+
> >>+static const struct attribute_group ltc2990_group = {
> >>+    .attrs = ltc2990_attrs,
> >>+    .is_visible = ltc2990_attrs_visible,
> >>+};
> >>+__ATTRIBUTE_GROUPS(ltc2990);
> >>
> >> static int ltc2990_i2c_probe(struct i2c_client *i2c,
> >>                  const struct i2c_device_id *id)
> >> {
> >>     int ret;
> >>     struct device *hwmon_dev;
> >>+    struct ltc2990_data *data;
> >>+    struct device_node *of_node = i2c->dev.of_node;
> >>
> >>     if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
> >>                      I2C_FUNC_SMBUS_WORD_DATA))
> >>         return -ENODEV;
> >>
> >>-    /* Setup continuous mode, current monitor */
> >>+    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data), GFP_KERNEL);
> >>+    if (unlikely(!data))
> >>+        return -ENOMEM;
> >>+    data->i2c = i2c;
> >>+
> >>+    if (!of_node || of_property_read_u32(of_node, "lltc,mode", &data->mode))
> >>+        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
> >
> >Iam arguing with myself if we should still do this or if we should read the mode
> >from the chip instead if it isn't provided (after all, it may have been
> >initialized
> >by the BIOS/ROMMON).
> >
> >Mike, would that break your application, or can you specify the mode in
> >devicetree ?
> 
> OMFG, I just spent half the day implementing the exact same thing, and only
> after sumbmitting it, I stumbled upon this thread. I must be getting old.
> 
> A well, seems I implemented things a bit differently, so you get to pick and
> choose which patch was better.
> 

As I just replied to your patch, there is no question here. The compatible
statement refers to chip compatibility; one can not use it to also provide
configuration information.

> Whatever happened to this patch though? It didn't make it to mainline,
> otherwise I'd have found it sooner...
> 
I'll have to look it up, but I guess I didn't get an updated version.

Guenter

> 
> >
> >Thanks,
> >Guenter
> >
> >>+
> >>+    if (data->mode > LTC2990_CONTROL_MODE_MAX) {
> >>+        dev_err(&i2c->dev, "Error: Invalid mode %d.\n", data->mode);
> >>+        return -EINVAL;
> >>+    }
> >>+
> >>+    /* Setup continuous mode */
> >>     ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
> >>                     LTC2990_CONTROL_MEASURE_ALL |
> >>-                    LTC2990_CONTROL_MODE_CURRENT);
> >>+                    data->mode);
> >>     if (ret < 0) {
> >>         dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
> >>         return ret;
> >>@@ -127,7 +244,7 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
> >>
> >>     hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
> >>                                i2c->name,
> >>-                               i2c,
> >>+                               data,
> >>                                ltc2990_groups);
> >>
> >>     return PTR_ERR_OR_ZERO(hwmon_dev);
> >>
> >
> 
> 
> 
> Kind regards,
> 
> Mike Looijmans
> System Expert
> 
> TOPIC Products
> Materiaalweg 4, NL-5681 RJ Best
> Postbus 440, NL-5680 AK Best
> Telefoon: +31 (0) 499 33 69 79
> E-mail: mike.looijmans@topicproducts.com
> Website: www.topicproducts.com
> 
> Please consider the environment before printing this e-mail
> 
> 
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-28 15:01       ` Guenter Roeck
@ 2017-06-28 15:29         ` Tom Levens
  2017-06-28 16:00           ` Guenter Roeck
  0 siblings, 1 reply; 29+ messages in thread
From: Tom Levens @ 2017-06-28 15:29 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mike Looijmans, Tom Levens, jdelvare, robh+dt, mark.rutland,
	linux-kernel, linux-hwmon, devicetree

[-- Attachment #1: Type: text/plain, Size: 16041 bytes --]



On Wed, 28 Jun 2017, Guenter Roeck wrote:

> On Wed, Jun 28, 2017 at 04:24:03PM +0200, Mike Looijmans wrote:
>> On 17-11-16 17:56, Guenter Roeck wrote:
>>> On 11/17/2016 04:10 AM, Tom Levens wrote:
>>>> Updated version of the ltc2990 driver which supports all measurement
>>>> modes available in the chip. The mode can be set through a devicetree
>>>> attribute.
>>>
>>> property
>>>
>>>>
>>>> Signed-off-by: Tom Levens <tom.levens@cern.ch>
>>>> ---
>>>>
>>>> Changes since v1:
>>>>  * Refactored value conversion (patch 1/3)
>>>>  * Split the devicetree binding into separate patch (patch 2/3)
>>>>  * Specifying an invalid mode now returns -EINVAL, previously this
>>>>    only issued a warning and used the default value
>>>>  * Removed the "mode" sysfs attribute, as the mode of the chip is
>>>>    hardware specific and should not be user configurable. This allows much
>>>>    simpler code as a result.
>>>>
>>>> Documentation/hwmon/ltc2990 |   24 ++++---
>>>> drivers/hwmon/Kconfig       |    7 +--
>>>> drivers/hwmon/ltc2990.c     |  167 ++++++++++++++++++++++++++++++++++++-------
>>>> 3 files changed, 159 insertions(+), 39 deletions(-)
>>>>
>>>> diff --git a/Documentation/hwmon/ltc2990 b/Documentation/hwmon/ltc2990
>>>> index c25211e..3ed68f6 100644
>>>> --- a/Documentation/hwmon/ltc2990
>>>> +++ b/Documentation/hwmon/ltc2990
>>>> @@ -8,6 +8,7 @@ Supported chips:
>>>>     Datasheet: http://www.linear.com/product/ltc2990
>>>>
>>>> Author: Mike Looijmans <mike.looijmans@topic.nl>
>>>> +        Tom Levens <tom.levens@cern.ch>
>>>>
>>>>
>>>> Description
>>>> @@ -16,10 +17,8 @@ Description
>>>> LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
>>>> The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4)
>>>> can be combined to measure a differential voltage, which is typically used to
>>>> -measure current through a series resistor, or a temperature.
>>>> -
>>>> -This driver currently uses the 2x differential mode only. In order to support
>>>> -other modes, the driver will need to be expanded.
>>>> +measure current through a series resistor, or a temperature with an external
>>>> +diode.
>>>>
>>>>
>>>> Usage Notes
>>>> @@ -32,12 +31,19 @@ devices explicitly.
>>>> Sysfs attributes
>>>> ----------------
>>>>
>>>> +in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
>>>> +temp1_input   Internal chip temperature in millidegrees Celcius
>>>> +
>>>> +A subset of the following attributes are visible, depending on the measurement
>>>> +mode of the chip.
>>>> +
>>>> +in[1-4]_input Voltage at V[1-4] pin in millivolt
>>>> +temp2_input   External temperature sensor TR1 in millidegrees Celcius
>>>> +temp3_input   External temperature sensor TR2 in millidegrees Celcius
>>>> +curr1_input   Current in mA across V1-V2 assuming a 1mOhm sense resistor
>>>> +curr2_input   Current in mA across V3-V4 assuming a 1mOhm sense resistor
>>>> +
>>>> The "curr*_input" measurements actually report the voltage drop across the
>>>> input pins in microvolts. This is equivalent to the current through a 1mOhm
>>>> sense resistor. Divide the reported value by the actual sense resistor value
>>>> in mOhm to get the actual value.
>>>> -
>>>> -in0_input     Voltage at Vcc pin in millivolt (range 2.5V to 5V)
>>>> -temp1_input   Internal chip temperature in millidegrees Celcius
>>>> -curr1_input   Current in mA across v1-v2 assuming a 1mOhm sense resistor.
>>>> -curr2_input   Current in mA across v3-v4 assuming a 1mOhm sense resistor.
>>>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>>>> index 45cef3d..f7096ca 100644
>>>> --- a/drivers/hwmon/Kconfig
>>>> +++ b/drivers/hwmon/Kconfig
>>>> @@ -699,15 +699,12 @@ config SENSORS_LTC2945
>>>>       be called ltc2945.
>>>>
>>>> config SENSORS_LTC2990
>>>> -    tristate "Linear Technology LTC2990 (current monitoring mode only)"
>>>> +    tristate "Linear Technology LTC2990"
>>>>     depends on I2C
>>>>     help
>>>>       If you say yes here you get support for Linear Technology LTC2990
>>>>       I2C System Monitor. The LTC2990 supports a combination of voltage,
>>>> -      current and temperature monitoring, but in addition to the Vcc supply
>>>> -      voltage and chip temperature, this driver currently only supports
>>>> -      reading two currents by measuring two differential voltages across
>>>> -      series resistors.
>>>> +      current and temperature monitoring.
>>>>
>>>>       This driver can also be built as a module. If so, the module will
>>>>       be called ltc2990.
>>>> diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
>>>> index 0ec4102..e8d36f5 100644
>>>> --- a/drivers/hwmon/ltc2990.c
>>>> +++ b/drivers/hwmon/ltc2990.c
>>>> @@ -6,11 +6,7 @@
>>>>  *
>>>>  * License: GPLv2
>>>>  *
>>>> - * This driver assumes the chip is wired as a dual current monitor, and
>>>> - * reports the voltage drop across two series resistors. It also reports
>>>> - * the chip's internal temperature and Vcc power supply voltage.
>>>> - *
>>>> - * Value conversion refactored
>>>> + * Value conversion refactored and support for all measurement modes added
>>>>  * by Tom Levens <tom.levens@cern.ch>
>>>>  */
>>>>
>>>> @@ -21,6 +17,7 @@
>>>> #include <linux/i2c.h>
>>>> #include <linux/kernel.h>
>>>> #include <linux/module.h>
>>>> +#include <linux/of.h>
>>>>
>>>> #define LTC2990_STATUS    0x00
>>>> #define LTC2990_CONTROL    0x01
>>>> @@ -35,32 +32,96 @@
>>>> #define LTC2990_CONTROL_KELVIN        BIT(7)
>>>> #define LTC2990_CONTROL_SINGLE        BIT(6)
>>>> #define LTC2990_CONTROL_MEASURE_ALL    (0x3 << 3)
>>>> -#define LTC2990_CONTROL_MODE_CURRENT    0x06
>>>> -#define LTC2990_CONTROL_MODE_VOLTAGE    0x07
>>>> +#define LTC2990_CONTROL_MODE_DEFAULT    0x06
>>>
>>> I think LTC2990_CONTROL_MODE_CURRENT was better - it describes the actual mode.
>>> Changing the define to _DEFAULT does not really improve code readability.
>>>
>>>> +#define LTC2990_CONTROL_MODE_MAX    0x07
>>>> +
>>>> +#define LTC2990_IN0    BIT(0)
>>>> +#define LTC2990_IN1    BIT(1)
>>>> +#define LTC2990_IN2    BIT(2)
>>>> +#define LTC2990_IN3    BIT(3)
>>>> +#define LTC2990_IN4    BIT(4)
>>>> +#define LTC2990_CURR1    BIT(5)
>>>> +#define LTC2990_CURR2    BIT(6)
>>>> +#define LTC2990_TEMP1    BIT(7)
>>>> +#define LTC2990_TEMP2    BIT(8)
>>>> +#define LTC2990_TEMP3    BIT(9)
>>>> +
>>>> +static const int ltc2990_attrs_ena[] = {
>>>> +    LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
>>>> +    LTC2990_CURR1 | LTC2990_TEMP3,
>>>> +    LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
>>>> +    LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
>>>> +    LTC2990_TEMP2 | LTC2990_CURR2,
>>>> +    LTC2990_TEMP2 | LTC2990_TEMP3,
>>>> +    LTC2990_CURR1 | LTC2990_CURR2,
>>>> +    LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
>>>> +};
>>>> +
>>>> +struct ltc2990_data {
>>>> +    struct i2c_client *i2c;
>>>> +    u32 mode;
>>>> +};
>>>>
>>>> /* Return the converted value from the given register in uV or mC */
>>>> -static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, s32 *result)
>>>> +static int ltc2990_get_value(struct i2c_client *i2c, int index, s32 *result)
>>>> {
>>>>     s32 val;
>>>> +    u8 reg;
>>>> +
>>>> +    switch (index) {
>>>> +    case LTC2990_IN0:
>>>> +        reg = LTC2990_VCC_MSB;
>>>> +        break;
>>>> +    case LTC2990_IN1:
>>>> +    case LTC2990_CURR1:
>>>> +    case LTC2990_TEMP2:
>>>> +        reg = LTC2990_V1_MSB;
>>>> +        break;
>>>> +    case LTC2990_IN2:
>>>> +        reg = LTC2990_V2_MSB;
>>>> +        break;
>>>> +    case LTC2990_IN3:
>>>> +    case LTC2990_CURR2:
>>>> +    case LTC2990_TEMP3:
>>>> +        reg = LTC2990_V3_MSB;
>>>> +        break;
>>>> +    case LTC2990_IN4:
>>>> +        reg = LTC2990_V4_MSB;
>>>> +        break;
>>>> +    case LTC2990_TEMP1:
>>>> +        reg = LTC2990_TINT_MSB;
>>>> +        break;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>>
>>>>     val = i2c_smbus_read_word_swapped(i2c, reg);
>>>>     if (unlikely(val < 0))
>>>>         return val;
>>>>
>>>> -    switch (reg) {
>>>> -    case LTC2990_TINT_MSB:
>>>> -        /* internal temp, 0.0625 degrees/LSB, 13-bit  */
>>>> +    switch (index) {
>>>> +    case LTC2990_TEMP1:
>>>> +    case LTC2990_TEMP2:
>>>> +    case LTC2990_TEMP3:
>>>> +        /* temp, 0.0625 degrees/LSB, 13-bit  */
>>>>         *result = sign_extend32(val, 12) * 1000 / 16;
>>>>         break;
>>>> -    case LTC2990_V1_MSB:
>>>> -    case LTC2990_V3_MSB:
>>>> -         /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
>>>> +    case LTC2990_CURR1:
>>>> +    case LTC2990_CURR2:
>>>> +         /* Vx-Vy, 19.42uV/LSB */
>>>>         *result = sign_extend32(val, 14) * 1942 / 100;
>>>>         break;
>>>> -    case LTC2990_VCC_MSB:
>>>> -        /* Vcc, 305.18μV/LSB, 2.5V offset */
>>>> +    case LTC2990_IN0:
>>>> +        /* Vcc, 305.18uV/LSB, 2.5V offset */
>>>>         *result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
>>>>         break;
>>>> +    case LTC2990_IN1:
>>>> +    case LTC2990_IN2:
>>>> +    case LTC2990_IN3:
>>>> +    case LTC2990_IN4:
>>>> +        /* Vx: 305.18uV/LSB */
>>>> +        *result = sign_extend32(val, 14) * 30518 / (100 * 1000);
>>>> +        break;
>>>>     default:
>>>>         return -EINVAL; /* won't happen, keep compiler happy */
>>>>     }
>>>> @@ -72,48 +133,104 @@ static ssize_t ltc2990_show_value(struct device *dev,
>>>>                   struct device_attribute *da, char *buf)
>>>> {
>>>>     struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>>>> +    struct ltc2990_data *data = dev_get_drvdata(dev);
>>>>     s32 value;
>>>>     int ret;
>>>>
>>>> -    ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
>>>> +    ret = ltc2990_get_value(data->i2c, attr->index, &value);
>>>>     if (unlikely(ret < 0))
>>>>         return ret;
>>>>
>>>>     return snprintf(buf, PAGE_SIZE, "%d\n", value);
>>>> }
>>>>
>>>> +static umode_t ltc2990_attrs_visible(struct kobject *kobj,
>>>> +                     struct attribute *a, int n)
>>>> +{
>>>> +    struct device *dev = container_of(kobj, struct device, kobj);
>>>> +    struct ltc2990_data *data = dev_get_drvdata(dev);
>>>> +    struct device_attribute *da =
>>>> +            container_of(a, struct device_attribute, attr);
>>>> +    struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>>>> +
>>>> +    if (attr->index == LTC2990_TEMP1 ||
>>>> +        attr->index == LTC2990_IN0 ||
>>>> +        attr->index & ltc2990_attrs_ena[data->mode])
>>>> +        return a->mode;
>>>> +    else
>>>
>>> Unnecessary else
>>>
>>>> +        return 0;
>>>> +}
>>>> +
>>>> static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> -              LTC2990_TINT_MSB);
>>>> +              LTC2990_TEMP1);
>>>> +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> +              LTC2990_TEMP2);
>>>> +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> +              LTC2990_TEMP3);
>>>> static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> -              LTC2990_V1_MSB);
>>>> +              LTC2990_CURR1);
>>>> static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> -              LTC2990_V3_MSB);
>>>> +              LTC2990_CURR2);
>>>> static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> -              LTC2990_VCC_MSB);
>>>> +              LTC2990_IN0);
>>>> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> +              LTC2990_IN1);
>>>> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> +              LTC2990_IN2);
>>>> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> +              LTC2990_IN3);
>>>> +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc2990_show_value, NULL,
>>>> +              LTC2990_IN4);
>>>>
>>>> static struct attribute *ltc2990_attrs[] = {
>>>>     &sensor_dev_attr_temp1_input.dev_attr.attr,
>>>> +    &sensor_dev_attr_temp2_input.dev_attr.attr,
>>>> +    &sensor_dev_attr_temp3_input.dev_attr.attr,
>>>>     &sensor_dev_attr_curr1_input.dev_attr.attr,
>>>>     &sensor_dev_attr_curr2_input.dev_attr.attr,
>>>>     &sensor_dev_attr_in0_input.dev_attr.attr,
>>>> +    &sensor_dev_attr_in1_input.dev_attr.attr,
>>>> +    &sensor_dev_attr_in2_input.dev_attr.attr,
>>>> +    &sensor_dev_attr_in3_input.dev_attr.attr,
>>>> +    &sensor_dev_attr_in4_input.dev_attr.attr,
>>>>     NULL,
>>>> };
>>>> -ATTRIBUTE_GROUPS(ltc2990);
>>>> +
>>>> +static const struct attribute_group ltc2990_group = {
>>>> +    .attrs = ltc2990_attrs,
>>>> +    .is_visible = ltc2990_attrs_visible,
>>>> +};
>>>> +__ATTRIBUTE_GROUPS(ltc2990);
>>>>
>>>> static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>>>                  const struct i2c_device_id *id)
>>>> {
>>>>     int ret;
>>>>     struct device *hwmon_dev;
>>>> +    struct ltc2990_data *data;
>>>> +    struct device_node *of_node = i2c->dev.of_node;
>>>>
>>>>     if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
>>>>                      I2C_FUNC_SMBUS_WORD_DATA))
>>>>         return -ENODEV;
>>>>
>>>> -    /* Setup continuous mode, current monitor */
>>>> +    data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data), GFP_KERNEL);
>>>> +    if (unlikely(!data))
>>>> +        return -ENOMEM;
>>>> +    data->i2c = i2c;
>>>> +
>>>> +    if (!of_node || of_property_read_u32(of_node, "lltc,mode", &data->mode))
>>>> +        data->mode = LTC2990_CONTROL_MODE_DEFAULT;
>>>
>>> Iam arguing with myself if we should still do this or if we should read the mode
>>> from the chip instead if it isn't provided (after all, it may have been
>>> initialized
>>> by the BIOS/ROMMON).
>>>
>>> Mike, would that break your application, or can you specify the mode in
>>> devicetree ?
>>
>> OMFG, I just spent half the day implementing the exact same thing, and only
>> after sumbmitting it, I stumbled upon this thread. I must be getting old.
>>
>> A well, seems I implemented things a bit differently, so you get to pick and
>> choose which patch was better.
>>
>
> As I just replied to your patch, there is no question here. The compatible
> statement refers to chip compatibility; one can not use it to also provide
> configuration information.
>
>> Whatever happened to this patch though? It didn't make it to mainline,
>> otherwise I'd have found it sooner...
>>
> I'll have to look it up, but I guess I didn't get an updated version.

As far as I remember I had a working V3 of this patch, but it is entirely 
possible that it was never submitted as I have been busy with other 
projects recently. I'll dig it out and check that it is complete.

Cheers,

> Guenter
>
>>
>>>
>>> Thanks,
>>> Guenter
>>>
>>>> +
>>>> +    if (data->mode > LTC2990_CONTROL_MODE_MAX) {
>>>> +        dev_err(&i2c->dev, "Error: Invalid mode %d.\n", data->mode);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    /* Setup continuous mode */
>>>>     ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
>>>>                     LTC2990_CONTROL_MEASURE_ALL |
>>>> -                    LTC2990_CONTROL_MODE_CURRENT);
>>>> +                    data->mode);
>>>>     if (ret < 0) {
>>>>         dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
>>>>         return ret;
>>>> @@ -127,7 +244,7 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
>>>>
>>>>     hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
>>>>                                i2c->name,
>>>> -                               i2c,
>>>> +                               data,
>>>>                                ltc2990_groups);
>>>>
>>>>     return PTR_ERR_OR_ZERO(hwmon_dev);
>>>>
>>>
>>
>>
>>
>> Kind regards,
>>
>> Mike Looijmans
>> System Expert
>>
>> TOPIC Products
>> Materiaalweg 4, NL-5681 RJ Best
>> Postbus 440, NL-5680 AK Best
>> Telefoon: +31 (0) 499 33 69 79
>> E-mail: mike.looijmans@topicproducts.com
>> Website: www.topicproducts.com
>>
>> Please consider the environment before printing this e-mail
>>
>>
>>
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-28 15:29         ` Tom Levens
@ 2017-06-28 16:00           ` Guenter Roeck
  2017-06-28 17:02             ` Tom Levens
  0 siblings, 1 reply; 29+ messages in thread
From: Guenter Roeck @ 2017-06-28 16:00 UTC (permalink / raw)
  To: Tom Levens
  Cc: Mike Looijmans, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On Wed, Jun 28, 2017 at 05:29:38PM +0200, Tom Levens wrote:
> 
[ ... ]

> >
> >>Whatever happened to this patch though? It didn't make it to mainline,
> >>otherwise I'd have found it sooner...
> >>
> >I'll have to look it up, but I guess I didn't get an updated version.
> 
> As far as I remember I had a working V3 of this patch, but it is entirely
> possible that it was never submitted as I have been busy with other projects
> recently. I'll dig it out and check that it is complete.
> 
FWIW, I don't see it at
https://patchwork.kernel.org/project/linux-hwmon/list/?submitter=171225&state=*

Maybe you were waiting for a reply from Rob. Either case, it might make
sense to only provide valid modes, ie to abstract the mode bits from the
hardware, such as

0 - internal temp only
1 - Tr1
2 - V1
3 - V1-V2
4 - Tr2
5 - V3
6 - V3-V4
7 to 14 - per bit 0..2

Guenter

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-28 16:00           ` Guenter Roeck
@ 2017-06-28 17:02             ` Tom Levens
  2017-06-28 17:33               ` Mike Looijmans
  2017-06-29  7:45               ` Mike Looijmans
  0 siblings, 2 replies; 29+ messages in thread
From: Tom Levens @ 2017-06-28 17:02 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Tom Levens, Mike Looijmans, jdelvare, robh+dt, mark.rutland,
	linux-kernel, linux-hwmon, devicetree


On Wed, 28 Jun 2017, Guenter Roeck wrote:

> On Wed, Jun 28, 2017 at 05:29:38PM +0200, Tom Levens wrote:
>>
> [ ... ]
>
>>>
>>>> Whatever happened to this patch though? It didn't make it to mainline,
>>>> otherwise I'd have found it sooner...
>>>>
>>> I'll have to look it up, but I guess I didn't get an updated version.
>>
>> As far as I remember I had a working V3 of this patch, but it is entirely
>> possible that it was never submitted as I have been busy with other projects
>> recently. I'll dig it out and check that it is complete.
>>
> FWIW, I don't see it at
> https://patchwork.kernel.org/project/linux-hwmon/list/?submitter=171225&state=*
>
> Maybe you were waiting for a reply from Rob. Either case, it might make
> sense to only provide valid modes, ie to abstract the mode bits from the
> hardware, such as
>
> 0 - internal temp only
> 1 - Tr1
> 2 - V1
> 3 - V1-V2
> 4 - Tr2
> 5 - V3
> 6 - V3-V4
> 7 to 14 - per bit 0..2
>
> Guenter
>

You are right, there was still an open question about how best to handle 
the mode selection in DT.

In the latest version of my patch I have it implemented as an array for 
setting the two values, for example:

 	lltc,meas-mode = <7 3>;

This sets bits [2..0] = 7 and [4..3] = 3. Of course these could be split 
into two DT properties, but I was unsure what to name them as both fields 
are called "mode" in the datasheet and "mode-43"/"mode-20" (or similar) is 
ugly.

With regards to your proposal, it is not clear to me whether the modes 
which have the same result are exactly equivalent. Does disabling a 
measurement with the mode[4..3] bits really leaves the part in a safe 
state for all possible HW connections? With this doubt in my head, I would 
prefer to keep the option available to the user to select any specific 
mode. But I am open to suggestions.

Mike, if you would like to test it, the latest version of my code is here:

https://github.com/levens/ltc2990/blob/dev/drivers/hwmon/ltc2990.c

Cheers,

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-28 17:02             ` Tom Levens
@ 2017-06-28 17:33               ` Mike Looijmans
  2017-06-28 17:55                 ` Guenter Roeck
  2017-06-29  7:45               ` Mike Looijmans
  1 sibling, 1 reply; 29+ messages in thread
From: Mike Looijmans @ 2017-06-28 17:33 UTC (permalink / raw)
  To: Tom Levens, Guenter Roeck
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

On 28-06-17 19:02, Tom Levens wrote:
> 
> On Wed, 28 Jun 2017, Guenter Roeck wrote:
> 
>> On Wed, Jun 28, 2017 at 05:29:38PM +0200, Tom Levens wrote:
>>>
>> [ ... ]
>>
>>>>
>>>>> Whatever happened to this patch though? It didn't make it to mainline,
>>>>> otherwise I'd have found it sooner...
>>>>>
>>>> I'll have to look it up, but I guess I didn't get an updated version.
>>>
>>> As far as I remember I had a working V3 of this patch, but it is 
>>> entirely
>>> possible that it was never submitted as I have been busy with other 
>>> projects
>>> recently. I'll dig it out and check that it is complete.
>>>
>> FWIW, I don't see it at
>> https://patchwork.kernel.org/project/linux-hwmon/list/?submitter=171225&state=* 
>>
>>
>> Maybe you were waiting for a reply from Rob. Either case, it might make
>> sense to only provide valid modes, ie to abstract the mode bits from the
>> hardware, such as
>>
>> 0 - internal temp only
>> 1 - Tr1
>> 2 - V1
>> 3 - V1-V2
>> 4 - Tr2
>> 5 - V3
>> 6 - V3-V4
>> 7 to 14 - per bit 0..2
>>
>> Guenter
>>
> 
> You are right, there was still an open question about how best to handle 
> the mode selection in DT.
> 
> In the latest version of my patch I have it implemented as an array for 
> setting the two values, for example:
> 
>      lltc,meas-mode = <7 3>;
> 
> This sets bits [2..0] = 7 and [4..3] = 3. Of course these could be split 
> into two DT properties, but I was unsure what to name them as both 
> fields are called "mode" in the datasheet and "mode-43"/"mode-20" (or 
> similar) is ugly.
> 
> With regards to your proposal, it is not clear to me whether the modes 
> which have the same result are exactly equivalent. Does disabling a 
> measurement with the mode[4..3] bits really leaves the part in a safe 
> state for all possible HW connections? With this doubt in my head, I 
> would prefer to keep the option available to the user to select any 
> specific mode. But I am open to suggestions.

Well, the input restrictions always apply, so disabling V3 measurement 
doesn't imply that you can apply 20V to that input safely now.

I'd suggest to set unused input to plain voltage measurement. That is 
"passive" and safe for external components.

So I'd suggest just setting the mode as per device datasheet, I can see 
no real advantage in abstracting it away and forcing users to read yet 
another document to get it right, e.g.:

lltc,mode = <6>;

As for the input disabling, since I doubt anyone would use it (why 
purchase a 4-channel device and use only 2), just add two booleans, e.g. 
"disable-inputs-34" and "disable-inputs-12" which set the command bits 
appropriately, and change the mode such that the disabled inputs are 
voltage readout only.

A case could even be made for changing mode at runtime. This allows 
using it to measure both current and voltage on two inputs, by reading 
V1, and V3, and then switch mode to obtain (accurate) V1-V2 and V4-V3.

That might be a viable way to handle not setting the mode at all. If the 
mode can be selected via sysfs, the driver can keep the device in a 
"safe" mode until the mode has been selected.


> Mike, if you would like to test it, the latest version of my code is here:
> 
> https://github.com/levens/ltc2990/blob/dev/drivers/hwmon/ltc2990.c

Sure, I even have a board with 2 of these devices now :)

-- 
Mike Looijmans


Kind regards,

Mike Looijmans
System Expert

TOPIC Products
Materiaalweg 4, NL-5681 RJ Best
Postbus 440, NL-5680 AK Best
Telefoon: +31 (0) 499 33 69 79
E-mail: mike.looijmans@topicproducts.com
Website: www.topicproducts.com

Please consider the environment before printing this e-mail

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-28 17:33               ` Mike Looijmans
@ 2017-06-28 17:55                 ` Guenter Roeck
  0 siblings, 0 replies; 29+ messages in thread
From: Guenter Roeck @ 2017-06-28 17:55 UTC (permalink / raw)
  To: Mike Looijmans
  Cc: Tom Levens, jdelvare, robh+dt, mark.rutland, linux-kernel,
	linux-hwmon, devicetree

On Wed, Jun 28, 2017 at 07:33:33PM +0200, Mike Looijmans wrote:
> On 28-06-17 19:02, Tom Levens wrote:
> >
> >On Wed, 28 Jun 2017, Guenter Roeck wrote:
> >
> >>On Wed, Jun 28, 2017 at 05:29:38PM +0200, Tom Levens wrote:
> >>>
> >>[ ... ]
> >>
> >>>>
> >>>>>Whatever happened to this patch though? It didn't make it to mainline,
> >>>>>otherwise I'd have found it sooner...
> >>>>>
> >>>>I'll have to look it up, but I guess I didn't get an updated version.
> >>>
> >>>As far as I remember I had a working V3 of this patch, but it is
> >>>entirely
> >>>possible that it was never submitted as I have been busy with other
> >>>projects
> >>>recently. I'll dig it out and check that it is complete.
> >>>
> >>FWIW, I don't see it at
> >>https://patchwork.kernel.org/project/linux-hwmon/list/?submitter=171225&state=*
> >>
> >>
> >>Maybe you were waiting for a reply from Rob. Either case, it might make
> >>sense to only provide valid modes, ie to abstract the mode bits from the
> >>hardware, such as
> >>
> >>0 - internal temp only
> >>1 - Tr1
> >>2 - V1
> >>3 - V1-V2
> >>4 - Tr2
> >>5 - V3
> >>6 - V3-V4
> >>7 to 14 - per bit 0..2
> >>
> >>Guenter
> >>
> >
> >You are right, there was still an open question about how best to handle
> >the mode selection in DT.
> >
> >In the latest version of my patch I have it implemented as an array for
> >setting the two values, for example:
> >
> >     lltc,meas-mode = <7 3>;
> >
> >This sets bits [2..0] = 7 and [4..3] = 3. Of course these could be split
> >into two DT properties, but I was unsure what to name them as both fields
> >are called "mode" in the datasheet and "mode-43"/"mode-20" (or similar) is
> >ugly.
> >
> >With regards to your proposal, it is not clear to me whether the modes
> >which have the same result are exactly equivalent. Does disabling a
> >measurement with the mode[4..3] bits really leaves the part in a safe
> >state for all possible HW connections? With this doubt in my head, I would
> >prefer to keep the option available to the user to select any specific
> >mode. But I am open to suggestions.
> 
> Well, the input restrictions always apply, so disabling V3 measurement
> doesn't imply that you can apply 20V to that input safely now.
> 
> I'd suggest to set unused input to plain voltage measurement. That is
> "passive" and safe for external components.
> 
> So I'd suggest just setting the mode as per device datasheet, I can see no
> real advantage in abstracting it away and forcing users to read yet another
> document to get it right, e.g.:
> 
> lltc,mode = <6>;
> 
> As for the input disabling, since I doubt anyone would use it (why purchase
> a 4-channel device and use only 2), just add two booleans, e.g.
> "disable-inputs-34" and "disable-inputs-12" which set the command bits
> appropriately, and change the mode such that the disabled inputs are voltage
> readout only.
> 
> A case could even be made for changing mode at runtime. This allows using it
> to measure both current and voltage on two inputs, by reading V1, and V3,
> and then switch mode to obtain (accurate) V1-V2 and V4-V3.
> 
The hwmon subsystem doesn't really currently support that, and you'll likely
confuse userspace as well.

> That might be a viable way to handle not setting the mode at all. If the
> mode can be selected via sysfs, the driver can keep the device in a "safe"
> mode until the mode has been selected.
> 

You'll have to present me with a really good use case for me to agree with that
approach.

Guenter

> 
> >Mike, if you would like to test it, the latest version of my code is here:
> >
> >https://github.com/levens/ltc2990/blob/dev/drivers/hwmon/ltc2990.c
> 
> Sure, I even have a board with 2 of these devices now :)
> 
> -- 
> Mike Looijmans
> 
> 
> Kind regards,
> 
> Mike Looijmans
> System Expert
> 
> TOPIC Products
> Materiaalweg 4, NL-5681 RJ Best
> Postbus 440, NL-5680 AK Best
> Telefoon: +31 (0) 499 33 69 79
> E-mail: mike.looijmans@topicproducts.com
> Website: www.topicproducts.com
> 
> Please consider the environment before printing this e-mail
> 
> 
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-28 17:02             ` Tom Levens
  2017-06-28 17:33               ` Mike Looijmans
@ 2017-06-29  7:45               ` Mike Looijmans
  2017-06-29 11:46                 ` Tom Levens
  1 sibling, 1 reply; 29+ messages in thread
From: Mike Looijmans @ 2017-06-29  7:45 UTC (permalink / raw)
  To: Tom Levens, Guenter Roeck
  Cc: jdelvare, robh+dt, mark.rutland, linux-kernel, linux-hwmon, devicetree

On 28-06-17 19:02, Tom Levens wrote:
> 
> On Wed, 28 Jun 2017, Guenter Roeck wrote:
> 
>> On Wed, Jun 28, 2017 at 05:29:38PM +0200, Tom Levens wrote:
>>>
>> [ ... ]
>>
>>>>
>>>>> Whatever happened to this patch though? It didn't make it to mainline,
>>>>> otherwise I'd have found it sooner...
>>>>>
>>>> I'll have to look it up, but I guess I didn't get an updated version.
>>>
>>> As far as I remember I had a working V3 of this patch, but it is entirely
>>> possible that it was never submitted as I have been busy with other projects
>>> recently. I'll dig it out and check that it is complete.
>>>
>> FWIW, I don't see it at
>> https://patchwork.kernel.org/project/linux-hwmon/list/?submitter=171225&state=*
>>
>> Maybe you were waiting for a reply from Rob. Either case, it might make
>> sense to only provide valid modes, ie to abstract the mode bits from the
>> hardware, such as
>>
>> 0 - internal temp only
>> 1 - Tr1
>> 2 - V1
>> 3 - V1-V2
>> 4 - Tr2
>> 5 - V3
>> 6 - V3-V4
>> 7 to 14 - per bit 0..2
>>
>> Guenter
>>
> 
> You are right, there was still an open question about how best to handle the 
> mode selection in DT.
> 
> In the latest version of my patch I have it implemented as an array for 
> setting the two values, for example:
> 
>      lltc,meas-mode = <7 3>;
> 
> This sets bits [2..0] = 7 and [4..3] = 3. Of course these could be split into 
> two DT properties, but I was unsure what to name them as both fields are 
> called "mode" in the datasheet and "mode-43"/"mode-20" (or similar) is ugly.
> 
> With regards to your proposal, it is not clear to me whether the modes which 
> have the same result are exactly equivalent. Does disabling a measurement with 
> the mode[4..3] bits really leaves the part in a safe state for all possible HW 
> connections? With this doubt in my head, I would prefer to keep the option 
> available to the user to select any specific mode. But I am open to suggestions.
> 
> Mike, if you would like to test it, the latest version of my code is here:
> 
> https://github.com/levens/ltc2990/blob/dev/drivers/hwmon/ltc2990.c
> 

I pulled your patches, added two lines to the devicetree and it works fine, 
I'm seeing plausible results:
root@topic-miami:/sys/class/hwmon# grep . */*
hwmon0/name:battery-gauge
hwmon0/temp1_input:291700
hwmon1/in0_input:3258
hwmon1/in1_input:1824
hwmon1/in2_input:1916
hwmon1/in3_input:1512
hwmon1/in4_input:2066
hwmon1/name:ltc2990
hwmon1/temp1_input:28062
hwmon1/uevent:OF_NAME=ltc2990
hwmon1/uevent:OF_FULLNAME=/gpios-i2c@50/ltc2990@4c
hwmon1/uevent:OF_COMPATIBLE_0=lltc,ltc2990
hwmon1/uevent:OF_COMPATIBLE_N=1
hwmon2/curr1_input:1825
hwmon2/curr2_input:349
hwmon2/in0_input:3244
hwmon2/name:ltc2990
hwmon2/temp1_input:31500
hwmon2/uevent:OF_NAME=ltc2990
hwmon2/uevent:OF_FULLNAME=/gpios-i2c@52/ltc2990@4C
hwmon2/uevent:OF_COMPATIBLE_0=ltc2990
hwmon2/uevent:OF_COMPATIBLE_N=1


As you can see, two ltc2990 on two busses, one measuring current and one 
measuring 4 independent voltages.

I also like your implementation of the mode setting better, it's much simpler 
than mine. And you've solved a bug in the temperature reading as well.



Kind regards,

Mike Looijmans
System Expert

TOPIC Products
Materiaalweg 4, NL-5681 RJ Best
Postbus 440, NL-5680 AK Best
Telefoon: +31 (0) 499 33 69 79
E-mail: mike.looijmans@topicproducts.com
Website: www.topicproducts.com

Please consider the environment before printing this e-mail

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes
  2017-06-29  7:45               ` Mike Looijmans
@ 2017-06-29 11:46                 ` Tom Levens
  0 siblings, 0 replies; 29+ messages in thread
From: Tom Levens @ 2017-06-29 11:46 UTC (permalink / raw)
  To: Mike Looijmans
  Cc: Tom Levens, Guenter Roeck, jdelvare, robh+dt, mark.rutland,
	linux-kernel, linux-hwmon, devicetree



On Thu, 29 Jun 2017, Mike Looijmans wrote:

> On 28-06-17 19:02, Tom Levens wrote:
>>
>>  On Wed, 28 Jun 2017, Guenter Roeck wrote:
>> 
>> >  On Wed, Jun 28, 2017 at 05:29:38PM +0200, Tom Levens wrote:
>> > > 
>> >  [ ... ]
>> > 
>> > > > 
>> > > > >  Whatever happened to this patch though? It didn't make it to 
>> > > > >  mainline,
>> > > > >  otherwise I'd have found it sooner...
>> > > > > 
>> > > >  I'll have to look it up, but I guess I didn't get an updated 
>> > > >  version.
>> > > 
>> > >  As far as I remember I had a working V3 of this patch, but it is 
>> > >  entirely
>> > >  possible that it was never submitted as I have been busy with other 
>> > >  projects
>> > >  recently. I'll dig it out and check that it is complete.
>> > > 
>> >  FWIW, I don't see it at
>> >  https://patchwork.kernel.org/project/linux-hwmon/list/?submitter=171225&state=*
>> > 
>> >  Maybe you were waiting for a reply from Rob. Either case, it might make
>> >  sense to only provide valid modes, ie to abstract the mode bits from the
>> >  hardware, such as
>> > 
>> >  0 - internal temp only
>> >  1 - Tr1
>> >  2 - V1
>> >  3 - V1-V2
>> >  4 - Tr2
>> >  5 - V3
>> >  6 - V3-V4
>> >  7 to 14 - per bit 0..2
>> > 
>> >  Guenter
>> >
>>
>>  You are right, there was still an open question about how best to handle
>>  the mode selection in DT.
>>
>>  In the latest version of my patch I have it implemented as an array for
>>  setting the two values, for example:
>>
>>       lltc,meas-mode = <7 3>;
>>
>>  This sets bits [2..0] = 7 and [4..3] = 3. Of course these could be split
>>  into two DT properties, but I was unsure what to name them as both fields
>>  are called "mode" in the datasheet and "mode-43"/"mode-20" (or similar) is
>>  ugly.
>>
>>  With regards to your proposal, it is not clear to me whether the modes
>>  which have the same result are exactly equivalent. Does disabling a
>>  measurement with the mode[4..3] bits really leaves the part in a safe
>>  state for all possible HW connections? With this doubt in my head, I would
>>  prefer to keep the option available to the user to select any specific
>>  mode. But I am open to suggestions.
>>
>>  Mike, if you would like to test it, the latest version of my code is here:
>>
>>  https://github.com/levens/ltc2990/blob/dev/drivers/hwmon/ltc2990.c
>> 
>
> I pulled your patches, added two lines to the devicetree and it works fine, 
> I'm seeing plausible results:
> root@topic-miami:/sys/class/hwmon# grep . */*
> hwmon0/name:battery-gauge
> hwmon0/temp1_input:291700
> hwmon1/in0_input:3258
> hwmon1/in1_input:1824
> hwmon1/in2_input:1916
> hwmon1/in3_input:1512
> hwmon1/in4_input:2066
> hwmon1/name:ltc2990
> hwmon1/temp1_input:28062
> hwmon1/uevent:OF_NAME=ltc2990
> hwmon1/uevent:OF_FULLNAME=/gpios-i2c@50/ltc2990@4c
> hwmon1/uevent:OF_COMPATIBLE_0=lltc,ltc2990
> hwmon1/uevent:OF_COMPATIBLE_N=1
> hwmon2/curr1_input:1825
> hwmon2/curr2_input:349
> hwmon2/in0_input:3244
> hwmon2/name:ltc2990
> hwmon2/temp1_input:31500
> hwmon2/uevent:OF_NAME=ltc2990
> hwmon2/uevent:OF_FULLNAME=/gpios-i2c@52/ltc2990@4C
> hwmon2/uevent:OF_COMPATIBLE_0=ltc2990
> hwmon2/uevent:OF_COMPATIBLE_N=1
>
>
> As you can see, two ltc2990 on two busses, one measuring current and one 
> measuring 4 independent voltages.
>
> I also like your implementation of the mode setting better, it's much simpler 
> than mine. And you've solved a bug in the temperature reading as well.

Excellent, I am glad it also works for you! In this case, I would propose 
that I submit the V3 of the patch for further discussion.

Thanks,

>
> Kind regards,
>
> Mike Looijmans
> System Expert
>
> TOPIC Products
> Materiaalweg 4, NL-5681 RJ Best
> Postbus 440, NL-5680 AK Best
> Telefoon: +31 (0) 499 33 69 79
> E-mail: mike.looijmans@topicproducts.com
> Website: www.topicproducts.com
>
> Please consider the environment before printing this e-mail
>
>
>
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2017-06-29 11:46 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-17 12:10 [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Tom Levens
2016-11-17 12:10 ` [PATCH v2 2/3] hwmon: ltc2990: add devicetree binding Tom Levens
2016-11-18 14:50   ` Rob Herring
2016-11-18 15:36     ` Tom Levens
2016-11-17 12:10 ` [PATCH v2 3/3] hwmon: ltc2990: support all measurement modes Tom Levens
2016-11-17 16:56   ` Guenter Roeck
2016-11-17 17:40     ` Mike Looijmans
2016-11-17 18:56       ` Guenter Roeck
2016-11-17 19:52         ` Mike Looijmans
2016-11-17 21:54           ` Guenter Roeck
2016-11-17 23:25             ` Tom Levens
2016-11-17 23:40               ` Guenter Roeck
2016-11-18 12:23                 ` Tom Levens
2016-11-18 14:16                   ` Guenter Roeck
2017-06-28 14:24     ` Mike Looijmans
2017-06-28 15:01       ` Guenter Roeck
2017-06-28 15:29         ` Tom Levens
2017-06-28 16:00           ` Guenter Roeck
2017-06-28 17:02             ` Tom Levens
2017-06-28 17:33               ` Mike Looijmans
2017-06-28 17:55                 ` Guenter Roeck
2017-06-29  7:45               ` Mike Looijmans
2017-06-29 11:46                 ` Tom Levens
2016-11-17 15:06 ` [PATCH v2 1/3] hwmon: ltc2990: refactor value conversion Guenter Roeck
2016-11-17 16:23   ` Tom Levens
2016-11-17 16:36     ` Guenter Roeck
2016-11-18  8:18       ` Tom Levens
2016-11-18 14:09         ` Guenter Roeck
2016-11-18 14:17           ` Guenter Roeck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).