From e41fae2b1ed8c78283d73651cd65be0228c0dd1c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 15 Nov 2013 10:40:38 +0100 Subject: [PATCH 1/7] hwmon: (lm90) Fix max6696 alarm handling Bit 2 of status register 2 on MAX6696 (external diode 2 open) sets ALERT; the bit thus has to be listed in alert_alarms. Also display a message in the alert handler if the condition is encountered. Even though not all overtemperature conditions cause ALERT to be set, we should not ignore them in the alert handler. Display messages for all out-of-range conditions. Reported-by: Jean Delvare Signed-off-by: Guenter Roeck Cc: stable@vger.kernel.org Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index cdff74282955..14e36c114d61 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -278,7 +278,7 @@ static const struct lm90_params lm90_params[] = { [max6696] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, - .alert_alarms = 0x187c, + .alert_alarms = 0x1c7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, @@ -1500,19 +1500,22 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) if ((alarms & 0x7f) == 0 && (alarms2 & 0xfe) == 0) { dev_info(&client->dev, "Everything OK\n"); } else { - if (alarms & 0x61) + if ((alarms & 0x61) || (alarms2 & 0x80)) dev_warn(&client->dev, "temp%d out of range, please check!\n", 1); - if (alarms & 0x1a) + if ((alarms & 0x1a) || (alarms2 & 0x20)) dev_warn(&client->dev, "temp%d out of range, please check!\n", 2); if (alarms & 0x04) dev_warn(&client->dev, "temp%d diode open, please check!\n", 2); - if (alarms2 & 0x18) + if (alarms2 & 0x5a) dev_warn(&client->dev, "temp%d out of range, please check!\n", 3); + if (alarms2 & 0x04) + dev_warn(&client->dev, + "temp%d diode open, please check!\n", 3); /* * Disable ALERT# output, because these chips don't implement From 072de4969f431c5ab4cbbcd9cd5af6e960c665c2 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Fri, 15 Nov 2013 10:40:38 +0100 Subject: [PATCH 2/7] hwmon: (lm90) Define status bits Add bit defines for the status register. And add a function lm90_is_tripped() which will read status register and return tripped or not, then lm90_alert can call it directly, and in the future the IRQ thread also can use it. [JD: Adjusted to include all the new MAX6696 status flags.] Signed-off-by: Wei Ni Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 88 ++++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 14e36c114d61..5e414ca80a78 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -179,6 +179,23 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ #define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */ +/* LM90 status */ +#define LM90_STATUS_LTHRM (1 << 0) /* local THERM limit tripped */ +#define LM90_STATUS_RTHRM (1 << 1) /* remote THERM limit tripped */ +#define LM90_STATUS_ROPEN (1 << 2) /* remote is an open circuit */ +#define LM90_STATUS_RLOW (1 << 3) /* remote low temp limit tripped */ +#define LM90_STATUS_RHIGH (1 << 4) /* remote high temp limit tripped */ +#define LM90_STATUS_LLOW (1 << 5) /* local low temp limit tripped */ +#define LM90_STATUS_LHIGH (1 << 6) /* local high temp limit tripped */ + +#define MAX6696_STATUS2_R2THRM (1 << 1) /* remote2 THERM limit tripped */ +#define MAX6696_STATUS2_R2OPEN (1 << 2) /* remote2 is an open circuit */ +#define MAX6696_STATUS2_R2LOW (1 << 3) /* remote2 low temp limit tripped */ +#define MAX6696_STATUS2_R2HIGH (1 << 4) /* remote2 high temp limit tripped */ +#define MAX6696_STATUS2_ROT2 (1 << 5) /* remote emergency limit tripped */ +#define MAX6696_STATUS2_R2OT2 (1 << 6) /* remote2 emergency limit tripped */ +#define MAX6696_STATUS2_LOT2 (1 << 7) /* local emergency limit tripped */ + /* * Driver data (common to all clients) */ @@ -1391,6 +1408,43 @@ static void lm90_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); } +static bool lm90_is_tripped(struct i2c_client *client, u16 *status) +{ + struct lm90_data *data = i2c_get_clientdata(client); + u8 st, st2 = 0; + + lm90_read_reg(client, LM90_REG_R_STATUS, &st); + + if (data->kind == max6696) + lm90_read_reg(client, MAX6696_REG_R_STATUS2, &st2); + + *status = st | (st2 << 8); + + if ((st & 0x7f) == 0 && (st2 & 0xfe) == 0) + return false; + + if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) || + (st2 & MAX6696_STATUS2_LOT2)) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 1); + if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) || + (st2 & MAX6696_STATUS2_ROT2)) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 2); + if (st & LM90_STATUS_ROPEN) + dev_warn(&client->dev, + "temp%d diode open, please check!\n", 2); + if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH | + MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2)) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 3); + if (st2 & MAX6696_STATUS2_R2OPEN) + dev_warn(&client->dev, + "temp%d diode open, please check!\n", 3); + + return true; +} + static int lm90_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1489,46 +1543,26 @@ static int lm90_remove(struct i2c_client *client) static void lm90_alert(struct i2c_client *client, unsigned int flag) { - struct lm90_data *data = i2c_get_clientdata(client); - u8 config, alarms, alarms2 = 0; - - lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); - - if (data->kind == max6696) - lm90_read_reg(client, MAX6696_REG_R_STATUS2, &alarms2); - - if ((alarms & 0x7f) == 0 && (alarms2 & 0xfe) == 0) { - dev_info(&client->dev, "Everything OK\n"); - } else { - if ((alarms & 0x61) || (alarms2 & 0x80)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 1); - if ((alarms & 0x1a) || (alarms2 & 0x20)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 2); - if (alarms & 0x04) - dev_warn(&client->dev, - "temp%d diode open, please check!\n", 2); - - if (alarms2 & 0x5a) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 3); - if (alarms2 & 0x04) - dev_warn(&client->dev, - "temp%d diode open, please check!\n", 3); + u16 alarms; + if (lm90_is_tripped(client, &alarms)) { /* * Disable ALERT# output, because these chips don't implement * SMBus alert correctly; they should only hold the alert line * low briefly. */ + struct lm90_data *data = i2c_get_clientdata(client); + if ((data->flags & LM90_HAVE_BROKEN_ALERT) && (alarms & data->alert_alarms)) { + u8 config; dev_dbg(&client->dev, "Disabling ALERT#\n"); lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config | 0x80); } + } else { + dev_info(&client->dev, "Everything OK\n"); } } From 109b1283fb532ac773a076748ffccf76a7067cab Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Fri, 15 Nov 2013 10:40:39 +0100 Subject: [PATCH 3/7] hwmon: (lm90) Add support to handle IRQ When the temperature exceed the limit range value, the driver can handle the interrupt. Signed-off-by: Wei Ni Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 5e414ca80a78..499f473e0780 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -89,6 +89,7 @@ #include #include #include +#include /* * Addresses to scan @@ -1445,6 +1446,17 @@ static bool lm90_is_tripped(struct i2c_client *client, u16 *status) return true; } +static irqreturn_t lm90_irq_thread(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + u16 status; + + if (lm90_is_tripped(client, &status)) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + static int lm90_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1521,6 +1533,18 @@ static int lm90_probe(struct i2c_client *client, goto exit_remove_files; } + if (client->irq) { + dev_dbg(dev, "IRQ: %d\n", client->irq); + err = devm_request_threaded_irq(dev, client->irq, + NULL, lm90_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "lm90", client); + if (err < 0) { + dev_err(dev, "cannot request IRQ %d\n", client->irq); + goto exit_remove_files; + } + } + return 0; exit_remove_files: From 40465d9424453aa499ea91114d94b52ef8f75d61 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Fri, 15 Nov 2013 10:40:39 +0100 Subject: [PATCH 4/7] hwmon: (lm90) Use enums for the indexes of temp8 and temp11 Using enums for the indexes and nrs of temp8 and temp11. This make the code much more readable. Signed-off-by: Wei Ni Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 152 +++++++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 499f473e0780..6b0a39264bb0 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -313,6 +313,36 @@ static const struct lm90_params lm90_params[] = { }, }; +/* + * TEMP8 register index + */ +enum lm90_temp8_reg_index { + LOCAL_LOW = 0, + LOCAL_HIGH, + LOCAL_CRIT, + REMOTE_CRIT, + LOCAL_EMERG, /* max6659 and max6695/96 */ + REMOTE_EMERG, /* max6659 and max6695/96 */ + REMOTE2_CRIT, /* max6695/96 only */ + REMOTE2_EMERG, /* max6695/96 only */ + TEMP8_REG_NUM +}; + +/* + * TEMP11 register index + */ +enum lm90_temp11_reg_index { + REMOTE_TEMP = 0, + REMOTE_LOW, + REMOTE_HIGH, + REMOTE_OFFSET, /* except max6646, max6657/58/59, and max6695/96 */ + LOCAL_TEMP, + REMOTE2_TEMP, /* max6695/96 only */ + REMOTE2_LOW, /* max6695/96 only */ + REMOTE2_HIGH, /* max6695/96 only */ + TEMP11_REG_NUM +}; + /* * Client data (each client gets its own) */ @@ -335,25 +365,8 @@ struct lm90_data { u8 reg_local_ext; /* local extension register offset */ /* registers values */ - s8 temp8[8]; /* 0: local low limit - * 1: local high limit - * 2: local critical limit - * 3: remote critical limit - * 4: local emergency limit (max6659 and max6695/96) - * 5: remote emergency limit (max6659 and max6695/96) - * 6: remote 2 critical limit (max6695/96 only) - * 7: remote 2 emergency limit (max6695/96 only) - */ - s16 temp11[8]; /* 0: remote input - * 1: remote low limit - * 2: remote high limit - * 3: remote offset (except max6646, max6657/58/59, - * and max6695/96) - * 4: local input - * 5: remote 2 input (max6695/96 only) - * 6: remote 2 low limit (max6695/96 only) - * 7: remote 2 high limit (max6695/96 only) - */ + s8 temp8[TEMP8_REG_NUM]; + s16 temp11[TEMP11_REG_NUM]; u8 temp_hyst; u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ }; @@ -495,37 +508,42 @@ static struct lm90_data *lm90_update_device(struct device *dev) u8 alarms; dev_dbg(&client->dev, "Updating lm90 data.\n"); - lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); - lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); - lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); + lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, + &data->temp8[LOCAL_LOW]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, + &data->temp8[LOCAL_HIGH]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, + &data->temp8[LOCAL_CRIT]); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, + &data->temp8[REMOTE_CRIT]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); if (data->reg_local_ext) { lm90_read16(client, LM90_REG_R_LOCAL_TEMP, data->reg_local_ext, - &data->temp11[4]); + &data->temp11[LOCAL_TEMP]); } else { if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &h) == 0) - data->temp11[4] = h << 8; + data->temp11[LOCAL_TEMP] = h << 8; } lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); + LM90_REG_R_REMOTE_TEMPL, + &data->temp11[REMOTE_TEMP]); if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { - data->temp11[1] = h << 8; + data->temp11[REMOTE_LOW] = h << 8; if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) - data->temp11[1] |= l; + data->temp11[REMOTE_LOW] |= l; } if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { - data->temp11[2] = h << 8; + data->temp11[REMOTE_HIGH] = h << 8; if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) - data->temp11[2] |= l; + data->temp11[REMOTE_HIGH] |= l; } if (data->flags & LM90_HAVE_OFFSET) { @@ -533,13 +551,13 @@ static struct lm90_data *lm90_update_device(struct device *dev) &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, &l) == 0) - data->temp11[3] = (h << 8) | l; + data->temp11[REMOTE_OFFSET] = (h << 8) | l; } if (data->flags & LM90_HAVE_EMERGENCY) { lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG, - &data->temp8[4]); + &data->temp8[LOCAL_EMERG]); lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, - &data->temp8[5]); + &data->temp8[REMOTE_EMERG]); } lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); data->alarms = alarms; /* save as 16 bit value */ @@ -547,15 +565,16 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (data->kind == max6696) { lm90_select_remote_channel(client, data, 1); lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, - &data->temp8[6]); + &data->temp8[REMOTE2_CRIT]); lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, - &data->temp8[7]); + &data->temp8[REMOTE2_EMERG]); lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL, &data->temp11[5]); + LM90_REG_R_REMOTE_TEMPL, + &data->temp11[REMOTE2_TEMP]); if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h)) - data->temp11[6] = h << 8; + data->temp11[REMOTE2_LOW] = h << 8; if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h)) - data->temp11[7] = h << 8; + data->temp11[REMOTE2_HIGH] = h << 8; lm90_select_remote_channel(client, data, 0); if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2, @@ -744,7 +763,7 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[8] = { + static const u8 reg[TEMP8_REG_NUM] = { LM90_REG_W_LOCAL_LOW, LM90_REG_W_LOCAL_HIGH, LM90_REG_W_LOCAL_CRIT, @@ -897,11 +916,11 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, mutex_lock(&data->update_lock); if (data->kind == adt7461) - temp = temp_from_u8_adt7461(data, data->temp8[2]); + temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); else if (data->kind == max6646) - temp = temp_from_u8(data->temp8[2]); + temp = temp_from_u8(data->temp8[LOCAL_CRIT]); else - temp = temp_from_s8(data->temp8[2]); + temp = temp_from_s8(data->temp8[LOCAL_CRIT]); data->temp_hyst = hyst_to_reg(temp - val); i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, @@ -955,25 +974,28 @@ static ssize_t set_update_interval(struct device *dev, return count; } -static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, 0, 4); -static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, + 0, LOCAL_TEMP); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, + 0, REMOTE_TEMP); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 0); + set_temp8, LOCAL_LOW); static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 0, 1); + set_temp11, 0, REMOTE_LOW); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 1); + set_temp8, LOCAL_HIGH); static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 1, 2); + set_temp11, 1, REMOTE_HIGH); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 2); + set_temp8, LOCAL_CRIT); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 3); + set_temp8, REMOTE_CRIT); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, - set_temphyst, 2); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); + set_temphyst, LOCAL_CRIT); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, + REMOTE_CRIT); static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 2, 3); + set_temp11, 2, REMOTE_OFFSET); /* Individual alarm files */ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); @@ -1021,13 +1043,13 @@ static const struct attribute_group lm90_group = { * Additional attributes for devices with emergency sensors */ static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 4); + set_temp8, LOCAL_EMERG); static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 5); + set_temp8, REMOTE_EMERG); static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst, - NULL, 4); + NULL, LOCAL_EMERG); static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst, - NULL, 5); + NULL, REMOTE_EMERG); static struct attribute *lm90_emergency_attributes[] = { &sensor_dev_attr_temp1_emergency.dev_attr.attr, @@ -1057,18 +1079,20 @@ static const struct attribute_group lm90_emergency_alarm_group = { /* * Additional attributes for devices with 3 temperature sensors */ -static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL, 0, 5); +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL, + 0, REMOTE2_TEMP); static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 3, 6); + set_temp11, 3, REMOTE2_LOW); static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 4, 7); + set_temp11, 4, REMOTE2_HIGH); static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 6); -static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, 6); + set_temp8, REMOTE2_CRIT); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, + REMOTE2_CRIT); static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 7); + set_temp8, REMOTE2_EMERG); static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst, - NULL, 7); + NULL, REMOTE2_EMERG); static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9); static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10); From 1daaceb26d5e6cc08eedf03f64ab220f62243c22 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Fri, 15 Nov 2013 10:40:39 +0100 Subject: [PATCH 5/7] hwmon: (lm90) Add support for TI TMP451 TI TMP451 is mostly compatible with ADT7461, except for local temperature low byte and max conversion rate. Add support to the LM90 driver. Signed-off-by: Wei Ni Reviewed-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 6 ++++++ drivers/hwmon/lm90.c | 45 +++++++++++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index b466974e142f..ab81013cc390 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -122,6 +122,12 @@ Supported chips: Prefix: 'g781' Addresses scanned: I2C 0x4c, 0x4d Datasheet: Not publicly available from GMT + * Texas Instruments TMP451 + Prefix: 'tmp451' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at TI website + http://www.ti.com/litv/pdf/sbos686 + Author: Jean Delvare diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 6b0a39264bb0..03735c490891 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -60,6 +60,11 @@ * This driver also supports the G781 from GMT. This device is compatible * with the ADM1032. * + * This driver also supports TMP451 from Texas Instruments. This device is + * supported in both compatibility and extended mode. It's mostly compatible + * with ADT7461 except for local temperature low byte register and max + * conversion rate. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -111,7 +116,7 @@ static const unsigned short normal_i2c[] = { 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696, sa56004, g781 }; + max6646, w83l771, max6696, sa56004, g781, tmp451 }; /* * The LM90 registers @@ -168,6 +173,9 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */ #define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ +/* TMP451 registers */ +#define TMP451_REG_R_LOCAL_TEMPL 0x15 + /* * Device flags */ @@ -223,6 +231,7 @@ static const struct i2c_device_id lm90_id[] = { { "nct1008", adt7461 }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, + { "tmp451", tmp451 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -311,6 +320,13 @@ static const struct lm90_params lm90_params[] = { .max_convrate = 9, .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL, }, + [tmp451] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 9, + .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL, + } }; /* @@ -746,7 +762,7 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, struct lm90_data *data = lm90_update_device(dev); int temp; - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else if (data->kind == max6646) temp = temp_from_u8(data->temp8[attr->index]); @@ -790,7 +806,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, val -= 16000; mutex_lock(&data->update_lock); - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) data->temp8[nr] = temp_to_u8_adt7461(data, val); else if (data->kind == max6646) data->temp8[nr] = temp_to_u8(val); @@ -812,7 +828,7 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, struct lm90_data *data = lm90_update_device(dev); int temp; - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); else if (data->kind == max6646) temp = temp_from_u16(data->temp11[attr->index]); @@ -858,7 +874,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, val -= 16000; mutex_lock(&data->update_lock); - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) data->temp11[index] = temp_to_u16_adt7461(data, val); else if (data->kind == max6646) data->temp11[index] = temp_to_u8(val) << 8; @@ -887,7 +903,7 @@ static ssize_t show_temphyst(struct device *dev, struct lm90_data *data = lm90_update_device(dev); int temp; - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else if (data->kind == max6646) temp = temp_from_u8(data->temp8[attr->index]); @@ -915,7 +931,7 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, return err; mutex_lock(&data->update_lock); - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); else if (data->kind == max6646) temp = temp_from_u8(data->temp8[LOCAL_CRIT]); @@ -1348,6 +1364,19 @@ static int lm90_detect(struct i2c_client *client, && (config1 & 0x3F) == 0x00 && convrate <= 0x08) name = "g781"; + } else + if (address == 0x4C + && man_id == 0x55) { /* Texas Instruments */ + int local_ext; + + local_ext = i2c_smbus_read_byte_data(client, + TMP451_REG_R_LOCAL_TEMPL); + + if (chip_id == 0x00 /* TMP451 */ + && (config1 & 0x1B) == 0x00 + && convrate <= 0x09 + && (local_ext & 0x0F) == 0x00) + name = "tmp451"; } if (!name) { /* identification failed */ @@ -1409,7 +1438,7 @@ static void lm90_init_client(struct i2c_client *client) data->config_orig = config; /* Check Temperature Range Select */ - if (data->kind == adt7461) { + if (data->kind == adt7461 || data->kind == tmp451) { if (config & 0x04) data->flags |= LM90_FLAG_ADT7461_EXT; } From 3e0f964f2ad38d2d4c2616fb8f12016cf6ea1758 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Fri, 15 Nov 2013 10:40:39 +0100 Subject: [PATCH 6/7] hwmon: (lm90) Add power control The device lm90 can be controlled by the vcc rail. Adding the regulator support to power on/off the vcc rail. Enable the "vcc" regulator before accessing the device. [JD: Rename variables to avoid confusion with registers.] Signed-off-by: Wei Ni Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 03735c490891..4c4c1421bf28 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -95,6 +95,7 @@ #include #include #include +#include /* * Addresses to scan @@ -366,6 +367,7 @@ enum lm90_temp11_reg_index { struct lm90_data { struct device *hwmon_dev; struct mutex update_lock; + struct regulator *regulator; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ int kind; @@ -1516,8 +1518,20 @@ static int lm90_probe(struct i2c_client *client, struct device *dev = &client->dev; struct i2c_adapter *adapter = to_i2c_adapter(dev->parent); struct lm90_data *data; + struct regulator *regulator; int err; + regulator = devm_regulator_get(dev, "vcc"); + if (IS_ERR(regulator)) + return PTR_ERR(regulator); + + err = regulator_enable(regulator); + if (err < 0) { + dev_err(&client->dev, + "Failed to enable regulator: %d\n", err); + return err; + } + data = devm_kzalloc(&client->dev, sizeof(struct lm90_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1525,6 +1539,8 @@ static int lm90_probe(struct i2c_client *client, i2c_set_clientdata(client, data); mutex_init(&data->update_lock); + data->regulator = regulator; + /* Set the device type */ data->kind = id->driver_data; if (data->kind == adm1032) { @@ -1604,6 +1620,8 @@ static int lm90_probe(struct i2c_client *client, lm90_remove_files(client, data); exit_restore: lm90_restore_conf(client, data); + regulator_disable(data->regulator); + return err; } @@ -1614,6 +1632,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); lm90_remove_files(client, data); lm90_restore_conf(client, data); + regulator_disable(data->regulator); return 0; } From aae7bce48176d5ffa4ccd4dbdb861488db892837 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Fri, 15 Nov 2013 10:40:39 +0100 Subject: [PATCH 7/7] Documentation: dt: hwmon: Add OF document for LM90 Add OF document for LM90 in Documentation/devicetree/. [JD: Add this new file to the LM90 MAINTAINERS entry.] Signed-off-by: Wei Ni Signed-off-by: Jean Delvare --- .../devicetree/bindings/hwmon/lm90.txt | 44 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/lm90.txt diff --git a/Documentation/devicetree/bindings/hwmon/lm90.txt b/Documentation/devicetree/bindings/hwmon/lm90.txt new file mode 100644 index 000000000000..e8632486b9ef --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/lm90.txt @@ -0,0 +1,44 @@ +* LM90 series thermometer. + +Required node properties: +- compatible: manufacturer and chip name, one of + "adi,adm1032" + "adi,adt7461" + "adi,adt7461a" + "gmt,g781" + "national,lm90" + "national,lm86" + "national,lm89" + "national,lm99" + "dallas,max6646" + "dallas,max6647" + "dallas,max6649" + "dallas,max6657" + "dallas,max6658" + "dallas,max6659" + "dallas,max6680" + "dallas,max6681" + "dallas,max6695" + "dallas,max6696" + "onnn,nct1008" + "winbond,w83l771" + "nxp,sa56004" + +- reg: I2C bus address of the device + +- vcc-supply: vcc regulator for the supply voltage. + +Optional properties: +- interrupts: Contains a single interrupt specifier which describes the + LM90 "-ALERT" pin output. + See interrupt-controller/interrupts.txt for the format. + +Example LM90 node: + +temp-sensor { + compatible = "onnn,nct1008"; + reg = <0x4c>; + vcc-supply = <&palmas_ldo6_reg>; + interrupt-parent = <&gpio>; + interrupts = ; +} diff --git a/MAINTAINERS b/MAINTAINERS index ffcaf975bed7..0da69578ccee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5175,6 +5175,7 @@ M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/lm90 +F: Documentation/devicetree/bindings/hwmon/lm90.txt F: drivers/hwmon/lm90.c LM95234 HARDWARE MONITOR DRIVER