[PATCH 2/5] hwmon: MAX6620 fix rpm calculation accuracy

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

 



From: Cumulus Networks <support@xxxxxxxxxxxxxxxxxxx>

The driver only fills the most significant 8 bits of the fan tach
count (11 bit value). Fixing the driver to use all of 11 bits for
more accuracy.
---
 drivers/hwmon/max6620.c | 105 ++++++++++++++++++----------------------
 1 file changed, 46 insertions(+), 59 deletions(-)

diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
index 3c337c7b0f4d..2cd3b0beb6b4 100644
--- a/drivers/hwmon/max6620.c
+++ b/drivers/hwmon/max6620.c
@@ -46,6 +46,8 @@
 
 /* clock: The clock frequency of the chip the driver should assume */
 static int clock = 8192;
+static u32 sr = 2;
+static u32 np = 2;
 
 module_param(clock, int, S_IRUGO);
 
@@ -213,22 +215,22 @@ static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, cha
 
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct max6620_data *data = max6620_update_device(dev);
-	int rpm;
-
-	/*
-	 * Calculation details:
-	 *
-	 * Each tachometer counts over an interval given by the "count"
-	 * register (0.25, 0.5, 1 or 2 seconds). This module assumes
-	 * that the fans produce two pulses per revolution (this seems
-	 * to be the most common).
-	 */
-	if(data->tach[attr->index] == 0 || data->tach[attr->index] == 255) {
+	struct i2c_client *client = to_i2c_client(dev);
+	u32 rpm = 0;
+	u32 tach = 0;
+	u32 tach1 = 0;
+	u32 tach2 = 0;
+
+	tach1 = i2c_smbus_read_byte_data(client, tach_reg[attr->index]);
+	tach1 = (tach1 << 3) & 0x7f8;
+	tach2 = i2c_smbus_read_byte_data(client, tach_reg[attr->index] + 1);
+	tach2 = (tach2 >> 5) & 0x7;
+	tach = tach1 | tach2;
+	if (tach == 0) {
 		rpm = 0;
 	} else {
-		rpm = ((clock / (data->tach[attr->index] << 3)) * 30 * DIV_FROM_REG(data->fandyn[attr->index]));
+		rpm = (60 * DIV_FROM_REG(data->fandyn[attr->index]) * clock)/(tach * np);
 	}
-
 	return sprintf(buf, "%d\n", rpm);
 }
 
@@ -236,22 +238,21 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
 
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct max6620_data *data = max6620_update_device(dev);
-	int kscale, ktach, rpm;
-
-	/*
-	 * Use the datasheet equation:
-	 *
-	 *    FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
-	 *
-	 * then multiply by 60 to give rpm.
-	 */
-
-	kscale = DIV_FROM_REG(data->fandyn[attr->index]);
-	ktach = data->target[attr->index];
-	if(ktach == 0) {
+	struct i2c_client *client = to_i2c_client(dev);
+	u32 rpm;
+	u32 target;
+	u32 target1;
+	u32 target2;
+
+	target1 = i2c_smbus_read_byte_data(client, target_reg[attr->index]);
+	target1 = (target1 << 3) & 0x7f8;
+	target2 = i2c_smbus_read_byte_data(client, target_reg[attr->index] + 1);
+	target2 = (target2 >> 5) & 0x7;
+	target = target1 | target2;
+	if (target == 0) {
 		rpm = 0;
 	} else {
-		rpm = ((60 * kscale * clock) / (ktach << 3));
+		rpm = (60 * DIV_FROM_REG(data->fandyn[attr->index]) * clock)/(target * np);
 	}
 	return sprintf(buf, "%d\n", rpm);
 }
@@ -261,9 +262,11 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
 	struct i2c_client *client = to_i2c_client(dev);
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct max6620_data *data = i2c_get_clientdata(client);
-	int kscale, ktach;
-	unsigned long rpm;
+	u32 rpm;
 	int err;
+	u32 target;
+	u32 target1;
+	u32 target2;
 
 	err = kstrtoul(buf, 10, &rpm);
 	if (err)
@@ -271,25 +274,13 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
 
 	rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
 
-	/*
-	 * Divide the required speed by 60 to get from rpm to rps, then
-	 * use the datasheet equation:
-	 *
-	 *     KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
-	 */
-
 	mutex_lock(&data->update_lock);
 
-	kscale = DIV_FROM_REG(data->fandyn[attr->index]);
-	ktach = ((60 * kscale * clock) / rpm);
-	if (ktach < 0)
-		ktach = 0;
-	if (ktach > 255)
-		ktach = 255;
-	data->target[attr->index] = ktach;
-
-	i2c_smbus_write_byte_data(client, target_reg[attr->index], data->target[attr->index]);
-	i2c_smbus_write_byte_data(client, target_reg[attr->index]+0x01, 0x00);
+	target = (60 * DIV_FROM_REG(data->fandyn[attr->index]) * clock)/(rpm * np);
+	target1 = (target >> 3) & 0xff;
+	target2 = (target << 5) & 0xe0;
+	i2c_smbus_write_byte_data(client, target_reg[attr->index], target1);
+	i2c_smbus_write_byte_data(client, target_reg[attr->index] + 1, target2);
 
 	mutex_unlock(&data->update_lock);
 
@@ -609,8 +600,11 @@ static int max6620_init_client(struct i2c_client *client) {
 	}
 
 
-
-	if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config)) {
+	/*
+	 * Set bit 4, disable other fans from going full speed on a fail
+	 * failure.
+	 */
+	if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config | 0x10)) {
 		dev_err(&client->dev, "Config write error, aborting.\n");
 		return err;
 	}
@@ -618,28 +612,21 @@ static int max6620_init_client(struct i2c_client *client) {
 	data->config = config;
 	for (i = 0; i < 4; i++) {
 		data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]);
-		data->fancfg[i] |= 0x80;		// enable TACH monitoring
+		data->fancfg[i] |= 0xa8;		// enable TACH monitoring
 		i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]);
 		data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
-		data-> fandyn[i] |= 0x1C;
+                /* 2 counts (001) and Rate change 100 (0.125 secs) */
+		data-> fandyn[i] = 0x30;
 		i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]);
 		data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
 		data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
 		data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
 		data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
 
-
-
 	}
-
-
-
 	return 0;
 }
 
-
-
-
 static struct max6620_data *max6620_update_device(struct device *dev)
 {
 	int i;
@@ -678,7 +665,7 @@ static struct max6620_data *max6620_update_device(struct device *dev)
 	return data;
 }
 
-module_i2c_driver(max6620_driver);
+// module_i2c_driver(max6620_driver);
 
 static int __init max6620_init(void)
 {
-- 
2.32.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