[i2c] [PATCH] hwmon: LM96000 support

Herbert Poetzl herbert at 13thfloor.at
Wed Oct 1 20:52:35 CEST 2008


this patch adds proper support for LM96000 (at least
the version I could test with) and provides some
'generic' control of the tachometer monitor mode for
lm85/lm96000 pwm control.

please consider for (mainline) inclusion!

TIA,
Herbert

Signed-off-by: Herbert Poetzl <herbert at 13thfloor.at>

diff -NurpP --minimal linux-2.6.27-rc8-khali/Documentation/hwmon/lm85 linux-2.6.27-rc8-khali-lm96k-v0.1/Documentation/hwmon/lm85
--- linux-2.6.27-rc8-khali/Documentation/hwmon/lm85	2008-10-01 19:39:51.000000000 +0200
+++ linux-2.6.27-rc8-khali-lm96k-v0.1/Documentation/hwmon/lm85	2008-10-01 20:39:01.000000000 +0200
@@ -189,6 +189,22 @@ Configuration choices:
      -1    PWM always 100%  (full on)
      -2    Manual control (write to 'pwm#' to set)
 
+* PWM Tachometer Monitor Mode
+
+* pwm#_tmm - controls the monitor mode for pwm#
+
+Configuration choices:
+
+   Value     Meaning                                 <min
+  ------  ----------------------------------------  ------
+      0    Traditional tach input monitor             any   
+      1    Traditional tach input monitor            FFFFh
+      2    Most accurate readings                    FFFFh
+      3    Least effect on programmed PWM of Fan     FFFFh
+
+In the default setting, you will get false readings when under 
+minimum detctable RPM, in all other modes FFFFh.
+
 The National LM85's have two vendor specific configuration
 features. Tach. mode and Spinup Control. For more details on these,
 see the LM85 datasheet or Application Note AN-1260. These features
diff -NurpP --minimal linux-2.6.27-rc8-khali/drivers/hwmon/lm85.c linux-2.6.27-rc8-khali-lm96k-v0.1/drivers/hwmon/lm85.c
--- linux-2.6.27-rc8-khali/drivers/hwmon/lm85.c	2008-10-01 19:39:51.000000000 +0200
+++ linux-2.6.27-rc8-khali-lm96k-v0.1/drivers/hwmon/lm85.c	2008-10-01 20:29:25.000000000 +0200
@@ -39,7 +39,8 @@
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102);
+I2C_CLIENT_INSMOD_7(lm85b, lm85c, lm96000, adm1027, adt7463, emc6d100,
+		    emc6d102);
 
 /* The LM85 registers */
 
@@ -67,6 +68,7 @@ I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm102
 #define	LM85_VERSTEP_GENERIC		0x60
 #define	LM85_VERSTEP_LM85C		0x60
 #define	LM85_VERSTEP_LM85B		0x62
+#define	LM85_VERSTEP_LM96000		0x69
 #define	LM85_VERSTEP_ADM1027		0x60
 #define	LM85_VERSTEP_ADT7463		0x62
 #define	LM85_VERSTEP_ADT7463C		0x6A
@@ -91,6 +93,8 @@ I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm102
 #define	LM85_REG_AFAN_HYST1		0x6d
 #define	LM85_REG_AFAN_HYST2		0x6e
 
+#define	LM85_REG_TACHO_MON_MODE		0x74
+
 #define	ADM1027_REG_EXTEND_ADC1		0x76
 #define	ADM1027_REG_EXTEND_ADC2		0x77
 
@@ -185,11 +189,16 @@ static int RANGE_TO_REG(int range)
 #define RANGE_FROM_REG(val)	lm85_range_map[(val) & 0x0f]
 
 /* These are the PWM frequency encodings */
