From 6388a388ffb720f40fc8046c261252ea2be9c12f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:09 +0200 Subject: hwmon: (lm90) Move 16-bit value read to a separate function Move the code which aggregates two 8-bit register values into a 16-bit value to a separate function. We'll need to do it a second time soon and I don't want to duplicate the code. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- drivers/hwmon/lm90.c | 72 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c24fe36ac78..73a1c622fb7 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1,7 +1,7 @@ /* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2006 Jean Delvare + * Copyright (C) 2003-2008 Jean Delvare * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to @@ -736,6 +736,38 @@ static int lm90_remove(struct i2c_client *client) return 0; } +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) +{ + int err; + u8 oldh, newh, l; + + /* + * There is a trick here. We have to read two registers to have the + * sensor temperature, but we have to beware a conversion could occur + * inbetween the readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * byte once, then the low byte, then the high byte again. If the new + * high byte matches the old one, then we have a valid reading. Else + * we have to read the low byte again, and now we believe we have a + * correct reading. + */ + if ((err = lm90_read_reg(client, regh, &oldh)) + || (err = lm90_read_reg(client, regl, &l)) + || (err = lm90_read_reg(client, regh, &newh))) + return err; + if (oldh != newh) { + err = lm90_read_reg(client, regl, &l); + if (err) + return err; + } + *value = (newh << 8) | l; + + return 0; +} + static struct lm90_data *lm90_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -744,7 +776,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - u8 oldh, newh, l; + u8 h, l; dev_dbg(&client->dev, "Updating lm90 data.\n"); lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]); @@ -754,39 +786,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - /* - * There is a trick here. We have to read two registers to - * have the remote sensor temperature, but we have to beware - * a conversion could occur inbetween the readings. The - * datasheet says we should either use the one-shot - * conversion register, which we don't want to do (disables - * hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit - * at the exact same time). So the solution used here is to - * read the high byte once, then the low byte, then the high - * byte again. If the new high byte matches the old one, - * then we have a valid reading. Else we have to read the low - * byte again, and now we believe we have a correct reading. - */ - if (lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &oldh) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &newh) == 0 - && (newh == oldh - || lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0)) - data->temp11[0] = (newh << 8) | l; - - if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &newh) == 0 + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); + + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) - data->temp11[1] = (newh << 8) | l; - if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &newh) == 0 + data->temp11[1] = (h << 8) | l; + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) - data->temp11[2] = (newh << 8) | l; + data->temp11[2] = (h << 8) | l; if (data->kind != max6657) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, - &newh) == 0 + &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, &l) == 0) - data->temp11[3] = (newh << 8) | l; + data->temp11[3] = (h << 8) | l; } lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); -- cgit v1.2.3 From f65e17086fc141bee1592bbf6e709e9c7a43541b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:09 +0200 Subject: hwmon: (lm90) Support the extra resolution bits of MAX6657 The Maxim MAX6657, MAX6658 and MAX6659 have extra resolution bits for the local temperature measurement. Let the lm90 driver read them and export them to user-space. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- Documentation/hwmon/lm90 | 10 +++++---- drivers/hwmon/lm90.c | 54 +++++++++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index aa4a0ec2008..0b3e8bb7c1f 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -86,9 +86,8 @@ family is that it features critical limits with hysteresis, and an increased resolution of the remote temperature measurement. The different chipsets of the family are not strictly identical, although -very similar. This driver doesn't handle any specific feature for now, -with the exception of SMBus PEC. For reference, here comes a non-exhaustive -list of specific features: +very similar. For reference, here comes a non-exhaustive list of specific +features: LM90: * Filter and alert configuration register at 0xBF. @@ -114,9 +113,11 @@ ADT7461: * Lower resolution for remote temperature MAX6657 and MAX6658: + * Better local resolution * Remote sensor type selection MAX6659: + * Better local resolution * Selectable address * Second critical temperature limit * Remote sensor type selection @@ -127,7 +128,8 @@ MAX6680 and MAX6681: All temperature values are given in degrees Celsius. Resolution is 1.0 degree for the local temperature, 0.125 degree for the remote -temperature. +temperature, except for the MAX6657, MAX6658 and MAX6659 which have a +resolution of 0.125 degree for both temperatures. Each sensor has its own high and low limits, plus a critical limit. Additionally, there is a relative hysteresis value common to both critical diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 73a1c622fb7..16b99e0bdff 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -149,6 +149,10 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 +/* MAX6657-specific registers */ + +#define MAX6657_REG_R_LOCAL_TEMPL 0x11 + /* * Conversions and various macros * For local temperatures and limits, critical limits and the hysteresis @@ -239,15 +243,15 @@ struct lm90_data { int kind; /* registers values */ - s8 temp8[5]; /* 0: local input - 1: local low limit - 2: local high limit - 3: local critical limit - 4: remote critical limit */ - s16 temp11[4]; /* 0: remote input + s8 temp8[4]; /* 0: local low limit + 1: local high limit + 2: local critical limit + 3: remote critical limit */ + s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6657) */ + 3: remote offset (except max6657) + 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ }; @@ -285,7 +289,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); else data->temp8[nr] = TEMP1_TO_REG(val); - i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]); + i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; } @@ -347,7 +351,7 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = TEMP1_FROM_REG(data->temp8[3]) - val; + hyst = TEMP1_FROM_REG(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, HYST_TO_REG(hyst)); mutex_unlock(&data->update_lock); @@ -371,23 +375,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 1); + set_temp8, 0); static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 1); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 2); + set_temp8, 1); static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 2); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 3); + set_temp8, 2); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 4); + set_temp8, 3); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, - set_temphyst, 3); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); + set_temphyst, 2); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 3); @@ -779,13 +783,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) u8 h, l; dev_dbg(&client->dev, "Updating lm90 data.\n"); - lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]); - lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[1]); - lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[2]); - lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[3]); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]); + 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_TCRIT_HYST, &data->temp_hyst); + if (data->kind == max6657) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + MAX6657_REG_R_LOCAL_TEMPL, + &data->temp11[4]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[4] = h << 8; + } lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); -- cgit v1.2.3 From 5f502a834a6471dc3cc456ccef66292e9e3a152e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:09 +0200 Subject: hwmon: (lm90) Don't access nonexistent registers on Maxim chips The Maxim chips supported by the lm90 driver have 8-bit high and low remote limit values, not 11-bit as the other chips have. So stop reading from and writing to registers that do not exist on these chips. Also round the limit values set by the user properly. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- drivers/hwmon/lm90.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 16b99e0bdff..90489b8f5c8 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -323,12 +323,16 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); + else if (data->kind == max6657 || data->kind == max6680) + data->temp11[nr] = TEMP1_TO_REG(val) << 8; else data->temp11[nr] = TEMP2_TO_REG(val); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], - data->temp11[nr] & 0xff); + if (data->kind != max6657 && data->kind != max6680) + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], + data->temp11[nr] & 0xff); mutex_unlock(&data->update_lock); return count; } @@ -801,12 +805,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); - if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) - data->temp11[1] = (h << 8) | l; - if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) - data->temp11[2] = (h << 8) | l; + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { + data->temp11[1] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, + &l) == 0) + data->temp11[1] |= l; + } + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { + data->temp11[2] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, + &l) == 0) + data->temp11[2] |= l; + } + if (data->kind != max6657) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, &h) == 0 -- cgit v1.2.3 From a874a10cf0b7105ae5eeb98b4860eae0fc78fcdd Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Update datasheet links Update the links to the datasheet of some of the devices supported by the lm90 driver. Also remove the links from the driver itself, so that we don't have to update them twice each time they change. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- Documentation/hwmon/lm90 | 12 ++++++------ drivers/hwmon/lm90.c | 30 +++++++----------------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 0b3e8bb7c1f..0c08c5c5637 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -11,7 +11,7 @@ Supported chips: Prefix: 'lm99' Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM89.html + http://www.national.com/mpf/LM/LM89.html * National Semiconductor LM99 Prefix: 'lm99' Addresses scanned: I2C 0x4c and 0x4d @@ -21,17 +21,17 @@ Supported chips: Prefix: 'lm86' Addresses scanned: I2C 0x4c Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM86.html + http://www.national.com/mpf/LM/LM86.html * Analog Devices ADM1032 Prefix: 'adm1032' Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/en/prod/0,2877,ADM1032,00.html + Datasheet: Publicly available at the ON Semiconductor website + http://www.onsemi.com/PowerSolutions/product.do?id=ADM1032 * Analog Devices ADT7461 Prefix: 'adt7461' Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/en/prod/0,2877,ADT7461,00.html + Datasheet: Publicly available at the ON Semiconductor website + http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 Note: Only if in ADM1032 compatibility mode * Maxim MAX6657 Prefix: 'max6657' diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 90489b8f5c8..d96e403d59e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -6,9 +6,7 @@ * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to * one external one) with a 0.125 deg resolution (1 deg for local - * temperature) and a 3-4 deg accuracy. Complete datasheet can be - * obtained from National's website at: - * http://www.national.com/pf/LM/LM90.html + * temperature) and a 3-4 deg accuracy. * * This driver also supports the LM89 and LM99, two other sensor chips * made by National Semiconductor. Both have an increased remote @@ -16,29 +14,19 @@ * additionally shifts remote temperatures (measured and limits) by 16 * degrees, which allows for higher temperatures measurement. The * driver doesn't handle it since it can be done easily in user-space. - * Complete datasheets can be obtained from National's website at: - * http://www.national.com/pf/LM/LM89.html - * http://www.national.com/pf/LM/LM99.html * Note that there is no way to differentiate between both chips. * * This driver also supports the LM86, another sensor chip made by * National Semiconductor. It is exactly similar to the LM90 except it * has a higher accuracy. - * Complete datasheet can be obtained from National's website at: - * http://www.national.com/pf/LM/LM86.html * * This driver also supports the ADM1032, a sensor chip made by Analog * Devices. That chip is similar to the LM90, with a few differences - * that are not handled by this driver. Complete datasheet can be - * obtained from Analog's website at: - * http://www.analog.com/en/prod/0,2877,ADM1032,00.html - * Among others, it has a higher accuracy than the LM90, much like the - * LM86 does. + * that are not handled by this driver. Among others, it has a higher + * accuracy than the LM90, much like the LM86 does. * * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor - * chips made by Maxim. These chips are similar to the LM86. Complete - * datasheet can be obtained at Maxim's website at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + * chips made by Maxim. These chips are similar to the LM86. * Note that there is no easy way to differentiate between the three * variants. The extra address and features of the MAX6659 are not * supported by this driver. These chips lack the remote temperature @@ -46,18 +34,14 @@ * * This driver also supports the MAX6680 and MAX6681, two other sensor * chips made by Maxim. These are quite similar to the other Maxim - * chips. Complete datasheet can be obtained at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370 - * The MAX6680 and MAX6681 only differ in the pinout so they can be - * treated identically. + * chips. The MAX6680 and MAX6681 only differ in the pinout so they can + * be treated identically. * * This driver also supports the ADT7461 chip from Analog Devices but * only in its "compatability mode". If an ADT7461 chip is found but * is configured in non-compatible mode (where its temperature * register values are decoded differently) it is ignored by this - * driver. Complete datasheet can be obtained from Analog's website - * at: - * http://www.analog.com/en/prod/0,2877,ADT7461,00.html + * driver. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and -- cgit v1.2.3 From cea50fe2fdea36174aa24b58c69c4eb9770e7c49 Mon Sep 17 00:00:00 2001 From: Nate Case Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Convert some macros to static functions Use static functions instead of the TEMPx_FROM_REG* and TEMPx_TO_REG* macros. This will ensure type safety and eliminate any side effects from arguments passed in since the macros referenced 'val' multiple times. This change should not affect functionality. Signed-off-by: Nate Case Signed-off-by: Jean Delvare Tested-by: Martyn Welch --- drivers/hwmon/lm90.c | 128 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 45 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index d96e403d59e..b2d9b3f0946 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -137,40 +137,6 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define MAX6657_REG_R_LOCAL_TEMPL 0x11 -/* - * Conversions and various macros - * For local temperatures and limits, critical limits and the hysteresis - * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. - * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. - */ - -#define TEMP1_FROM_REG(val) ((val) * 1000) -#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \ - (val) >= 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) -#define TEMP2_FROM_REG(val) ((val) / 32 * 125) -#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ - (val) >= 127875 ? 0x7FE0 : \ - (val) < 0 ? ((val) - 62) / 125 * 32 : \ - ((val) + 62) / 125 * 32) -#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \ - ((val) + 500) / 1000) - -/* - * ADT7461 is almost identical to LM90 except that attempts to write - * values that are outside the range 0 < temp < 127 are treated as - * the boundary value. - */ - -#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127000 ? 127 : \ - ((val) + 500) / 1000) -#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127750 ? 0x7FC0 : \ - ((val) + 125) / 250 * 64) - /* * Functions declaration */ @@ -240,6 +206,78 @@ struct lm90_data { u8 alarms; /* bitvector */ }; +/* + * Conversions + * For local temperatures and limits, critical limits and the hysteresis + * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. + * For remote temperatures and limits, it uses signed 11-bit values with + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + */ + +static inline int temp1_from_reg(s8 val) +{ + return val * 1000; +} + +static inline int temp2_from_reg(s16 val) +{ + return val / 32 * 125; +} + +static s8 temp1_to_reg(long val) +{ + if (val <= -128000) + return -128; + if (val >= 127000) + return 127; + if (val < 0) + return (val - 500) / 1000; + return (val + 500) / 1000; +} + +static s16 temp2_to_reg(long val) +{ + if (val <= -128000) + return 0x8000; + if (val >= 127875) + return 0x7FE0; + if (val < 0) + return (val - 62) / 125 * 32; + return (val + 62) / 125 * 32; +} + +static u8 hyst_to_reg(long val) +{ + if (val <= 0) + return 0; + if (val >= 30500) + return 31; + return (val + 500) / 1000; +} + +/* + * ADT7461 is almost identical to LM90 except that attempts to write + * values that are outside the range 0 < temp < 127 are treated as + * the boundary value. + */ +static u8 temp1_to_reg_adt7461(long val) +{ + if (val <= 0) + return 0; + if (val >= 127000) + return 127; + return (val + 500) / 1000; +} + +static u16 temp2_to_reg_adt7461(long val) +{ + if (val <= 0) + return 0; + if (val >= 127750) + return 0x7FC0; + return (val + 125) / 250 * 64; +} + /* * Sysfs stuff */ @@ -249,7 +287,7 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])); + return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index])); } static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, @@ -270,9 +308,9 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); + data->temp8[nr] = temp1_to_reg_adt7461(val); else - data->temp8[nr] = TEMP1_TO_REG(val); + data->temp8[nr] = temp1_to_reg(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; @@ -283,7 +321,7 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index])); + return sprintf(buf, "%d\n", temp2_from_reg(data->temp11[attr->index])); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, @@ -306,11 +344,11 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); + data->temp11[nr] = temp2_to_reg_adt7461(val); else if (data->kind == max6657 || data->kind == max6680) - data->temp11[nr] = TEMP1_TO_REG(val) << 8; + data->temp11[nr] = temp1_to_reg(val) << 8; else - data->temp11[nr] = TEMP2_TO_REG(val); + data->temp11[nr] = temp2_to_reg(val); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); @@ -326,8 +364,8 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]) - - TEMP1_FROM_REG(data->temp_hyst)); + return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index]) + - temp1_from_reg(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -339,9 +377,9 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = TEMP1_FROM_REG(data->temp8[2]) - val; + hyst = temp1_from_reg(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, - HYST_TO_REG(hyst)); + hyst_to_reg(hyst)); mutex_unlock(&data->update_lock); return count; } -- cgit v1.2.3 From 23b2d4778ad33ee6bfe60439fb73c16580f204f2 Mon Sep 17 00:00:00 2001 From: Nate Case Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Support ADT7461 in extended mode Support ADT7461 in extended temperature range mode, which will change the range of readings from 0..127 to -64..191 degC. Adjust the register conversion functions accordingly. Signed-off-by: Nate Case Signed-off-by: Jean Delvare Tested-by: Martyn Welch --- Documentation/hwmon/lm90 | 8 +--- drivers/hwmon/Kconfig | 7 +-- drivers/hwmon/lm90.c | 120 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 97 insertions(+), 38 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 0c08c5c5637..53cd829f3f4 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -32,7 +32,6 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the ON Semiconductor website http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 - Note: Only if in ADM1032 compatibility mode * Maxim MAX6657 Prefix: 'max6657' Addresses scanned: I2C 0x4c @@ -70,16 +69,13 @@ Description The LM90 is a digital temperature sensor. It senses its own temperature as well as the temperature of up to one external diode. It is compatible -with many other devices such as the LM86, the LM89, the LM99, the ADM1032, -the MAX6657, MAX6658, MAX6659, MAX6680 and the MAX6681 all of which are -supported by this driver. +with many other devices, many of which are supported by this driver. Note that there is no easy way to differentiate between the MAX6657, MAX6658 and MAX6659 variants. The extra address and features of the MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously can't (and don't need to) -be distinguished. Additionally, the ADT7461 is supported if found in -ADM1032 compatibility mode. +be distinguished. The specificity of this family of chipsets over the ADM1021/LM84 family is that it features critical limits with hysteresis, and an diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ebacc0af40f..96701e099e8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -510,11 +510,8 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657, - MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. - - The Analog Devices ADT7461 sensor chip is also supported, but only - if found in ADM1032 compatibility mode. + LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim + MAX6657, MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index b2d9b3f0946..fe5d860fc83 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -37,11 +37,10 @@ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can * be treated identically. * - * This driver also supports the ADT7461 chip from Analog Devices but - * only in its "compatability mode". If an ADT7461 chip is found but - * is configured in non-compatible mode (where its temperature - * register values are decoded differently) it is ignored by this - * driver. + * This driver also supports the ADT7461 chip from Analog Devices. + * It's supported in both compatibility and extended mode. It is mostly + * compatible with LM90 except for a data format difference for the + * temperature value registers. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and @@ -137,6 +136,11 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +/* + * Device flags + */ +#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */ + /* * Functions declaration */ @@ -191,6 +195,7 @@ struct lm90_data { char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ int kind; + int flags; /* registers values */ s8 temp8[4]; /* 0: local low limit @@ -256,26 +261,61 @@ static u8 hyst_to_reg(long val) } /* - * ADT7461 is almost identical to LM90 except that attempts to write - * values that are outside the range 0 < temp < 127 are treated as - * the boundary value. + * ADT7461 in compatibility mode is almost identical to LM90 except that + * attempts to write values that are outside the range 0 < temp < 127 are + * treated as the boundary value. + * + * ADT7461 in "extended mode" operation uses unsigned integers offset by + * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. */ -static u8 temp1_to_reg_adt7461(long val) +static inline int temp1_from_reg_adt7461(struct lm90_data *data, u8 val) { - if (val <= 0) - return 0; - if (val >= 127000) - return 127; - return (val + 500) / 1000; + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 64) * 1000; + else + return temp1_from_reg(val); } -static u16 temp2_to_reg_adt7461(long val) +static inline int temp2_from_reg_adt7461(struct lm90_data *data, u16 val) { - if (val <= 0) - return 0; - if (val >= 127750) - return 0x7FC0; - return (val + 125) / 250 * 64; + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 0x4000) / 64 * 250; + else + return temp2_from_reg(val); +} + +static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191000) + return 0xFF; + return (val + 500 + 64000) / 1000; + } else { + if (val <= 0) + return 0; + if (val >= 127000) + return 127; + return (val + 500) / 1000; + } +} + +static u16 temp2_to_reg_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191750) + return 0xFFC0; + return (val + 64000 + 125) / 250 * 64; + } else { + if (val <= 0) + return 0; + if (val >= 127750) + return 0x7FC0; + return (val + 125) / 250 * 64; + } } /* @@ -287,7 +327,14 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + else + temp = temp1_from_reg(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, @@ -308,7 +355,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = temp1_to_reg_adt7461(val); + data->temp8[nr] = temp1_to_reg_adt7461(data, val); else data->temp8[nr] = temp1_to_reg(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); @@ -321,7 +368,14 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp2_from_reg(data->temp11[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp2_from_reg_adt7461(data, data->temp11[attr->index]); + else + temp = temp2_from_reg(data->temp11[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, @@ -344,7 +398,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = temp2_to_reg_adt7461(val); + data->temp11[nr] = temp2_to_reg_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) data->temp11[nr] = temp1_to_reg(val) << 8; else @@ -364,8 +418,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index]) - - temp1_from_reg(data->temp_hyst)); + int temp; + + if (data->kind == adt7461) + temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + else + temp = temp1_from_reg(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp - temp1_from_reg(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -598,7 +658,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind, kind = adm1032; } else if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1F) == 0x00 /* check compat mode */ + && (reg_config1 & 0x1B) == 0x00 && reg_convrate <= 0x0A) { kind = adt7461; } @@ -737,6 +797,12 @@ static void lm90_init_client(struct i2c_client *client) } config_orig = config; + /* Check Temperature Range Select */ + if (data->kind == adt7461) { + if (config & 0x04) + data->flags |= LM90_FLAG_ADT7461_EXT; + } + /* * Put MAX6680/MAX8881 into extended resolution (bit 0x10, * 0.125 degree resolution) and range (0x08, extend range -- cgit v1.2.3 From 9d4d3834229e9949c066c2d0f73ed5d4b4965761 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Rename temperature conversion functions to match usage The encoding of temperatures varies between chips and modes. So do not use "temp1" or "temp2" in the names of the conversion functions, but specify the encoding. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare Tested-by: Martyn Welch --- drivers/hwmon/lm90.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index fe5d860fc83..85ba2c4feb4 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -219,17 +219,17 @@ struct lm90_data { * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. */ -static inline int temp1_from_reg(s8 val) +static inline int temp_from_s8(s8 val) { return val * 1000; } -static inline int temp2_from_reg(s16 val) +static inline int temp_from_s16(s16 val) { return val / 32 * 125; } -static s8 temp1_to_reg(long val) +static s8 temp_to_s8(long val) { if (val <= -128000) return -128; @@ -240,7 +240,7 @@ static s8 temp1_to_reg(long val) return (val + 500) / 1000; } -static s16 temp2_to_reg(long val) +static s16 temp_to_s16(long val) { if (val <= -128000) return 0x8000; @@ -268,23 +268,23 @@ static u8 hyst_to_reg(long val) * ADT7461 in "extended mode" operation uses unsigned integers offset by * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. */ -static inline int temp1_from_reg_adt7461(struct lm90_data *data, u8 val) +static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val) { if (data->flags & LM90_FLAG_ADT7461_EXT) return (val - 64) * 1000; else - return temp1_from_reg(val); + return temp_from_s8(val); } -static inline int temp2_from_reg_adt7461(struct lm90_data *data, u16 val) +static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val) { if (data->flags & LM90_FLAG_ADT7461_EXT) return (val - 0x4000) / 64 * 250; else - return temp2_from_reg(val); + return temp_from_s16(val); } -static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val) +static u8 temp_to_u8_adt7461(struct lm90_data *data, long val) { if (data->flags & LM90_FLAG_ADT7461_EXT) { if (val <= -64000) @@ -301,7 +301,7 @@ static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val) } } -static u16 temp2_to_reg_adt7461(struct lm90_data *data, long val) +static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) { if (data->flags & LM90_FLAG_ADT7461_EXT) { if (val <= -64000) @@ -330,9 +330,9 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, int temp; if (data->kind == adt7461) - temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else - temp = temp1_from_reg(data->temp8[attr->index]); + temp = temp_from_s8(data->temp8[attr->index]); return sprintf(buf, "%d\n", temp); } @@ -355,9 +355,9 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = temp1_to_reg_adt7461(data, val); + data->temp8[nr] = temp_to_u8_adt7461(data, val); else - data->temp8[nr] = temp1_to_reg(val); + data->temp8[nr] = temp_to_s8(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; @@ -371,9 +371,9 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, int temp; if (data->kind == adt7461) - temp = temp2_from_reg_adt7461(data, data->temp11[attr->index]); + temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); else - temp = temp2_from_reg(data->temp11[attr->index]); + temp = temp_from_s16(data->temp11[attr->index]); return sprintf(buf, "%d\n", temp); } @@ -398,11 +398,11 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = temp2_to_reg_adt7461(data, val); + data->temp11[nr] = temp_to_u16_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) - data->temp11[nr] = temp1_to_reg(val) << 8; + data->temp11[nr] = temp_to_s8(val) << 8; else - data->temp11[nr] = temp2_to_reg(val); + data->temp11[nr] = temp_to_s16(val); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); @@ -421,11 +421,11 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt int temp; if (data->kind == adt7461) - temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else - temp = temp1_from_reg(data->temp8[attr->index]); + temp = temp_from_s8(data->temp8[attr->index]); - return sprintf(buf, "%d\n", temp - temp1_from_reg(data->temp_hyst)); + return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -437,7 +437,7 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = temp1_from_reg(data->temp8[2]) - val; + hyst = temp_from_s8(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, hyst_to_reg(hyst)); mutex_unlock(&data->update_lock); -- cgit v1.2.3 From 271dabf5bbf6ae6e2792cd5cf6f0434230e5c18c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm90) Support MAX6646, MAX6647 and MAX6649 These Maxim chips are similar to MAX6657 but use unsigned temperature values to allow for readings up to 145 degrees. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 15 ++++++++++ drivers/hwmon/Kconfig | 3 +- drivers/hwmon/lm90.c | 77 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 53cd829f3f4..e0d5206d1de 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -32,6 +32,21 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the ON Semiconductor website http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 + * Maxim MAX6646 + Prefix: 'max6646' + Addresses scanned: I2C 0x4d + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + * Maxim MAX6647 + Prefix: 'max6646' + Addresses scanned: I2C 0x4e + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + * Maxim MAX6649 + Prefix: 'max6646' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 * Maxim MAX6657 Prefix: 'max6657' Addresses scanned: I2C 0x4c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 96701e099e8..6de1e0ffd39 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -511,7 +511,8 @@ config SENSORS_LM90 help If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim - MAX6657, MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. + MAX6646, MAX6647, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680 and + MAX6681 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 85ba2c4feb4..fe09f82c42e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -32,6 +32,11 @@ * supported by this driver. These chips lack the remote temperature * offset feature. * + * This driver also supports the MAX6646, MAX6647 and MAX6649 chips + * made by Maxim. These are again similar to the LM86, but they use + * unsigned temperature values and can report temperatures from 0 to + * 145 degrees. + * * This driver also supports the MAX6680 and MAX6681, two other sensor * chips made by Maxim. These are quite similar to the other Maxim * chips. The MAX6680 and MAX6681 only differ in the pinout so they can @@ -76,9 +81,10 @@ * Addresses to scan * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. - * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658 - * have address 0x4c. - * ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657 + * and MAX6658 have address 0x4c. + * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. + * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. @@ -91,7 +97,8 @@ static const unsigned short normal_i2c[] = { * Insmod parameters */ -I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); +I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, + max6646); /* * The LM90 registers @@ -132,7 +139,7 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 -/* MAX6657-specific registers */ +/* MAX6646/6647/6649/6657/6658/6659 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 @@ -164,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = { { "lm86", lm86 }, { "lm89", lm99 }, { "lm99", lm99 }, /* Missing temperature offset */ + { "max6646", max6646 }, + { "max6647", max6646 }, + { "max6649", max6646 }, { "max6657", max6657 }, { "max6658", max6657 }, { "max6659", max6657 }, @@ -205,7 +215,7 @@ struct lm90_data { s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6657) + 3: remote offset (except max6646 and max6657) 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ @@ -216,7 +226,8 @@ struct lm90_data { * For local temperatures and limits, critical limits and the hysteresis * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some + * Maxim chips use unsigned values. */ static inline int temp_from_s8(s8 val) @@ -224,11 +235,21 @@ static inline int temp_from_s8(s8 val) return val * 1000; } +static inline int temp_from_u8(u8 val) +{ + return val * 1000; +} + static inline int temp_from_s16(s16 val) { return val / 32 * 125; } +static inline int temp_from_u16(u16 val) +{ + return val / 32 * 125; +} + static s8 temp_to_s8(long val) { if (val <= -128000) @@ -240,6 +261,15 @@ static s8 temp_to_s8(long val) return (val + 500) / 1000; } +static u8 temp_to_u8(long val) +{ + if (val <= 0) + return 0; + if (val >= 255000) + return 255; + return (val + 500) / 1000; +} + static s16 temp_to_s16(long val) { if (val <= -128000) @@ -331,6 +361,8 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, if (data->kind == adt7461) temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u8(data->temp8[attr->index]); else temp = temp_from_s8(data->temp8[attr->index]); @@ -356,6 +388,8 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) data->temp8[nr] = temp_to_u8_adt7461(data, val); + else if (data->kind == max6646) + data->temp8[nr] = temp_to_u8(val); else data->temp8[nr] = temp_to_s8(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); @@ -372,6 +406,8 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, if (data->kind == adt7461) temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u16(data->temp11[attr->index]); else temp = temp_from_s16(data->temp11[attr->index]); @@ -401,12 +437,15 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, data->temp11[nr] = temp_to_u16_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) data->temp11[nr] = temp_to_s8(val) << 8; + else if (data->kind == max6646) + data->temp11[nr] = temp_to_u8(val) << 8; else data->temp11[nr] = temp_to_s16(val); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); - if (data->kind != max6657 && data->kind != max6680) + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646) i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], data->temp11[nr] & 0xff); mutex_unlock(&data->update_lock); @@ -689,6 +728,16 @@ static int lm90_detect(struct i2c_client *new_client, int kind, && (reg_config1 & 0x03) == 0x00 && reg_convrate <= 0x07) { kind = max6680; + } else + /* The chip_id register of the MAX6646/6647/6649 + * holds the revision of the chip. + * The lowest 6 bits of the config1 register are + * unused and should return zero when read. + */ + if (chip_id == 0x59 + && (reg_config1 & 0x3f) == 0x00 + && reg_convrate <= 0x07) { + kind = max6646; } } @@ -719,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind, name = "max6680"; } else if (kind == adt7461) { name = "adt7461"; + } else if (kind == max6646) { + name = "max6646"; } strlcpy(info->type, name, I2C_NAME_SIZE); @@ -758,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client, &dev_attr_pec))) goto exit_remove_files; } - if (data->kind != max6657) { + if (data->kind != max6657 && data->kind != max6646) { if ((err = device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr))) goto exit_remove_files; @@ -824,7 +875,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm90_group); device_remove_file(&client->dev, &dev_attr_pec); - if (data->kind != max6657) + if (data->kind != max6657 && data->kind != max6646) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); @@ -881,7 +932,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - if (data->kind == max6657) { + if (data->kind == max6657 || data->kind == max6646) { lm90_read16(client, LM90_REG_R_LOCAL_TEMP, MAX6657_REG_R_LOCAL_TEMPL, &data->temp11[4]); @@ -896,6 +947,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { data->temp11[1] = h << 8; if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) data->temp11[1] |= l; @@ -903,12 +955,13 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { data->temp11[2] = h << 8; if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) data->temp11[2] |= l; } - if (data->kind != max6657) { + if (data->kind != max6657 && data->kind != max6646) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, -- cgit v1.2.3 From 4b4e7a72fda549e309919931d8a39dea4e5b4be1 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm90) Don't spam the kernel log Degrade the "Unsupported chip" message from info to debug level. There's nothing wrong with this, so no need to bother the user. Also make the message slightly more descriptive. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- drivers/hwmon/lm90.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index fe09f82c42e..3edeebc0b83 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -742,9 +742,9 @@ static int lm90_detect(struct i2c_client *new_client, int kind, } if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); + dev_dbg(&adapter->dev, + "Unsupported chip at 0x%02x (man_id=0x%02X, " + "chip_id=0x%02X)\n", address, man_id, chip_id); return -ENODEV; } } -- cgit v1.2.3 From 49ae6cc8005f45edadf237089c31885834f2608e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm87) Fix masking of config register in lm87_init_client() lm87_init_client() conditionally sets the Start bit and clears the INT#_Clear bit in the Config 1 register. The condition should be that either of these bits needs changing, but currently it checks the (self-clearing) Initialization bit instead of INT#_Clear. Fix the condition and also ensure we never set the Initialization bit. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- drivers/hwmon/lm87.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 21970f0d53a..0fecbfd2ba5 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -867,11 +867,11 @@ static void lm87_init_client(struct i2c_client *client) lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); } } - if ((config & 0x81) != 0x01) { - /* Start monitoring */ + + /* Make sure Start is set and INT#_Clear is clear */ + if ((config & 0x09) != 0x01) lm87_write_value(client, LM87_REG_CONFIG, - (config & 0xF7) | 0x01); - } + (config & 0x77) | 0x01); } static int lm87_remove(struct i2c_client *client) -- cgit v1.2.3 From d2cac802feae6f0c246a9251eefc482bf5ec0f0f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm87) Restore original configuration register on removal This means that if we have to start the monitor when probed, we also stop it on removal. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- drivers/hwmon/lm87.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 0fecbfd2ba5..fa0e3794d9a 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -199,6 +199,7 @@ struct lm87_data { unsigned long last_updated; /* In jiffies */ u8 channel; /* register value */ + u8 config; /* original register value */ u8 in[8]; /* register value */ u8 in_max[8]; /* register value */ @@ -832,6 +833,7 @@ exit_remove: sysfs_remove_group(&new_client->dev.kobj, &lm87_group); sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt); exit_free: + lm87_write_value(new_client, LM87_REG_CONFIG, data->config); kfree(data); exit: return err; @@ -840,12 +842,11 @@ exit: static void lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); - u8 config; data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; - config = lm87_read_value(client, LM87_REG_CONFIG); - if (!(config & 0x01)) { + if (!(data->config & 0x01)) { int i; /* Limits are left uninitialized after power-up */ @@ -869,9 +870,9 @@ static void lm87_init_client(struct i2c_client *client) } /* Make sure Start is set and INT#_Clear is clear */ - if ((config & 0x09) != 0x01) + if ((data->config & 0x09) != 0x01) lm87_write_value(client, LM87_REG_CONFIG, - (config & 0x77) | 0x01); + (data->config & 0x77) | 0x01); } static int lm87_remove(struct i2c_client *client) @@ -882,6 +883,7 @@ static int lm87_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &lm87_group); sysfs_remove_group(&client->dev.kobj, &lm87_group_opt); + lm87_write_value(client, LM87_REG_CONFIG, data->config); kfree(data); return 0; } -- cgit v1.2.3 From 47064d645bc55863c7887a7c96cde39c9a37ee5f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (lm87) Add support for configuration through platform_data The lm87 driver normally assumes that firmware configured the chip correctly. Since this is not always the case, alllow platform code to set the channel register value via platform_data. All other configuration registers can be changed after driver initialisation. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- Documentation/hwmon/lm87 | 9 ++++----- drivers/hwmon/lm87.c | 17 +++++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Documentation/hwmon/lm87 b/Documentation/hwmon/lm87 index ec27aa1b94c..6b47b67fd96 100644 --- a/Documentation/hwmon/lm87 +++ b/Documentation/hwmon/lm87 @@ -65,11 +65,10 @@ The LM87 has four pins which can serve one of two possible functions, depending on the hardware configuration. Some functions share pins, so not all functions are available at the same -time. Which are depends on the hardware setup. This driver assumes that -the BIOS configured the chip correctly. In that respect, it differs from -the original driver (from lm_sensors for Linux 2.4), which would force the -LM87 to an arbitrary, compile-time chosen mode, regardless of the actual -chipset wiring. +time. Which are depends on the hardware setup. This driver normally +assumes that firmware configured the chip correctly. Where this is not +the case, platform code must set the I2C client's platform_data to point +to a u8 value to be written to the channel register. For reference, here is the list of exclusive functions: - in0+in5 (default) or temp3 diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index fa0e3794d9a..2e4a3cea95f 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -21,11 +21,10 @@ * http://www.national.com/pf/LM/LM87.html * * Some functions share pins, so not all functions are available at the same - * time. Which are depends on the hardware setup. This driver assumes that - * the BIOS configured the chip correctly. In that respect, it differs from - * the original driver (from lm_sensors for Linux 2.4), which would force the - * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual - * chipset wiring. + * time. Which are depends on the hardware setup. This driver normally + * assumes that firmware configured the chip correctly. Where this is not + * the case, platform code must set the I2C client's platform_data to point + * to a u8 value to be written to the channel register. * For reference, here is the list of exclusive functions: * - in0+in5 (default) or temp3 * - fan1 (default) or in6 @@ -843,7 +842,13 @@ static void lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); - data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + if (client->dev.platform_data) { + data->channel = *(u8 *)client->dev.platform_data; + lm87_write_value(client, + LM87_REG_CHANNEL_MODE, data->channel); + } else { + data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + } data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; if (!(data->config & 0x01)) { -- cgit v1.2.3 From 810ad7b62c0f075dc44ecc781b24c7f6ba388da5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (ams) Convert to a new-style i2c driver The legacy i2c binding model is phasing out, so the ams driver needs to be converted to a new-style i2c driver. Here is a naive approach of this conversion. Basically it is moving the i2c device creation from the ams driver to the i2c-powermac driver. This should work, but I suspect we could come up with something cleaner by declaring the i2c device as part of the platform setup. This could be done later by someone more familiar with openfirmware-based platforms than I am myself. One nice thing brought by this conversion is that the ams driver should be loaded automatically on systems where is is needed (at least when the I2C interface to the chip is used) providing coldplug-aware user-space environment. Signed-off-by: Jean Delvare Acked-by: Johannes Berg Cc: Stelian Pop Cc: Michael Hanselmann Cc: Benjamin Herrenschmidt --- drivers/hwmon/ams/ams-i2c.c | 56 +++++++++++++-------------------------- drivers/hwmon/ams/ams.h | 4 +-- drivers/i2c/busses/i2c-powermac.c | 29 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index 957760536a4..da26de01068 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c @@ -60,26 +60,34 @@ enum ams_i2c_cmd { AMS_CMD_START, }; -static int ams_i2c_attach(struct i2c_adapter *adapter); -static int ams_i2c_detach(struct i2c_adapter *adapter); +static int ams_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int ams_i2c_remove(struct i2c_client *client); + +static const struct i2c_device_id ams_id[] = { + { "ams", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ams_id); static struct i2c_driver ams_i2c_driver = { .driver = { .name = "ams", .owner = THIS_MODULE, }, - .attach_adapter = ams_i2c_attach, - .detach_adapter = ams_i2c_detach, + .probe = ams_i2c_probe, + .remove = ams_i2c_remove, + .id_table = ams_id, }; static s32 ams_i2c_read(u8 reg) { - return i2c_smbus_read_byte_data(&ams_info.i2c_client, reg); + return i2c_smbus_read_byte_data(ams_info.i2c_client, reg); } static int ams_i2c_write(u8 reg, u8 value) { - return i2c_smbus_write_byte_data(&ams_info.i2c_client, reg, value); + return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value); } static int ams_i2c_cmd(enum ams_i2c_cmd cmd) @@ -152,9 +160,9 @@ static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z) *z = ams_i2c_read(AMS_DATAZ); } -static int ams_i2c_attach(struct i2c_adapter *adapter) +static int ams_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - unsigned long bus; int vmaj, vmin; int result; @@ -162,17 +170,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) if (unlikely(ams_info.has_device)) return -ENODEV; - if (strncmp(adapter->name, "uni-n", 5)) - return -ENODEV; - - bus = simple_strtoul(adapter->name + 6, NULL, 10); - if (bus != ams_info.i2c_bus) - return -ENODEV; - - ams_info.i2c_client.addr = ams_info.i2c_address; - ams_info.i2c_client.adapter = adapter; - ams_info.i2c_client.driver = &ams_i2c_driver; - strcpy(ams_info.i2c_client.name, "Apple Motion Sensor"); + ams_info.i2c_client = client; if (ams_i2c_cmd(AMS_CMD_RESET)) { printk(KERN_INFO "ams: Failed to reset the device\n"); @@ -237,7 +235,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) return 0; } -static int ams_i2c_detach(struct i2c_adapter *adapter) +static int ams_i2c_remove(struct i2c_client *client) { if (ams_info.has_device) { /* Disable interrupts */ @@ -261,9 +259,7 @@ static void ams_i2c_exit(void) int __init ams_i2c_init(struct device_node *np) { - char *tmp_bus; int result; - const u32 *prop; mutex_lock(&ams_info.lock); @@ -275,24 +271,8 @@ int __init ams_i2c_init(struct device_node *np) ams_info.clear_irq = ams_i2c_clear_irq; ams_info.bustype = BUS_I2C; - /* look for bus either using "reg" or by path */ - prop = of_get_property(ams_info.of_node, "reg", NULL); - if (!prop) { - result = -ENODEV; - - goto exit; - } - - tmp_bus = strstr(ams_info.of_node->full_name, "/i2c-bus@"); - if (tmp_bus) - ams_info.i2c_bus = *(tmp_bus + 9) - '0'; - else - ams_info.i2c_bus = ((*prop) >> 8) & 0x0f; - ams_info.i2c_address = ((*prop) & 0xff) >> 1; - result = i2c_add_driver(&ams_i2c_driver); -exit: mutex_unlock(&ams_info.lock); return result; diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h index 221ef6915a5..5ed387b0bd9 100644 --- a/drivers/hwmon/ams/ams.h +++ b/drivers/hwmon/ams/ams.h @@ -46,9 +46,7 @@ struct ams { #ifdef CONFIG_SENSORS_AMS_I2C /* I2C properties */ - int i2c_bus; - int i2c_address; - struct i2c_client i2c_client; + struct i2c_client *i2c_client; #endif /* Joystick emulation */ diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 0e7b1c6724a..60ca91745e5 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -259,6 +259,35 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) } printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); + + if (!strncmp(basename, "uni-n", 5)) { + struct device_node *np; + const u32 *prop; + struct i2c_board_info info; + + /* Instantiate I2C motion sensor if present */ + np = of_find_node_by_name(NULL, "accelerometer"); + if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") && + (prop = of_get_property(np, "reg", NULL))) { + int i2c_bus; + const char *tmp_bus; + + /* look for bus either using "reg" or by path */ + tmp_bus = strstr(np->full_name, "/i2c-bus@"); + if (tmp_bus) + i2c_bus = *(tmp_bus + 9) - '0'; + else + i2c_bus = ((*prop) >> 8) & 0x0f; + + if (pmac_i2c_get_channel(bus) == i2c_bus) { + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = ((*prop) & 0xff) >> 1; + strlcpy(info.type, "ams", I2C_NAME_SIZE); + i2c_new_device(adapter, &info); + } + } + } + return rc; } -- cgit v1.2.3 From 0a02002268bf624a8b0eaf3b4eb5c4207bd80d8b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (ams) Fix permissions on 'joystick' module parameter We should not allow writes to the 'joystick' module parameters since writing there will not trigger creation of the input device. Disable writes since we provide alternative way of enabling input device via AMS device's sysfs attribute. Signed-off-by: Dmitry Torokhov Acked-by: Johannes Berg Signed-off-by: Jean Delvare --- drivers/hwmon/ams/ams-input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c index 7b81e0c2c2d..48dbf7d6a66 100644 --- a/drivers/hwmon/ams/ams-input.c +++ b/drivers/hwmon/ams/ams-input.c @@ -20,11 +20,11 @@ #include "ams.h" static unsigned int joystick; -module_param(joystick, bool, 0644); +module_param(joystick, bool, S_IRUGO); MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); static unsigned int invert; -module_param(invert, bool, 0644); +module_param(invert, bool, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); static void ams_idev_poll(struct input_polled_dev *dev) -- cgit v1.2.3 From ee4cd32ee8a68e22081f698602e4315fb4272853 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (ams) Fix locking issues Use a separate mutex to serialize input device creation/removal, otheriwse we deadlock if we try to remove input device while it is being polled. Also do not take ams_info.lock when it is not needed. Signed-off-by: Dmitry Torokhov Signed-off-by: Johannes Berg Signed-off-by: Jean Delvare --- drivers/hwmon/ams/ams-core.c | 40 +++++++++++++---------------- drivers/hwmon/ams/ams-i2c.c | 4 --- drivers/hwmon/ams/ams-input.c | 59 +++++++++++++++++++++++++------------------ drivers/hwmon/ams/ams-pmu.c | 18 ++++--------- 4 files changed, 56 insertions(+), 65 deletions(-) diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index fbefa82a015..9b4a0e7a564 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c @@ -223,34 +223,28 @@ int __init ams_init(void) void ams_exit(void) { - mutex_lock(&ams_info.lock); - - if (ams_info.has_device) { - /* Remove input device */ - ams_input_exit(); + /* Remove input device */ + ams_input_exit(); - /* Shut down implementation */ - ams_info.exit(); - - /* Flush interrupt worker - * - * We do this after ams_info.exit(), because an interrupt might - * have arrived before disabling them. - */ - flush_scheduled_work(); + /* Remove attributes */ + device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); - /* Remove attributes */ - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); + /* Shut down implementation */ + ams_info.exit(); - /* Remove device */ - of_device_unregister(ams_info.of_dev); + /* Flush interrupt worker + * + * We do this after ams_info.exit(), because an interrupt might + * have arrived before disabling them. + */ + flush_scheduled_work(); - /* Remove handler */ - pmf_unregister_irq_client(&ams_shock_client); - pmf_unregister_irq_client(&ams_freefall_client); - } + /* Remove device */ + of_device_unregister(ams_info.of_dev); - mutex_unlock(&ams_info.lock); + /* Remove handler */ + pmf_unregister_irq_client(&ams_shock_client); + pmf_unregister_irq_client(&ams_freefall_client); } MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index da26de01068..2cbf8a6506c 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c @@ -261,8 +261,6 @@ int __init ams_i2c_init(struct device_node *np) { int result; - mutex_lock(&ams_info.lock); - /* Set implementation stuff */ ams_info.of_node = np; ams_info.exit = ams_i2c_exit; @@ -273,7 +271,5 @@ int __init ams_i2c_init(struct device_node *np) result = i2c_add_driver(&ams_i2c_driver); - mutex_unlock(&ams_info.lock); - return result; } diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c index 48dbf7d6a66..8a712392cd3 100644 --- a/drivers/hwmon/ams/ams-input.c +++ b/drivers/hwmon/ams/ams-input.c @@ -27,6 +27,8 @@ static unsigned int invert; module_param(invert, bool, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); +static DEFINE_MUTEX(ams_input_mutex); + static void ams_idev_poll(struct input_polled_dev *dev) { struct input_dev *idev = dev->input; @@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev) } /* Call with ams_info.lock held! */ -static void ams_input_enable(void) +static int ams_input_enable(void) { struct input_dev *input; s8 x, y, z; - - if (ams_info.idev) - return; + int error; ams_sensors(&x, &y, &z); ams_info.xcalib = x; @@ -65,7 +65,7 @@ static void ams_input_enable(void) ams_info.idev = input_allocate_polled_device(); if (!ams_info.idev) - return; + return -ENOMEM; ams_info.idev->poll = ams_idev_poll; ams_info.idev->poll_interval = 25; @@ -84,14 +84,18 @@ static void ams_input_enable(void) set_bit(EV_KEY, input->evbit); set_bit(BTN_TOUCH, input->keybit); - if (input_register_polled_device(ams_info.idev)) { + error = input_register_polled_device(ams_info.idev); + if (error) { input_free_polled_device(ams_info.idev); ams_info.idev = NULL; - return; + return error; } + + joystick = 1; + + return 0; } -/* Call with ams_info.lock held! */ static void ams_input_disable(void) { if (ams_info.idev) { @@ -99,6 +103,8 @@ static void ams_input_disable(void) input_free_polled_device(ams_info.idev); ams_info.idev = NULL; } + + joystick = 0; } static ssize_t ams_input_show_joystick(struct device *dev, @@ -110,39 +116,42 @@ static ssize_t ams_input_show_joystick(struct device *dev, static ssize_t ams_input_store_joystick(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - if (sscanf(buf, "%d\n", &joystick) != 1) + unsigned long enable; + int error = 0; + + if (strict_strtoul(buf, 0, &enable) || enable > 1) return -EINVAL; - mutex_lock(&ams_info.lock); + mutex_lock(&ams_input_mutex); - if (joystick) - ams_input_enable(); - else - ams_input_disable(); + if (enable != joystick) { + if (enable) + error = ams_input_enable(); + else + ams_input_disable(); + } - mutex_unlock(&ams_info.lock); + mutex_unlock(&ams_input_mutex); - return count; + return error ? error : count; } static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, ams_input_show_joystick, ams_input_store_joystick); -/* Call with ams_info.lock held! */ int ams_input_init(void) { - int result; - - result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); - - if (!result && joystick) + if (joystick) ams_input_enable(); - return result; + + return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); } -/* Call with ams_info.lock held! */ void ams_input_exit(void) { - ams_input_disable(); device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); + + mutex_lock(&ams_input_mutex); + ams_input_disable(); + mutex_unlock(&ams_input_mutex); } diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c index 9463e9768f6..fb18b3d3162 100644 --- a/drivers/hwmon/ams/ams-pmu.c +++ b/drivers/hwmon/ams/ams-pmu.c @@ -149,8 +149,6 @@ int __init ams_pmu_init(struct device_node *np) const u32 *prop; int result; - mutex_lock(&ams_info.lock); - /* Set implementation stuff */ ams_info.of_node = np; ams_info.exit = ams_pmu_exit; @@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np) /* Get PMU command, should be 0x4e, but we can never know */ prop = of_get_property(ams_info.of_node, "reg", NULL); - if (!prop) { - result = -ENODEV; - goto exit; - } + if (!prop) + return -ENODEV; + ams_pmu_cmd = ((*prop) >> 8) & 0xff; /* Disable interrupts */ @@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np) result = ams_sensor_attach(); if (result < 0) - goto exit; + return result; /* Set default values */ ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); @@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np) printk(KERN_INFO "ams: Found PMU based motion sensor\n"); - result = 0; - -exit: - mutex_unlock(&ams_info.lock); - - return result; + return 0; } -- cgit v1.2.3 From dbee356262bb1c84cfa585530e33e7003534052a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (ams) Simplify IRQ handling routine Simplify the IRQ handling routine of ams driver. Signed-off-by: Dmitry Torokhov Signed-off-by: Johannes Berg Signed-off-by: Jean Delvare --- drivers/hwmon/ams/ams-core.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index 9b4a0e7a564..6c9ace1b76f 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c @@ -99,39 +99,31 @@ static struct pmf_irq_client ams_shock_client = { */ static void ams_worker(struct work_struct *work) { - mutex_lock(&ams_info.lock); - - if (ams_info.has_device) { - unsigned long flags; + unsigned long flags; + u8 irqs_to_clear; - spin_lock_irqsave(&ams_info.irq_lock, flags); + mutex_lock(&ams_info.lock); - if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { - if (verbose) - printk(KERN_INFO "ams: freefall detected!\n"); + spin_lock_irqsave(&ams_info.irq_lock, flags); + irqs_to_clear = ams_info.worker_irqs; - ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; + if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { + if (verbose) + printk(KERN_INFO "ams: freefall detected!\n"); - /* we must call this with interrupts enabled */ - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - ams_info.clear_irq(AMS_IRQ_FREEFALL); - spin_lock_irqsave(&ams_info.irq_lock, flags); - } + ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; + } - if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { - if (verbose) - printk(KERN_INFO "ams: shock detected!\n"); + if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { + if (verbose) + printk(KERN_INFO "ams: shock detected!\n"); - ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; + ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; + } - /* we must call this with interrupts enabled */ - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - ams_info.clear_irq(AMS_IRQ_SHOCK); - spin_lock_irqsave(&ams_info.irq_lock, flags); - } + spin_unlock_irqrestore(&ams_info.irq_lock, flags); - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - } + ams_info.clear_irq(irqs_to_clear); mutex_unlock(&ams_info.lock); } -- cgit v1.2.3 From 69fc1feba2d5856ff74dedb6ae9d8c490210825c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (lm85) Rework the device detection Rework the device detection to make it clearer and faster in the general case (when a known device is found.) Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 123 +++++++++++++++++++++------------------------------ 1 file changed, 50 insertions(+), 73 deletions(-) diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 12d446f54f9..3594a02f281 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1106,7 +1106,6 @@ static void lm85_init_client(struct i2c_client *client) static int lm85_detect(struct i2c_adapter *adapter, int address, int kind) { - int company, verstep; struct i2c_client *client; struct lm85_data *data; int err = 0; @@ -1117,10 +1116,6 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, goto ERROR0; } - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm85_{read,write}_value. */ - if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) { err = -ENOMEM; goto ERROR0; @@ -1132,75 +1127,57 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, client->adapter = adapter; client->driver = &lm85_driver; - /* Now, we do the remaining detection. */ - - company = lm85_read_value(client, LM85_REG_COMPANY); - verstep = lm85_read_value(client, LM85_REG_VERSTEP); - - dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" - " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", - i2c_adapter_id(client->adapter), client->addr, - company, verstep); - - /* If auto-detecting, Determine the chip type. */ - if (kind <= 0) { - dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x ...\n", - i2c_adapter_id(adapter), address); - if (company == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85C) { - kind = lm85c; - } else if (company == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85B) { - kind = lm85b; - } else if (company == LM85_COMPANY_NATIONAL - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to LM85.\n", verstep); - kind = any_chip; - } else if (company == LM85_COMPANY_ANALOG_DEV - && verstep == LM85_VERSTEP_ADM1027) { - kind = adm1027; - } else if (company == LM85_COMPANY_ANALOG_DEV - && (verstep == LM85_VERSTEP_ADT7463 - || verstep == LM85_VERSTEP_ADT7463C)) { - kind = adt7463; - } else if (company == LM85_COMPANY_ANALOG_DEV - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep); - kind = any_chip; - } else if (company == LM85_COMPANY_SMSC - && (verstep == LM85_VERSTEP_EMC6D100_A0 - || verstep == LM85_VERSTEP_EMC6D100_A1)) { - /* Unfortunately, we can't tell a '100 from a '101 - * from the registers. Since a '101 is a '100 - * in a package with fewer pins and therefore no - * 3.3V, 1.5V or 1.8V inputs, perhaps if those - * inputs read 0, then it's a '101. - */ - kind = emc6d100; - } else if (company == LM85_COMPANY_SMSC - && verstep == LM85_VERSTEP_EMC6D102) { - kind = emc6d102; - } else if (company == LM85_COMPANY_SMSC - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "lm85: Detected SMSC chip\n"); - dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep); - kind = any_chip; - } else if (kind == any_chip - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n"); - /* Leave kind as "any_chip" */ - } else { - dev_dbg(&adapter->dev, "Autodetection failed\n"); - /* Not an LM85... */ - if (kind == any_chip) { /* User used force=x,y */ - dev_err(&adapter->dev, "Generic LM85 Version 6 not" - " found at %d,0x%02x. Try force_lm85c.\n", - i2c_adapter_id(adapter), address); + /* If auto-detecting, determine the chip type */ + if (kind < 0) { + int company = lm85_read_value(client, LM85_REG_COMPANY); + int verstep = lm85_read_value(client, LM85_REG_VERSTEP); + + dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " + "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + address, company, verstep); + + /* All supported chips have the version in common */ + if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) { + dev_dbg(&adapter->dev, "Autodetection failed: " + "unsupported version\n"); + goto ERROR1; + } + kind = any_chip; + + /* Now, refine the detection */ + if (company == LM85_COMPANY_NATIONAL) { + switch (verstep) { + case LM85_VERSTEP_LM85C: + kind = lm85c; + break; + case LM85_VERSTEP_LM85B: + kind = lm85b; + break; + } + } else if (company == LM85_COMPANY_ANALOG_DEV) { + switch (verstep) { + case LM85_VERSTEP_ADM1027: + kind = adm1027; + break; + case LM85_VERSTEP_ADT7463: + case LM85_VERSTEP_ADT7463C: + kind = adt7463; + break; } - err = 0; + } else if (company == LM85_COMPANY_SMSC) { + switch (verstep) { + case LM85_VERSTEP_EMC6D100_A0: + case LM85_VERSTEP_EMC6D100_A1: + /* Note: we can't tell a '100 from a '101 */ + kind = emc6d100; + break; + case LM85_VERSTEP_EMC6D102: + kind = emc6d102; + break; + } + } else { + dev_dbg(&adapter->dev, "Autodetection failed: " + "unknown vendor\n"); goto ERROR1; } } -- cgit v1.2.3 From 34e7dc6ca4a663a1bb0a0a4e118426849dccd72d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (lm85) Implement the standard PWM frequency interface Implement the standard PWM frequency interface: pwm[1-*]_freq in units of 1 Hz, instead of the non-standard pwm[1-*]_auto_pwm_freq in units of 0.1 Hz. The old naming was not only non-standard, it was also confusing, because it suggested that the frequency value only applied in automatic fan speed mode, which isn't true. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- Documentation/hwmon/lm85 | 10 ------- drivers/hwmon/lm85.c | 77 ++++++++++++++++++++++++------------------------ 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 index 6d41db7f17f..40062074129 100644 --- a/Documentation/hwmon/lm85 +++ b/Documentation/hwmon/lm85 @@ -163,16 +163,6 @@ configured individually according to the following options. * pwm#_auto_pwm_min - this specifies the PWM value for temp#_auto_temp_off temperature. (PWM value from 0 to 255) -* pwm#_auto_pwm_freq - select base frequency of PWM output. You can select - in range of 10.0 to 94.0 Hz in .1 Hz units. - (Values 100 to 940). - -The pwm#_auto_pwm_freq can be set to one of the following 8 values. Setting the -frequency to a value not on this list, will result in the next higher frequency -being selected. The actual device frequency may vary slightly from this -specification as designed by the manufacturer. Consult the datasheet for more -details. (PWM Frequency values: 100, 150, 230, 300, 380, 470, 620, 940) - * pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature the bahaviour of fans. Write 1 to let fans spinning at pwm#_auto_pwm_min or write 0 to let them off. diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 3594a02f281..6b676df3547 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -191,8 +191,8 @@ 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[] = { /* .1 Hz */ - 100, 150, 230, 300, 380, 470, 620, 940 +static const int lm85_freq_map[8] = { /* 1 Hz */ + 10, 15, 23, 30, 38, 47, 62, 94 }; static int FREQ_TO_REG(int freq) @@ -275,7 +275,6 @@ struct lm85_zone { struct lm85_autofan { u8 config; /* Register value */ - u8 freq; /* PWM frequency, encoded */ u8 min_pwm; /* Minimum PWM value, encoded */ u8 min_off; /* Min PWM or OFF below "limit", flag */ }; @@ -301,6 +300,7 @@ struct lm85_data { u16 fan[4]; /* Register value */ u16 fan_min[4]; /* Register value */ u8 pwm[3]; /* Register value */ + u8 pwm_freq[3]; /* Register encoding */ u8 temp_ext[3]; /* Decoded values */ u8 in_ext[8]; /* Decoded values */ u8 vid; /* Register value */ @@ -528,11 +528,38 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute return count; } +static ssize_t show_pwm_freq(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", FREQ_FROM_REG(data->pwm_freq[nr])); +} + +static ssize_t set_pwm_freq(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); + + mutex_lock(&data->update_lock); + data->pwm_freq[nr] = FREQ_TO_REG(val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->pwm_freq[nr]); + mutex_unlock(&data->update_lock); + return count; +} + #define show_pwm_reg(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - show_pwm_enable, set_pwm_enable, offset - 1) + 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_reg(1); show_pwm_reg(2); @@ -761,31 +788,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, return count; } -static ssize_t show_pwm_auto_pwm_freq(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", FREQ_FROM_REG(data->autofan[nr].freq)); -} - -static ssize_t set_pwm_auto_pwm_freq(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); - - mutex_lock(&data->update_lock); - data->autofan[nr].freq = FREQ_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->autofan[nr].freq); - mutex_unlock(&data->update_lock); - return count; -} - #define pwm_auto(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \ S_IRUGO | S_IWUSR, show_pwm_auto_channels, \ @@ -795,10 +797,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \ set_pwm_auto_pwm_min, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \ S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \ - set_pwm_auto_pwm_minctl, offset - 1); \ -static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_freq, \ - S_IRUGO | S_IWUSR, show_pwm_auto_pwm_freq, \ - set_pwm_auto_pwm_freq, offset - 1); + set_pwm_auto_pwm_minctl, offset - 1) pwm_auto(1); pwm_auto(2); @@ -867,7 +866,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev, TEMP_FROM_REG(data->zone[nr].limit)); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); /* Update temp_auto_hyst and temp_auto_off */ data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( @@ -910,7 +909,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev, val - min); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); mutex_unlock(&data->update_lock); return count; } @@ -984,6 +983,9 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &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_in0_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, @@ -1026,9 +1028,6 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_pwm_freq.dev_attr.attr, &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr, &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr, @@ -1458,7 +1457,7 @@ static struct lm85_data *lm85_update_device(struct device *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->autofan[i].freq = val & 0x07; + data->pwm_freq[i] = val & 0x07; data->zone[i].range = val >> 4; data->autofan[i].min_pwm = lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); -- cgit v1.2.3 From 86010c982db2030aad2c31f178b208964f5f6806 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (lm85) Select the closest PWM frequency The LM85 and compatible chips only support 8 arbitrary PWM frequencies. The algorithm to pick one of them based on the user input is not optimum. Improve it to always pick the closest supported frequency. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 6b676df3547..c323c2b8980 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -199,10 +199,9 @@ static int FREQ_TO_REG(int freq) { int i; - if (freq >= lm85_freq_map[7]) - return 7; + /* Find the closest match */ for (i = 0; i < 7; ++i) - if (freq <= lm85_freq_map[i]) + if (freq <= (lm85_freq_map[i] + lm85_freq_map[i + 1]) / 2) break; return i; } -- cgit v1.2.3 From 8a0795d9b8ce2247f9b34da81f8a229702c90f2d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Support different PWM frequency tables The Analog Devices and SMSC devices supported by the lm85 driver do not have the same PWM frequency table as the National Semiconductor devices. Add support for per-device frequency tables. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index c323c2b8980..3a84dc863df 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -5,6 +5,7 @@ Copyright (c) 2002, 2003 Philip Pokorny Copyright (c) 2003 Margit Schubert-While Copyright (c) 2004 Justin Thiessen + Copyright (C) 2007, 2008 Jean Delvare Chip details at @@ -192,20 +193,27 @@ static int RANGE_TO_REG(int range) /* These are the PWM frequency encodings */ static const int lm85_freq_map[8] = { /* 1 Hz */ - 10, 15, 23, 30, 38, 47, 62, 94 + 10, 15, 23, 30, 38, 47, 61, 94 +}; +static const int adm1027_freq_map[8] = { /* 1 Hz */ + 11, 15, 22, 29, 35, 44, 59, 88 }; -static int FREQ_TO_REG(int freq) +static int FREQ_TO_REG(const int *map, int freq) { int i; /* Find the closest match */ for (i = 0; i < 7; ++i) - if (freq <= (lm85_freq_map[i] + lm85_freq_map[i + 1]) / 2) + if (freq <= (map[i] + map[i + 1]) / 2) break; return i; } -#define FREQ_FROM_REG(val) lm85_freq_map[(val) & 0x07] + +static int FREQ_FROM_REG(const int *map, u8 reg) +{ + return map[reg & 0x07]; +} /* Since we can't use strings, I'm abusing these numbers * to stand in for the following meanings: @@ -283,6 +291,7 @@ struct lm85_autofan { struct lm85_data { struct i2c_client client; struct device *hwmon_dev; + const int *freq_map; enum chips type; struct mutex update_lock; @@ -532,7 +541,8 @@ static ssize_t show_pwm_freq(struct device *dev, { int nr = to_sensor_dev_attr(attr)->index; struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->pwm_freq[nr])); + return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map, + data->pwm_freq[nr])); } static ssize_t set_pwm_freq(struct device *dev, @@ -544,7 +554,7 @@ static ssize_t set_pwm_freq(struct device *dev, long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); - data->pwm_freq[nr] = FREQ_TO_REG(val); + 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]); @@ -1184,24 +1194,31 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, switch (kind) { case lm85b: type_name = "lm85b"; + data->freq_map = lm85_freq_map; break; case lm85c: type_name = "lm85c"; + data->freq_map = lm85_freq_map; break; case adm1027: type_name = "adm1027"; + data->freq_map = adm1027_freq_map; break; case adt7463: type_name = "adt7463"; + data->freq_map = adm1027_freq_map; break; case emc6d100: type_name = "emc6d100"; + data->freq_map = adm1027_freq_map; break; case emc6d102: type_name = "emc6d102"; + data->freq_map = adm1027_freq_map; break; default: type_name = "lm85"; + data->freq_map = lm85_freq_map; } strlcpy(client->name, type_name, I2C_NAME_SIZE); -- cgit v1.2.3 From 67712d01929295112b55fedd0b3c13f7d5c34f98 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Convert to a new-style i2c driver The new-style lm85 driver implements the optional detect() callback to cover the use cases of the legacy driver. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 121 +++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 57 deletions(-) diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 3a84dc863df..4ee9388df98 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -289,7 +289,6 @@ struct lm85_autofan { /* For each registered chip, we need to keep some data in memory. The structure is dynamically allocated. */ struct lm85_data { - struct i2c_client client; struct device *hwmon_dev; const int *freq_map; enum chips type; @@ -318,22 +317,40 @@ struct lm85_data { struct lm85_zone zone[3]; }; -static int lm85_attach_adapter(struct i2c_adapter *adapter); -static int lm85_detect(struct i2c_adapter *adapter, int address, - int kind); -static int lm85_detach_client(struct i2c_client *client); +static int lm85_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int lm85_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int lm85_remove(struct i2c_client *client); static int lm85_read_value(struct i2c_client *client, u8 reg); static void lm85_write_value(struct i2c_client *client, u8 reg, int value); static struct lm85_data *lm85_update_device(struct device *dev); +static const struct i2c_device_id lm85_id[] = { + { "adm1027", adm1027 }, + { "adt7463", adt7463 }, + { "lm85", any_chip }, + { "lm85b", lm85b }, + { "lm85c", lm85c }, + { "emc6d100", emc6d100 }, + { "emc6d101", emc6d100 }, + { "emc6d102", emc6d102 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm85_id); + static struct i2c_driver lm85_driver = { + .class = I2C_CLASS_HWMON, .driver = { .name = "lm85", }, - .attach_adapter = lm85_attach_adapter, - .detach_client = lm85_detach_client, + .probe = lm85_probe, + .remove = lm85_remove, + .id_table = lm85_id, + .detect = lm85_detect, + .address_data = &addr_data, }; @@ -965,13 +982,6 @@ temp_auto(1); temp_auto(2); temp_auto(3); -static int lm85_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_probe(adapter, &addr_data, lm85_detect); -} - static struct attribute *lm85_attributes[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, @@ -1111,30 +1121,19 @@ static void lm85_init_client(struct i2c_client *client) dev_warn(&client->dev, "Device is not ready\n"); } -static int lm85_detect(struct i2c_adapter *adapter, int address, - int kind) +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm85_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { - struct i2c_client *client; - struct lm85_data *data; - int err = 0; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *type_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { /* We need to be able to do byte I/O */ - goto ERROR0; + return -ENODEV; } - if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR0; - } - - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = address; - client->adapter = adapter; - client->driver = &lm85_driver; - /* If auto-detecting, determine the chip type */ if (kind < 0) { int company = lm85_read_value(client, LM85_REG_COMPANY); @@ -1148,7 +1147,7 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) { dev_dbg(&adapter->dev, "Autodetection failed: " "unsupported version\n"); - goto ERROR1; + return -ENODEV; } kind = any_chip; @@ -1186,50 +1185,62 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, } else { dev_dbg(&adapter->dev, "Autodetection failed: " "unknown vendor\n"); - goto ERROR1; + return -ENODEV; } } - /* Fill in the chip specific driver values */ switch (kind) { case lm85b: type_name = "lm85b"; - data->freq_map = lm85_freq_map; break; case lm85c: type_name = "lm85c"; - data->freq_map = lm85_freq_map; break; case adm1027: type_name = "adm1027"; - data->freq_map = adm1027_freq_map; break; case adt7463: type_name = "adt7463"; - data->freq_map = adm1027_freq_map; break; case emc6d100: type_name = "emc6d100"; - data->freq_map = adm1027_freq_map; break; case emc6d102: type_name = "emc6d102"; - data->freq_map = adm1027_freq_map; break; default: type_name = "lm85"; - data->freq_map = lm85_freq_map; } - strlcpy(client->name, type_name, I2C_NAME_SIZE); + strlcpy(info->type, type_name, I2C_NAME_SIZE); + + return 0; +} - /* Fill in the remaining client fields */ - data->type = kind; +static int lm85_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm85_data *data; + int err; + + data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->type = id->driver_data; mutex_init(&data->update_lock); - /* Tell the I2C layer a new client has arrived */ - err = i2c_attach_client(client); - if (err) - goto ERROR1; + /* Fill in the chip specific driver values */ + switch (data->type) { + case adm1027: + case adt7463: + case emc6d100: + case emc6d102: + data->freq_map = adm1027_freq_map; + break; + default: + data->freq_map = lm85_freq_map; + } /* Set the VRM version */ data->vrm = vid_which_vrm(); @@ -1240,18 +1251,18 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &lm85_group); if (err) - goto ERROR2; + goto ERROR1; /* The ADT7463 has an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ data->vid = lm85_read_value(client, LM85_REG_VID); - if (!(kind == adt7463 && (data->vid & 0x80))) + if (!(data->type == adt7463 && (data->vid & 0x80))) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4))) goto ERROR3; /* The EMC6D100 has 3 additional voltage inputs */ - if (kind == emc6d100) + if (data->type == emc6d100) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in567))) goto ERROR3; @@ -1268,17 +1279,14 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, ERROR3: sysfs_remove_group(&client->dev.kobj, &lm85_group); sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); - if (kind == emc6d100) + if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - ERROR2: - i2c_detach_client(client); ERROR1: kfree(data); - ERROR0: return err; } -static int lm85_detach_client(struct i2c_client *client) +static int lm85_remove(struct i2c_client *client) { struct lm85_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); @@ -1286,7 +1294,6 @@ static int lm85_detach_client(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - i2c_detach_client(client); kfree(data); return 0; } -- cgit v1.2.3 From 1b92adaddd7e96e1ba68d4f17a25747ab8057efe Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Simplify RANGE_TO_REG Function RANGE_TO_REG can easily be simplified. Credits go to Herbert Poetzl for indirectly suggesting this to me. I tested that the new implementation returns the same result as the original implementation for all input values. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 4ee9388df98..94a10ecd24d 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -174,20 +174,13 @@ static int RANGE_TO_REG(int range) { int i; - if (range >= lm85_range_map[15]) - return 15; - /* Find the closest match */ - for (i = 14; i >= 0; --i) { - if (range >= lm85_range_map[i]) { - if ((lm85_range_map[i + 1] - range) < - (range - lm85_range_map[i])) - return i + 1; - return i; - } + for (i = 0; i < 15; ++i) { + if (range <= (lm85_range_map[i] + lm85_range_map[i + 1]) / 2) + break; } - return 0; + return i; } #define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f] -- cgit v1.2.3 From f908037a01e53a48ff8eb049bb4a1fbb15449908 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Better label names Label names ERROR1 and ERROR3 aren't exactly explicit. Change them for better names that indicate what we are up to. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 94a10ecd24d..3ff0285396f 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1244,7 +1244,7 @@ static int lm85_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &lm85_group); if (err) - goto ERROR1; + goto err_kfree; /* The ADT7463 has an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ @@ -1252,29 +1252,29 @@ static int lm85_probe(struct i2c_client *client, if (!(data->type == adt7463 && (data->vid & 0x80))) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4))) - goto ERROR3; + goto err_remove_files; /* The EMC6D100 has 3 additional voltage inputs */ if (data->type == emc6d100) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in567))) - goto ERROR3; + goto err_remove_files; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR3; + goto err_remove_files; } return 0; /* Error out and cleanup code */ - ERROR3: + err_remove_files: sysfs_remove_group(&client->dev.kobj, &lm85_group); sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - ERROR1: + err_kfree: kfree(data); return err; } -- cgit v1.2.3 From 47c15532ddcd6818f51cb15f914d63864b3ee9ab Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Fix I/O resource conflict with PNP Only request I/O ports 0x295-0x296 instead of the full I/O address range. This solves a conflict with PNP resources on a few motherboards. Also request the I/O ports in two parts (4 low ports, 4 high ports) during device detection, otherwise the PNP resource make the request (and thus the detection) fail. This is the exact same fix that was applied to driver w83781d in March 2008 to address the same problem: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=2961cb22ef02850d90e7a12c28a14d74e327df8d Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index ed7859f0e16..2c96d8a548f 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -655,7 +655,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, LM78_EXTENT, "lm78")) { + if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { err = -EBUSY; goto exit; } @@ -699,7 +699,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_name); kfree(data); exit_release_region: - release_region(res->start, LM78_EXTENT); + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); exit: return err; } @@ -711,7 +711,7 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &lm78_group); device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr, LM78_EXTENT); + release_region(data->client.addr + LM78_ADDR_REG_OFFSET, 2); kfree(data); return 0; @@ -837,8 +837,17 @@ static int __init lm78_isa_found(unsigned short address) { int val, save, found = 0; - if (!request_region(address, LM78_EXTENT, "lm78")) + /* We have to request the region in two parts because some + boards declare base+4 to base+7 as a PNP device */ + if (!request_region(address, 4, "lm78")) { + pr_debug("lm78: Failed to request low part of region\n"); return 0; + } + if (!request_region(address + 4, 4, "lm78")) { + pr_debug("lm78: Failed to request high part of region\n"); + release_region(address, 4); + return 0; + } #define REALLY_SLOW_IO /* We need the timeouts for at least some LM78-like @@ -901,7 +910,8 @@ static int __init lm78_isa_found(unsigned short address) val & 0x80 ? "LM79" : "LM78", (int)address); release: - release_region(address, LM78_EXTENT); + release_region(address + 4, 4); + release_region(address, 4); return found; } -- cgit v1.2.3 From 18c73f90421f9a87a0f6bc3a08880d0f1f9b2a74 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Detect alias chips The LM78 and LM79 can be accessed either on the I2C bus or the ISA bus. We must not access the same chip through both interfaces. So far we were relying on the user passing the correct ignore parameter to skip the registration of the I2C interface as suggested by sensors-detect, but this is fragile: the user may load the lm78 driver without running sensors-detect, and the i2c bus numbers are not stable across reboots and hardware changes. So, better detect alias chips in the driver directly, and skip any I2C chip which is obviously an alias of the ISA chip. This is done by comparing the value of 26 selected registers. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 78 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 2c96d8a548f..ec601bbf91b 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -120,10 +120,7 @@ static inline int TEMP_FROM_REG(s8 val) LM78 chips available (well, actually, that is probably never done; but it is a clean illustration of how to handle a case like that). Finally, a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the LM78 at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ + not like to detect it double. */ /* For ISA chips, we abuse the i2c_client addr and name fields. We also use the driver field to differentiate between I2C and ISA chips. */ @@ -457,12 +454,24 @@ static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); /* This function is called when: * lm78_driver is inserted (when this module is loaded), for each available adapter - * when a new adapter is inserted (and lm78_driver is still present) */ + * when a new adapter is inserted (and lm78_driver is still present) + We block updates of the ISA device to minimize the risk of concurrent + access to the same LM78 chip through different interfaces. */ static int lm78_attach_adapter(struct i2c_adapter *adapter) { + struct lm78_data *data; + int err; + if (!(adapter->class & I2C_CLASS_HWMON)) return 0; - return i2c_probe(adapter, &addr_data, lm78_detect); + + data = pdev ? platform_get_drvdata(pdev) : NULL; + if (data) + mutex_lock(&data->update_lock); + err = i2c_probe(adapter, &addr_data, lm78_detect); + if (data) + mutex_unlock(&data->update_lock); + return err; } static struct attribute *lm78_attributes[] = { @@ -531,6 +540,40 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ +static int lm78_alias_detect(struct i2c_client *client, u8 chipid) +{ + struct lm78_data *i2c, *isa; + int i; + + if (!pdev) /* No ISA chip */ + return 0; + + i2c = i2c_get_clientdata(client); + isa = platform_get_drvdata(pdev); + + if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe)) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + return 0; + } + if (lm78_read_value(isa, LM78_REG_CONFIG) != + lm78_read_value(i2c, LM78_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + return 0; + } + + return 1; +} + /* This function is called by i2c_probe */ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -589,6 +632,13 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) err = -ENODEV; goto ERROR2; } + + if (lm78_alias_detect(new_client, i)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + err = -ENODEV; + goto ERROR2; + } } if (kind == lm78) { @@ -959,14 +1009,12 @@ static int __init sm_lm78_init(void) { int res; - res = i2c_add_driver(&lm78_driver); - if (res) - goto exit; - + /* We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. */ if (lm78_isa_found(isa_address)) { res = platform_driver_register(&lm78_isa_driver); if (res) - goto exit_unreg_i2c_driver; + goto exit; /* Sets global pdev as a side effect */ res = lm78_isa_device_add(isa_address); @@ -974,12 +1022,16 @@ static int __init sm_lm78_init(void) goto exit_unreg_isa_driver; } + res = i2c_add_driver(&lm78_driver); + if (res) + goto exit_unreg_isa_device; + return 0; + exit_unreg_isa_device: + platform_device_unregister(pdev); exit_unreg_isa_driver: platform_driver_unregister(&lm78_isa_driver); - exit_unreg_i2c_driver: - i2c_del_driver(&lm78_driver); exit: return res; } -- cgit v1.2.3 From ad3273be8e2a5bfe16f4940beef3da308daf259a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Prevent misdetection of Winbond chips The LM78 detection is relatively weak, and sometimes recent Winbond chips can be misdetected as an LM78. We have had repeated reports of this happening. We have an explicit check against this for the ISA access, do the same for I2C access now. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index ec601bbf91b..f284ecbb9ca 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -613,6 +613,12 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) err = -ENODEV; goto ERROR2; } + /* Explicitly prevent the misdetection of Winbond chips */ + i = lm78_read_value(data, 0x4f); + if (i == 0xa3 || i == 0x5c) { + err = -ENODEV; + goto ERROR2; + } } /* Determine the chip type. */ -- cgit v1.2.3 From 6e1b5029dc0e4cfa765309312ebdc88711e37a20 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Stop abusing struct i2c_client for ISA devices Upcoming changes to the I2C part of the lm78 driver will cause ISA devices to no longer have a struct i2c_client at hand. So, we must stop (ab)using it now. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index f284ecbb9ca..177eaddde32 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -114,22 +114,16 @@ static inline int TEMP_FROM_REG(s8 val) #define DIV_FROM_REG(val) (1 << (val)) -/* There are some complications in a module like this. First off, LM78 chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - LM78 chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. */ - -/* For ISA chips, we abuse the i2c_client addr and name fields. We also use - the driver field to differentiate between I2C and ISA chips. */ struct lm78_data { struct i2c_client client; struct device *hwmon_dev; struct mutex lock; enum chips type; + /* For ISA device only */ + const char *name; + int isa_addr; + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -536,7 +530,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute { struct lm78_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); + return sprintf(buf, "%s\n", data->name); } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); @@ -707,7 +701,6 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) int err; struct lm78_data *data; struct resource *res; - const char *name; /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -721,18 +714,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) goto exit_release_region; } mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); + data->isa_addr = res->start; platform_set_drvdata(pdev, data); if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { data->type = lm79; - name = "lm79"; + data->name = "lm79"; } else { data->type = lm78; - name = "lm78"; + data->name = "lm78"; } - strlcpy(data->client.name, name, I2C_NAME_SIZE); /* Initialize the LM78 chip */ lm78_init_device(data); @@ -763,13 +754,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) static int __devexit lm78_isa_remove(struct platform_device *pdev) { struct lm78_data *data = platform_get_drvdata(pdev); + struct resource *res; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &lm78_group); device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr + LM78_ADDR_REG_OFFSET, 2); kfree(data); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); + return 0; } @@ -785,8 +779,8 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) if (!client->driver) { /* ISA device */ int res; mutex_lock(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - res = inb_p(client->addr + LM78_DATA_REG_OFFSET); + outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); + res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET); mutex_unlock(&data->lock); return res; } else @@ -806,8 +800,8 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) if (!client->driver) { /* ISA device */ mutex_lock(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - outb_p(value, client->addr + LM78_DATA_REG_OFFSET); + outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); + outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET); mutex_unlock(&data->lock); return 0; } else -- cgit v1.2.3 From 0c6e97317102a8f480bdfb418f19fe989ad1c047 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (lm78) Convert to a new-style i2c driver The new-style lm78 driver implements the optional detect() callback to cover the use cases of the legacy driver. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 194 +++++++++++++++++++++++---------------------------- 1 file changed, 89 insertions(+), 105 deletions(-) diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 177eaddde32..b5e3b285169 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -115,7 +115,7 @@ static inline int TEMP_FROM_REG(s8 val) #define DIV_FROM_REG(val) (1 << (val)) struct lm78_data { - struct i2c_client client; + struct i2c_client *client; struct device *hwmon_dev; struct mutex lock; enum chips type; @@ -142,9 +142,11 @@ struct lm78_data { }; -static int lm78_attach_adapter(struct i2c_adapter *adapter); -static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); -static int lm78_detach_client(struct i2c_client *client); +static int lm78_i2c_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int lm78_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int lm78_i2c_remove(struct i2c_client *client); static int __devinit lm78_isa_probe(struct platform_device *pdev); static int __devexit lm78_isa_remove(struct platform_device *pdev); @@ -155,12 +157,23 @@ static struct lm78_data *lm78_update_device(struct device *dev); static void lm78_init_device(struct lm78_data *data); +static const struct i2c_device_id lm78_i2c_id[] = { + { "lm78", lm78 }, + { "lm79", lm79 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); + static struct i2c_driver lm78_driver = { + .class = I2C_CLASS_HWMON, .driver = { .name = "lm78", }, - .attach_adapter = lm78_attach_adapter, - .detach_client = lm78_detach_client, + .probe = lm78_i2c_probe, + .remove = lm78_i2c_remove, + .id_table = lm78_i2c_id, + .detect = lm78_i2c_detect, + .address_data = &addr_data, }; static struct platform_driver lm78_isa_driver = { @@ -445,29 +458,6 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); -/* This function is called when: - * lm78_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and lm78_driver is still present) - We block updates of the ISA device to minimize the risk of concurrent - access to the same LM78 chip through different interfaces. */ -static int lm78_attach_adapter(struct i2c_adapter *adapter) -{ - struct lm78_data *data; - int err; - - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - - data = pdev ? platform_get_drvdata(pdev) : NULL; - if (data) - mutex_lock(&data->update_lock); - err = i2c_probe(adapter, &addr_data, lm78_detect); - if (data) - mutex_unlock(&data->update_lock); - return err; -} - static struct attribute *lm78_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, @@ -537,13 +527,11 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) { - struct lm78_data *i2c, *isa; + struct lm78_data *isa; int i; if (!pdev) /* No ISA chip */ return 0; - - i2c = i2c_get_clientdata(client); isa = platform_get_drvdata(pdev); if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr) @@ -554,70 +542,55 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) /* We compare all the limit registers, the config register and the * interrupt mask registers */ for (i = 0x2b; i <= 0x3d; i++) { - if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + if (lm78_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } if (lm78_read_value(isa, LM78_REG_CONFIG) != - lm78_read_value(i2c, LM78_REG_CONFIG)) + i2c_smbus_read_byte_data(client, LM78_REG_CONFIG)) return 0; for (i = 0x43; i <= 0x46; i++) { - if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + if (lm78_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } return 1; } -/* This function is called by i2c_probe */ -static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) +static int lm78_i2c_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { - int i, err; - struct i2c_client *new_client; - struct lm78_data *data; - const char *client_name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -ENODEV; - goto ERROR1; - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm78_{read,write}_value. */ + int i; + struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL; + const char *client_name; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; - if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm78_driver; + /* We block updates of the ISA device to minimize the risk of + concurrent access to the same LM78 chip through different + interfaces. */ + if (isa) + mutex_lock(&isa->update_lock); - /* Now, we do the remaining detection. */ if (kind < 0) { - if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) { - err = -ENODEV; - goto ERROR2; - } - if (lm78_read_value(data, LM78_REG_I2C_ADDR) != - address) { - err = -ENODEV; - goto ERROR2; - } + if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80) + || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) + != address) + goto err_nodev; + /* Explicitly prevent the misdetection of Winbond chips */ - i = lm78_read_value(data, 0x4f); - if (i == 0xa3 || i == 0x5c) { - err = -ENODEV; - goto ERROR2; - } + i = i2c_smbus_read_byte_data(client, 0x4f); + if (i == 0xa3 || i == 0x5c) + goto err_nodev; } /* Determine the chip type. */ if (kind <= 0) { - i = lm78_read_value(data, LM78_REG_CHIPID); + i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID); if (i == 0x00 || i == 0x20 /* LM78 */ || i == 0x40) /* LM78-J */ kind = lm78; @@ -629,40 +602,59 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) "parameter for unknown chip at " "adapter %d, address 0x%02x\n", i2c_adapter_id(adapter), address); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - if (lm78_alias_detect(new_client, i)) { + if (lm78_alias_detect(client, i)) { dev_dbg(&adapter->dev, "Device at 0x%02x appears to " "be the same as ISA device\n", address); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } } - if (kind == lm78) { - client_name = "lm78"; - } else if (kind == lm79) { + if (isa) + mutex_unlock(&isa->update_lock); + + switch (kind) { + case lm79: client_name = "lm79"; + break; + default: + client_name = "lm78"; } + strlcpy(info->type, client_name, I2C_NAME_SIZE); - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - data->type = kind; + return 0; - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; + err_nodev: + if (isa) + mutex_unlock(&isa->update_lock); + return -ENODEV; +} + +static int lm78_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm78_data *data; + int err; + + data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + data->type = id->driver_data; /* Initialize the LM78 chip */ lm78_init_device(data); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group))) + err = sysfs_create_group(&client->dev.kobj, &lm78_group); + if (err) goto ERROR3; - data->hwmon_dev = hwmon_device_register(&new_client->dev); + data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto ERROR4; @@ -671,26 +663,18 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) return 0; ERROR4: - sysfs_remove_group(&new_client->dev.kobj, &lm78_group); + sysfs_remove_group(&client->dev.kobj, &lm78_group); ERROR3: - i2c_detach_client(new_client); -ERROR2: kfree(data); -ERROR1: return err; } -static int lm78_detach_client(struct i2c_client *client) +static int lm78_i2c_remove(struct i2c_client *client) { struct lm78_data *data = i2c_get_clientdata(client); - int err; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm78_group); - - if ((err = i2c_detach_client(client))) - return err; - kfree(data); return 0; @@ -774,9 +758,9 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev) would slow down the LM78 access and should not be necessary. */ static int lm78_read_value(struct lm78_data *data, u8 reg) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; - if (!client->driver) { /* ISA device */ + if (!client) { /* ISA device */ int res; mutex_lock(&data->lock); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); @@ -796,9 +780,9 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) nowhere else be necessary! */ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; - if (!client->driver) { /* ISA device */ + if (!client) { /* ISA device */ mutex_lock(&data->lock); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET); -- cgit v1.2.3 From 4ed1077953f531b3fef4af4b4ade48a828c48869 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (it87) Fix thermal sensor type values The it87 driver doesn't follow the standard sensor type values as documented in Documentation/hwmon/sysfs-interface. It uses value 2 for thermistors instead of value 4. This causes "sensors" to tell the user that the chip is setup for a transistor while it is actually setup for a thermistor. Using value 4 for thermistors solves the problem. For compatibility reasons, we still accept value 2 but emit a warning message so that users update their configuration files. Signed-off-by: Jean Delvare Acked-by: Hans de Goede --- Documentation/hwmon/it87 | 4 ++-- drivers/hwmon/it87.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 3496b7020e7..042c0415140 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -136,10 +136,10 @@ once-only alarms. The IT87xx only updates its values each 1.5 seconds; reading it more often will do no harm, but will return 'old' values. -To change sensor N to a thermistor, 'echo 2 > tempN_type' where N is 1, 2, +To change sensor N to a thermistor, 'echo 4 > tempN_type' where N is 1, 2, or 3. To change sensor N to a thermal diode, 'echo 3 > tempN_type'. Give 0 for unused sensor. Any other value is invalid. To configure this at -startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; +startup, consult lm_sensors's /etc/sensors.conf. (4 = thermistor; 3 = thermal diode) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index d793cc01199..b74c95735f9 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -477,7 +477,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, if (reg & (1 << nr)) return sprintf(buf, "3\n"); /* thermal diode */ if (reg & (8 << nr)) - return sprintf(buf, "2\n"); /* thermistor */ + return sprintf(buf, "4\n"); /* thermistor */ return sprintf(buf, "0\n"); /* disabled */ } static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, @@ -493,10 +493,15 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, data->sensor &= ~(1 << nr); data->sensor &= ~(8 << nr); - /* 3 = thermal diode; 2 = thermistor; 0 = disabled */ + if (val == 2) { /* backwards compatibility */ + dev_warn(dev, "Sensor type 2 is deprecated, please use 4 " + "instead\n"); + val = 4; + } + /* 3 = thermal diode; 4 = thermistor; 0 = disabled */ if (val == 3) data->sensor |= 1 << nr; - else if (val == 2) + else if (val == 4) data->sensor |= 8 << nr; else if (val != 0) { mutex_unlock(&data->update_lock); -- cgit v1.2.3 From a80e8ee66793ec2e7ce27fd0495a7b9c8e0a2a24 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (max1619) Use inline functions instead of macros Macros evaluating their arguments more than once are evil. Signed-off-by: Jean Delvare --- drivers/hwmon/max1619.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 1ab1cacad59..7897754f3a5 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -69,11 +69,18 @@ I2C_CLIENT_INSMOD_1(max1619); #define MAX1619_REG_W_TCRIT_HYST 0x13 /* - * Conversions and various macros + * Conversions */ -#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) -#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000) +static int temp_from_reg(int val) +{ + return (val & 0x80 ? val-0x100 : val) * 1000; +} + +static int temp_to_reg(int val) +{ + return (val < 0 ? val+0x100*1000 : val) / 1000; +} /* * Functions declaration @@ -135,7 +142,7 @@ struct max1619_data { static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct max1619_data *data = max1619_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ + return sprintf(buf, "%d\n", temp_from_reg(data->value)); \ } show_temp(temp_input1); show_temp(temp_input2); @@ -153,7 +160,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ - data->value = TEMP_TO_REG(val); \ + data->value = temp_to_reg(val); \ i2c_smbus_write_byte_data(client, reg, data->value); \ mutex_unlock(&data->update_lock); \ return count; \ -- cgit v1.2.3 From 6e1ecd9b8f1358ed4d099f0c54434240dc40debe Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (w83791d) fan 4/5 pins can also be used for gpio Pins fan/pwm 4-5 can be in use as GPIO. If that is the case, do not create their sysfs-interface. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/w83791d.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index de21142d106..6b1cec9950f 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -160,6 +160,7 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = { 0xA3, /* BEEP Control Register 3 */ }; +#define W83791D_REG_GPIO 0x15 #define W83791D_REG_CONFIG 0x40 #define W83791D_REG_VID_FANDIV 0x47 #define W83791D_REG_DID_VID4 0x49 @@ -908,8 +909,6 @@ static struct attribute *w83791d_attributes[] = { FAN_UNIT_ATTRS(0), FAN_UNIT_ATTRS(1), FAN_UNIT_ATTRS(2), - FAN_UNIT_ATTRS(3), - FAN_UNIT_ATTRS(4), TEMP_UNIT_ATTRS(0), TEMP_UNIT_ATTRS(1), TEMP_UNIT_ATTRS(2), @@ -925,6 +924,18 @@ static const struct attribute_group w83791d_group = { .attrs = w83791d_attributes, }; +/* Separate group of attributes for fan/pwm 4-5. Their pins can also be + in use for GPIO in which case their sysfs-interface should not be made + available */ +static struct attribute *w83791d_attributes_fanpwm45[] = { + FAN_UNIT_ATTRS(3), + FAN_UNIT_ATTRS(4), + NULL +}; + +static const struct attribute_group w83791d_group_fanpwm45 = { + .attrs = w83791d_attributes_fanpwm45, +}; static int w83791d_detect_subclients(struct i2c_client *client) { @@ -1056,6 +1067,7 @@ static int w83791d_probe(struct i2c_client *client, struct w83791d_data *data; struct device *dev = &client->dev; int i, err; + u8 has_fanpwm45; #ifdef DEBUG int val1; @@ -1090,15 +1102,27 @@ static int w83791d_probe(struct i2c_client *client, if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group))) goto error3; + /* Check if pins of fan/pwm 4-5 are in use as GPIO */ + has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10; + if (has_fanpwm45) { + err = sysfs_create_group(&client->dev.kobj, + &w83791d_group_fanpwm45); + if (err) + goto error4; + } + /* Everything is ready, now register the working device */ data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto error4; + goto error5; } return 0; +error5: + if (has_fanpwm45) + sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45); error4: sysfs_remove_group(&client->dev.kobj, &w83791d_group); error3: -- cgit v1.2.3 From 6495ce184033d5e70dfdf5bb8d149e9e02feaaa9 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add manual PWM support Add PWM manual control. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 15 ++++++---- drivers/hwmon/w83791d.c | 67 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index a67d3b7a709..49c0e94a118 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -58,29 +58,32 @@ internal state that allows no clean access (Bank with ID register is not currently selected). If you know the address of the chip, use a 'force' parameter; this will put it into a more well-behaved state first. -The driver implements three temperature sensors, five fan rotation speed -sensors, and ten voltage sensors. +The driver implements three temperature sensors, ten voltage sensors, +five fan rotation speed sensors and manual PWM control of each fan. Temperatures are measured in degrees Celsius and measurement resolution is 1 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when the temperature gets higher than the Overtemperature Shutdown value; it stays on until the temperature falls below the Hysteresis value. +Voltage sensors (also known as IN sensors) report their values in millivolts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. + Fan rotation speeds are reported in RPM (rotations per minute). An alarm is triggered if the rotation speed has dropped below a programmable limit. Fan readings can be divided by a programmable divider (1, 2, 4, 8, 16, 32, 64 or 128 for all fans) to give the readings more range or accuracy. -Voltage sensors (also known as IN sensors) report their values in millivolts. -An alarm is triggered if the voltage has crossed a programmable minimum -or maximum limit. +Each fan controlled is controlled by PWM. The PWM duty cycle can be read and +set for each fan separately. Valid values range from 0 (stop) to 255 (full). The w83791d has a global bit used to enable beeping from the speaker when an alarm is triggered as well as a bitmask to enable or disable the beep for specific alarms. You need both the global beep enable bit and the corresponding beep bit to be on for a triggered alarm to sound a beep. -The sysfs interface to the gloabal enable is via the sysfs beep_enable file. +The sysfs interface to the global enable is via the sysfs beep_enable file. This file is used for both legacy and new code. The sysfs interface to the beep bitmask has migrated from the original legacy diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 6b1cec9950f..a8ff4e12671 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -23,7 +23,7 @@ Supports following chips: Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83791d 10 5 3 3 0x71 0x5ca3 yes no + w83791d 10 5 5 3 0x71 0x5ca3 yes no The w83791d chip appears to be part way between the 83781d and the 83792d. Thus, this file is derived from both the w83792d.c and @@ -45,6 +45,7 @@ #define NUMBER_OF_VIN 10 #define NUMBER_OF_FANIN 5 #define NUMBER_OF_TEMPIN 3 +#define NUMBER_OF_PWM 5 /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -116,6 +117,14 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = { 0xBD, /* FAN 5 Count Low Limit in DataSheet */ }; +static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = { + 0x81, /* PWM 1 duty cycle register in DataSheet */ + 0x83, /* PWM 2 duty cycle register in DataSheet */ + 0x94, /* PWM 3 duty cycle register in DataSheet */ + 0xA0, /* PWM 4 duty cycle register in DataSheet */ + 0xA1, /* PWM 5 duty cycle register in DataSheet */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -276,6 +285,9 @@ struct w83791d_data { two sensors with three values (cur, over, hyst) */ + /* PWMs */ + u8 pwm[5]; /* pwm duty cycle */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -653,6 +665,48 @@ static struct sensor_device_attribute sda_fan_alarm[] = { SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22), }; +/* read/write PWMs */ +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm[nr]); +} + +static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); + w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_pwm[] = { + SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 0), + SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 1), + SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 2), + SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 3), + SENSOR_ATTR(pwm5, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 4), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -917,6 +971,9 @@ static struct attribute *w83791d_attributes[] = { &sda_beep_ctrl[1].dev_attr.attr, &dev_attr_cpu0_vid.attr, &dev_attr_vrm.attr, + &sda_pwm[0].dev_attr.attr, + &sda_pwm[1].dev_attr.attr, + &sda_pwm[2].dev_attr.attr, NULL }; @@ -930,6 +987,8 @@ static const struct attribute_group w83791d_group = { static struct attribute *w83791d_attributes_fanpwm45[] = { FAN_UNIT_ATTRS(3), FAN_UNIT_ATTRS(4), + &sda_pwm[3].dev_attr.attr, + &sda_pwm[4].dev_attr.attr, NULL }; @@ -1260,6 +1319,12 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) for (i = 0; i < 3; i++) data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04; + /* Update PWM duty cycle */ + for (i = 0; i < NUMBER_OF_PWM; i++) { + data->pwm[i] = w83791d_read(client, + W83791D_REG_PWM[i]); + } + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3 From b5938f8c4a530b2fad18f2293ffaf79ac9f5a148 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add pwm_enable support Add support for pwm_enable. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 13 +++++++- drivers/hwmon/w83791d.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index 49c0e94a118..b1e4798764e 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -108,6 +108,17 @@ going forward. The driver reads the hardware chip values at most once every three seconds. User mode code requesting values more often will receive cached values. +/sys files +---------- +The sysfs-interface is documented in the 'sysfs-interface' file. Only +chip-specific options are documented here. + +pwm[1-3]_enable - this file controls mode of fan/temperature control for + fan 1-3. Fan/PWM 4-5 only support manual mode. + * 1 Manual mode + * 2 Thermal Cruise mode (no further support) + * 3 Fan Speed Cruise mode (no further support) + Alarms bitmap vs. beep_mask bitmask ------------------------------------ For legacy code using the alarms and beep_mask files: @@ -138,4 +149,4 @@ global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) W83791D TODO: --------------- -Provide a patch for smart-fan control (still need appropriate motherboard/fans) +Provide a patch for Thermal Cruise registers. diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a8ff4e12671..a4d2b02d9e0 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -287,6 +287,8 @@ struct w83791d_data { /* PWMs */ u8 pwm[5]; /* pwm duty cycle */ + u8 pwm_enable[3]; /* pwm enable status for fan 1-3 + (fan 4-5 only support manual mode) */ /* Misc */ u32 alarms; /* realtime status register encoding,combined */ @@ -707,6 +709,71 @@ static struct sensor_device_attribute sda_pwm[] = { show_pwm, store_pwm, 4), }; +static ssize_t show_pwmenable(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm_enable[nr] + 1); +} + +static ssize_t store_pwmenable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 reg_cfg_tmp; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + int ret = strict_strtoul(buf, 10, &val); + + if (ret || val < 1 || val > 3) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val - 1; + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 2; + keep_mask = 0xf3; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0xcf; + break; + case 2: + reg_idx = 1; + val_shift = 2; + keep_mask = 0xf3; + break; + } + + reg_cfg_tmp = w83791d_read(client, W83791D_REG_FAN_CFG[reg_idx]); + reg_cfg_tmp = (reg_cfg_tmp & keep_mask) | + data->pwm_enable[nr] << val_shift; + + w83791d_write(client, W83791D_REG_FAN_CFG[reg_idx], reg_cfg_tmp); + mutex_unlock(&data->update_lock); + + return count; +} +static struct sensor_device_attribute sda_pwmenable[] = { + SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 0), + SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 1), + SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -974,6 +1041,9 @@ static struct attribute *w83791d_attributes[] = { &sda_pwm[0].dev_attr.attr, &sda_pwm[1].dev_attr.attr, &sda_pwm[2].dev_attr.attr, + &sda_pwmenable[0].dev_attr.attr, + &sda_pwmenable[1].dev_attr.attr, + &sda_pwmenable[2].dev_attr.attr, NULL }; @@ -1325,6 +1395,15 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) W83791D_REG_PWM[i]); } + /* Update PWM enable status */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_FAN_CFG[i]); + } + data->pwm_enable[0] = (reg_array_tmp[0] >> 2) & 0x03; + data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; + data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3 From a5a4598cd2e7cae456a7f2a100bf0e5c3c7811c7 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add support for thermal cruise mode Add support to set target temperature and tolerance for thermal cruise mode. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 19 ++++-- drivers/hwmon/w83791d.c | 148 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 5 deletions(-) diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index b1e4798764e..5663e491655 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -77,6 +77,9 @@ readings can be divided by a programmable divider (1, 2, 4, 8, 16, Each fan controlled is controlled by PWM. The PWM duty cycle can be read and set for each fan separately. Valid values range from 0 (stop) to 255 (full). +PWM 1-3 support Thermal Cruise mode, in which the PWMs are automatically +regulated to keep respectively temp 1-3 at a certain target temperature. +See below for the description of the sysfs-interface. The w83791d has a global bit used to enable beeping from the speaker when an alarm is triggered as well as a bitmask to enable or disable the beep for @@ -116,9 +119,19 @@ chip-specific options are documented here. pwm[1-3]_enable - this file controls mode of fan/temperature control for fan 1-3. Fan/PWM 4-5 only support manual mode. * 1 Manual mode - * 2 Thermal Cruise mode (no further support) + * 2 Thermal Cruise mode * 3 Fan Speed Cruise mode (no further support) +temp[1-3]_target - defines the target temperature for Thermal Cruise mode. + Unit: millidegree Celsius + RW + +temp[1-3]_tolerance - temperature tolerance for Thermal Cruise mode. + Specifies an interval around the target temperature + in which the fan speed is not changed. + Unit: millidegree Celsius + RW + Alarms bitmap vs. beep_mask bitmask ------------------------------------ For legacy code using the alarms and beep_mask files: @@ -146,7 +159,3 @@ tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch case_open : alarms: 0x001000 beep_mask: 0x001000 global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) - -W83791D TODO: ---------------- -Provide a patch for Thermal Cruise registers. diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a4d2b02d9e0..5768def8a4f 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -125,6 +125,17 @@ static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = { 0xA1, /* PWM 5 duty cycle register in DataSheet */ }; +static const u8 W83791D_REG_TEMP_TARGET[3] = { + 0x85, /* PWM 1 target temperature for temp 1 */ + 0x86, /* PWM 2 target temperature for temp 2 */ + 0x96, /* PWM 3 target temperature for temp 3 */ +}; + +static const u8 W83791D_REG_TEMP_TOL[2] = { + 0x87, /* PWM 1/2 temperature tolerance */ + 0x97, /* PWM 3 temperature tolerance */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -234,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div) (val) < 0 ? ((val) - 250) / 500 * 128 : \ ((val) + 250) / 500 * 128) +/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */ +#define TARGET_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 127000 ? 127 : \ + ((val) + 500) / 1000) + +/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */ +#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 15000 ? 15 : \ + ((val) + 500) / 1000) #define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) #define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff) @@ -290,6 +310,9 @@ struct w83791d_data { u8 pwm_enable[3]; /* pwm enable status for fan 1-3 (fan 4-5 only support manual mode) */ + u8 temp_target[3]; /* pwm 1-3 target temperature */ + u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -774,6 +797,110 @@ static struct sensor_device_attribute sda_pwmenable[] = { show_pwmenable, store_pwmenable, 2), }; +/* For Smart Fan I / Thermal Cruise */ +static ssize_t show_temp_target(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr])); +} + +static ssize_t store_temp_target(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->temp_target[nr] = TARGET_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TARGET[nr]) & 0x80; + w83791d_write(client, W83791D_REG_TEMP_TARGET[nr], + data->temp_target[nr] | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_target[] = { + SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 0), + SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 1), + SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 2), +}; + +static ssize_t show_temp_tolerance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr])); +} + +static ssize_t store_temp_tolerance(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 0; + keep_mask = 0xf0; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0x0f; + break; + case 2: + reg_idx = 1; + val_shift = 0; + keep_mask = 0xf0; + break; + } + + mutex_lock(&data->update_lock); + data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask; + w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx], + (data->temp_tolerance[nr] << val_shift) | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_tolerance[] = { + SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0), + SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 1), + SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1044,6 +1171,12 @@ static struct attribute *w83791d_attributes[] = { &sda_pwmenable[0].dev_attr.attr, &sda_pwmenable[1].dev_attr.attr, &sda_pwmenable[2].dev_attr.attr, + &sda_temp_target[0].dev_attr.attr, + &sda_temp_target[1].dev_attr.attr, + &sda_temp_target[2].dev_attr.attr, + &sda_temp_tolerance[0].dev_attr.attr, + &sda_temp_tolerance[1].dev_attr.attr, + &sda_temp_tolerance[2].dev_attr.attr, NULL }; @@ -1404,6 +1537,21 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; + /* Update PWM target temperature */ + for (i = 0; i < 3; i++) { + data->temp_target[i] = w83791d_read(client, + W83791D_REG_TEMP_TARGET[i]) & 0x7f; + } + + /* Update PWM temperature tolerance */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_TEMP_TOL[i]); + } + data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f; + data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f; + data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3 From 6aa693b85257cd41fdb3554016b663519dbf9d14 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: Drop dead links to old National Semiconductor chip datasheets Signed-off-by: Jean Delvare --- Documentation/hwmon/pc87360 | 7 +------ Documentation/hwmon/pc87427 | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Documentation/hwmon/pc87360 b/Documentation/hwmon/pc87360 index 89a8fcfa78d..cbac32b59c8 100644 --- a/Documentation/hwmon/pc87360 +++ b/Documentation/hwmon/pc87360 @@ -5,12 +5,7 @@ Supported chips: * National Semiconductor PC87360, PC87363, PC87364, PC87365 and PC87366 Prefixes: 'pc87360', 'pc87363', 'pc87364', 'pc87365', 'pc87366' Addresses scanned: none, address read from Super I/O config space - Datasheets: - http://www.national.com/pf/PC/PC87360.html - http://www.national.com/pf/PC/PC87363.html - http://www.national.com/pf/PC/PC87364.html - http://www.national.com/pf/PC/PC87365.html - http://www.national.com/pf/PC/PC87366.html + Datasheets: No longer available Authors: Jean Delvare diff --git a/Documentation/hwmon/pc87427 b/Documentation/hwmon/pc87427 index 9a0708f9f49..d1ebbe510f3 100644 --- a/Documentation/hwmon/pc87427 +++ b/Documentation/hwmon/pc87427 @@ -5,7 +5,7 @@ Supported chips: * National Semiconductor PC87427 Prefix: 'pc87427' Addresses scanned: none, address read from Super I/O config space - Datasheet: http://www.winbond.com.tw/E-WINBONDHTM/partner/apc_007.html + Datasheet: No longer available Author: Jean Delvare -- cgit v1.2.3 From 2fbbbf148840332d614790ea00d9a7ecb38d648d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Refactor beep enable handling We can handle the beep enable bit as any other beep mask bit for slightly smaller code. Signed-off-by: Jean Delvare Cc: Wolfgang Grandegger --- drivers/hwmon/w83781d.c | 46 +++++++++------------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index f942ecdd47c..136bec3fd64 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -178,9 +178,9 @@ FAN_FROM_REG(u8 val, int div) #define TEMP_FROM_REG(val) ((val) * 1000) #define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \ - (val) ^ 0x7fff : (val)) + (~(val)) & 0x7fff : (val) & 0xff7fff) #define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \ - (~(val)) & 0x7fff : (val) & 0xffffff) + (~(val)) & 0x7fff : (val) & 0xff7fff) #define DIV_FROM_REG(val) (1 << (val)) @@ -240,7 +240,6 @@ struct w83781d_data { u8 vid; /* Register encoding, combined */ u32 alarms; /* Register encoding, combined */ u32 beep_mask; /* Register encoding, combined */ - u8 beep_enable; /* Boolean */ u8 pwm[4]; /* Register value */ u8 pwm2_enable; /* Boolean */ u16 sens[3]; /* 782D/783S only. @@ -513,11 +512,6 @@ static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr return sprintf(buf, "%ld\n", (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); } -static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long)data->beep_enable); -} static ssize_t store_beep_mask(struct device *dev, struct device_attribute *attr, @@ -529,12 +523,12 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); - data->beep_mask = BEEP_MASK_TO_REG(val, data->type); + data->beep_mask &= 0x8000; /* preserve beep enable */ + data->beep_mask |= BEEP_MASK_TO_REG(val, data->type); w83781d_write_value(data, W83781D_REG_BEEP_INTS1, data->beep_mask & 0xff); w83781d_write_value(data, W83781D_REG_BEEP_INTS2, - ((data->beep_mask >> 8) & 0x7f) - | data->beep_enable << 7); + (data->beep_mask >> 8) & 0xff); if (data->type != w83781d && data->type != as99127f) { w83781d_write_value(data, W83781D_REG_BEEP_INTS3, ((data->beep_mask) >> 16) & 0xff); @@ -544,31 +538,8 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, return count; } -static ssize_t -store_beep_enable(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct w83781d_data *data = dev_get_drvdata(dev); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - if (val != 0 && val != 1) - return -EINVAL; - - mutex_lock(&data->update_lock); - data->beep_enable = val; - val = w83781d_read_value(data, W83781D_REG_BEEP_INTS2) & 0x7f; - val |= data->beep_enable << 7; - w83781d_write_value(data, W83781D_REG_BEEP_INTS2, val); - mutex_unlock(&data->update_lock); - - return count; -} - static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR, show_beep_mask, store_beep_mask); -static DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, - show_beep_enable, store_beep_enable); static ssize_t show_beep(struct device *dev, struct device_attribute *attr, char *buf) @@ -663,6 +634,8 @@ static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, 5); static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_temp3_beep, store_beep, 13); +static SENSOR_DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, + show_beep, store_beep, 15); static ssize_t show_fan_div(struct device *dev, struct device_attribute *da, char *buf) @@ -1029,7 +1002,7 @@ static struct attribute* w83781d_attributes[] = { &dev_attr_vrm.attr, &dev_attr_alarms.attr, &dev_attr_beep_mask.attr, - &dev_attr_beep_enable.attr, + &sensor_dev_attr_beep_enable.dev_attr.attr, NULL }; static const struct attribute_group w83781d_group = { @@ -1775,8 +1748,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) W83781D_REG_ALARM2) << 8); } i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); - data->beep_enable = i >> 7; - data->beep_mask = ((i & 0x7f) << 8) + + data->beep_mask = (i << 8) + w83781d_read_value(data, W83781D_REG_BEEP_INTS1); if ((data->type != w83781d) && (data->type != as99127f)) { data->beep_mask |= -- cgit v1.2.3 From c6566206c6f9583b529d62f05fb67182978b959e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Detect alias chips The W83781D and W83782D can be accessed either on the I2C bus or the ISA bus. We must not access the same chip through both interfaces. So far we were relying on the user passing the correct ignore parameter to skip the registration of the I2C interface as suggested by sensors-detect, but this is fragile: the user may load the w83781d driver without running sensors-detect, and the i2c bus numbers are not stable across reboots and hardware changes. So, better detect alias chips in the driver directly, and skip any I2C chip which is obviously an alias of the ISA chip. This is done by comparing the value of 26 selected registers. Signed-off-by: Jean Delvare Cc: Wolfgang Grandegger --- drivers/hwmon/w83781d.c | 79 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 136bec3fd64..1c00d9f7c14 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -205,10 +205,7 @@ DIV_TO_REG(long val, enum chips type) W83781D chips available (well, actually, that is probably never done; but it is a clean illustration of how to handle a case like that). Finally, a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the W83781D at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ + not like to detect it double. */ /* For ISA chips, we abuse the i2c_client addr and name fields. We also use the driver field to differentiate between I2C and ISA chips. */ @@ -852,13 +849,25 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* This function is called when: * w83781d_driver is inserted (when this module is loaded), for each available adapter - * when a new adapter is inserted (and w83781d_driver is still present) */ + * when a new adapter is inserted (and w83781d_driver is still present) + We block updates of the ISA device to minimize the risk of concurrent + access to the same W83781D chip through different interfaces. */ static int w83781d_attach_adapter(struct i2c_adapter *adapter) { + struct w83781d_data *data; + int err; + if (!(adapter->class & I2C_CLASS_HWMON)) return 0; - return i2c_probe(adapter, &addr_data, w83781d_detect); + + data = pdev ? platform_get_drvdata(pdev) : NULL; + if (data) + mutex_lock(&data->update_lock); + err = i2c_probe(adapter, &addr_data, w83781d_detect); + if (data) + mutex_unlock(&data->update_lock); + return err; } /* Assumes that adapter is of I2C, not ISA variety. @@ -1028,6 +1037,40 @@ static const struct attribute_group w83781d_group_opt = { .attrs = w83781d_attributes_opt, }; +/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ +static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + struct w83781d_data *i2c, *isa; + int i; + + if (!pdev) /* No ISA chip */ + return 0; + + i2c = i2c_get_clientdata(client); + isa = platform_get_drvdata(pdev); + + if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + if (w83781d_read_value(isa, W83781D_REG_CONFIG) != + w83781d_read_value(i2c, W83781D_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + + return 1; +} + /* No clean up is done on error, it's up to the caller */ static int w83781d_create_files(struct device *dev, int kind, int is_isa) @@ -1242,6 +1285,14 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) err = -EINVAL; goto ERROR2; } + + if ((kind == w83781d || kind == w83782d) + && w83781d_alias_detect(client, val1)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + err = -ENODEV; + goto ERROR2; + } } if (kind == w83781d) { @@ -1904,14 +1955,12 @@ sensors_w83781d_init(void) { int res; - res = i2c_add_driver(&w83781d_driver); - if (res) - goto exit; - + /* We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. */ if (w83781d_isa_found(isa_address)) { res = platform_driver_register(&w83781d_isa_driver); if (res) - goto exit_unreg_i2c_driver; + goto exit; /* Sets global pdev as a side effect */ res = w83781d_isa_device_add(isa_address); @@ -1919,12 +1968,16 @@ sensors_w83781d_init(void) goto exit_unreg_isa_driver; } + res = i2c_add_driver(&w83781d_driver); + if (res) + goto exit_unreg_isa_device; + return 0; + exit_unreg_isa_device: + platform_device_unregister(pdev); exit_unreg_isa_driver: platform_driver_unregister(&w83781d_isa_driver); - exit_unreg_i2c_driver: - i2c_del_driver(&w83781d_driver); exit: return res; } -- cgit v1.2.3 From 10c08f937d832e1d5a77e65767a6e2c05bc25c69 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Additional information about AS99127F PWM This information was provided in lm-sensors ticket #2350: http://www.lm-sensors.org/ticket/2350 This is IMHO still not enough to be able to safely implement fan control support for the AS99127F, but this is valuable information so I am adding it to the documentation. Signed-off-by: Jean Delvare --- Documentation/hwmon/w83781d | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/w83781d b/Documentation/hwmon/w83781d index 6f800a0283e..c91e0b63ea1 100644 --- a/Documentation/hwmon/w83781d +++ b/Documentation/hwmon/w83781d @@ -353,7 +353,7 @@ in6=255 # PWM -Additional info about PWM on the AS99127F (may apply to other Asus +* Additional info about PWM on the AS99127F (may apply to other Asus chips as well) by Jean Delvare as of 2004-04-09: AS99127F revision 2 seems to have two PWM registers at 0x59 and 0x5A, @@ -396,7 +396,7 @@ Please contact us if you can figure out how it is supposed to work. As long as we don't know more, the w83781d driver doesn't handle PWM on AS99127F chips at all. -Additional info about PWM on the AS99127F rev.1 by Hector Martin: +* Additional info about PWM on the AS99127F rev.1 by Hector Martin: I've been fiddling around with the (in)famous 0x59 register and found out the following values do work as a form of coarse pwm: @@ -418,3 +418,36 @@ change. My mobo is an ASUS A7V266-E. This behavior is similar to what I got with speedfan under Windows, where 0-15% would be off, 15-2x% (can't remember the exact value) would be 70% and higher would be full on. + +* Additional info about PWM on the AS99127F rev.1 from lm-sensors + ticket #2350: + +I conducted some experiment on Asus P3B-F motherboard with AS99127F +(Ver. 1). + +I confirm that 0x59 register control the CPU_Fan Header on this +motherboard, and 0x5a register control PWR_Fan. + +In order to reduce the dependency of specific fan, the measurement is +conducted with a digital scope without fan connected. I found out that +P3B-F actually output variable DC voltage on fan header center pin, +looks like PWM is filtered on this motherboard. + +Here are some of measurements: + +0x80 20 mV +0x81 20 mV +0x82 232 mV +0x83 1.2 V +0x84 2.31 V +0x85 3.44 V +0x86 4.62 V +0x87 5.81 V +0x88 7.01 V +9x89 8.22 V +0x8a 9.42 V +0x8b 10.6 V +0x8c 11.9 V +0x8d 12.4 V +0x8e 12.4 V +0x8f 12.4 V -- cgit v1.2.3 From 443850ce5880b24583d2be9d4b146e471fdf3dad Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Make ISA interface depend on CONFIG_ISA Probing the ISA bus on systems without ISA bus may hang the system. This patch makes the ISA bus related code depend on the kernel configuration parameter CONFIG_ISA. It moves ISA bus related code into one #ifdef CONFIG_ISA ... #endif block and adds some helper function. Signed-off-by: Wolfgang Grandegger Signed-off-by: Jean Delvare --- drivers/hwmon/w83781d.c | 673 ++++++++++++++++++++++++++++-------------------- 1 file changed, 393 insertions(+), 280 deletions(-) diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 1c00d9f7c14..1a729a74927 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -38,25 +38,24 @@ #include #include #include -#include -#include #include #include #include #include #include #include + +#ifdef CONFIG_ISA +#include +#include #include -#include "lm75.h" +#endif -/* ISA device, if found */ -static struct platform_device *pdev; +#include "lm75.h" /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned short isa_address = 0x290; - /* Insmod parameters */ I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f); I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " @@ -245,13 +244,13 @@ struct w83781d_data { u8 vrm; }; +static struct w83781d_data *w83781d_data_if_isa(void); +static int w83781d_alias_detect(struct i2c_client *client, u8 chipid); + static int w83781d_attach_adapter(struct i2c_adapter *adapter); static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); static int w83781d_detach_client(struct i2c_client *client); -static int __devinit w83781d_isa_probe(struct platform_device *pdev); -static int __devexit w83781d_isa_remove(struct platform_device *pdev); - static int w83781d_read_value(struct w83781d_data *data, u16 reg); static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); static struct w83781d_data *w83781d_update_device(struct device *dev); @@ -265,16 +264,6 @@ static struct i2c_driver w83781d_driver = { .detach_client = w83781d_detach_client, }; -static struct platform_driver w83781d_isa_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "w83781d", - }, - .probe = w83781d_isa_probe, - .remove = w83781d_isa_remove, -}; - - /* following are the sysfs callback functions */ #define show_in_reg(reg) \ static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ @@ -836,16 +825,6 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_sensor, store_sensor, 2); -/* I2C devices get this name attribute automatically, but for ISA devices - we must create it by ourselves. */ -static ssize_t -show_name(struct device *dev, struct device_attribute *devattr, char *buf) -{ - struct w83781d_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); -} -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - /* This function is called when: * w83781d_driver is inserted (when this module is loaded), for each available adapter @@ -855,13 +834,12 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static int w83781d_attach_adapter(struct i2c_adapter *adapter) { - struct w83781d_data *data; + struct w83781d_data *data = w83781d_data_if_isa(); int err; if (!(adapter->class & I2C_CLASS_HWMON)) return 0; - data = pdev ? platform_get_drvdata(pdev) : NULL; if (data) mutex_lock(&data->update_lock); err = i2c_probe(adapter, &addr_data, w83781d_detect); @@ -1037,40 +1015,6 @@ static const struct attribute_group w83781d_group_opt = { .attrs = w83781d_attributes_opt, }; -/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ -static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) -{ - struct w83781d_data *i2c, *isa; - int i; - - if (!pdev) /* No ISA chip */ - return 0; - - i2c = i2c_get_clientdata(client); - isa = platform_get_drvdata(pdev); - - if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) - return 0; /* Address doesn't match */ - if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) - return 0; /* Chip type doesn't match */ - - /* We compare all the limit registers, the config register and the - * interrupt mask registers */ - for (i = 0x2b; i <= 0x3d; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) - return 0; - } - if (w83781d_read_value(isa, W83781D_REG_CONFIG) != - w83781d_read_value(i2c, W83781D_REG_CONFIG)) - return 0; - for (i = 0x43; i <= 0x46; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) - return 0; - } - - return 1; -} - /* No clean up is done on error, it's up to the caller */ static int w83781d_create_files(struct device *dev, int kind, int is_isa) @@ -1167,12 +1111,6 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) } } - if (is_isa) { - err = device_create_file(&pdev->dev, &dev_attr_name); - if (err) - return err; - } - return 0; } @@ -1381,221 +1319,80 @@ w83781d_detach_client(struct i2c_client *client) return 0; } -static int __devinit -w83781d_isa_probe(struct platform_device *pdev) -{ - int err, reg; - struct w83781d_data *data; - struct resource *res; - const char *name; - - /* Reserve the ISA region */ - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, - "w83781d")) { - err = -EBUSY; - goto exit; - } - - if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release_region; - } - mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); - platform_set_drvdata(pdev, data); - - reg = w83781d_read_value(data, W83781D_REG_WCHIPID); - switch (reg) { - case 0x30: - data->type = w83782d; - name = "w83782d"; - break; - default: - data->type = w83781d; - name = "w83781d"; - } - strlcpy(data->client.name, name, I2C_NAME_SIZE); - - /* Initialize the W83781D chip */ - w83781d_init_device(&pdev->dev); - - /* Register sysfs hooks */ - err = w83781d_create_files(&pdev->dev, data->type, 1); - if (err) - goto exit_remove_files; - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - - exit_remove_files: - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); - device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - exit_release_region: - release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); - exit: - return err; -} - -static int __devexit -w83781d_isa_remove(struct platform_device *pdev) -{ - struct w83781d_data *data = platform_get_drvdata(pdev); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); - device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); - kfree(data); - - return 0; -} - -/* The SMBus locks itself, usually, but nothing may access the Winbond between - bank switches. ISA access must always be locked explicitly! - We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, - would slow down the W83781D access and should not be necessary. - There are some ugly typecasts here, but the good news is - they should - nowhere else be necessary! */ static int -w83781d_read_value(struct w83781d_data *data, u16 reg) +w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) { struct i2c_client *client = &data->client; - int res, word_sized, bank; + int res, bank; struct i2c_client *cl; - mutex_lock(&data->lock); - if (!client->driver) { /* ISA device */ - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x50) - || ((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - res = - (res << 8) + inb_p(client->addr + - W83781D_DATA_REG_OFFSET); - } - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - res = i2c_smbus_read_byte_data(client, reg & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(cl, 0)); - break; - case 0x52: /* CONFIG */ - res = i2c_smbus_read_byte_data(cl, 1); - break; - case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data(cl, 2)); - break; - case 0x55: /* OVER */ - default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); - break; - } + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = swab16(i2c_smbus_read_word_data(cl, 0)); + break; + case 0x52: /* CONFIG */ + res = i2c_smbus_read_byte_data(cl, 1); + break; + case 0x53: /* HYST */ + res = swab16(i2c_smbus_read_word_data(cl, 2)); + break; + case 0x55: /* OVER */ + default: + res = swab16(i2c_smbus_read_word_data(cl, 3)); + break; } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); } - mutex_unlock(&data->lock); + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + return res; } static int -w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) { struct i2c_client *client = &data->client; - int word_sized, bank; + int bank; struct i2c_client *cl; - mutex_lock(&data->lock); - if (!client->driver) { /* ISA device */ - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - } - outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, + value & 0xff); } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - i2c_smbus_write_byte_data(client, reg & 0xff, - value & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x52: /* CONFIG */ - i2c_smbus_write_byte_data(cl, 1, value & 0xff); - break; - case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); - break; - case 0x55: /* OVER */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); - break; - } + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, swab16(value)); + break; + case 0x55: /* OVER */ + i2c_smbus_write_word_data(cl, 3, swab16(value)); + break; } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); } - mutex_unlock(&data->lock); + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + return 0; } @@ -1815,6 +1612,255 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) return data; } +#ifdef CONFIG_ISA + +/* ISA device, if found */ +static struct platform_device *pdev; + +static unsigned short isa_address = 0x290; + +/* I2C devices get this name attribute automatically, but for ISA devices + we must create it by ourselves. */ +static ssize_t +show_name(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct w83781d_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->client.name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct w83781d_data *w83781d_data_if_isa(void) +{ + return pdev ? platform_get_drvdata(pdev) : NULL; +} + +/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ +static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + struct w83781d_data *i2c, *isa; + int i; + + if (!pdev) /* No ISA chip */ + return 0; + + i2c = i2c_get_clientdata(client); + isa = platform_get_drvdata(pdev); + + if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + if (w83781d_read_value(isa, W83781D_REG_CONFIG) != + w83781d_read_value(i2c, W83781D_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + + return 1; +} + +static int +w83781d_read_value_isa(struct w83781d_data *data, u16 reg) +{ + struct i2c_client *client = &data->client; + int word_sized, res; + + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(client->addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + return res; +} + +static void +w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) +{ + struct i2c_client *client = &data->client; + int word_sized; + + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } +} + +/* The SMBus locks itself, usually, but nothing may access the Winbond between + bank switches. ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +static int +w83781d_read_value(struct w83781d_data *data, u16 reg) +{ + struct i2c_client *client = &data->client; + int res; + + mutex_lock(&data->lock); + if (client->driver) + res = w83781d_read_value_i2c(data, reg); + else + res = w83781d_read_value_isa(data, reg); + mutex_unlock(&data->lock); + return res; +} + +static int +w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +{ + struct i2c_client *client = &data->client; + + mutex_lock(&data->lock); + if (client->driver) + w83781d_write_value_i2c(data, reg, value); + else + w83781d_write_value_isa(data, reg, value); + mutex_unlock(&data->lock); + return 0; +} + +static int __devinit +w83781d_isa_probe(struct platform_device *pdev) +{ + int err, reg; + struct w83781d_data *data; + struct resource *res; + const char *name; + + /* Reserve the ISA region */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, + "w83781d")) { + err = -EBUSY; + goto exit; + } + + data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit_release_region; + } + mutex_init(&data->lock); + data->client.addr = res->start; + i2c_set_clientdata(&data->client, data); + platform_set_drvdata(pdev, data); + + reg = w83781d_read_value(data, W83781D_REG_WCHIPID); + switch (reg) { + case 0x30: + data->type = w83782d; + name = "w83782d"; + break; + default: + data->type = w83781d; + name = "w83781d"; + } + strlcpy(data->client.name, name, I2C_NAME_SIZE); + + /* Initialize the W83781D chip */ + w83781d_init_device(&pdev->dev); + + /* Register sysfs hooks */ + err = w83781d_create_files(&pdev->dev, data->type, 1); + if (err) + goto exit_remove_files; + + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto exit_remove_files; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + + exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + kfree(data); + exit_release_region: + release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); + exit: + return err; +} + +static int __devexit +w83781d_isa_remove(struct platform_device *pdev) +{ + struct w83781d_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); + kfree(data); + + return 0; +} + +static struct platform_driver w83781d_isa_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "w83781d", + }, + .probe = w83781d_isa_probe, + .remove = __devexit_p(w83781d_isa_remove), +}; + /* return 1 if a supported chip is found, 0 otherwise */ static int __init w83781d_isa_found(unsigned short address) @@ -1951,12 +1997,10 @@ w83781d_isa_device_add(unsigned short address) } static int __init -sensors_w83781d_init(void) +w83781d_isa_register(void) { int res; - /* We register the ISA device first, so that we can skip the - * registration of an I2C interface to the same device. */ if (w83781d_isa_found(isa_address)) { res = platform_driver_register(&w83781d_isa_driver); if (res) @@ -1968,27 +2012,96 @@ sensors_w83781d_init(void) goto exit_unreg_isa_driver; } - res = i2c_add_driver(&w83781d_driver); - if (res) - goto exit_unreg_isa_device; - return 0; - exit_unreg_isa_device: - platform_device_unregister(pdev); - exit_unreg_isa_driver: +exit_unreg_isa_driver: platform_driver_unregister(&w83781d_isa_driver); - exit: +exit: return res; } static void __exit -sensors_w83781d_exit(void) +w83781d_isa_unregister(void) { if (pdev) { platform_device_unregister(pdev); platform_driver_unregister(&w83781d_isa_driver); } +} +#else /* !CONFIG_ISA */ + +static struct w83781d_data *w83781d_data_if_isa(void) +{ + return NULL; +} + +static int +w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + return 0; +} + +static int +w83781d_read_value(struct w83781d_data *data, u16 reg) +{ + int res; + + mutex_lock(&data->lock); + res = w83781d_read_value_i2c(data, reg); + mutex_unlock(&data->lock); + + return res; +} + +static int +w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +{ + mutex_lock(&data->lock); + w83781d_write_value_i2c(data, reg, value); + mutex_unlock(&data->lock); + + return 0; +} + +static int __init +w83781d_isa_register(void) +{ + return 0; +} + +static void __exit +w83781d_isa_unregister(void) +{ +} +#endif /* CONFIG_ISA */ + +static int __init +sensors_w83781d_init(void) +{ + int res; + + /* We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. */ + res = w83781d_isa_register(); + if (res) + goto exit; + + res = i2c_add_driver(&w83781d_driver); + if (res) + goto exit_unreg_isa; + + return 0; + + exit_unreg_isa: + w83781d_isa_unregister(); + exit: + return res; +} + +static void __exit +sensors_w83781d_exit(void) +{ + w83781d_isa_unregister(); i2c_del_driver(&w83781d_driver); } -- cgit v1.2.3 From 360782dde00a2e6e7d9fd57535f90934707ab8a8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (w83781d) Stop abusing struct i2c_client for ISA devices Upcoming changes to the I2C part of the w83781d driver will cause ISA devices to no longer have a struct i2c_client at hand. So, we must stop (ab)using it now. Signed-off-by: Jean Delvare Cc: Wolfgang Grandegger --- drivers/hwmon/w83781d.c | 63 ++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 1a729a74927..50580c88305 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -4,7 +4,7 @@ Copyright (c) 1998 - 2001 Frodo Looijaard , Philip Edelbrock , and Mark Studebaker - Copyright (c) 2007 Jean Delvare + Copyright (c) 2007 - 2008 Jean Delvare This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -198,22 +198,16 @@ DIV_TO_REG(long val, enum chips type) return i; } -/* There are some complications in a module like this. First off, W83781D chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - W83781D chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. */ - -/* For ISA chips, we abuse the i2c_client addr and name fields. We also use - the driver field to differentiate between I2C and ISA chips. */ struct w83781d_data { struct i2c_client client; struct device *hwmon_dev; struct mutex lock; enum chips type; + /* For ISA device only */ + const char *name; + int isa_addr; + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -1625,7 +1619,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { struct w83781d_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); + return sprintf(buf, "%s\n", data->name); } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); @@ -1671,7 +1665,6 @@ static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) static int w83781d_read_value_isa(struct w83781d_data *data, u16 reg) { - struct i2c_client *client = &data->client; int word_sized, res; word_sized = (((reg & 0xff00) == 0x100) @@ -1681,23 +1674,23 @@ w83781d_read_value_isa(struct w83781d_data *data, u16 reg) || ((reg & 0x00ff) == 0x55)); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_DATA_REG_OFFSET); } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET); if (word_sized) { outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); res = - (res << 8) + inb_p(client->addr + + (res << 8) + inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET); } if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); } return res; } @@ -1705,7 +1698,6 @@ w83781d_read_value_isa(struct w83781d_data *data, u16 reg) static void w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) { - struct i2c_client *client = &data->client; int word_sized; word_sized = (((reg & 0xff00) == 0x100) @@ -1714,22 +1706,22 @@ w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) || ((reg & 0x00ff) == 0x55)); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_DATA_REG_OFFSET); } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); if (word_sized) { outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_DATA_REG_OFFSET); outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); } - outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); + outb_p(value & 0xff, data->isa_addr + W83781D_DATA_REG_OFFSET); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); } } @@ -1774,7 +1766,6 @@ w83781d_isa_probe(struct platform_device *pdev) int err, reg; struct w83781d_data *data; struct resource *res; - const char *name; /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1790,21 +1781,19 @@ w83781d_isa_probe(struct platform_device *pdev) goto exit_release_region; } mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); + data->isa_addr = res->start; platform_set_drvdata(pdev, data); reg = w83781d_read_value(data, W83781D_REG_WCHIPID); switch (reg) { case 0x30: data->type = w83782d; - name = "w83782d"; + data->name = "w83782d"; break; default: data->type = w83781d; - name = "w83781d"; + data->name = "w83781d"; } - strlcpy(data->client.name, name, I2C_NAME_SIZE); /* Initialize the W83781D chip */ w83781d_init_device(&pdev->dev); @@ -1846,7 +1835,7 @@ w83781d_isa_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); + release_region(data->isa_addr + W83781D_ADDR_REG_OFFSET, 2); kfree(data); return 0; -- cgit v1.2.3 From 0217eae3a825d551b99991bc30555c3daeb0a4df Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (w83781d) Use new style driver binding This patch modifies the w83781d driver to use new style driver binding. Substantial code modifications are required to deal with the new interface, especially legacy device detection. [JD: largely edited to make the patch smaller and to get the driver to work again on ISA devices.] Signed-off-by: Wolfgang Grandegger Signed-off-by: Jean Delvare --- drivers/hwmon/w83781d.c | 309 +++++++++++++++++++++--------------------------- 1 file changed, 134 insertions(+), 175 deletions(-) diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 50580c88305..d4d1b859d4f 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -199,7 +199,7 @@ DIV_TO_REG(long val, enum chips type) } struct w83781d_data { - struct i2c_client client; + struct i2c_client *client; struct device *hwmon_dev; struct mutex lock; enum chips type; @@ -241,23 +241,11 @@ struct w83781d_data { static struct w83781d_data *w83781d_data_if_isa(void); static int w83781d_alias_detect(struct i2c_client *client, u8 chipid); -static int w83781d_attach_adapter(struct i2c_adapter *adapter); -static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); -static int w83781d_detach_client(struct i2c_client *client); - static int w83781d_read_value(struct w83781d_data *data, u16 reg); static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); static struct w83781d_data *w83781d_update_device(struct device *dev); static void w83781d_init_device(struct device *dev); -static struct i2c_driver w83781d_driver = { - .driver = { - .name = "w83781d", - }, - .attach_adapter = w83781d_attach_adapter, - .detach_client = w83781d_detach_client, -}; - /* following are the sysfs callback functions */ #define show_in_reg(reg) \ static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ @@ -819,46 +807,19 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_sensor, store_sensor, 2); -/* This function is called when: - * w83781d_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and w83781d_driver is still present) - We block updates of the ISA device to minimize the risk of concurrent - access to the same W83781D chip through different interfaces. */ -static int -w83781d_attach_adapter(struct i2c_adapter *adapter) -{ - struct w83781d_data *data = w83781d_data_if_isa(); - int err; - - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - - if (data) - mutex_lock(&data->update_lock); - err = i2c_probe(adapter, &addr_data, w83781d_detect); - if (data) - mutex_unlock(&data->update_lock); - return err; -} - /* Assumes that adapter is of I2C, not ISA variety. * OTHERWISE DON'T CALL THIS */ static int -w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, - struct i2c_client *new_client) +w83781d_detect_subclients(struct i2c_client *new_client) { int i, val1 = 0, id; int err; - const char *client_name = ""; + int address = new_client->addr; + unsigned short sc_addr[2]; + struct i2c_adapter *adapter = new_client->adapter; struct w83781d_data *data = i2c_get_clientdata(new_client); - - data->lm75[0] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[0])) { - err = -ENOMEM; - goto ERROR_SC_0; - } + enum chips kind = data->type; id = i2c_adapter_id(adapter); @@ -876,55 +837,35 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, w83781d_write_value(data, W83781D_REG_I2C_SUBADDR, (force_subclients[2] & 0x07) | ((force_subclients[3] & 0x07) << 4)); - data->lm75[0]->addr = force_subclients[2]; + sc_addr[0] = force_subclients[2]; } else { val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR); - data->lm75[0]->addr = 0x48 + (val1 & 0x07); + sc_addr[0] = 0x48 + (val1 & 0x07); } if (kind != w83783s) { - data->lm75[1] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[1])) { - err = -ENOMEM; - goto ERROR_SC_1; - } - if (force_subclients[0] == id && force_subclients[1] == address) { - data->lm75[1]->addr = force_subclients[3]; + sc_addr[1] = force_subclients[3]; } else { - data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07); + sc_addr[1] = 0x48 + ((val1 >> 4) & 0x07); } - if (data->lm75[0]->addr == data->lm75[1]->addr) { + if (sc_addr[0] == sc_addr[1]) { dev_err(&new_client->dev, "Duplicate addresses 0x%x for subclients.\n", - data->lm75[0]->addr); + sc_addr[0]); err = -EBUSY; goto ERROR_SC_2; } } - if (kind == w83781d) - client_name = "w83781d subclient"; - else if (kind == w83782d) - client_name = "w83782d subclient"; - else if (kind == w83783s) - client_name = "w83783s subclient"; - else if (kind == as99127f) - client_name = "as99127f subclient"; - for (i = 0; i <= 1; i++) { - /* store all data in w83781d */ - i2c_set_clientdata(data->lm75[i], NULL); - data->lm75[i]->adapter = adapter; - data->lm75[i]->driver = &w83781d_driver; - data->lm75[i]->flags = 0; - strlcpy(data->lm75[i]->name, client_name, - I2C_NAME_SIZE); - if ((err = i2c_attach_client(data->lm75[i]))) { + data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]); + if (!data->lm75[i]) { dev_err(&new_client->dev, "Subclient %d " "registration at address 0x%x " - "failed.\n", i, data->lm75[i]->addr); + "failed.\n", i, sc_addr[i]); + err = -ENOMEM; if (i == 1) goto ERROR_SC_3; goto ERROR_SC_2; @@ -937,12 +878,9 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, /* Undo inits in case of errors */ ERROR_SC_3: - i2c_detach_client(data->lm75[0]); + i2c_unregister_device(data->lm75[0]); ERROR_SC_2: - kfree(data->lm75[1]); ERROR_SC_1: - kfree(data->lm75[0]); -ERROR_SC_0: return err; } @@ -1108,87 +1046,71 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) return 0; } +/* Return 0 if detection is successful, -ENODEV otherwise */ static int -w83781d_detect(struct i2c_adapter *adapter, int address, int kind) +w83781d_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { int val1 = 0, val2; - struct i2c_client *client; - struct device *dev; - struct w83781d_data *data; - int err; + struct w83781d_data *isa = w83781d_data_if_isa(); + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *client_name = ""; enum vendor { winbond, asus } vendid; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -EINVAL; - goto ERROR1; - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access w83781d_{read,write}_value. */ - - if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } - - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = address; - mutex_init(&data->lock); - client->adapter = adapter; - client->driver = &w83781d_driver; - dev = &client->dev; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; - /* Now, we do the remaining detection. */ + /* We block updates of the ISA device to minimize the risk of + concurrent access to the same W83781D chip through different + interfaces. */ + if (isa) + mutex_lock(&isa->update_lock); /* The w8378?d may be stuck in some other bank than bank 0. This may make reading other information impossible. Specify a force=... or force_*=... parameter, and the Winbond will be reset to the right bank. */ if (kind < 0) { - if (w83781d_read_value(data, W83781D_REG_CONFIG) & 0x80) { + if (i2c_smbus_read_byte_data + (client, W83781D_REG_CONFIG) & 0x80) { dev_dbg(&adapter->dev, "Detection of w83781d chip " "failed at step 3\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - val1 = w83781d_read_value(data, W83781D_REG_BANK); - val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); /* Check for Winbond or Asus ID if in bank 0 */ if ((!(val1 & 0x07)) && (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { dev_dbg(&adapter->dev, "Detection of w83781d chip " "failed at step 4\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } /* If Winbond SMBus, check address at 0x48. Asus doesn't support, except for as99127f rev.2 */ if ((!(val1 & 0x80) && (val2 == 0xa3)) || ((val1 & 0x80) && (val2 == 0x5c))) { - if (w83781d_read_value - (data, W83781D_REG_I2C_ADDR) != address) { + if (i2c_smbus_read_byte_data + (client, W83781D_REG_I2C_ADDR) != address) { dev_dbg(&adapter->dev, "Detection of w83781d " "chip failed at step 5\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } } } /* We have either had a force parameter, or we have already detected the Winbond. Put it now into bank 0 and Vendor ID High Byte */ - w83781d_write_value(data, W83781D_REG_BANK, - (w83781d_read_value(data, W83781D_REG_BANK) - & 0x78) | 0x80); + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + (i2c_smbus_read_byte_data(client, W83781D_REG_BANK) + & 0x78) | 0x80); /* Determine the chip type. */ if (kind <= 0) { /* get vendor ID */ - val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); if (val2 == 0x5c) vendid = winbond; else if (val2 == 0x12) @@ -1196,11 +1118,10 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) else { dev_dbg(&adapter->dev, "w83781d chip vendor is " "neither Winbond nor Asus\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - val1 = w83781d_read_value(data, W83781D_REG_WCHIPID); + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) kind = w83781d; else if (val1 == 0x30 && vendid == winbond) @@ -1214,19 +1135,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) dev_warn(&adapter->dev, "Ignoring 'force' " "parameter for unknown chip at " "address 0x%02x\n", address); - err = -EINVAL; - goto ERROR2; + goto err_nodev; } if ((kind == w83781d || kind == w83782d) && w83781d_alias_detect(client, val1)) { dev_dbg(&adapter->dev, "Device at 0x%02x appears to " "be the same as ISA device\n", address); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } } + if (isa) + mutex_unlock(&isa->update_lock); + if (kind == w83781d) { client_name = "w83781d"; } else if (kind == w83782d) { @@ -1237,24 +1159,46 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) client_name = "as99127f"; } - /* Fill in the remaining client fields and put into the global list */ - strlcpy(client->name, client_name, I2C_NAME_SIZE); - data->type = kind; + strlcpy(info->type, client_name, I2C_NAME_SIZE); - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(client))) - goto ERROR2; + return 0; + + err_nodev: + if (isa) + mutex_unlock(&isa->update_lock); + return -ENODEV; +} + +static int +w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct w83781d_data *data; + int err; + + data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto ERROR1; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + mutex_init(&data->update_lock); + + data->type = id->driver_data; + data->client = client; /* attach secondary i2c lm75-like clients */ - if ((err = w83781d_detect_subclients(adapter, address, - kind, client))) + err = w83781d_detect_subclients(client); + if (err) goto ERROR3; /* Initialize the chip */ w83781d_init_device(dev); /* Register sysfs hooks */ - err = w83781d_create_files(dev, kind, 0); + err = w83781d_create_files(dev, data->type, 0); if (err) goto ERROR4; @@ -1270,45 +1214,35 @@ ERROR4: sysfs_remove_group(&dev->kobj, &w83781d_group); sysfs_remove_group(&dev->kobj, &w83781d_group_opt); - if (data->lm75[1]) { - i2c_detach_client(data->lm75[1]); - kfree(data->lm75[1]); - } - if (data->lm75[0]) { - i2c_detach_client(data->lm75[0]); - kfree(data->lm75[0]); - } + if (data->lm75[0]) + i2c_unregister_device(data->lm75[0]); + if (data->lm75[1]) + i2c_unregister_device(data->lm75[1]); ERROR3: - i2c_detach_client(client); -ERROR2: + i2c_set_clientdata(client, NULL); kfree(data); ERROR1: return err; } static int -w83781d_detach_client(struct i2c_client *client) +w83781d_remove(struct i2c_client *client) { struct w83781d_data *data = i2c_get_clientdata(client); - int err; + struct device *dev = &client->dev; - /* main client */ - if (data) { - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &w83781d_group); - sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt); - } + hwmon_device_unregister(data->hwmon_dev); - if ((err = i2c_detach_client(client))) - return err; + sysfs_remove_group(&dev->kobj, &w83781d_group); + sysfs_remove_group(&dev->kobj, &w83781d_group_opt); - /* main client */ - if (data) - kfree(data); + if (data->lm75[0]) + i2c_unregister_device(data->lm75[0]); + if (data->lm75[1]) + i2c_unregister_device(data->lm75[1]); - /* subclient */ - else - kfree(client); + i2c_set_clientdata(client, NULL); + kfree(data); return 0; } @@ -1316,7 +1250,7 @@ w83781d_detach_client(struct i2c_client *client) static int w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int res, bank; struct i2c_client *cl; @@ -1356,7 +1290,7 @@ w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) static int w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int bank; struct i2c_client *cl; @@ -1493,7 +1427,7 @@ w83781d_init_device(struct device *dev) static struct w83781d_data *w83781d_update_device(struct device *dev) { struct w83781d_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int i; mutex_lock(&data->update_lock); @@ -1606,6 +1540,30 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) return data; } +static const struct i2c_device_id w83781d_ids[] = { + { "w83781d", w83781d, }, + { "w83782d", w83782d, }, + { "w83783s", w83783s, }, + { "as99127f", as99127f }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, w83781d_ids); + +static struct i2c_driver w83781d_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "w83781d", + }, + .probe = w83781d_probe, + .remove = w83781d_remove, + .id_table = w83781d_ids, + .detect = w83781d_detect, + .address_data = &addr_data, +}; + +/* + * ISA related code + */ #ifdef CONFIG_ISA /* ISA device, if found */ @@ -1631,13 +1589,12 @@ static struct w83781d_data *w83781d_data_if_isa(void) /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) { - struct w83781d_data *i2c, *isa; + struct w83781d_data *isa; int i; if (!pdev) /* No ISA chip */ return 0; - i2c = i2c_get_clientdata(client); isa = platform_get_drvdata(pdev); if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) @@ -1648,14 +1605,16 @@ static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) /* We compare all the limit registers, the config register and the * interrupt mask registers */ for (i = 0x2b; i <= 0x3d; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + if (w83781d_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } if (w83781d_read_value(isa, W83781D_REG_CONFIG) != - w83781d_read_value(i2c, W83781D_REG_CONFIG)) + i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG)) return 0; for (i = 0x43; i <= 0x46; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + if (w83781d_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } @@ -1734,11 +1693,11 @@ w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) static int w83781d_read_value(struct w83781d_data *data, u16 reg) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int res; mutex_lock(&data->lock); - if (client->driver) + if (client) res = w83781d_read_value_i2c(data, reg); else res = w83781d_read_value_isa(data, reg); @@ -1749,10 +1708,10 @@ w83781d_read_value(struct w83781d_data *data, u16 reg) static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; mutex_lock(&data->lock); - if (client->driver) + if (client) w83781d_write_value_i2c(data, reg, value); else w83781d_write_value_isa(data, reg, value); -- cgit v1.2.3 From ff92136131c66a51b820798067e53f05a8f36a24 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (ibmpex) Automatically load on IBM systems via DMI Signed-off-by: Darrick J. Wong Signed-off-by: Jean Delvare --- drivers/hwmon/ibmpex.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 4e9b19c6732..537d9fb2ff8 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -608,3 +608,9 @@ MODULE_LICENSE("GPL"); module_init(ibmpex_init); module_exit(ibmpex_exit); + +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); -- cgit v1.2.3 From 21d93c14b32f1d74ebed09caca7d6c10cf3f952e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (ibmaem) Automatically load on IBM systems via DMI Signed-off-by: Darrick J. Wong Signed-off-by: Jean Delvare --- drivers/hwmon/ibmaem.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 0f70dc20410..7b0ed5dea39 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -1118,3 +1118,10 @@ MODULE_LICENSE("GPL"); module_init(aem_init); module_exit(aem_exit); + +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*"); -- cgit v1.2.3 From d664a4809e73c878a43607d584b2e2b60fd07468 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adt7470) Add documentation Add at least the bare minimum of documentation for this chip. Signed-off-by: Darrick J. Wong Signed-off-by: Jean Delvare --- Documentation/hwmon/adt7470 | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/hwmon/adt7470 diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470 new file mode 100644 index 00000000000..75d13ca147c --- /dev/null +++ b/Documentation/hwmon/adt7470 @@ -0,0 +1,76 @@ +Kernel driver adt7470 +===================== + +Supported chips: + * Analog Devices ADT7470 + Prefix: 'adt7470' + Addresses scanned: I2C 0x2C, 0x2E, 0x2F + Datasheet: Publicly available at the Analog Devices website + +Author: Darrick J. Wong + +Description +----------- + +This driver implements support for the Analog Devices ADT7470 chip. There may +be other chips that implement this interface. + +The ADT7470 uses the 2-wire interface compatible with the SMBus 2.0 +specification. Using an analog to digital converter it measures up to ten (10) +external temperatures. It has four (4) 16-bit counters for measuring fan speed. +There are four (4) PWM outputs that can be used to control fan speed. + +A sophisticated control system for the PWM outputs is designed into the ADT7470 +that allows fan speed to be adjusted automatically based on any of the ten +temperature sensors. Each PWM output is individually adjustable and +programmable. Once configured, the ADT7470 will adjust the PWM outputs in +response to the measured temperatures with further host intervention. This +feature can also be disabled for manual control of the PWM's. + +Each of the measured inputs (temperature, fan speed) has corresponding high/low +limit values. The ADT7470 will signal an ALARM if any measured value exceeds +either limit. + +The ADT7470 DOES NOT sample all inputs continuously. A single pin on the +ADT7470 is connected to a multitude of thermal diodes, but the chip must be +instructed explicitly to read the multitude of diodes. If you want to use +automatic fan control mode, you must manually read any of the temperature +sensors or the fan control algorithm will not run. The chip WILL NOT DO THIS +AUTOMATICALLY; this must be done from userspace. This may be a bug in the chip +design, given that many other AD chips take care of this. The driver will not +read the registers more often than once every 5 seconds. Further, +configuration data is only read once per minute. + +Special Features +---------------- + +The ADT7470 has a 8-bit ADC and is capable of measuring temperatures with 1 +degC resolution. + +The Analog Devices datasheet is very detailed and describes a procedure for +determining an optimal configuration for the automatic PWM control. + +Configuration Notes +------------------- + +Besides standard interfaces driver adds the following: + +* PWM Control + +* pwm#_auto_point1_pwm and pwm#_auto_point1_temp and +* pwm#_auto_point2_pwm and pwm#_auto_point2_temp - + +point1: Set the pwm speed at a lower temperature bound. +point2: Set the pwm speed at a higher temperature bound. + +The ADT7470 will scale the pwm between the lower and higher pwm speed when +the temperature is between the two temperature boundaries. PWM values range +from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the +temperature sensor associated with the PWM control exceeds +pwm#_auto_point2_temp. + +Notes +----- + +As stated above, the temperature inputs must be read periodically from +userspace in order for the automatic pwm algorithm to run. -- cgit v1.2.3 From 366716e6aabfb6f7c65525cc1637e035bfaf422d Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adm1029) Use mask for fan_div value This is my patch for testing correct values of fan div in adm1029 and prevent a division by 0 for some (unlikely) register values. Signed-off-by: Corentin Labbe Signed-off-by: Jean Delvare --- drivers/hwmon/adm1029.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index ba84ca5923f..36718150b47 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -179,7 +179,8 @@ show_fan(struct device *dev, struct device_attribute *devattr, char *buf) struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); u16 val; - if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0 + if (data->fan[attr->index] == 0 + || (data->fan_div[attr->index] & 0xC0) == 0 || data->fan[attr->index] == 255) { return sprintf(buf, "0\n"); } @@ -194,7 +195,7 @@ show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); - if (data->fan_div[attr->index] == 0) + if ((data->fan_div[attr->index] & 0xC0) == 0) return sprintf(buf, "0\n"); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index])); } -- cgit v1.2.3 From 885fe4b1f621fa2c8499e222059b59dbc98583e2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adm1026) Fix debug messages * Add missing new-line to one debug message. * Remove leading colon from 3 debug messages. Signed-off-by: Jean Delvare Cc: Philip Pokorny --- drivers/hwmon/adm1026.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 7fe2441fc84..546be058879 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -455,7 +455,7 @@ static void adm1026_print_gpio(struct i2c_client *client) struct adm1026_data *data = i2c_get_clientdata(client); int i; - dev_dbg(&client->dev, "GPIO config is:"); + dev_dbg(&client->dev, "GPIO config is:\n"); for (i = 0;i <= 7;++i) { if (data->config2 & (1 << i)) { dev_dbg(&client->dev, "\t%sGP%s%d\n", @@ -1681,17 +1681,16 @@ static int adm1026_detect(struct i2c_client *client, int kind, kind = adm1026; } else if (company == ADM1026_COMPANY_ANALOG_DEV && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, ": Unrecognized stepping " + dev_err(&adapter->dev, "Unrecognized stepping " "0x%02x. Defaulting to ADM1026.\n", verstep); kind = adm1026; } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, ": Found version/stepping " + dev_err(&adapter->dev, "Found version/stepping " "0x%02x. Assuming generic ADM1026.\n", verstep); kind = any_chip; } else { - dev_dbg(&adapter->dev, ": Autodetection " - "failed\n"); + dev_dbg(&adapter->dev, "Autodetection failed\n"); /* Not an ADM1026 ... */ if (kind == 0) { /* User used force=x,y */ dev_err(&adapter->dev, "Generic ADM1026 not " -- cgit v1.2.3 From a0cf354a71bd2969b2f1868530d3fecaebd6dc3d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adm1026) Prevent log spamming When debugging is enabled, the adm1026 driver currently logs the message "Setting VID from GPIO11-15" 108 times each time you run "sensors". Once should be enough. Signed-off-by: Jean Delvare Cc: Philip Pokorny --- drivers/hwmon/adm1026.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 546be058879..ff7de40b6e3 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -279,7 +279,6 @@ struct adm1026_data { u8 fan_min[8]; /* Register value */ u8 fan_div[8]; /* Decoded value */ struct pwm_data pwm1; /* Pwm control values */ - int vid; /* Decoded value */ u8 vrm; /* VRM version */ u8 analog_out; /* Register value (DAC) */ long alarms; /* Register encoding, combined */ @@ -697,8 +696,6 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) data->last_config = jiffies; }; /* last_config */ - dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n"); - data->vid = (data->gpio >> 11) & 0x1f; data->valid = 1; mutex_unlock(&data->update_lock); return data; @@ -1215,7 +1212,10 @@ static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf, "%d\n", vid_from_reg(data->vid & 0x3f, data->vrm)); + int vid = (data->gpio >> 11) & 0x1f; + + dev_dbg(dev, "Setting VID from GPIO11-15.\n"); + return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -- cgit v1.2.3