[PATCH 1/1] hwmon: (adt7475) Added attenuator bypass support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Added support for reading and writing the individual and global voltage
attenuator bypasses. Added loading DTS properties to bypass attenuators on
device probe.

Signed-off-by: Logan Shaw <logan.shaw@xxxxxxxxxxxxxxxxxxx>
---
---
 drivers/hwmon/adt7475.c | 163 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 162 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index 6c64d50c9aae..92a4be9e33eb 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -19,6 +19,7 @@
 #include <linux/hwmon-vid.h>
 #include <linux/err.h>
 #include <linux/jiffies.h>
+#include <linux/of.h>
 #include <linux/util_macros.h>
 
 /* Indexes for the sysfs hooks */
@@ -39,6 +40,7 @@
 
 #define ALARM		9
 #define FAULT		10
+#define ATTENUATE	11
 
 /* 7475 Common Registers */
 
@@ -126,9 +128,11 @@
 #define ADT7475_TACH_COUNT	4
 #define ADT7475_PWM_COUNT	3
 
-/* Macro to read the registers */
+/* Macros to read and write registers */
 
 #define adt7475_read(reg) i2c_smbus_read_byte_data(client, (reg))
+#define adt7475_write(reg, data) i2c_smbus_write_byte_data(client, (reg), \
+								(data))
 
 /* Macros to easily index the registers */
 
@@ -534,6 +538,82 @@ static ssize_t temp_store(struct device *dev, struct device_attribute *attr,
 	return count;
 }
 
+/**
+ * Gets the attenuator bit index for a sattr_index.
+ *
+ * If there is no attenuator bit index for a given sattr_index then returns
+ * a negative error code.
+ */
+static int config4_attenuate_index(char sattr_index)
+{
+	int index = -EBADR;
+
+	switch (sattr_index) {
+	case 0:
+		index = 4;
+		break;
+	case 1:
+		index = 5;
+		break;
+	case 3:
+		index = 6;
+		break;
+	case 4:
+		index = 7;
+		break;
+	}
+
+	return index;
+}
+
+/**
+ * Gets the bypass attenuator bit for a voltage input and stores it in the char
+ * array buf.
+ */
+static ssize_t bypass_attenuator_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct adt7475_data *data = adt7475_update_device(dev);
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+	u8 attn_bypassed = !!(data->config4 &
+				(1 << config4_attenuate_index(sattr->index)));
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%d\n", attn_bypassed);
+}
+
+/**
+ * Sets the bypass attenuator bit for a given voltage input.
+ */
+static ssize_t bypass_attenuator_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adt7475_data *data = i2c_get_clientdata(client);
+	long val;
+
+	if (kstrtol(buf, 2, &val))
+		return -EINVAL;
+
+	mutex_lock(&data->lock);
+	data->config4 = adt7475_read(REG_CONFIG4);
+	if (data->config4 < 0)
+		return data->config4;
+
+	if (val == 0)
+		data->config4 &= ~(1 << config4_attenuate_index(sattr->index));
+	else
+		data->config4 |= (1 << config4_attenuate_index(sattr->index));
+
+	adt7475_write(REG_CONFIG4, data->config4);
+	mutex_unlock(&data->lock);
+
+	return count;
+}
+
 /* Assuming CONFIG6[SLOW] is 0 */
 static const int ad7475_st_map[] = {
 	37500, 18800, 12500, 7500, 4700, 3100, 1600, 800,
@@ -1080,10 +1160,14 @@ static SENSOR_DEVICE_ATTR_2_RO(in0_input, voltage, INPUT, 0);
 static SENSOR_DEVICE_ATTR_2_RW(in0_max, voltage, MAX, 0);
 static SENSOR_DEVICE_ATTR_2_RW(in0_min, voltage, MIN, 0);
 static SENSOR_DEVICE_ATTR_2_RO(in0_alarm, voltage, ALARM, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in0_attenuator_bypass, bypass_attenuator,
+				ATTENUATE, 0);
 static SENSOR_DEVICE_ATTR_2_RO(in1_input, voltage, INPUT, 1);
 static SENSOR_DEVICE_ATTR_2_RW(in1_max, voltage, MAX, 1);
 static SENSOR_DEVICE_ATTR_2_RW(in1_min, voltage, MIN, 1);
 static SENSOR_DEVICE_ATTR_2_RO(in1_alarm, voltage, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in1_attenuator_bypass, bypass_attenuator,
+				ATTENUATE, 1);
 static SENSOR_DEVICE_ATTR_2_RO(in2_input, voltage, INPUT, 2);
 static SENSOR_DEVICE_ATTR_2_RW(in2_max, voltage, MAX, 2);
 static SENSOR_DEVICE_ATTR_2_RW(in2_min, voltage, MIN, 2);
@@ -1092,10 +1176,14 @@ static SENSOR_DEVICE_ATTR_2_RO(in3_input, voltage, INPUT, 3);
 static SENSOR_DEVICE_ATTR_2_RW(in3_max, voltage, MAX, 3);
 static SENSOR_DEVICE_ATTR_2_RW(in3_min, voltage, MIN, 3);
 static SENSOR_DEVICE_ATTR_2_RO(in3_alarm, voltage, ALARM, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in3_attenuator_bypass, bypass_attenuator,
+				ATTENUATE, 3);
 static SENSOR_DEVICE_ATTR_2_RO(in4_input, voltage, INPUT, 4);
 static SENSOR_DEVICE_ATTR_2_RW(in4_max, voltage, MAX, 4);
 static SENSOR_DEVICE_ATTR_2_RW(in4_min, voltage, MIN, 4);
 static SENSOR_DEVICE_ATTR_2_RO(in4_alarm, voltage, ALARM, 8);
+static SENSOR_DEVICE_ATTR_2_RW(in4_attenuator_bypass, bypass_attenuator,
+				ATTENUATE, 4);
 static SENSOR_DEVICE_ATTR_2_RO(in5_input, voltage, INPUT, 5);
 static SENSOR_DEVICE_ATTR_2_RW(in5_max, voltage, MAX, 5);
 static SENSOR_DEVICE_ATTR_2_RW(in5_min, voltage, MIN, 5);
@@ -1177,6 +1265,7 @@ static struct attribute *adt7475_attrs[] = {
 	&sensor_dev_attr_in1_max.dev_attr.attr,
 	&sensor_dev_attr_in1_min.dev_attr.attr,
 	&sensor_dev_attr_in1_alarm.dev_attr.attr,
+	&sensor_dev_attr_in1_attenuator_bypass.dev_attr.attr,
 	&sensor_dev_attr_in2_input.dev_attr.attr,
 	&sensor_dev_attr_in2_max.dev_attr.attr,
 	&sensor_dev_attr_in2_min.dev_attr.attr,
@@ -1263,6 +1352,7 @@ static struct attribute *in0_attrs[] = {
 	&sensor_dev_attr_in0_max.dev_attr.attr,
 	&sensor_dev_attr_in0_min.dev_attr.attr,
 	&sensor_dev_attr_in0_alarm.dev_attr.attr,
+	&sensor_dev_attr_in0_attenuator_bypass.dev_attr.attr,
 	NULL
 };
 
@@ -1271,6 +1361,7 @@ static struct attribute *in3_attrs[] = {
 	&sensor_dev_attr_in3_max.dev_attr.attr,
 	&sensor_dev_attr_in3_min.dev_attr.attr,
 	&sensor_dev_attr_in3_alarm.dev_attr.attr,
+	&sensor_dev_attr_in3_attenuator_bypass.dev_attr.attr,
 	NULL
 };
 
@@ -1279,6 +1370,7 @@ static struct attribute *in4_attrs[] = {
 	&sensor_dev_attr_in4_max.dev_attr.attr,
 	&sensor_dev_attr_in4_min.dev_attr.attr,
 	&sensor_dev_attr_in4_alarm.dev_attr.attr,
+	&sensor_dev_attr_in4_attenuator_bypass.dev_attr.attr,
 	NULL
 };
 
@@ -1457,6 +1549,69 @@ static int adt7475_update_limits(struct i2c_client *client)
 	return 0;
 }
 
+/**
+ * Reads individual voltage input bypass attenuator properties from the DTS,
+ * and if the property is present the corresponding bit is set in the
+ * register.
+ *
+ * Properties must be in the form of "bypass-attenuator-inx", where x is an
+ * integer from the set {0, 1, 3, 4} (can not bypass in2 attenuator).
+ *
+ * Returns a negative error code if there was an error writing to the register.
+ */
+static int load_individual_bypass_attenuators(const struct i2c_client *client,
+					      u8 *config4)
+{
+	char buf[32];
+	int attenuate_index, input_index;
+	u8 config4_copy = *config4;
+
+	for (input_index = 0; input_index < 5; input_index++) {
+		attenuate_index = config4_attenuate_index(input_index);
+		if (attenuate_index < 0)
+			continue;
+
+		sprintf(buf, "bypass-attenuator-in%d", input_index);
+		if (of_get_property(client->dev.of_node, buf, NULL))
+			config4_copy |= (1 << attenuate_index);
+	}
+
+	if (adt7475_write(REG_CONFIG4, config4_copy) < 0)
+		// Failed to update register
+		return -EREMOTEIO;
+
+	*config4 = config4_copy;
+
+	return 0;
+}
+
+/**
+ * Sets the bypass all attenuators bit, if the "bypass-attenuator-all"
+ * property exists in the DTS.
+ *
+ * Returns a negative error code if there was an error writing to the
+ * register.
+ */
+static int load_all_bypass_attenuator(const struct i2c_client *client,
+				      u8 *config2)
+{
+	u8 config2_copy = *config2;
+
+	if (!of_get_property(client->dev.of_node,
+			     "bypass-attenuator-all", NULL))
+		return 0;
+
+	config2_copy |= (1 << 5);
+
+	if (adt7475_write(REG_CONFIG2, config2_copy) < 0)
+		// failed to write to register
+		return -EREMOTEIO;
+
+	*config2 = config2_copy;
+
+	return 0;
+}
+
 static int adt7475_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
@@ -1545,7 +1700,13 @@ static int adt7475_probe(struct i2c_client *client,
 	}
 
 	/* Voltage attenuators can be bypassed, globally or individually */
+	if (load_individual_bypass_attenuators(client, &(data->config4)) < 0)
+		dev_warn(&client->dev,
+			 "Error setting bypass attenuator bits\n");
+
 	config2 = adt7475_read(REG_CONFIG2);
+	if (load_all_bypass_attenuator(client, &config2) < 0)
+		dev_warn(&client->dev, "Error setting bypass all attenuator\n");
 	if (config2 & CONFIG2_ATTN) {
 		data->bypass_attn = (0x3 << 3) | 0x3;
 	} else {
-- 
2.23.0




[Index of Archives]     [LM Sensors]     [Linux Sound]     [ALSA Users]     [ALSA Devel]     [Linux Audio Users]     [Linux Media]     [Kernel]     [Gimp]     [Yosemite News]     [Linux Media]

  Powered by Linux