From 10f2ed31aae11040dfd64cee10c47db79b4b4647 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 27 May 2010 19:58:38 +0200 Subject: hwmon: (lm63) Add basic support for LM64 The LM64 appears to be an LM63 with added GPIO lines. Add support for the hwmon functionality - GPIO can be added at some later stage if someone has a need for them. Signed-off-by: Matthew Garrett Signed-off-by: Jean Delvare --- Documentation/hwmon/lm63 | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm63 b/Documentation/hwmon/lm63 index 31660bf9797..b9843eab1af 100644 --- a/Documentation/hwmon/lm63 +++ b/Documentation/hwmon/lm63 @@ -7,6 +7,11 @@ Supported chips: Addresses scanned: I2C 0x4c Datasheet: Publicly available at the National Semiconductor website http://www.national.com/pf/LM/LM63.html + * National Semiconductor LM64 + Prefix: 'lm64' + Addresses scanned: I2C 0x18 and 0x4e + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM64.html Author: Jean Delvare @@ -55,3 +60,5 @@ The lm63 driver will not update its values more frequently than every second; reading them more often will do no harm, but will return 'old' values. +The LM64 is effectively an LM63 with GPIO lines. The driver does not +support these GPIO lines at present. -- cgit v1.2.3-70-g09d2 From d2b847d489ee4e9921bc61f47c3d0e992692fc0f Mon Sep 17 00:00:00 2001 From: "Ira W. Snyder" Date: Thu, 27 May 2010 19:58:45 +0200 Subject: hwmon: Add description of the update_rate sysfs attribute The update_rate attribute can be used by drivers to let userspace choose the update rate of the chip, if it is configurable. Signed-off-by: Ira W. Snyder Signed-off-by: Jean Delvare --- Documentation/hwmon/sysfs-interface | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 3de6b0bcb14..d4e2917c6f1 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -80,9 +80,9 @@ All entries (except name) are optional, and should only be created in a given driver if the chip has the feature. -******** -* Name * -******** +********************* +* Global attributes * +********************* name The chip name. This should be a short, lowercase string, not containing @@ -91,6 +91,13 @@ name The chip name. I2C devices get this attribute created automatically. RO +update_rate The rate at which the chip will update readings. + Unit: millisecond + RW + Some devices have a variable update rate. This attribute + can be used to change the update rate to the desired + frequency. + ************ * Voltages * -- cgit v1.2.3-70-g09d2 From beb1b6bbf2b448b97b9611200eb4b5a555336c60 Mon Sep 17 00:00:00 2001 From: Steven King Date: Thu, 27 May 2010 19:58:56 +0200 Subject: hwmon: Driver for TI TMP102 temperature sensor Driver for the TI TMP102. The TI TMP102 is similar to the LM75. It differs from the LM75 by having a 16-bit conf register and the temp registers have a minimum resolution of 12 bits; the extended conf register can select 13-bit resolution (which this driver does) and also change the update rate (which this driver currently doesn't use). [JD: Fix tmp102_exit tag, must be __exit, not __init.] Signed-off-by: Steven King Signed-off-by: Jean Delvare --- Documentation/hwmon/tmp102 | 27 +++++ drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/tmp102.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 335 insertions(+) create mode 100644 Documentation/hwmon/tmp102 create mode 100644 drivers/hwmon/tmp102.c (limited to 'Documentation') diff --git a/Documentation/hwmon/tmp102 b/Documentation/hwmon/tmp102 new file mode 100644 index 00000000000..239dded8539 --- /dev/null +++ b/Documentation/hwmon/tmp102 @@ -0,0 +1,27 @@ +Kernel driver tmp102 +==================== + +Supported chips: + * Texas Instruments TMP102 + Prefix: 'tmp102' + Addresses scanned: I2C 0x48 0x49 0x4a 0x4b + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html + +Author: + Steven King + +Description +----------- + +The Texas Instruments TMP102 implements one temperature sensor. Limits can be +set through the Overtemperature Shutdown register and Hysteresis register. The +sensor is accurate to 0.5 degrees over the range of -25 to +85 C, and to 1.0 +degrees from -40 to +125 C. Resolution of the sensor is 0.0625 degree. The +operating temperature has a minimum of -55 C and a maximum of +150 C. + +The TMP102 has a programmable update rate that can select between 8, 4, 1, and +0.5 Hz. (Currently the driver only supports the default of 4 Hz). + +The driver provides the common sysfs-interface for temperatures (see +/Documentation/hwmon/sysfs-interface under Temperatures). + diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6dddad81281..6d87892a663 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -842,6 +842,16 @@ config SENSORS_THMC50 This driver can also be built as a module. If so, the module will be called thmc50. +config SENSORS_TMP102 + tristate "Texas Instruments TMP102 and compatibles" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for Texas Instruments TMP102 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called tmp102. + config SENSORS_TMP401 tristate "Texas Instruments TMP401 and compatibles" depends on I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 9b1364a4bd8..2138ceb1a71 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o obj-$(CONFIG_SENSORS_THMC50) += thmc50.o +obj-$(CONFIG_SENSORS_TMP102) += tmp102.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c new file mode 100644 index 00000000000..e4def62c0eb --- /dev/null +++ b/drivers/hwmon/tmp102.c @@ -0,0 +1,297 @@ +/* Texas Instruments TMP102 SMBUS temperature sensor driver + * + * Copyright 2010 Steven King + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "tmp102" + +#define TMP102_TEMP_REG 0x00 +#define TMP102_CONF_REG 0x01 +/* note: these bit definitions are byte swapped */ +#define TMP102_CONF_SD 0x0100 +#define TMP102_CONF_TM 0x0200 +#define TMP102_CONF_POL 0x0400 +#define TMP102_CONF_F0 0x0800 +#define TMP102_CONF_F1 0x1000 +#define TMP102_CONF_R0 0x2000 +#define TMP102_CONF_R1 0x4000 +#define TMP102_CONF_OS 0x8000 +#define TMP102_CONF_EM 0x0010 +#define TMP102_CONF_AL 0x0020 +#define TMP102_CONF_CR0 0x0040 +#define TMP102_CONF_CR1 0x0080 +#define TMP102_TLOW_REG 0x02 +#define TMP102_THIGH_REG 0x03 + +struct tmp102 { + struct device *hwmon_dev; + struct mutex lock; + unsigned long last_update; + int temp[3]; +}; + +/* the TMP102 registers are big endian so we have to swab16 the values */ +static int tmp102_read_reg(struct i2c_client *client, u8 reg) +{ + int result = i2c_smbus_read_word_data(client, reg); + return result < 0 ? result : swab16(result); +} + +static int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val) +{ + return i2c_smbus_write_word_data(client, reg, swab16(val)); +} + +/* convert left adjusted 13bit TMP102 register value to miliCelsius */ +static int tmp102_reg_to_mC(s16 val) +{ + return (val * 1000) / 128; +} + +/* convert miliCelsius to left adjusted 13bit TMP102 register value */ +static u16 tmp102_mC_to_reg(int val) +{ + return (val * 128) / 1000; +} + +static const u8 tmp102_reg[] = { + TMP102_TEMP_REG, + TMP102_TLOW_REG, + TMP102_THIGH_REG, +}; + +static struct tmp102 *tmp102_update_device(struct i2c_client *client) +{ + struct tmp102 *tmp102 = i2c_get_clientdata(client); + + mutex_lock(&tmp102->lock); + if (time_after(jiffies, tmp102->last_update + HZ / 4)) { + int i; + for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) { + int status = tmp102_read_reg(client, tmp102_reg[i]); + if (status > -1) + tmp102->temp[i] = tmp102_reg_to_mC(status); + } + tmp102->last_update = jiffies; + } + mutex_unlock(&tmp102->lock); + return tmp102; +} + +static ssize_t tmp102_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct tmp102 *tmp102 = tmp102_update_device(to_i2c_client(dev)); + + return sprintf(buf, "%d\n", tmp102->temp[sda->index]); +} + +static ssize_t tmp102_set_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct tmp102 *tmp102 = i2c_get_clientdata(client); + long val; + int status = 0; + + if ((strict_strtol(buf, 10, &val) < 0) || (abs(val) > 150000)) + return -EINVAL; + mutex_lock(&tmp102->lock); + if (tmp102->temp[sda->index] != val) { + tmp102->temp[sda->index] = val; + status = tmp102_write_reg(client, tmp102_reg[sda->index], + tmp102_mC_to_reg(val)); + } + mutex_unlock(&tmp102->lock); + return status ? : count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL , 0); + +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp, + tmp102_set_temp, 1); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp, + tmp102_set_temp, 2); + +static struct attribute *tmp102_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + NULL +}; + +static const struct attribute_group tmp102_attr_group = { + .attrs = tmp102_attributes, +}; + +#define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1) +#define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL) + +static int __devinit tmp102_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tmp102 *tmp102; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_dbg(&client->dev, "adapter doesnt support SMBUS\n"); + return -ENODEV; + } + + tmp102 = kzalloc(sizeof(*tmp102), GFP_KERNEL); + if (!tmp102) { + dev_dbg(&client->dev, "kzalloc failed\n"); + return -ENOMEM; + } + i2c_set_clientdata(client, tmp102); + + tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG); + status = tmp102_read_reg(client, TMP102_CONF_REG); + if (status < 0) { + dev_dbg(&client->dev, "error reading config register\n"); + goto fail0; + } + status &= ~TMP102_CONFIG_RD_ONLY; + if (status != TMP102_CONFIG) { + dev_dbg(&client->dev, "could not verify config settings\n"); + status = -EIO; + goto fail0; + } + tmp102->last_update = jiffies - HZ; + mutex_init(&tmp102->lock); + + status = sysfs_create_group(&client->dev.kobj, &tmp102_attr_group); + if (status) { + dev_dbg(&client->dev, "could not create sysfs files\n"); + goto fail0; + } + tmp102->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(tmp102->hwmon_dev)) { + dev_dbg(&client->dev, "unable to register hwmon device\n"); + status = PTR_ERR(tmp102->hwmon_dev); + goto fail1; + } + + dev_info(&client->dev, "initialized\n"); + + return 0; +fail1: + sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group); +fail0: + i2c_set_clientdata(client, NULL); + kfree(tmp102); + + return 0; +} + +static int __devexit tmp102_remove(struct i2c_client *client) +{ + struct tmp102 *tmp102 = i2c_get_clientdata(client); + + /* shutdown the chip */ + tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONF_SD); + + hwmon_device_unregister(tmp102->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group); + i2c_set_clientdata(client, NULL); + kfree(tmp102); + + return 0; +} + +#ifdef CONFIG_PM +static int tmp102_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONF_SD); + + return 0; +} + +static int tmp102_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG); + + return 0; +} + +static const struct dev_pm_ops tmp102_dev_pm_ops = { + .suspend = tmp102_suspend, + .resume = tmp102_resume, +}; + +#define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops) +#else +#define TMP102_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static const unsigned short normal_i2c[] = { + 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END +}; + +static const struct i2c_device_id tmp102_id[] = { + { DRIVER_NAME, 0 }, + { } +}; + +static struct i2c_driver tmp102_driver = { + .driver.name = DRIVER_NAME, + .driver.pm = TMP102_DEV_PM_OPS, + .class = I2C_CLASS_HWMON, + .probe = tmp102_probe, + .remove = __devexit_p(tmp102_remove), + .id_table = tmp102_id, + .address_list = normal_i2c, +}; + +static int __init tmp102_init(void) +{ + return i2c_add_driver(&tmp102_driver); +} +module_init(tmp102_init); + +static void __exit tmp102_exit(void) +{ + i2c_del_driver(&tmp102_driver); +} +module_exit(tmp102_exit); + + +MODULE_AUTHOR("Steven King "); +MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From cff37c9e82e022068840b3d33167e64c6a0ecc06 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 27 May 2010 19:58:57 +0200 Subject: hwmon: (tmp102) Various fixes Fixes from my driver review: http://lists.lm-sensors.org/pipermail/lm-sensors/2010-March/028051.html Only the small changes are in there, more important changes will come later separately as time permits. * Drop the remnants of the now gone detect function * The TMP102 has no known compatible chip * Include the right header files * Clarify why byte swapping of register values is needed * Strip resolution info bit from temperature register value * Set cache lifetime to 1/3 second * Don't arbitrarily reject limit values; clamp as needed * Make limit writing unconditional * Don't check for transaction types the driver doesn't use * Properly check for error when setting configuration * Report error on failed probe * Make the driver load automatically where needed * Various other minor fixes Signed-off-by: Jean Delvare Cc: Steven King --- Documentation/hwmon/tmp102 | 9 +++--- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/tmp102.c | 70 ++++++++++++++++++++++------------------------ 3 files changed, 39 insertions(+), 42 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/tmp102 b/Documentation/hwmon/tmp102 index 239dded8539..8454a776312 100644 --- a/Documentation/hwmon/tmp102 +++ b/Documentation/hwmon/tmp102 @@ -4,7 +4,7 @@ Kernel driver tmp102 Supported chips: * Texas Instruments TMP102 Prefix: 'tmp102' - Addresses scanned: I2C 0x48 0x49 0x4a 0x4b + Addresses scanned: none Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html Author: @@ -15,13 +15,12 @@ Description The Texas Instruments TMP102 implements one temperature sensor. Limits can be set through the Overtemperature Shutdown register and Hysteresis register. The -sensor is accurate to 0.5 degrees over the range of -25 to +85 C, and to 1.0 -degrees from -40 to +125 C. Resolution of the sensor is 0.0625 degree. The +sensor is accurate to 0.5 degree over the range of -25 to +85 C, and to 1.0 +degree from -40 to +125 C. Resolution of the sensor is 0.0625 degree. The operating temperature has a minimum of -55 C and a maximum of +150 C. The TMP102 has a programmable update rate that can select between 8, 4, 1, and 0.5 Hz. (Currently the driver only supports the default of 4 Hz). The driver provides the common sysfs-interface for temperatures (see -/Documentation/hwmon/sysfs-interface under Temperatures). - +Documentation/hwmon/sysfs-interface under Temperatures). diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6d87892a663..de22ae41f75 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -843,7 +843,7 @@ config SENSORS_THMC50 will be called thmc50. config SENSORS_TMP102 - tristate "Texas Instruments TMP102 and compatibles" + tristate "Texas Instruments TMP102" depends on I2C && EXPERIMENTAL help If you say yes here you get support for Texas Instruments TMP102 diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index e4def62c0eb..e9de28df0e4 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -1,6 +1,6 @@ -/* Texas Instruments TMP102 SMBUS temperature sensor driver +/* Texas Instruments TMP102 SMBus temperature sensor driver * - * Copyright 2010 Steven King + * Copyright (C) 2010 Steven King * * 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 @@ -17,8 +17,6 @@ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA */ - - #include #include #include @@ -27,7 +25,7 @@ #include #include #include -#include +#include #define DRIVER_NAME "tmp102" @@ -56,26 +54,27 @@ struct tmp102 { int temp[3]; }; -/* the TMP102 registers are big endian so we have to swab16 the values */ -static int tmp102_read_reg(struct i2c_client *client, u8 reg) +/* SMBus specifies low byte first, but the TMP102 returns high byte first, + * so we have to swab16 the values */ +static inline int tmp102_read_reg(struct i2c_client *client, u8 reg) { int result = i2c_smbus_read_word_data(client, reg); return result < 0 ? result : swab16(result); } -static int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val) +static inline int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val) { return i2c_smbus_write_word_data(client, reg, swab16(val)); } -/* convert left adjusted 13bit TMP102 register value to miliCelsius */ -static int tmp102_reg_to_mC(s16 val) +/* convert left adjusted 13-bit TMP102 register value to milliCelsius */ +static inline int tmp102_reg_to_mC(s16 val) { - return (val * 1000) / 128; + return ((val & ~0x01) * 1000) / 128; } -/* convert miliCelsius to left adjusted 13bit TMP102 register value */ -static u16 tmp102_mC_to_reg(int val) +/* convert milliCelsius to left adjusted 13-bit TMP102 register value */ +static inline u16 tmp102_mC_to_reg(int val) { return (val * 128) / 1000; } @@ -91,7 +90,7 @@ static struct tmp102 *tmp102_update_device(struct i2c_client *client) struct tmp102 *tmp102 = i2c_get_clientdata(client); mutex_lock(&tmp102->lock); - if (time_after(jiffies, tmp102->last_update + HZ / 4)) { + if (time_after(jiffies, tmp102->last_update + HZ / 3)) { int i; for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) { int status = tmp102_read_reg(client, tmp102_reg[i]); @@ -122,16 +121,16 @@ static ssize_t tmp102_set_temp(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct tmp102 *tmp102 = i2c_get_clientdata(client); long val; - int status = 0; + int status; - if ((strict_strtol(buf, 10, &val) < 0) || (abs(val) > 150000)) + if (strict_strtol(buf, 10, &val) < 0) return -EINVAL; + val = SENSORS_LIMIT(val, -256000, 255000); + mutex_lock(&tmp102->lock); - if (tmp102->temp[sda->index] != val) { - tmp102->temp[sda->index] = val; - status = tmp102_write_reg(client, tmp102_reg[sda->index], - tmp102_mC_to_reg(val)); - } + tmp102->temp[sda->index] = val; + status = tmp102_write_reg(client, tmp102_reg[sda->index], + tmp102_mC_to_reg(val)); mutex_unlock(&tmp102->lock); return status ? : count; } @@ -164,9 +163,10 @@ static int __devinit tmp102_probe(struct i2c_client *client, struct tmp102 *tmp102; int status; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_dbg(&client->dev, "adapter doesnt support SMBUS\n"); + dev_err(&client->dev, "adapter doesnt support SMBus word " + "transactions\n"); return -ENODEV; } @@ -177,16 +177,20 @@ static int __devinit tmp102_probe(struct i2c_client *client, } i2c_set_clientdata(client, tmp102); - tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG); + status = tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG); + if (status < 0) { + dev_err(&client->dev, "error writing config register\n"); + goto fail0; + } status = tmp102_read_reg(client, TMP102_CONF_REG); if (status < 0) { - dev_dbg(&client->dev, "error reading config register\n"); + dev_err(&client->dev, "error reading config register\n"); goto fail0; } status &= ~TMP102_CONFIG_RD_ONLY; if (status != TMP102_CONFIG) { - dev_dbg(&client->dev, "could not verify config settings\n"); - status = -EIO; + dev_err(&client->dev, "config settings did not stick\n"); + status = -ENODEV; goto fail0; } tmp102->last_update = jiffies - HZ; @@ -213,7 +217,7 @@ fail0: i2c_set_clientdata(client, NULL); kfree(tmp102); - return 0; + return status; } static int __devexit tmp102_remove(struct i2c_client *client) @@ -260,23 +264,18 @@ static const struct dev_pm_ops tmp102_dev_pm_ops = { #define TMP102_DEV_PM_OPS NULL #endif /* CONFIG_PM */ -static const unsigned short normal_i2c[] = { - 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END -}; - static const struct i2c_device_id tmp102_id[] = { - { DRIVER_NAME, 0 }, + { "tmp102", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, tmp102_id); static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, .driver.pm = TMP102_DEV_PM_OPS, - .class = I2C_CLASS_HWMON, .probe = tmp102_probe, .remove = __devexit_p(tmp102_remove), .id_table = tmp102_id, - .address_list = normal_i2c, }; static int __init tmp102_init(void) @@ -291,7 +290,6 @@ static void __exit tmp102_exit(void) } module_exit(tmp102_exit); - MODULE_AUTHOR("Steven King "); MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From ea694431f9c862bd409c90ba1cb3bdc6fdde8635 Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Thu, 27 May 2010 19:59:01 +0200 Subject: hwmon: (dme1737) Add SCH5127 support Add support for the hardware monitoring capabilities of the SCH5127 chip to the dme1737 driver. Signed-off-by: Juerg Haefliger Signed-off-by: Jean Delvare Tested-by: Jeff Rickman --- Documentation/hwmon/dme1737 | 51 +++++-- drivers/hwmon/dme1737.c | 328 ++++++++++++++++++++++++++++++-------------- 2 files changed, 266 insertions(+), 113 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/dme1737 b/Documentation/hwmon/dme1737 index 001d2e70bc1..fc5df7654d6 100644 --- a/Documentation/hwmon/dme1737 +++ b/Documentation/hwmon/dme1737 @@ -9,11 +9,15 @@ Supported chips: * SMSC SCH3112, SCH3114, SCH3116 Prefix: 'sch311x' Addresses scanned: none, address read from Super-I/O config space - Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf + Datasheet: Available on the Internet * SMSC SCH5027 Prefix: 'sch5027' Addresses scanned: I2C 0x2c, 0x2d, 0x2e Datasheet: Provided by SMSC upon request and under NDA + * SMSC SCH5127 + Prefix: 'sch5127' + Addresses scanned: none, address read from Super-I/O config space + Datasheet: Provided by SMSC upon request and under NDA Authors: Juerg Haefliger @@ -36,8 +40,8 @@ Description ----------- This driver implements support for the hardware monitoring capabilities of the -SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, and SMSC -SCH311x Super-I/O chips. These chips feature monitoring of 3 temp sensors +SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, SCH311x, +and SCH5127 Super-I/O chips. These chips feature monitoring of 3 temp sensors temp[1-3] (2 remote diodes and 1 internal), 7 voltages in[0-6] (6 external and 1 internal) and up to 6 fan speeds fan[1-6]. Additionally, the chips implement up to 5 PWM outputs pwm[1-3,5-6] for controlling fan speeds both manually and @@ -48,14 +52,14 @@ Fan[3-6] and pwm[3,5-6] are optional features and their availability depends on the configuration of the chip. The driver will detect which features are present during initialization and create the sysfs attributes accordingly. -For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and -pwm[5-6] don't exist. +For the SCH311x and SCH5127, fan[1-3] and pwm[1-3] are always present and +fan[4-6] and pwm[5-6] don't exist. The hardware monitoring features of the DME1737, A8000, and SCH5027 are only -accessible via SMBus, while the SCH311x only provides access via the ISA bus. -The driver will therefore register itself as an I2C client driver if it detects -a DME1737, A8000, or SCH5027 and as a platform driver if it detects a SCH311x -chip. +accessible via SMBus, while the SCH311x and SCH5127 only provide access via +the ISA bus. The driver will therefore register itself as an I2C client driver +if it detects a DME1737, A8000, or SCH5027 and as a platform driver if it +detects a SCH311x or SCH5127 chip. Voltage Monitoring @@ -76,7 +80,7 @@ DME1737, A8000: in6: Vbat (+3.0V) 0V - 4.38V SCH311x: - in0: +2.5V 0V - 6.64V + in0: +2.5V 0V - 3.32V in1: Vccp (processor core) 0V - 2V in2: VCC (internal +3.3V) 0V - 4.38V in3: +5V 0V - 6.64V @@ -93,6 +97,15 @@ SCH5027: in5: VTR (+3.3V standby) 0V - 4.38V in6: Vbat (+3.0V) 0V - 4.38V +SCH5127: + in0: +2.5 0V - 3.32V + in1: Vccp (processor core) 0V - 3V + in2: VCC (internal +3.3V) 0V - 4.38V + in3: V2_IN 0V - 1.5V + in4: V1_IN 0V - 1.5V + in5: VTR (+3.3V standby) 0V - 4.38V + in6: Vbat (+3.0V) 0V - 4.38V + Each voltage input has associated min and max limits which trigger an alarm when crossed. @@ -293,3 +306,21 @@ pwm[1-3]_auto_point1_pwm RW Auto PWM pwm point. Auto_point1 is the pwm[1-3]_auto_point2_pwm RO Auto PWM pwm point. Auto_point2 is the full-speed duty-cycle which is hard- wired to 255 (100% duty-cycle). + +Chip Differences +---------------- + +Feature dme1737 sch311x sch5027 sch5127 +------------------------------------------------------- +temp[1-3]_offset yes yes +vid yes +zone3 yes yes yes +zone[1-3]_hyst yes yes +pwm min/off yes yes +fan3 opt yes opt yes +pwm3 opt yes opt yes +fan4 opt opt +fan5 opt opt +pwm5 opt opt +fan6 opt opt +pwm6 opt opt diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 823dd28a902..980c17d5eea 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -1,12 +1,14 @@ /* - * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x and - * SCH5027 Super-I/O chips integrated hardware monitoring features. - * Copyright (c) 2007, 2008 Juerg Haefliger + * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x, SCH5027, + * and SCH5127 Super-I/O chips integrated hardware monitoring + * features. + * Copyright (c) 2007, 2008, 2009, 2010 Juerg Haefliger * * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access * the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus - * if a SCH311x chip is found. Both types of chips have very similar hardware - * monitoring capabilities but differ in the way they can be accessed. + * if a SCH311x or SCH5127 chip is found. Both types of chips have very + * similar hardware monitoring capabilities but differ in the way they can be + * accessed. * * 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 @@ -57,7 +59,7 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC " /* Addresses to scan */ static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END}; -enum chips { dme1737, sch5027, sch311x }; +enum chips { dme1737, sch5027, sch311x, sch5127 }; /* --------------------------------------------------------------------- * Registers @@ -164,10 +166,29 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23}; #define DME1737_VERSTEP_MASK 0xf8 #define SCH311X_DEVICE 0x8c #define SCH5027_VERSTEP 0x69 +#define SCH5127_DEVICE 0x8e + +/* Device ID values (global configuration register index 0x20) */ +#define DME1737_ID_1 0x77 +#define DME1737_ID_2 0x78 +#define SCH3112_ID 0x7c +#define SCH3114_ID 0x7d +#define SCH3116_ID 0x7f +#define SCH5027_ID 0x89 +#define SCH5127_ID 0x86 /* Length of ISA address segment */ #define DME1737_EXTENT 2 +/* chip-dependent features */ +#define HAS_TEMP_OFFSET (1 << 0) /* bit 0 */ +#define HAS_VID (1 << 1) /* bit 1 */ +#define HAS_ZONE3 (1 << 2) /* bit 2 */ +#define HAS_ZONE_HYST (1 << 3) /* bit 3 */ +#define HAS_PWM_MIN (1 << 4) /* bit 4 */ +#define HAS_FAN(ix) (1 << ((ix) + 5)) /* bits 5-10 */ +#define HAS_PWM(ix) (1 << ((ix) + 11)) /* bits 11-16 */ + /* --------------------------------------------------------------------- * Data structures and manipulation thereof * --------------------------------------------------------------------- */ @@ -187,8 +208,7 @@ struct dme1737_data { u8 vid; u8 pwm_rr_en; - u8 has_pwm; - u8 has_fan; + u32 has_features; /* Register values */ u16 in[7]; @@ -224,8 +244,11 @@ static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300, 3300}; static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300, 3300}; +static const int IN_NOMINAL_SCH5127[] = {2500, 2250, 3300, 1125, 1125, 3300, + 3300}; #define IN_NOMINAL(type) ((type) == sch311x ? IN_NOMINAL_SCH311x : \ (type) == sch5027 ? IN_NOMINAL_SCH5027 : \ + (type) == sch5127 ? IN_NOMINAL_SCH5127 : \ IN_NOMINAL_DME1737) /* Voltage input @@ -568,7 +591,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Sample register contents every 1 sec */ if (time_after(jiffies, data->last_update + HZ) || !data->valid) { - if (data->type == dme1737) { + if (data->has_features & HAS_VID) { data->vid = dme1737_read(data, DME1737_REG_VID) & 0x3f; } @@ -599,7 +622,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) DME1737_REG_TEMP_MIN(ix)); data->temp_max[ix] = dme1737_read(data, DME1737_REG_TEMP_MAX(ix)); - if (data->type != sch5027) { + if (data->has_features & HAS_TEMP_OFFSET) { data->temp_offset[ix] = dme1737_read(data, DME1737_REG_TEMP_OFFSET(ix)); } @@ -626,7 +649,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) { /* Skip reading registers if optional fans are not * present */ - if (!(data->has_fan & (1 << ix))) { + if (!(data->has_features & HAS_FAN(ix))) { continue; } data->fan[ix] = dme1737_read(data, @@ -650,7 +673,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) for (ix = 0; ix < ARRAY_SIZE(data->pwm); ix++) { /* Skip reading registers if optional PWMs are not * present */ - if (!(data->has_pwm & (1 << ix))) { + if (!(data->has_features & HAS_PWM(ix))) { continue; } data->pwm[ix] = dme1737_read(data, @@ -672,12 +695,24 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Thermal zone registers */ for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) { - data->zone_low[ix] = dme1737_read(data, - DME1737_REG_ZONE_LOW(ix)); - data->zone_abs[ix] = dme1737_read(data, - DME1737_REG_ZONE_ABS(ix)); + /* Skip reading registers if zone3 is not present */ + if ((ix == 2) && !(data->has_features & HAS_ZONE3)) { + continue; + } + /* sch5127 zone2 registers are special */ + if ((ix == 1) && (data->type == sch5127)) { + data->zone_low[1] = dme1737_read(data, + DME1737_REG_ZONE_LOW(2)); + data->zone_abs[1] = dme1737_read(data, + DME1737_REG_ZONE_ABS(2)); + } else { + data->zone_low[ix] = dme1737_read(data, + DME1737_REG_ZONE_LOW(ix)); + data->zone_abs[ix] = dme1737_read(data, + DME1737_REG_ZONE_ABS(ix)); + } } - if (data->type != sch5027) { + if (data->has_features & HAS_ZONE_HYST) { for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) { data->zone_hyst[ix] = dme1737_read(data, DME1737_REG_ZONE_HYST(ix)); @@ -1594,10 +1629,6 @@ static struct attribute *dme1737_attr[] ={ &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr, NULL }; @@ -1605,27 +1636,23 @@ static const struct attribute_group dme1737_group = { .attrs = dme1737_attr, }; -/* The following struct holds misc attributes, which are not available in all - * chips. Their creation depends on the chip type which is determined during - * module load. */ -static struct attribute *dme1737_misc_attr[] = { - /* Temperatures */ +/* The following struct holds temp offset attributes, which are not available + * in all chips. The following chips support them: + * DME1737, SCH311x */ +static struct attribute *dme1737_temp_offset_attr[] = { &sensor_dev_attr_temp1_offset.dev_attr.attr, &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp3_offset.dev_attr.attr, - /* Zones */ - &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr, - &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr, NULL }; -static const struct attribute_group dme1737_misc_group = { - .attrs = dme1737_misc_attr, +static const struct attribute_group dme1737_temp_offset_group = { + .attrs = dme1737_temp_offset_attr, }; -/* The following struct holds VID-related attributes. Their creation - depends on the chip type which is determined during module load. */ +/* The following struct holds VID related attributes, which are not available + * in all chips. The following chips support them: + * DME1737 */ static struct attribute *dme1737_vid_attr[] = { &dev_attr_vrm.attr, &dev_attr_cpu0_vid.attr, @@ -1636,6 +1663,36 @@ static const struct attribute_group dme1737_vid_group = { .attrs = dme1737_vid_attr, }; +/* The following struct holds temp zone 3 related attributes, which are not + * available in all chips. The following chips support them: + * DME1737, SCH311x, SCH5027 */ +static struct attribute *dme1737_zone3_attr[] = { + &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr, + NULL +}; + +static const struct attribute_group dme1737_zone3_group = { + .attrs = dme1737_zone3_attr, +}; + + +/* The following struct holds temp zone hysteresis related attributes, which + * are not available in all chips. The following chips support them: + * DME1737, SCH311x */ +static struct attribute *dme1737_zone_hyst_attr[] = { + &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group dme1737_zone_hyst_group = { + .attrs = dme1737_zone_hyst_attr, +}; + /* The following structs hold the PWM attributes, some of which are optional. * Their creation depends on the chip configuration which is determined during * module load. */ @@ -1691,10 +1748,10 @@ static const struct attribute_group dme1737_pwm_group[] = { { .attrs = dme1737_pwm6_attr }, }; -/* The following struct holds misc PWM attributes, which are not available in - * all chips. Their creation depends on the chip type which is determined +/* The following struct holds auto PWM min attributes, which are not available + * in all chips. Their creation depends on the chip type which is determined * during module load. */ -static struct attribute *dme1737_pwm_misc_attr[] = { +static struct attribute *dme1737_auto_pwm_min_attr[] = { &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr, @@ -1764,14 +1821,25 @@ static struct attribute *dme1737_zone_chmod_attr[] = { &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr, + NULL +}; + +static const struct attribute_group dme1737_zone_chmod_group = { + .attrs = dme1737_zone_chmod_attr, +}; + + +/* The permissions of the following zone 3 attributes are changed to read- + * writeable if the chip is *not* locked. Otherwise they stay read-only. */ +static struct attribute *dme1737_zone3_chmod_attr[] = { &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, NULL }; -static const struct attribute_group dme1737_zone_chmod_group = { - .attrs = dme1737_zone_chmod_attr, +static const struct attribute_group dme1737_zone3_chmod_group = { + .attrs = dme1737_zone3_chmod_attr, }; /* The permissions of the following PWM attributes are changed to read- @@ -1887,30 +1955,35 @@ static void dme1737_remove_files(struct device *dev) int ix; for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) { - if (data->has_fan & (1 << ix)) { + if (data->has_features & HAS_FAN(ix)) { sysfs_remove_group(&dev->kobj, &dme1737_fan_group[ix]); } } for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) { - if (data->has_pwm & (1 << ix)) { + if (data->has_features & HAS_PWM(ix)) { sysfs_remove_group(&dev->kobj, &dme1737_pwm_group[ix]); - if (data->type != sch5027 && ix < 3) { + if ((data->has_features & HAS_PWM_MIN) && ix < 3) { sysfs_remove_file(&dev->kobj, - dme1737_pwm_misc_attr[ix]); + dme1737_auto_pwm_min_attr[ix]); } } } - if (data->type != sch5027) { - sysfs_remove_group(&dev->kobj, &dme1737_misc_group); + if (data->has_features & HAS_TEMP_OFFSET) { + sysfs_remove_group(&dev->kobj, &dme1737_temp_offset_group); } - if (data->type == dme1737) { + if (data->has_features & HAS_VID) { sysfs_remove_group(&dev->kobj, &dme1737_vid_group); } - + if (data->has_features & HAS_ZONE3) { + sysfs_remove_group(&dev->kobj, &dme1737_zone3_group); + } + if (data->has_features & HAS_ZONE_HYST) { + sysfs_remove_group(&dev->kobj, &dme1737_zone_hyst_group); + } sysfs_remove_group(&dev->kobj, &dme1737_group); if (!data->client) { @@ -1934,23 +2007,31 @@ static int dme1737_create_files(struct device *dev) goto exit_remove; } - /* Create misc sysfs attributes */ - if ((data->type != sch5027) && + /* Create chip-dependent sysfs attributes */ + if ((data->has_features & HAS_TEMP_OFFSET) && (err = sysfs_create_group(&dev->kobj, - &dme1737_misc_group))) { + &dme1737_temp_offset_group))) { goto exit_remove; } - - /* Create VID-related sysfs attributes */ - if ((data->type == dme1737) && + if ((data->has_features & HAS_VID) && (err = sysfs_create_group(&dev->kobj, &dme1737_vid_group))) { goto exit_remove; } + if ((data->has_features & HAS_ZONE3) && + (err = sysfs_create_group(&dev->kobj, + &dme1737_zone3_group))) { + goto exit_remove; + } + if ((data->has_features & HAS_ZONE_HYST) && + (err = sysfs_create_group(&dev->kobj, + &dme1737_zone_hyst_group))) { + goto exit_remove; + } /* Create fan sysfs attributes */ for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) { - if (data->has_fan & (1 << ix)) { + if (data->has_features & HAS_FAN(ix)) { if ((err = sysfs_create_group(&dev->kobj, &dme1737_fan_group[ix]))) { goto exit_remove; @@ -1960,14 +2041,14 @@ static int dme1737_create_files(struct device *dev) /* Create PWM sysfs attributes */ for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) { - if (data->has_pwm & (1 << ix)) { + if (data->has_features & HAS_PWM(ix)) { if ((err = sysfs_create_group(&dev->kobj, &dme1737_pwm_group[ix]))) { goto exit_remove; } - if (data->type != sch5027 && ix < 3 && + if ((data->has_features & HAS_PWM_MIN) && ix < 3 && (err = sysfs_create_file(&dev->kobj, - dme1737_pwm_misc_attr[ix]))) { + dme1737_auto_pwm_min_attr[ix]))) { goto exit_remove; } } @@ -1983,21 +2064,30 @@ static int dme1737_create_files(struct device *dev) dme1737_chmod_group(dev, &dme1737_zone_chmod_group, S_IRUGO | S_IWUSR); - /* Change permissions of misc sysfs attributes */ - if (data->type != sch5027) { - dme1737_chmod_group(dev, &dme1737_misc_group, + /* Change permissions of chip-dependent sysfs attributes */ + if (data->has_features & HAS_TEMP_OFFSET) { + dme1737_chmod_group(dev, &dme1737_temp_offset_group, + S_IRUGO | S_IWUSR); + } + if (data->has_features & HAS_ZONE3) { + dme1737_chmod_group(dev, &dme1737_zone3_chmod_group, + S_IRUGO | S_IWUSR); + } + if (data->has_features & HAS_ZONE_HYST) { + dme1737_chmod_group(dev, &dme1737_zone_hyst_group, S_IRUGO | S_IWUSR); } /* Change permissions of PWM sysfs attributes */ for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) { - if (data->has_pwm & (1 << ix)) { + if (data->has_features & HAS_PWM(ix)) { dme1737_chmod_group(dev, &dme1737_pwm_chmod_group[ix], S_IRUGO | S_IWUSR); - if (data->type != sch5027 && ix < 3) { + if ((data->has_features & HAS_PWM_MIN) && + ix < 3) { dme1737_chmod_file(dev, - dme1737_pwm_misc_attr[ix], + dme1737_auto_pwm_min_attr[ix], S_IRUGO | S_IWUSR); } } @@ -2005,7 +2095,7 @@ static int dme1737_create_files(struct device *dev) /* Change permissions of pwm[1-3] if in manual mode */ for (ix = 0; ix < 3; ix++) { - if ((data->has_pwm & (1 << ix)) && + if ((data->has_features & HAS_PWM(ix)) && (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) { dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix], @@ -2052,20 +2142,20 @@ static int dme1737_init_device(struct device *dev) return -EFAULT; } - /* Determine which optional fan and pwm features are enabled/present */ + /* Determine which optional fan and pwm features are enabled (only + * valid for I2C devices) */ if (client) { /* I2C chip */ data->config2 = dme1737_read(data, DME1737_REG_CONFIG2); /* Check if optional fan3 input is enabled */ if (data->config2 & 0x04) { - data->has_fan |= (1 << 2); + data->has_features |= HAS_FAN(2); } /* Fan4 and pwm3 are only available if the client's I2C address * is the default 0x2e. Otherwise the I/Os associated with * these functions are used for addr enable/select. */ if (client->addr == 0x2e) { - data->has_fan |= (1 << 3); - data->has_pwm |= (1 << 2); + data->has_features |= HAS_FAN(3) | HAS_PWM(2); } /* Determine which of the optional fan[5-6] and pwm[5-6] @@ -2077,26 +2167,40 @@ static int dme1737_init_device(struct device *dev) dev_warn(dev, "Failed to query Super-IO for optional " "features.\n"); } - } else { /* ISA chip */ - /* Fan3 and pwm3 are always available. Fan[4-5] and pwm[5-6] - * don't exist in the ISA chip. */ - data->has_fan |= (1 << 2); - data->has_pwm |= (1 << 2); } - /* Fan1, fan2, pwm1, and pwm2 are always present */ - data->has_fan |= 0x03; - data->has_pwm |= 0x03; + /* Fan[1-2] and pwm[1-2] are present in all chips */ + data->has_features |= HAS_FAN(0) | HAS_FAN(1) | HAS_PWM(0) | HAS_PWM(1); + + /* Chip-dependent features */ + switch (data->type) { + case dme1737: + data->has_features |= HAS_TEMP_OFFSET | HAS_VID | HAS_ZONE3 | + HAS_ZONE_HYST | HAS_PWM_MIN; + break; + case sch311x: + data->has_features |= HAS_TEMP_OFFSET | HAS_ZONE3 | + HAS_ZONE_HYST | HAS_PWM_MIN | HAS_FAN(2) | HAS_PWM(2); + break; + case sch5027: + data->has_features |= HAS_ZONE3; + break; + case sch5127: + data->has_features |= HAS_FAN(2) | HAS_PWM(2); + break; + default: + break; + } dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, " "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n", - (data->has_pwm & (1 << 2)) ? "yes" : "no", - (data->has_pwm & (1 << 4)) ? "yes" : "no", - (data->has_pwm & (1 << 5)) ? "yes" : "no", - (data->has_fan & (1 << 2)) ? "yes" : "no", - (data->has_fan & (1 << 3)) ? "yes" : "no", - (data->has_fan & (1 << 4)) ? "yes" : "no", - (data->has_fan & (1 << 5)) ? "yes" : "no"); + (data->has_features & HAS_PWM(2)) ? "yes" : "no", + (data->has_features & HAS_PWM(4)) ? "yes" : "no", + (data->has_features & HAS_PWM(5)) ? "yes" : "no", + (data->has_features & HAS_FAN(2)) ? "yes" : "no", + (data->has_features & HAS_FAN(3)) ? "yes" : "no", + (data->has_features & HAS_FAN(4)) ? "yes" : "no", + (data->has_features & HAS_FAN(5)) ? "yes" : "no"); reg = dme1737_read(data, DME1737_REG_TACH_PWM); /* Inform if fan-to-pwm mapping differs from the default */ @@ -2122,7 +2226,7 @@ static int dme1737_init_device(struct device *dev) for (ix = 0; ix < 3; ix++) { data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); - if ((data->has_pwm & (1 << ix)) && + if ((data->has_features & HAS_PWM(ix)) && (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) { dev_info(dev, "Switching pwm%d to " "manual mode.\n", ix + 1); @@ -2142,7 +2246,7 @@ static int dme1737_init_device(struct device *dev) data->pwm_acz[2] = 4; /* pwm3 -> zone3 */ /* Set VRM */ - if (data->type == dme1737) { + if (data->has_features & HAS_VID) { data->vrm = vid_which_vrm(); } @@ -2163,10 +2267,10 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data) dme1737_sio_enter(sio_cip); /* Check device ID - * The DME1737 can return either 0x78 or 0x77 as its device ID. - * The SCH5027 returns 0x89 as its device ID. */ + * We currently know about two kinds of DME1737 and SCH5027. */ reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20); - if (!(reg == 0x77 || reg == 0x78 || reg == 0x89)) { + if (!(reg == DME1737_ID_1 || reg == DME1737_ID_2 || + reg == SCH5027_ID)) { err = -ENODEV; goto exit; } @@ -2185,16 +2289,16 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data) * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set * to '10' if the respective feature is enabled. */ if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */ - data->has_fan |= (1 << 5); + data->has_features |= HAS_FAN(5); } if ((inb(addr + 0x44) & 0x0c) == 0x08) { /* pwm6 */ - data->has_pwm |= (1 << 5); + data->has_features |= HAS_PWM(5); } if ((inb(addr + 0x45) & 0x0c) == 0x08) { /* fan5 */ - data->has_fan |= (1 << 4); + data->has_features |= HAS_FAN(4); } if ((inb(addr + 0x46) & 0x0c) == 0x08) { /* pwm5 */ - data->has_pwm |= (1 << 4); + data->has_features |= HAS_PWM(4); } exit: @@ -2222,7 +2326,6 @@ static int dme1737_i2c_detect(struct i2c_client *client, if (company == DME1737_COMPANY_SMSC && verstep == SCH5027_VERSTEP) { name = "sch5027"; - } else if (company == DME1737_COMPANY_SMSC && (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { name = "dme1737"; @@ -2329,10 +2432,10 @@ static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr) dme1737_sio_enter(sio_cip); /* Check device ID - * We currently know about SCH3112 (0x7c), SCH3114 (0x7d), and - * SCH3116 (0x7f). */ + * We currently know about SCH3112, SCH3114, SCH3116, and SCH5127 */ reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20); - if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) { + if (!(reg == SCH3112_ID || reg == SCH3114_ID || reg == SCH3116_ID || + reg == SCH5127_ID)) { err = -ENODEV; goto exit; } @@ -2424,23 +2527,42 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); /* Skip chip detection if module is loaded with force_id parameter */ - if (!force_id) { + switch (force_id) { + case SCH3112_ID: + case SCH3114_ID: + case SCH3116_ID: + data->type = sch311x; + break; + case SCH5127_ID: + data->type = sch5127; + break; + default: company = dme1737_read(data, DME1737_REG_COMPANY); device = dme1737_read(data, DME1737_REG_DEVICE); - if (!((company == DME1737_COMPANY_SMSC) && - (device == SCH311X_DEVICE))) { + if ((company == DME1737_COMPANY_SMSC) && + (device == SCH311X_DEVICE)) { + data->type = sch311x; + } else if ((company == DME1737_COMPANY_SMSC) && + (device == SCH5127_DEVICE)) { + data->type = sch5127; + } else { err = -ENODEV; goto exit_kfree; } } - data->type = sch311x; - /* Fill in the remaining client fields and initialize the mutex */ - data->name = "sch311x"; + if (data->type == sch5127) { + data->name = "sch5127"; + } else { + data->name = "sch311x"; + } + + /* Initialize the mutex */ mutex_init(&data->update_lock); - dev_info(dev, "Found a SCH311x chip at 0x%04x\n", data->addr); + dev_info(dev, "Found a %s chip at 0x%04x\n", + data->type == sch5127 ? "SCH5127" : "SCH311x", data->addr); /* Initialize the chip */ if ((err = dme1737_init_device(dev))) { -- cgit v1.2.3-70-g09d2 From df16dd53c575d0cb9dbee20a3149927c862a9ff6 Mon Sep 17 00:00:00 2001 From: "Ira W. Snyder" Date: Thu, 27 May 2010 19:59:02 +0200 Subject: hwmon: (ltc4245) Read only one GPIO pin Read only one of the GPIO pins as an analog voltage. The ADC can be switched to a different GPIO pin at runtime, but this is not supported. Previously, this driver would report the analog voltage of the currently selected GPIO pin as all three GPIO voltages: in9_input, in10_input and in11_input. Signed-off-by: Ira W. Snyder Signed-off-by: Jean Delvare Cc: stable@kernel.org --- Documentation/hwmon/ltc4245 | 4 +--- drivers/hwmon/ltc4245.c | 18 +++++------------- 2 files changed, 6 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/ltc4245 b/Documentation/hwmon/ltc4245 index 02838a47d86..86b5880d850 100644 --- a/Documentation/hwmon/ltc4245 +++ b/Documentation/hwmon/ltc4245 @@ -72,9 +72,7 @@ in6_min_alarm 5v output undervoltage alarm in7_min_alarm 3v output undervoltage alarm in8_min_alarm Vee (-12v) output undervoltage alarm -in9_input GPIO #1 voltage data -in10_input GPIO #2 voltage data -in11_input GPIO #3 voltage data +in9_input GPIO voltage data power1_input 12v power usage (mW) power2_input 5v power usage (mW) diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 65c232a9d0c..21d201befc2 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -45,9 +45,7 @@ enum ltc4245_cmd { LTC4245_VEEIN = 0x19, LTC4245_VEESENSE = 0x1a, LTC4245_VEEOUT = 0x1b, - LTC4245_GPIOADC1 = 0x1c, - LTC4245_GPIOADC2 = 0x1d, - LTC4245_GPIOADC3 = 0x1e, + LTC4245_GPIOADC = 0x1c, }; struct ltc4245_data { @@ -61,7 +59,7 @@ struct ltc4245_data { u8 cregs[0x08]; /* Voltage registers */ - u8 vregs[0x0f]; + u8 vregs[0x0d]; }; static struct ltc4245_data *ltc4245_update_device(struct device *dev) @@ -86,7 +84,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) data->cregs[i] = val; } - /* Read voltage registers -- 0x10 to 0x1f */ + /* Read voltage registers -- 0x10 to 0x1c */ for (i = 0; i < ARRAY_SIZE(data->vregs); i++) { val = i2c_smbus_read_byte_data(client, i+0x10); if (unlikely(val < 0)) @@ -128,9 +126,7 @@ static int ltc4245_get_voltage(struct device *dev, u8 reg) case LTC4245_VEEOUT: voltage = regval * -55; break; - case LTC4245_GPIOADC1: - case LTC4245_GPIOADC2: - case LTC4245_GPIOADC3: + case LTC4245_GPIOADC: voltage = regval * 10; break; default: @@ -297,9 +293,7 @@ LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2); LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); /* GPIO voltages */ -LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC1); -LTC4245_VOLTAGE(in10_input, LTC4245_GPIOADC2); -LTC4245_VOLTAGE(in11_input, LTC4245_GPIOADC3); +LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC); /* Power Consumption (virtual) */ LTC4245_POWER(power1_input, LTC4245_12VSENSE); @@ -342,8 +336,6 @@ static struct attribute *ltc4245_attributes[] = { &sensor_dev_attr_in8_min_alarm.dev_attr.attr, &sensor_dev_attr_in9_input.dev_attr.attr, - &sensor_dev_attr_in10_input.dev_attr.attr, - &sensor_dev_attr_in11_input.dev_attr.attr, &sensor_dev_attr_power1_input.dev_attr.attr, &sensor_dev_attr_power2_input.dev_attr.attr, -- cgit v1.2.3-70-g09d2