Commit 53de3342 authored by Jean Delvare's avatar Jean Delvare

hwmon: (lm90) Add SMBus alert support

Tested successfully with an ADM1032 chip on its evaluation board. It
should work fine with all other chips as well.

At this point this is more of a proof-of-concept, we don't do anything
terribly useful on SMBus alert: we simply log the event. But this could
later evolve into libsensors signaling so that user-space applications
can take an appropriate action.
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Cc: Trent Piepho <tpiepho@freescale.com>
parent 95238364
...@@ -173,6 +173,18 @@ The lm90 driver will not update its values more frequently than every ...@@ -173,6 +173,18 @@ The lm90 driver will not update its values more frequently than every
other second; reading them more often will do no harm, but will return other second; reading them more often will do no harm, but will return
'old' values. 'old' values.
SMBus Alert Support
-------------------
This driver has basic support for SMBus alert. When an alert is received,
the status register is read and the faulty temperature channel is logged.
The Analog Devices chips (ADM1032 and ADT7461) do not implement the SMBus
alert protocol properly so additional care is needed: the ALERT output is
disabled when an alert is received, and is re-enabled only when the alarm
is gone. Otherwise the chip would block alerts from other chips in the bus
as long as the alarm is active.
PEC Support PEC Support
----------- -----------
......
...@@ -152,6 +152,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info); ...@@ -152,6 +152,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info);
static int lm90_probe(struct i2c_client *client, static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id); const struct i2c_device_id *id);
static void lm90_init_client(struct i2c_client *client); static void lm90_init_client(struct i2c_client *client);
static void lm90_alert(struct i2c_client *client, unsigned int flag);
static int lm90_remove(struct i2c_client *client); static int lm90_remove(struct i2c_client *client);
static struct lm90_data *lm90_update_device(struct device *dev); static struct lm90_data *lm90_update_device(struct device *dev);
...@@ -186,6 +187,7 @@ static struct i2c_driver lm90_driver = { ...@@ -186,6 +187,7 @@ static struct i2c_driver lm90_driver = {
}, },
.probe = lm90_probe, .probe = lm90_probe,
.remove = lm90_remove, .remove = lm90_remove,
.alert = lm90_alert,
.id_table = lm90_id, .id_table = lm90_id,
.detect = lm90_detect, .detect = lm90_detect,
.address_list = normal_i2c, .address_list = normal_i2c,
...@@ -204,6 +206,7 @@ struct lm90_data { ...@@ -204,6 +206,7 @@ struct lm90_data {
int flags; int flags;
u8 config_orig; /* Original configuration register value */ u8 config_orig; /* Original configuration register value */
u8 alert_alarms; /* Which alarm bits trigger ALERT# */
/* registers values */ /* registers values */
s8 temp8[4]; /* 0: local low limit s8 temp8[4]; /* 0: local low limit
...@@ -806,6 +809,19 @@ static int lm90_probe(struct i2c_client *new_client, ...@@ -806,6 +809,19 @@ static int lm90_probe(struct i2c_client *new_client,
new_client->flags &= ~I2C_CLIENT_PEC; new_client->flags &= ~I2C_CLIENT_PEC;
} }
/* Different devices have different alarm bits triggering the
* ALERT# output */
switch (data->kind) {
case lm90:
case lm99:
case lm86:
data->alert_alarms = 0x7b;
break;
default:
data->alert_alarms = 0x7c;
break;
}
/* Initialize the LM90 chip */ /* Initialize the LM90 chip */
lm90_init_client(new_client); lm90_init_client(new_client);
...@@ -895,6 +911,38 @@ static int lm90_remove(struct i2c_client *client) ...@@ -895,6 +911,38 @@ static int lm90_remove(struct i2c_client *client)
return 0; return 0;
} }
static void lm90_alert(struct i2c_client *client, unsigned int flag)
{
struct lm90_data *data = i2c_get_clientdata(client);
u8 config, alarms;
lm90_read_reg(client, LM90_REG_R_STATUS, &alarms);
if ((alarms & 0x7f) == 0) {
dev_info(&client->dev, "Everything OK\n");
} else {
if (alarms & 0x61)
dev_warn(&client->dev,
"temp%d out of range, please check!\n", 1);
if (alarms & 0x1a)
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);
/* Disable ALERT# output, because these chips don't implement
SMBus alert correctly; they should only hold the alert line
low briefly. */
if ((data->kind == adm1032 || data->kind == adt7461)
&& (alarms & data->alert_alarms)) {
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);
}
}
}
static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
{ {
int err; int err;
...@@ -982,6 +1030,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) ...@@ -982,6 +1030,21 @@ static struct lm90_data *lm90_update_device(struct device *dev)
} }
lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms);
/* Re-enable ALERT# output if it was originally enabled and
* relevant alarms are all clear */
if ((data->config_orig & 0x80) == 0
&& (data->alarms & data->alert_alarms) == 0) {
u8 config;
lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
if (config & 0x80) {
dev_dbg(&client->dev, "Re-enabling ALERT#\n");
i2c_smbus_write_byte_data(client,
LM90_REG_W_CONFIG1,
config & ~0x80);
}
}
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = 1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment