summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2010-03-05 22:17:15 +0100
committerJean Delvare <khali@linux-fr.org>2010-03-05 22:17:15 +0100
commit53de33427fa3d7dd62cc5ec75ce0d4e6c6d602dd (patch)
tree82a1fd88a2d132b534fe3df3e2ea536c1b5099ae
parent95238364167edaf93ce2890e5f55326b63194851 (diff)
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: Jean Delvare <khali@linux-fr.org> Cc: David Brownell <dbrownell@users.sourceforge.net> Cc: Trent Piepho <tpiepho@freescale.com>
-rw-r--r--Documentation/hwmon/lm9012
-rw-r--r--drivers/hwmon/lm90.c63
2 files changed, 75 insertions, 0 deletions
diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90
index 08106ad7089c..6a03dd4bcc94 100644
--- a/Documentation/hwmon/lm90
+++ b/Documentation/hwmon/lm90
@@ -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
'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
-----------
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 4cbbf1563de6..7cc2708871ab 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -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,
const struct i2c_device_id *id);
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 struct lm90_data *lm90_update_device(struct device *dev);
@@ -186,6 +187,7 @@ static struct i2c_driver lm90_driver = {
},
.probe = lm90_probe,
.remove = lm90_remove,
+ .alert = lm90_alert,
.id_table = lm90_id,
.detect = lm90_detect,
.address_list = normal_i2c,
@@ -204,6 +206,7 @@ struct lm90_data {
int flags;
u8 config_orig; /* Original configuration register value */
+ u8 alert_alarms; /* Which alarm bits trigger ALERT# */
/* registers values */
s8 temp8[4]; /* 0: local low limit
@@ -806,6 +809,19 @@ static int lm90_probe(struct i2c_client *new_client,
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 */
lm90_init_client(new_client);
@@ -895,6 +911,38 @@ static int lm90_remove(struct i2c_client *client)
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)
{
int err;
@@ -982,6 +1030,21 @@ static struct lm90_data *lm90_update_device(struct device *dev)
}
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->valid = 1;
}