[PATCH] hwmon: (adt7470) Add support for PWM inversion

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

 



Add sysfs attributes to control whether the PWM output signal from the
device is inverted (i.e. active-low rather than active-high).

Signed-off-by: Robert Hancock <robert.hancock@xxxxxxxxxx>
---
 Documentation/hwmon/adt7470.rst |  5 ++-
 drivers/hwmon/adt7470.c         | 72 ++++++++++++++++++++++++++++++++-
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/Documentation/hwmon/adt7470.rst b/Documentation/hwmon/adt7470.rst
index d225f816e992..8293f93d6cd5 100644
--- a/Documentation/hwmon/adt7470.rst
+++ b/Documentation/hwmon/adt7470.rst
@@ -69,7 +69,10 @@ from 0 (off) to 255 (full speed).  Fan speed will be set to maximum when the
 temperature sensor associated with the PWM control exceeds
 pwm#_auto_point2_temp.
 
-The driver also allows control of the PWM frequency:
+The driver also allows control of the PWM inversion and frequency:
+
+* pwm#_invert: Controls whether the PWM output signal is inverted
+  (i.e. low when active rather than high).
 
 * pwm1_freq
 
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 2e8feacccf84..f0863a6e1560 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -61,10 +61,14 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
 #define ADT7470_REG_FAN_MAX_MAX_ADDR		0x67
 #define ADT7470_REG_PWM_CFG_BASE_ADDR		0x68
 #define ADT7470_REG_PWM12_CFG			0x68
+#define		ADT7470_PWM2_INVERT_MASK	0x10
+#define		ADT7470_PWM1_INVERT_MASK	0x20
 #define		ADT7470_PWM2_AUTO_MASK		0x40
 #define		ADT7470_PWM1_AUTO_MASK		0x80
 #define		ADT7470_PWM_AUTO_MASK		0xC0
 #define ADT7470_REG_PWM34_CFG			0x69
+#define		ADT7470_PWM4_INVERT_MASK	0x10
+#define		ADT7470_PWM3_INVERT_MASK	0x20
 #define		ADT7470_PWM3_AUTO_MASK		0x40
 #define		ADT7470_PWM4_AUTO_MASK		0x80
 #define	ADT7470_REG_PWM_MIN_BASE_ADDR		0x6A
@@ -162,6 +166,7 @@ struct adt7470_data {
 	u8			pwm_min[ADT7470_PWM_COUNT];
 	s8			pwm_tmin[ADT7470_PWM_COUNT];
 	u8			pwm_auto_temp[ADT7470_PWM_COUNT];
+	u8			pwm_invert[ADT7470_PWM_COUNT];
 
 	struct task_struct	*auto_update;
 	unsigned int		auto_update_interval;
@@ -300,11 +305,22 @@ static int adt7470_update_sensors(struct adt7470_data *data)
 			reg_mask = ADT7470_PWM1_AUTO_MASK;
 
 		reg = ADT7470_REG_PWM_CFG(i);
-		if (i2c_smbus_read_byte_data(client, reg) & reg_mask)
+		cfg = i2c_smbus_read_byte_data(client, reg);
+		if (cfg & reg_mask)
 			data->pwm_automatic[i] = 1;
 		else
 			data->pwm_automatic[i] = 0;
 
+		if (i % 2)
+			reg_mask = ADT7470_PWM2_INVERT_MASK;
+		else
+			reg_mask = ADT7470_PWM1_INVERT_MASK;
+
+		if (cfg & reg_mask)
+			data->pwm_invert[i] = 1;
+		else
+			data->pwm_invert[i] = 0;
+
 		reg = ADT7470_REG_PWM_AUTO_TEMP(i);
 		cfg = i2c_smbus_read_byte_data(client, reg);
 		if (!(i % 2))
@@ -935,6 +951,51 @@ static ssize_t pwm_tmin_store(struct device *dev,
 	return count;
 }
 
+static ssize_t pwm_invert_show(struct device *dev,
+			       struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct adt7470_data *data = adt7470_update_device(dev);
+
+	return sprintf(buf, "%d\n", (int)data->pwm_invert[attr->index]);
+}
+
+static ssize_t pwm_invert_store(struct device *dev,
+				struct device_attribute *devattr,
+				const char *buf, size_t count)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct adt7470_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+	int pwm_invert_reg = ADT7470_REG_PWM_CFG(attr->index);
+	int pwm_invert_reg_mask;
+	long temp;
+	u8 reg;
+
+	if (kstrtol(buf, 10, &temp))
+		return -EINVAL;
+
+	if (attr->index % 2)
+		pwm_invert_reg_mask = ADT7470_PWM2_INVERT_MASK;
+	else
+		pwm_invert_reg_mask = ADT7470_PWM1_INVERT_MASK;
+
+	if (temp != 1 && temp != 0)
+		return -EINVAL;
+
+	mutex_lock(&data->lock);
+	data->pwm_invert[attr->index] = temp;
+	reg = i2c_smbus_read_byte_data(client, pwm_invert_reg);
+	if (temp)
+		reg |= pwm_invert_reg_mask;
+	else
+		reg &= ~pwm_invert_reg_mask;
+	i2c_smbus_write_byte_data(client, pwm_invert_reg, reg);
+	mutex_unlock(&data->lock);
+
+	return count;
+}
+
 static ssize_t pwm_auto_show(struct device *dev,
 			     struct device_attribute *devattr, char *buf)
 {
@@ -1135,6 +1196,11 @@ static SENSOR_DEVICE_ATTR_RW(pwm4, pwm, 3);
 
 static DEVICE_ATTR_RW(pwm1_freq);
 
+static SENSOR_DEVICE_ATTR_RW(pwm1_invert, pwm_invert, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_invert, pwm_invert, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm3_invert, pwm_invert, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm4_invert, pwm_invert, 3);
+
 static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm_min, 0);
 static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_pwm, pwm_min, 1);
 static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_pwm, pwm_min, 2);
@@ -1231,6 +1297,10 @@ static struct attribute *adt7470_attrs[] = {
 	&sensor_dev_attr_pwm2.dev_attr.attr,
 	&sensor_dev_attr_pwm3.dev_attr.attr,
 	&sensor_dev_attr_pwm4.dev_attr.attr,
+	&sensor_dev_attr_pwm1_invert.dev_attr.attr,
+	&sensor_dev_attr_pwm2_invert.dev_attr.attr,
+	&sensor_dev_attr_pwm3_invert.dev_attr.attr,
+	&sensor_dev_attr_pwm4_invert.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
-- 
2.27.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