-static const int lm85_freq_map[8] = { /* 1 Hz */
-	10, 15, 23, 30, 38, 47, 62, 94
+static const int lm85_freq_map[] = { /* 1 Hz */
+	10, 15, 23, 30, 38, 47, 62, 94, 0
 };
-static const int adm1027_freq_map[8] = { /* 1 Hz */
-	11, 15, 22, 29, 35, 44, 59, 88
+static const int lm96000_freq_map[] = { /* 1 Hz */
+	10, 15, 23, 30, 38, 47, 61, 94,
+	22500, 24000, 25700, 25700,
+	27700, 27700, 30000, 30000, 0
+};
+static const int adm1027_freq_map[] = { /* 1 Hz */
+	11, 15, 22, 29, 35, 44, 59, 88, 0
 };
 
 static int FREQ_TO_REG(const int *map, int freq)
@@ -197,7 +206,7 @@ static int FREQ_TO_REG(const int *map, i
 	int i;
 
 	/* Find the closest match */
-	for (i = 0; i < 7; ++i)
+	for (i = 0; map[i + 1]; i++)
 		if (freq <= (map[i] + map[i + 1]) / 2)
 			break;
 	return i;
@@ -205,7 +214,13 @@ static int FREQ_TO_REG(const int *map, i
 
 static int FREQ_FROM_REG(const int *map, u8 reg)
 {
-	return map[reg & 0x07];
+	int i;
+
+	for (i = 0; map[i]; i++)
+		if (reg == i)
+			break;
+
+	return map[i];
 }
 
 /* Since we can't use strings, I'm abusing these numbers
@@ -304,6 +319,7 @@ struct lm85_data {
 	u8 temp_ext[3];		/* Decoded values */
 	u8 in_ext[8];		/* Decoded values */
 	u8 vid;			/* Register value */
+	u8 tmm;			/* Register value */
 	u8 vrm;			/* VRM version */
 	u32 alarms;		/* Register encoding, combined */
 	struct lm85_autofan autofan[3];
@@ -327,6 +343,7 @@ static const struct i2c_device_id lm85_i
 	{ "lm85", any_chip },
 	{ "lm85b", lm85b },
 	{ "lm85c", lm85c },
+	{ "lm96000", lm96000 },
 	{ "emc6d100", emc6d100 },
 	{ "emc6d101", emc6d100 },
 	{ "emc6d102", emc6d102 },
@@ -567,7 +584,31 @@ static ssize_t set_pwm_freq(struct devic
 	data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val);
 	lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
 		(data->zone[nr].range << 4)
-		| data->pwm_freq[nr]);
+		| (data->pwm_freq[nr] & 0x0f));
+	mutex_unlock(&data->update_lock);
+	return count;
+}
+
+static ssize_t show_pwm_tmm(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int nr = to_sensor_dev_attr(attr)->index;
+	struct lm85_data *data = lm85_update_device(dev);
+	return sprintf(buf, "%d\n", (data->tmm >> (2*nr)) & 3);
+}
+
+static ssize_t set_pwm_tmm(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int nr = to_sensor_dev_attr(attr)->index;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm85_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	int mask = 3 << (2*nr);
+
+	mutex_lock(&data->update_lock);
+	data->tmm = (data->tmm & ~mask) | ((val & 3) << (2*nr));
+	lm85_write_value(client, LM85_REG_TACHO_MON_MODE, data->tmm);
 	mutex_unlock(&data->update_lock);
 	return count;
 }
@@ -578,7 +619,9 @@ static SENSOR_DEVICE_ATTR(pwm##offset, S
 static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR,	\
 		show_pwm_enable, set_pwm_enable, offset - 1);		\
 static SENSOR_DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR,	\
-		show_pwm_freq, set_pwm_freq, offset - 1)
+		show_pwm_freq, set_pwm_freq, offset - 1);		\
+static SENSOR_DEVICE_ATTR(pwm##offset##_tmm, S_IRUGO | S_IWUSR,		\
+		show_pwm_tmm, set_pwm_tmm, offset - 1)
 
 
 show_pwm_reg(1);
@@ -886,7 +929,7 @@ static ssize_t set_temp_auto_temp_min(st
 		TEMP_FROM_REG(data->zone[nr].limit));
 	lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
 		((data->zone[nr].range & 0x0f) << 4)
-		| (data->pwm_freq[nr] & 0x07));
+		| (data->pwm_freq[nr] & 0x0f));
 
 /* Update temp_auto_hyst and temp_auto_off */
 	data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG(
@@ -929,7 +972,7 @@ static ssize_t set_temp_auto_temp_max(st
 		val - min);
 	lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
 		((data->zone[nr].range & 0x0f) << 4)
-		| (data->pwm_freq[nr] & 0x07));
+		| (data->pwm_freq[nr] & 0x0f));
 	mutex_unlock(&data->update_lock);
 	return count;
 }
@@ -999,6 +1042,9 @@ static struct attribute *lm85_attributes
 	&sensor_dev_attr_pwm1_freq.dev_attr.attr,
 	&sensor_dev_attr_pwm2_freq.dev_attr.attr,
 	&sensor_dev_attr_pwm3_freq.dev_attr.attr,
+	&sensor_dev_attr_pwm1_tmm.dev_attr.attr,
+	&sensor_dev_attr_pwm2_tmm.dev_attr.attr,
+	&sensor_dev_attr_pwm3_tmm.dev_attr.attr,
 
 	&sensor_dev_attr_in0_input.dev_attr.attr,
 	&sensor_dev_attr_in1_input.dev_attr.attr,
@@ -1145,6 +1191,9 @@ static int lm85_detect(struct i2c_client
 			case LM85_VERSTEP_LM85B:
 				kind = lm85b;
 				break;
+			case LM85_VERSTEP_LM96000:
+				kind = lm96000;
+				break;
 			}
 		} else if (company == LM85_COMPANY_ANALOG_DEV) {
 			switch (verstep) {
@@ -1193,6 +1242,9 @@ static int lm85_detect(struct i2c_client
 	case lm85c:
 		type_name = "lm85c";
 		break;
+	case lm96000:
+		type_name = "lm96000";
+		break;
 	case adm1027:
 		type_name = "adm1027";
 		break;
@@ -1237,6 +1289,9 @@ static int lm85_probe(struct i2c_client 
 	case emc6d102:
 		data->freq_map = adm1027_freq_map;
 		break;
+	case lm96000:
+		data->freq_map = lm96000_freq_map;
+		break;
 	default:
 		data->freq_map = lm85_freq_map;
 	}
@@ -1401,6 +1456,9 @@ static struct lm85_data *lm85_update_dev
 			    lm85_read_value(client, LM85_REG_PWM(i));
 		}
 
+		/* maybe restrict to LM85/LM96000? */
+		data->tmm = lm85_read_value(client, LM85_REG_TACHO_MON_MODE);
+
 		data->alarms = lm85_read_value(client, LM85_REG_ALARM1);
 
 		if (data->type == emc6d100) {
@@ -1480,7 +1538,7 @@ static struct lm85_data *lm85_update_dev
 			data->autofan[i].config =
 			    lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
 			val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
-			data->pwm_freq[i] = val & 0x07;
+			data->pwm_freq[i] = val & 0x0f;
 			data->zone[i].range = val >> 4;
 			data->autofan[i].min_pwm =
 			    lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));



More information about the i2c mailing list