diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-06 11:33:09 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-06 11:33:09 -0800 |
commit | dff6d1c5ef9116a4478908001d72ee67127ecf01 (patch) | |
tree | 43c6e7ff8e8059f8fd733efd45b51f84b510d305 /drivers/hwmon | |
parent | 66ce3cf84deba6cc71dcf43c9d56a4278e5f712d (diff) | |
parent | a0a5e3488a51c46f383c5faa86b53828e30ce153 (diff) | |
download | linux-dff6d1c5ef9116a4478908001d72ee67127ecf01.tar.bz2 |
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: (23 commits)
hwmon: Remove the deprecated adt7473 driver
hwmon: Fix off-by-one kind values
hwmon: (tmp421) Fix temperature conversions
hwmon: (tmp421) Restore missing inputs
hwmon: Driver for Andigilog aSC7621 family monitoring chips
hwmon: (adt7411) Improve locking
hwmon: Add driver for ADT7411 voltage and temperature sensor
hwmon: (w83793) Add watchdog functionality
hwmon: (g760a) Make rpm_from_cnt static
hwmon: (it87) Validate auto pwm settings
hwmon: (it87) Add support for old automatic fan speed control
hwmon: (it87) Drop dead web links in documentation
hwmon: (it87) Add an entry in MAINTAINERS
hwmon: (it87) Use strict_strtol instead of simple_strtol
hwmon: (it87) Fix many checkpatch errors and warnings
hwmon: (it87) Add support for beep on alarm
hwmon: (it87) Create vid attributes by group
hwmon: (it87) Refactor attributes creation and removal
hwmon: (it87) Expose the PWM/temperature mappings
hwmon: (it87) Display fan outputs in automatic mode as such
...
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 45 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 3 | ||||
-rw-r--r-- | drivers/hwmon/adt7411.c | 366 | ||||
-rw-r--r-- | drivers/hwmon/adt7473.c | 1180 | ||||
-rw-r--r-- | drivers/hwmon/asc7621.c | 1255 | ||||
-rw-r--r-- | drivers/hwmon/fschmd.c | 15 | ||||
-rw-r--r-- | drivers/hwmon/g760a.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/it87.c | 939 | ||||
-rw-r--r-- | drivers/hwmon/lm90.c | 89 | ||||
-rw-r--r-- | drivers/hwmon/tmp401.c | 7 | ||||
-rw-r--r-- | drivers/hwmon/tmp421.c | 24 | ||||
-rw-r--r-- | drivers/hwmon/w83793.c | 482 |
12 files changed, 2923 insertions, 1484 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 68cf87749a42..e4595e6147b4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -170,6 +170,16 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_ADT7411 + tristate "Analog Devices ADT7411" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Analog Devices + ADT7411 voltage and temperature monitoring chip. + + This driver can also be built as a module. If so, the module + will be called adt7411. + config SENSORS_ADT7462 tristate "Analog Devices ADT7462" depends on I2C && EXPERIMENTAL @@ -190,20 +200,6 @@ config SENSORS_ADT7470 This driver can also be built as a module. If so, the module will be called adt7470. -config SENSORS_ADT7473 - tristate "Analog Devices ADT7473 (DEPRECATED)" - depends on I2C && EXPERIMENTAL - select SENSORS_ADT7475 - help - If you say yes here you get support for the Analog Devices - ADT7473 temperature monitoring chips. - - This driver is deprecated, you should use the adt7475 driver - instead. - - This driver can also be built as a module. If so, the module - will be called adt7473. - config SENSORS_ADT7475 tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490" depends on I2C && EXPERIMENTAL @@ -216,6 +212,19 @@ config SENSORS_ADT7475 This driver can also be build as a module. If so, the module will be called adt7475. +config SENSORS_ASC7621 + tristate "Andigilog aSC7621" + depends on HWMON && I2C + help + If you say yes here you get support for the aSC7621 + family of SMBus sensors chip found on most Intel X48, X38, 975, + 965 and 945 desktop boards. Currently supported chips: + aSC7621 + aSC7621a + + This driver can also be built as a module. If so, the module + will be called asc7621. + config SENSORS_K8TEMP tristate "AMD Athlon64/FX or Opteron temperature sensor" depends on X86 && PCI && EXPERIMENTAL @@ -563,9 +572,10 @@ 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 ADT7461, and Maxim + LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, - MAX6680, MAX6681 and MAX6692 sensor chips. + MAX6680, MAX6681 and MAX6692, and Winbond/Nuvoton W83L771AWG/ASG + sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -909,7 +919,8 @@ config SENSORS_W83793 select HWMON_VID help If you say yes here you get support for the Winbond W83793 - hardware monitoring chip. + hardware monitoring chip, including support for the integrated + watchdog. This driver can also be built as a module. If so, the module will be called w83793. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4bc215c0953f..4aa1a3d112ad 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -29,12 +29,13 @@ obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o +obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o -obj-$(CONFIG_SENSORS_ADT7473) += adt7473.o obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_AMS) += ams/ +obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c new file mode 100644 index 000000000000..3471884e42d2 --- /dev/null +++ b/drivers/hwmon/adt7411.c @@ -0,0 +1,366 @@ +/* + * Driver for the ADT7411 (I2C/SPI 8 channel 10 bit ADC & temperature-sensor) + * + * Copyright (C) 2008, 2010 Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: SPI, support for external temperature sensor + * use power-down mode for suspend?, interrupt handling? + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#define ADT7411_REG_INT_TEMP_VDD_LSB 0x03 +#define ADT7411_REG_EXT_TEMP_AIN14_LSB 0x04 +#define ADT7411_REG_VDD_MSB 0x06 +#define ADT7411_REG_INT_TEMP_MSB 0x07 +#define ADT7411_REG_EXT_TEMP_AIN1_MSB 0x08 + +#define ADT7411_REG_CFG1 0x18 +#define ADT7411_CFG1_START_MONITOR (1 << 0) + +#define ADT7411_REG_CFG2 0x19 +#define ADT7411_CFG2_DISABLE_AVG (1 << 5) + +#define ADT7411_REG_CFG3 0x1a +#define ADT7411_CFG3_ADC_CLK_225 (1 << 0) +#define ADT7411_CFG3_REF_VDD (1 << 4) + +#define ADT7411_REG_DEVICE_ID 0x4d +#define ADT7411_REG_MANUFACTURER_ID 0x4e + +#define ADT7411_DEVICE_ID 0x2 +#define ADT7411_MANUFACTURER_ID 0x41 + +static const unsigned short normal_i2c[] = { 0x48, 0x4a, 0x4b, I2C_CLIENT_END }; + +struct adt7411_data { + struct mutex device_lock; /* for "atomic" device accesses */ + struct mutex update_lock; + unsigned long next_update; + int vref_cached; + struct device *hwmon_dev; +}; + +/* + * When reading a register containing (up to 4) lsb, all associated + * msb-registers get locked by the hardware. After _one_ of those msb is read, + * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb + * is protected here with a mutex, too. + */ +static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg, + u8 msb_reg, u8 lsb_shift) +{ + struct adt7411_data *data = i2c_get_clientdata(client); + int val, tmp; + + mutex_lock(&data->device_lock); + + val = i2c_smbus_read_byte_data(client, lsb_reg); + if (val < 0) + goto exit_unlock; + + tmp = (val >> lsb_shift) & 3; + val = i2c_smbus_read_byte_data(client, msb_reg); + + if (val >= 0) + val = (val << 2) | tmp; + + exit_unlock: + mutex_unlock(&data->device_lock); + + return val; +} + +static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit, + bool flag) +{ + struct adt7411_data *data = i2c_get_clientdata(client); + int ret, val; + + mutex_lock(&data->device_lock); + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + goto exit_unlock; + + if (flag) + val = ret | bit; + else + val = ret & ~bit; + + ret = i2c_smbus_write_byte_data(client, reg, val); + + exit_unlock: + mutex_unlock(&data->device_lock); + return ret; +} + +static ssize_t adt7411_show_vdd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, + ADT7411_REG_VDD_MSB, 2); + + return ret < 0 ? ret : sprintf(buf, "%u\n", ret * 7000 / 1024); +} + +static ssize_t adt7411_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, + ADT7411_REG_INT_TEMP_MSB, 0); + + if (val < 0) + return val; + + val = val & 0x200 ? val - 0x400 : val; /* 10 bit signed */ + + return sprintf(buf, "%d\n", val * 250); +} + +static ssize_t adt7411_show_input(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct adt7411_data *data = i2c_get_clientdata(client); + int val; + u8 lsb_reg, lsb_shift; + + mutex_lock(&data->update_lock); + if (time_after_eq(jiffies, data->next_update)) { + val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3); + if (val < 0) + goto exit_unlock; + + if (val & ADT7411_CFG3_REF_VDD) { + val = adt7411_read_10_bit(client, + ADT7411_REG_INT_TEMP_VDD_LSB, + ADT7411_REG_VDD_MSB, 2); + if (val < 0) + goto exit_unlock; + + data->vref_cached = val * 7000 / 1024; + } else { + data->vref_cached = 2250; + } + + data->next_update = jiffies + HZ; + } + + lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2); + lsb_shift = 2 * (nr & 0x03); + val = adt7411_read_10_bit(client, lsb_reg, + ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift); + if (val < 0) + goto exit_unlock; + + val = sprintf(buf, "%u\n", val * data->vref_cached / 1024); + exit_unlock: + mutex_unlock(&data->update_lock); + return val; +} + +static ssize_t adt7411_show_bit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + int ret = i2c_smbus_read_byte_data(client, attr2->index); + + return ret < 0 ? ret : sprintf(buf, "%u\n", !!(ret & attr2->nr)); +} + +static ssize_t adt7411_set_bit(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute_2 *s_attr2 = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7411_data *data = i2c_get_clientdata(client); + int ret; + unsigned long flag; + + ret = strict_strtoul(buf, 0, &flag); + if (ret || flag > 1) + return -EINVAL; + + ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); + + /* force update */ + mutex_lock(&data->update_lock); + data->next_update = jiffies; + mutex_unlock(&data->update_lock); + + return ret < 0 ? ret : count; +} + +#define ADT7411_BIT_ATTR(__name, __reg, __bit) \ + SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \ + adt7411_set_bit, __bit, __reg) + +static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL); +static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, adt7411_show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, adt7411_show_input, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, adt7411_show_input, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, adt7411_show_input, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, adt7411_show_input, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, adt7411_show_input, NULL, 7); +static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG); +static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225); +static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD); + +static struct attribute *adt7411_attrs[] = { + &dev_attr_temp1_input.attr, + &dev_attr_in0_input.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_no_average.dev_attr.attr, + &sensor_dev_attr_fast_sampling.dev_attr.attr, + &sensor_dev_attr_adc_ref_vdd.dev_attr.attr, + NULL +}; + +static const struct attribute_group adt7411_attr_grp = { + .attrs = adt7411_attrs, +}; + +static int adt7411_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int val; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID); + if (val < 0 || val != ADT7411_MANUFACTURER_ID) { + dev_dbg(&client->dev, "Wrong manufacturer ID. Got %d, " + "expected %d\n", val, ADT7411_MANUFACTURER_ID); + return -ENODEV; + } + + val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID); + if (val < 0 || val != ADT7411_DEVICE_ID) { + dev_dbg(&client->dev, "Wrong device ID. Got %d, " + "expected %d\n", val, ADT7411_DEVICE_ID); + return -ENODEV; + } + + strlcpy(info->type, "adt7411", I2C_NAME_SIZE); + + return 0; +} + +static int __devinit adt7411_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adt7411_data *data; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->device_lock); + mutex_init(&data->update_lock); + + ret = adt7411_modify_bit(client, ADT7411_REG_CFG1, + ADT7411_CFG1_START_MONITOR, 1); + if (ret < 0) + goto exit_free; + + /* force update on first occasion */ + data->next_update = jiffies; + + ret = sysfs_create_group(&client->dev.kobj, &adt7411_attr_grp); + if (ret) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "successfully registered\n"); + + return 0; + + exit_remove: + sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp); + exit_free: + i2c_set_clientdata(client, NULL); + kfree(data); + return ret; +} + +static int __devexit adt7411_remove(struct i2c_client *client) +{ + struct adt7411_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp); + i2c_set_clientdata(client, NULL); + kfree(data); + return 0; +} + +static const struct i2c_device_id adt7411_id[] = { + { "adt7411", 0 }, + { } +}; + +static struct i2c_driver adt7411_driver = { + .driver = { + .name = "adt7411", + }, + .probe = adt7411_probe, + .remove = __devexit_p(adt7411_remove), + .id_table = adt7411_id, + .detect = adt7411_detect, + .address_list = normal_i2c, + .class = I2C_CLASS_HWMON, +}; + +static int __init sensors_adt7411_init(void) +{ + return i2c_add_driver(&adt7411_driver); +} +module_init(sensors_adt7411_init) + +static void __exit sensors_adt7411_exit(void) +{ + i2c_del_driver(&adt7411_driver); +} +module_exit(sensors_adt7411_exit) + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de> and " + "Wolfram Sang <w.sang@pengutronix.de>"); +MODULE_DESCRIPTION("ADT7411 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c deleted file mode 100644 index 434576f61c84..000000000000 --- a/drivers/hwmon/adt7473.c +++ /dev/null @@ -1,1180 +0,0 @@ -/* - * A hwmon driver for the Analog Devices ADT7473 - * Copyright (C) 2007 IBM - * - * Author: Darrick J. Wong <djwong@us.ibm.com> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/module.h> -#include <linux/jiffies.h> -#include <linux/i2c.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/delay.h> -#include <linux/log2.h> - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, I2C_CLIENT_END }; - -/* ADT7473 registers */ -#define ADT7473_REG_BASE_ADDR 0x20 - -#define ADT7473_REG_VOLT_BASE_ADDR 0x21 -#define ADT7473_REG_VOLT_MIN_BASE_ADDR 0x46 - -#define ADT7473_REG_TEMP_BASE_ADDR 0x25 -#define ADT7473_REG_TEMP_LIMITS_BASE_ADDR 0x4E -#define ADT7473_REG_TEMP_TMIN_BASE_ADDR 0x67 -#define ADT7473_REG_TEMP_TMAX_BASE_ADDR 0x6A - -#define ADT7473_REG_FAN_BASE_ADDR 0x28 -#define ADT7473_REG_FAN_MIN_BASE_ADDR 0x54 - -#define ADT7473_REG_PWM_BASE_ADDR 0x30 -#define ADT7473_REG_PWM_MIN_BASE_ADDR 0x64 -#define ADT7473_REG_PWM_MAX_BASE_ADDR 0x38 -#define ADT7473_REG_PWM_BHVR_BASE_ADDR 0x5C -#define ADT7473_PWM_BHVR_MASK 0xE0 -#define ADT7473_PWM_BHVR_SHIFT 5 - -#define ADT7473_REG_CFG1 0x40 -#define ADT7473_CFG1_START 0x01 -#define ADT7473_CFG1_READY 0x04 -#define ADT7473_REG_CFG2 0x73 -#define ADT7473_REG_CFG3 0x78 -#define ADT7473_REG_CFG4 0x7D -#define ADT7473_CFG4_MAX_DUTY_AT_OVT 0x08 -#define ADT7473_REG_CFG5 0x7C -#define ADT7473_CFG5_TEMP_TWOS 0x01 -#define ADT7473_CFG5_TEMP_OFFSET 0x02 - -#define ADT7473_REG_DEVICE 0x3D -#define ADT7473_VENDOR 0x41 -#define ADT7473_REG_VENDOR 0x3E -#define ADT7473_DEVICE 0x73 -#define ADT7473_REG_REVISION 0x3F -#define ADT7473_REV_68 0x68 -#define ADT7473_REV_69 0x69 - -#define ADT7473_REG_ALARM1 0x41 -#define ADT7473_VCCP_ALARM 0x02 -#define ADT7473_VCC_ALARM 0x04 -#define ADT7473_R1T_ALARM 0x10 -#define ADT7473_LT_ALARM 0x20 -#define ADT7473_R2T_ALARM 0x40 -#define ADT7473_OOL 0x80 -#define ADT7473_REG_ALARM2 0x42 -#define ADT7473_OVT_ALARM 0x02 -#define ADT7473_FAN1_ALARM 0x04 -#define ADT7473_FAN2_ALARM 0x08 -#define ADT7473_FAN3_ALARM 0x10 -#define ADT7473_FAN4_ALARM 0x20 -#define ADT7473_R1T_SHORT 0x40 -#define ADT7473_R2T_SHORT 0x80 - -#define ALARM2(x) ((x) << 8) - -#define ADT7473_VOLT_COUNT 2 -#define ADT7473_REG_VOLT(x) (ADT7473_REG_VOLT_BASE_ADDR + (x)) -#define ADT7473_REG_VOLT_MIN(x) (ADT7473_REG_VOLT_MIN_BASE_ADDR + ((x) * 2)) -#define ADT7473_REG_VOLT_MAX(x) (ADT7473_REG_VOLT_MIN_BASE_ADDR + \ - ((x) * 2) + 1) - -#define ADT7473_TEMP_COUNT 3 -#define ADT7473_REG_TEMP(x) (ADT7473_REG_TEMP_BASE_ADDR + (x)) -#define ADT7473_REG_TEMP_MIN(x) (ADT7473_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2)) -#define ADT7473_REG_TEMP_MAX(x) (ADT7473_REG_TEMP_LIMITS_BASE_ADDR + \ - ((x) * 2) + 1) -#define ADT7473_REG_TEMP_TMIN(x) (ADT7473_REG_TEMP_TMIN_BASE_ADDR + (x)) -#define ADT7473_REG_TEMP_TMAX(x) (ADT7473_REG_TEMP_TMAX_BASE_ADDR + (x)) - -#define ADT7473_FAN_COUNT 4 -#define ADT7473_REG_FAN(x) (ADT7473_REG_FAN_BASE_ADDR + ((x) * 2)) -#define ADT7473_REG_FAN_MIN(x) (ADT7473_REG_FAN_MIN_BASE_ADDR + ((x) * 2)) - -#define ADT7473_PWM_COUNT 3 -#define ADT7473_REG_PWM(x) (ADT7473_REG_PWM_BASE_ADDR + (x)) -#define ADT7473_REG_PWM_MAX(x) (ADT7473_REG_PWM_MAX_BASE_ADDR + (x)) -#define ADT7473_REG_PWM_MIN(x) (ADT7473_REG_PWM_MIN_BASE_ADDR + (x)) -#define ADT7473_REG_PWM_BHVR(x) (ADT7473_REG_PWM_BHVR_BASE_ADDR + (x)) - -/* How often do we reread sensors values? (In jiffies) */ -#define SENSOR_REFRESH_INTERVAL (2 * HZ) - -/* How often do we reread sensor limit values? (In jiffies) */ -#define LIMIT_REFRESH_INTERVAL (60 * HZ) - -/* datasheet says to divide this number by the fan reading to get fan rpm */ -#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) -#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM -#define FAN_PERIOD_INVALID 65535 -#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) - -struct adt7473_data { - struct device *hwmon_dev; - struct attribute_group attrs; - struct mutex lock; - char sensors_valid; - char limits_valid; - unsigned long sensors_last_updated; /* In jiffies */ - unsigned long limits_last_updated; /* In jiffies */ - - u8 volt[ADT7473_VOLT_COUNT]; - s8 volt_min[ADT7473_VOLT_COUNT]; - s8 volt_max[ADT7473_VOLT_COUNT]; - - s8 temp[ADT7473_TEMP_COUNT]; - s8 temp_min[ADT7473_TEMP_COUNT]; - s8 temp_max[ADT7473_TEMP_COUNT]; - s8 temp_tmin[ADT7473_TEMP_COUNT]; - /* This is called the !THERM limit in the datasheet */ - s8 temp_tmax[ADT7473_TEMP_COUNT]; - - u16 fan[ADT7473_FAN_COUNT]; - u16 fan_min[ADT7473_FAN_COUNT]; - - u8 pwm[ADT7473_PWM_COUNT]; - u8 pwm_max[ADT7473_PWM_COUNT]; - u8 pwm_min[ADT7473_PWM_COUNT]; - u8 pwm_behavior[ADT7473_PWM_COUNT]; - - u8 temp_twos_complement; - u8 temp_offset; - - u16 alarm; - u8 max_duty_at_overheat; -}; - -static int adt7473_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int adt7473_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int adt7473_remove(struct i2c_client *client); - -static const struct i2c_device_id adt7473_id[] = { - { "adt7473", 0 }, - { } -}; - -static struct i2c_driver adt7473_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "adt7473", - }, - .probe = adt7473_probe, - .remove = adt7473_remove, - .id_table = adt7473_id, - .detect = adt7473_detect, - .address_list = normal_i2c, -}; - -/* - * 16-bit registers on the ADT7473 are low-byte first. The data sheet says - * that the low byte must be read before the high byte. - */ -static inline int adt7473_read_word_data(struct i2c_client *client, u8 reg) -{ - u16 foo; - foo = i2c_smbus_read_byte_data(client, reg); - foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); - return foo; -} - -static inline int adt7473_write_word_data(struct i2c_client *client, u8 reg, - u16 value) -{ - return i2c_smbus_write_byte_data(client, reg, value & 0xFF) - && i2c_smbus_write_byte_data(client, reg + 1, value >> 8); -} - -static void adt7473_init_client(struct i2c_client *client) -{ - int reg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG1); - - if (!(reg & ADT7473_CFG1_READY)) { - dev_err(&client->dev, "Chip not ready.\n"); - } else { - /* start monitoring */ - i2c_smbus_write_byte_data(client, ADT7473_REG_CFG1, - reg | ADT7473_CFG1_START); - } -} - -static struct adt7473_data *adt7473_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - unsigned long local_jiffies = jiffies; - u8 cfg; - int i; - - mutex_lock(&data->lock); - if (time_before(local_jiffies, data->sensors_last_updated + - SENSOR_REFRESH_INTERVAL) - && data->sensors_valid) - goto no_sensor_update; - - for (i = 0; i < ADT7473_VOLT_COUNT; i++) - data->volt[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_VOLT(i)); - - /* Determine temperature encoding */ - cfg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG5); - data->temp_twos_complement = (cfg & ADT7473_CFG5_TEMP_TWOS); - - /* - * What does this do? it implies a variable temperature sensor - * offset, but the datasheet doesn't say anything about this bit - * and other parts of the datasheet imply that "offset64" mode - * means that you shift temp values by -64 if the above bit was set. - */ - data->temp_offset = (cfg & ADT7473_CFG5_TEMP_OFFSET); - - for (i = 0; i < ADT7473_TEMP_COUNT; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP(i)); - - for (i = 0; i < ADT7473_FAN_COUNT; i++) - data->fan[i] = adt7473_read_word_data(client, - ADT7473_REG_FAN(i)); - - for (i = 0; i < ADT7473_PWM_COUNT; i++) - data->pwm[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM(i)); - - data->alarm = i2c_smbus_read_byte_data(client, ADT7473_REG_ALARM1); - if (data->alarm & ADT7473_OOL) - data->alarm |= ALARM2(i2c_smbus_read_byte_data(client, - ADT7473_REG_ALARM2)); - - data->sensors_last_updated = local_jiffies; - data->sensors_valid = 1; - -no_sensor_update: - if (time_before(local_jiffies, data->limits_last_updated + - LIMIT_REFRESH_INTERVAL) - && data->limits_valid) - goto out; - - for (i = 0; i < ADT7473_VOLT_COUNT; i++) { - data->volt_min[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_VOLT_MIN(i)); - data->volt_max[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_VOLT_MAX(i)); - } - - for (i = 0; i < ADT7473_TEMP_COUNT; i++) { - data->temp_min[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_MIN(i)); - data->temp_max[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_MAX(i)); - data->temp_tmin[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_TMIN(i)); - data->temp_tmax[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_TMAX(i)); - } - - for (i = 0; i < ADT7473_FAN_COUNT; i++) - data->fan_min[i] = adt7473_read_word_data(client, - ADT7473_REG_FAN_MIN(i)); - - for (i = 0; i < ADT7473_PWM_COUNT; i++) { - data->pwm_max[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_MAX(i)); - data->pwm_min[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_MIN(i)); - data->pwm_behavior[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_BHVR(i)); - } - - i = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4); - data->max_duty_at_overheat = !!(i & ADT7473_CFG4_MAX_DUTY_AT_OVT); - - data->limits_last_updated = local_jiffies; - data->limits_valid = 1; - -out: - mutex_unlock(&data->lock); - return data; -} - -/* - * Conversions - */ - -/* IN are scaled acording to built-in resistors */ -static const int adt7473_scaling[] = { /* .001 Volts */ - 2250, 3300 -}; -#define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from)) - -static int decode_volt(int volt_index, u8 raw) -{ - return SCALE(raw, 192, adt7473_scaling[volt_index]); -} - -static u8 encode_volt(int volt_index, int cooked) -{ - int raw = SCALE(cooked, adt7473_scaling[volt_index], 192); - return SENSORS_LIMIT(raw, 0, 255); -} - -static ssize_t show_volt_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", - decode_volt(attr->index, data->volt_min[attr->index])); -} - -static ssize_t set_volt_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long volt; - - if (strict_strtol(buf, 10, &volt)) - return -EINVAL; - - volt = encode_volt(attr->index, volt); - - mutex_lock(&data->lock); - data->volt_min[attr->index] = volt; - i2c_smbus_write_byte_data(client, ADT7473_REG_VOLT_MIN(attr->index), - volt); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_volt_max(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", - decode_volt(attr->index, data->volt_max[attr->index])); -} - -static ssize_t set_volt_max(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long volt; - - if (strict_strtol(buf, 10, &volt)) - return -EINVAL; - - volt = encode_volt(attr->index, volt); - - mutex_lock(&data->lock); - data->volt_max[attr->index] = volt; - i2c_smbus_write_byte_data(client, ADT7473_REG_VOLT_MAX(attr->index), - volt); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_volt(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - return sprintf(buf, "%d\n", - decode_volt(attr->index, data->volt[attr->index])); -} - -/* - * This chip can report temperature data either as a two's complement - * number in the range -128 to 127, or as an unsigned number that must - * be offset by 64. - */ -static int decode_temp(u8 twos_complement, u8 raw) -{ - return twos_complement ? (s8)raw : raw - 64; -} - -static u8 encode_temp(u8 twos_complement, int cooked) -{ - u8 ret = twos_complement ? cooked & 0xFF : cooked + 64; - return SENSORS_LIMIT(ret, 0, 255); -} - -static ssize_t show_temp_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_min[attr->index])); -} - -static ssize_t set_temp_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_MIN(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp_max(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_max[attr->index])); -} - -static ssize_t set_temp_max(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_MAX(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp[attr->index])); -} - -static ssize_t show_fan_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - if (FAN_DATA_VALID(data->fan_min[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan_min[attr->index])); - else - return sprintf(buf, "0\n"); -} - -static ssize_t set_fan_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp) || !temp) - return -EINVAL; - - temp = FAN_RPM_TO_PERIOD(temp); - temp = SENSORS_LIMIT(temp, 1, 65534); - - mutex_lock(&data->lock); - data->fan_min[attr->index] = temp; - adt7473_write_word_data(client, ADT7473_REG_FAN_MIN(attr->index), temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - if (FAN_DATA_VALID(data->fan[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan[attr->index])); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_max_duty_at_crit(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->max_duty_at_overheat); -} - -static ssize_t set_max_duty_at_crit(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - u8 reg; - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - mutex_lock(&data->lock); - data->max_duty_at_overheat = !!temp; - reg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4); - if (temp) - reg |= ADT7473_CFG4_MAX_DUTY_AT_OVT; - else - reg &= ~ADT7473_CFG4_MAX_DUTY_AT_OVT; - i2c_smbus_write_byte_data(client, ADT7473_REG_CFG4, reg); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->pwm[attr->index]); -} - -static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = SENSORS_LIMIT(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM(attr->index), temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_max(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->pwm_max[attr->index]); -} - -static ssize_t set_pwm_max(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = SENSORS_LIMIT(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_MAX(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->pwm_min[attr->index]); -} - -static ssize_t set_pwm_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = SENSORS_LIMIT(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_MIN(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp_tmax(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_tmax[attr->index])); -} - -static ssize_t set_temp_tmax(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_tmax[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_TMAX(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp_tmin(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_tmin[attr->index])); -} - -static ssize_t set_temp_tmin(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_tmin[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_TMIN(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_enable(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - switch (data->pwm_behavior[attr->index] >> ADT7473_PWM_BHVR_SHIFT) { - case 3: - return sprintf(buf, "0\n"); - case 7: - return sprintf(buf, "1\n"); - default: - return sprintf(buf, "2\n"); - } -} - -static ssize_t set_pwm_enable(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - u8 reg; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - switch (temp) { - case 0: - temp = 3; - break; - case 1: - temp = 7; - break; - case 2: - /* Enter automatic mode with fans off */ - temp = 4; - break; - default: - return -EINVAL; - } - - mutex_lock(&data->lock); - reg = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_BHVR(attr->index)); - reg = (temp << ADT7473_PWM_BHVR_SHIFT) | - (reg & ~ADT7473_PWM_BHVR_MASK); - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_BHVR(attr->index), - reg); - data->pwm_behavior[attr->index] = reg; - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_auto_temp(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - int bhvr = data->pwm_behavior[attr->index] >> ADT7473_PWM_BHVR_SHIFT; - - switch (bhvr) { - case 3: - case 4: - case 7: - return sprintf(buf, "0\n"); - case 0: - case 1: - case 5: - case 6: - return sprintf(buf, "%d\n", bhvr + 1); - case 2: - return sprintf(buf, "4\n"); - } - /* shouldn't ever get here */ - BUG(); -} - -static ssize_t set_pwm_auto_temp(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - u8 reg; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - switch (temp) { - case 1: - case 2: - case 6: - case 7: - temp--; - break; - case 0: - temp = 4; - break; - default: - return -EINVAL; - } - - mutex_lock(&data->lock); - reg = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_BHVR(attr->index)); - reg = (temp << ADT7473_PWM_BHVR_SHIFT) | - (reg & ~ADT7473_PWM_BHVR_MASK); - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_BHVR(attr->index), - reg); - data->pwm_behavior[attr->index] = reg; - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_alarm(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - if (data->alarm & attr->index) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - - -static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_volt_max, - set_volt_max, 0); -static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_volt_max, - set_volt_max, 1); - -static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_volt_min, - set_volt_min, 0); -static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_volt_min, - set_volt_min, 1); - -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_volt, NULL, 0); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_volt, NULL, 1); - -static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_VCCP_ALARM); -static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_VCC_ALARM); - -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, - set_temp_max, 0); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, - set_temp_max, 1); -static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_max, - set_temp_max, 2); - -static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min, - set_temp_min, 0); -static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min, - set_temp_min, 1); -static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_temp_min, - set_temp_min, 2); - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); - -static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_R1T_ALARM | ALARM2(ADT7473_R1T_SHORT)); -static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_LT_ALARM); -static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_R2T_ALARM | ALARM2(ADT7473_R2T_SHORT)); - -static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 0); -static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 1); -static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 2); -static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 3); - -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); -static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); -static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); -static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); - -static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN1_ALARM)); -static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN2_ALARM)); -static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN3_ALARM)); -static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN4_ALARM)); - -static SENSOR_DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO, - show_max_duty_at_crit, set_max_duty_at_crit, 0); - -static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); -static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); -static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); - -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_pwm_min, set_pwm_min, 0); -static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_pwm_min, set_pwm_min, 1); -static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_pwm_min, set_pwm_min, 2); - -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_pwm_max, set_pwm_max, 0); -static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_pwm_max, set_pwm_max, 1); -static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_pwm_max, set_pwm_max, 2); - -static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IWUSR | S_IRUGO, - show_temp_tmin, set_temp_tmin, 0); -static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO, - show_temp_tmin, set_temp_tmin, 1); -static SENSOR_DEVICE_ATTR(temp3_auto_point1_temp, S_IWUSR | S_IRUGO, - show_temp_tmin, set_temp_tmin, 2); - -static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO, - show_temp_tmax, set_temp_tmax, 0); -static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO, - show_temp_tmax, set_temp_tmax, 1); -static SENSOR_DEVICE_ATTR(temp3_auto_point2_temp, S_IWUSR | S_IRUGO, - show_temp_tmax, set_temp_tmax, 2); - -static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - set_pwm_enable, 0); -static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - set_pwm_enable, 1); -static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - set_pwm_enable, 2); - -static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IWUSR | S_IRUGO, - show_pwm_auto_temp, set_pwm_auto_temp, 0); -static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IWUSR | S_IRUGO, - show_pwm_auto_temp, set_pwm_auto_temp, 1); -static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO, - show_pwm_auto_temp, set_pwm_auto_temp, 2); - -static struct attribute *adt7473_attr[] = -{ - &sensor_dev_attr_in1_max.dev_attr.attr, - &sensor_dev_attr_in2_max.dev_attr.attr, - &sensor_dev_attr_in1_min.dev_attr.attr, - &sensor_dev_attr_in2_min.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in1_alarm.dev_attr.attr, - &sensor_dev_attr_in2_alarm.dev_attr.attr, - - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr, - &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr, - &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr, - - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan3_min.dev_attr.attr, - &sensor_dev_attr_fan4_min.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, - &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &sensor_dev_attr_fan4_alarm.dev_attr.attr, - - &sensor_dev_attr_pwm_use_point2_pwm_at_crit.dev_attr.attr, - - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, - - &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_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, - - NULL -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int adt7473_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - int vendor, device, revision; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR); - if (vendor != ADT7473_VENDOR) - return -ENODEV; - - device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE); - if (device != ADT7473_DEVICE) - return -ENODEV; - - revision = i2c_smbus_read_byte_data(client, ADT7473_REG_REVISION); - if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69) - return -ENODEV; - - strlcpy(info->type, "adt7473", I2C_NAME_SIZE); - - return 0; -} - -static int adt7473_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adt7473_data *data; - int err; - - data = kzalloc(sizeof(struct adt7473_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(client, data); - mutex_init(&data->lock); - - dev_info(&client->dev, "%s chip found\n", client->name); - - /* Initialize the ADT7473 chip */ - adt7473_init_client(client); - - /* Register sysfs hooks */ - data->attrs.attrs = adt7473_attr; - err = sysfs_create_group(&client->dev.kobj, &data->attrs); - if (err) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - sysfs_remove_group(&client->dev.kobj, &data->attrs); -exit_free: - kfree(data); -exit: - return err; -} - -static int adt7473_remove(struct i2c_client *client) -{ - struct adt7473_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &data->attrs); - kfree(data); - return 0; -} - -static int __init adt7473_init(void) -{ - pr_notice("The adt7473 driver is deprecated, please use the adt7475 " - "driver instead\n"); - return i2c_add_driver(&adt7473_driver); -} - -static void __exit adt7473_exit(void) -{ - i2c_del_driver(&adt7473_driver); -} - -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); -MODULE_DESCRIPTION("ADT7473 driver"); -MODULE_LICENSE("GPL"); - -module_init(adt7473_init); -module_exit(adt7473_exit); diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c new file mode 100644 index 000000000000..7f948105d8ad --- /dev/null +++ b/drivers/hwmon/asc7621.c @@ -0,0 +1,1255 @@ +/* + * asc7621.c - Part of lm_sensors, Linux kernel modules for hardware monitoring + * Copyright (c) 2007, 2010 George Joseph <george.joseph@fairview5.com> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { + 0x2c, 0x2d, 0x2e, I2C_CLIENT_END +}; + +enum asc7621_type { + asc7621, + asc7621a +}; + +#define INTERVAL_HIGH (HZ + HZ / 2) +#define INTERVAL_LOW (1 * 60 * HZ) +#define PRI_NONE 0 +#define PRI_LOW 1 +#define PRI_HIGH 2 +#define FIRST_CHIP asc7621 +#define LAST_CHIP asc7621a + +struct asc7621_chip { + char *name; + enum asc7621_type chip_type; + u8 company_reg; + u8 company_id; + u8 verstep_reg; + u8 verstep_id; + unsigned short *addresses; +}; + +static struct asc7621_chip asc7621_chips[] = { + { + .name = "asc7621", + .chip_type = asc7621, + .company_reg = 0x3e, + .company_id = 0x61, + .verstep_reg = 0x3f, + .verstep_id = 0x6c, + .addresses = normal_i2c, + }, + { + .name = "asc7621a", + .chip_type = asc7621a, + .company_reg = 0x3e, + .company_id = 0x61, + .verstep_reg = 0x3f, + .verstep_id = 0x6d, + .addresses = normal_i2c, + }, +}; + +/* + * Defines the highest register to be used, not the count. + * The actual count will probably be smaller because of gaps + * in the implementation (unused register locations). + * This define will safely set the array size of both the parameter + * and data arrays. + * This comes from the data sheet register description table. + */ +#define LAST_REGISTER 0xff + +struct asc7621_data { + struct i2c_client client; + struct device *class_dev; + struct mutex update_lock; + int valid; /* !=0 if following fields are valid */ + unsigned long last_high_reading; /* In jiffies */ + unsigned long last_low_reading; /* In jiffies */ + /* + * Registers we care about occupy the corresponding index + * in the array. Registers we don't care about are left + * at 0. + */ + u8 reg[LAST_REGISTER + 1]; +}; + +/* + * Macro to get the parent asc7621_param structure + * from a sensor_device_attribute passed into the + * show/store functions. + */ +#define to_asc7621_param(_sda) \ + container_of(_sda, struct asc7621_param, sda) + +/* + * Each parameter to be retrieved needs an asc7621_param structure + * allocated. It contains the sensor_device_attribute structure + * and the control info needed to retrieve the value from the register map. + */ +struct asc7621_param { + struct sensor_device_attribute sda; + u8 priority; + u8 msb[3]; + u8 lsb[3]; + u8 mask[3]; + u8 shift[3]; +}; + +/* + * This is the map that ultimately indicates whether we'll be + * retrieving a register value or not, and at what frequency. + */ +static u8 asc7621_register_priorities[255]; + +static struct asc7621_data *asc7621_update_device(struct device *dev); + +static inline u8 read_byte(struct i2c_client *client, u8 reg) +{ + int res = i2c_smbus_read_byte_data(client, reg); + if (res < 0) { + dev_err(&client->dev, + "Unable to read from register 0x%02x.\n", reg); + return 0; + }; + return res & 0xff; +} + +static inline int write_byte(struct i2c_client *client, u8 reg, u8 data) +{ + int res = i2c_smbus_write_byte_data(client, reg, data); + if (res < 0) { + dev_err(&client->dev, + "Unable to write value 0x%02x to register 0x%02x.\n", + data, reg); + }; + return res; +} + +/* + * Data Handlers + * Each function handles the formatting, storage + * and retrieval of like parameters. + */ + +#define SETUP_SHOW_data_param(d, a) \ + struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \ + struct asc7621_data *data = asc7621_update_device(d); \ + struct asc7621_param *param = to_asc7621_param(sda) + +#define SETUP_STORE_data_param(d, a) \ + struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \ + struct i2c_client *client = to_i2c_client(d); \ + struct asc7621_data *data = i2c_get_clientdata(client); \ + struct asc7621_param *param = to_asc7621_param(sda) + +/* + * u8 is just what it sounds like...an unsigned byte with no + * special formatting. + */ +static ssize_t show_u8(struct device *dev, struct device_attribute *attr, + char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + + return sprintf(buf, "%u\n", data->reg[param->msb[0]]); +} + +static ssize_t store_u8(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = SENSORS_LIMIT(reqval, 0, 255); + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = reqval; + write_byte(client, param->msb[0], reqval); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * Many of the config values occupy only a few bits of a register. + */ +static ssize_t show_bitmask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + + return sprintf(buf, "%u\n", + (data->reg[param->msb[0]] >> param-> + shift[0]) & param->mask[0]); +} + +static ssize_t store_bitmask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + u8 currval; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = SENSORS_LIMIT(reqval, 0, param->mask[0]); + + reqval = (reqval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + reqval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = reqval; + write_byte(client, param->msb[0], reqval); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * 16 bit fan rpm values + * reported by the device as the number of 11.111us periods (90khz) + * between full fan rotations. Therefore... + * RPM = (90000 * 60) / register value + */ +static ssize_t show_fan16(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u16 regval; + + mutex_lock(&data->update_lock); + regval = (data->reg[param->msb[0]] << 8) | data->reg[param->lsb[0]]; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", + (regval == 0 ? -1 : (regval) == + 0xffff ? 0 : 5400000 / regval)); +} + +static ssize_t store_fan16(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = + (SENSORS_LIMIT((reqval) <= 0 ? 0 : 5400000 / (reqval), 0, 65534)); + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = (reqval >> 8) & 0xff; + data->reg[param->lsb[0]] = reqval & 0xff; + write_byte(client, param->msb[0], data->reg[param->msb[0]]); + write_byte(client, param->lsb[0], data->reg[param->lsb[0]]); + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * Voltages are scaled in the device so that the nominal voltage + * is 3/4ths of the 0-255 range (i.e. 192). + * If all voltages are 'normal' then all voltage registers will + * read 0xC0. This doesn't help us if we don't have a point of refernce. + * The data sheet however provides us with the full scale value for each + * which is stored in in_scaling. The sda->index parameter value provides + * the index into in_scaling. + * + * NOTE: The chip expects the first 2 inputs be 2.5 and 2.25 volts + * respectively. That doesn't mean that's what the motherboard provides. :) + */ + +static int asc7621_in_scaling[] = { + 3320, 3000, 4380, 6640, 16000 +}; + +static ssize_t show_in10(struct device *dev, struct device_attribute *attr, + char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u16 regval; + u8 nr = sda->index; + + mutex_lock(&data->update_lock); + regval = (data->reg[param->msb[0]] * asc7621_in_scaling[nr]) / 256; + + /* The LSB value is a 2-bit scaling of the MSB's LSbit value. + * I.E. If the maximim voltage for this input is 6640 millivolts then + * a MSB register value of 0 = 0mv and 255 = 6640mv. + * A 1 step change therefore represents 25.9mv (6640 / 256). + * The extra 2-bits therefore represent increments of 6.48mv. + */ + regval += ((asc7621_in_scaling[nr] / 256) / 4) * + (data->reg[param->lsb[0]] >> 6); + + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", regval); +} + +/* 8 bit voltage values (the mins and maxs) */ +static ssize_t show_in8(struct device *dev, struct device_attribute *attr, + char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 nr = sda->index; + + return sprintf(buf, "%u\n", + ((data->reg[param->msb[0]] * + asc7621_in_scaling[nr]) / 256)); +} + +static ssize_t store_in8(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + u8 nr = sda->index; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = SENSORS_LIMIT(reqval, 0, asc7621_in_scaling[nr]); + + reqval = (reqval * 255 + 128) / asc7621_in_scaling[nr]; + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = reqval; + write_byte(client, param->msb[0], reqval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_temp8(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + + return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000); +} + +static ssize_t store_temp8(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + s8 temp; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = SENSORS_LIMIT(reqval, -127000, 127000); + + temp = reqval / 1000; + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = temp; + write_byte(client, param->msb[0], temp); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * Temperatures that occupy 2 bytes always have the whole + * number of degrees in the MSB with some part of the LSB + * indicating fractional degrees. + */ + +/* mmmmmmmm.llxxxxxx */ +static ssize_t show_temp10(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 msb, lsb; + int temp; + + mutex_lock(&data->update_lock); + msb = data->reg[param->msb[0]]; + lsb = (data->reg[param->lsb[0]] >> 6) & 0x03; + temp = (((s8) msb) * 1000) + (lsb * 250); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); +} + +/* mmmmmm.ll */ +static ssize_t show_temp62(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 regval = data->reg[param->msb[0]]; + int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250); + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t store_temp62(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval, i, f; + s8 temp; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = SENSORS_LIMIT(reqval, -32000, 31750); + i = reqval / 1000; + f = reqval - (i * 1000); + temp = i << 2; + temp |= f / 250; + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = temp; + write_byte(client, param->msb[0], temp); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * The aSC7621 doesn't provide an "auto_point2". Instead, you + * specify the auto_point1 and a range. To keep with the sysfs + * hwmon specs, we synthesize the auto_point_2 from them. + */ + +static u32 asc7621_range_map[] = { + 2000, 2500, 3330, 4000, 5000, 6670, 8000, 10000, + 13330, 16000, 20000, 26670, 32000, 40000, 53330, 80000, +}; + +static ssize_t show_ap2_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + long auto_point1; + u8 regval; + int temp; + + mutex_lock(&data->update_lock); + auto_point1 = ((s8) data->reg[param->msb[1]]) * 1000; + regval = + ((data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]); + temp = auto_point1 + asc7621_range_map[SENSORS_LIMIT(regval, 0, 15)]; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); + +} + +static ssize_t store_ap2_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval, auto_point1; + int i; + u8 currval, newval = 0; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + mutex_lock(&data->update_lock); + auto_point1 = data->reg[param->msb[1]] * 1000; + reqval = SENSORS_LIMIT(reqval, auto_point1 + 2000, auto_point1 + 80000); + + for (i = ARRAY_SIZE(asc7621_range_map) - 1; i >= 0; i--) { + if (reqval >= auto_point1 + asc7621_range_map[i]) { + newval = i; + break; + } + } + + newval = (newval & param->mask[0]) << param->shift[0]; + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_pwm_ac(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 config, altbit, regval; + u8 map[] = { + 0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10, + 0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f + }; + + mutex_lock(&data->update_lock); + config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1]; + regval = config | (altbit << 3); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", map[SENSORS_LIMIT(regval, 0, 15)]); +} + +static ssize_t store_pwm_ac(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + unsigned long reqval; + u8 currval, config, altbit, newval; + u16 map[] = { + 0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06, + 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + }; + + if (strict_strtoul(buf, 10, &reqval)) + return -EINVAL; + + if (reqval > 31) + return -EINVAL; + + reqval = map[reqval]; + if (reqval == 0xff) + return -EINVAL; + + config = reqval & 0x07; + altbit = (reqval >> 3) & 0x01; + + config = (config & param->mask[0]) << param->shift[0]; + altbit = (altbit & param->mask[1]) << param->shift[1]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval = config | (currval & ~(param->mask[0] << param->shift[0])); + newval = altbit | (newval & ~(param->mask[1] << param->shift[1])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 config, altbit, minoff, val, newval; + + mutex_lock(&data->update_lock); + config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1]; + minoff = (data->reg[param->msb[2]] >> param->shift[2]) & param->mask[2]; + mutex_unlock(&data->update_lock); + + val = config | (altbit << 3); + newval = 0; + + if (val == 3 || val >= 10) + newval = 255; + else if (val == 4) + newval = 0; + else if (val == 7) + newval = 1; + else if (minoff == 1) + newval = 2; + else + newval = 3; + + return sprintf(buf, "%u\n", newval); +} + +static ssize_t store_pwm_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + u8 currval, config, altbit, newval, minoff = 255; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + switch (reqval) { + case 0: + newval = 0x04; + break; + case 1: + newval = 0x07; + break; + case 2: + newval = 0x00; + minoff = 1; + break; + case 3: + newval = 0x00; + minoff = 0; + break; + case 255: + newval = 0x03; + break; + default: + return -EINVAL; + } + + config = newval & 0x07; + altbit = (newval >> 3) & 0x01; + + mutex_lock(&data->update_lock); + config = (config & param->mask[0]) << param->shift[0]; + altbit = (altbit & param->mask[1]) << param->shift[1]; + currval = read_byte(client, param->msb[0]); + newval = config | (currval & ~(param->mask[0] << param->shift[0])); + newval = altbit | (newval & ~(param->mask[1] << param->shift[1])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + if (minoff < 255) { + minoff = (minoff & param->mask[2]) << param->shift[2]; + currval = read_byte(client, param->msb[2]); + newval = + minoff | (currval & ~(param->mask[2] << param->shift[2])); + data->reg[param->msb[2]] = newval; + write_byte(client, param->msb[2], newval); + } + mutex_unlock(&data->update_lock); + return count; +} + +static u32 asc7621_pwm_freq_map[] = { + 10, 15, 23, 30, 38, 47, 62, 94, + 23000, 24000, 25000, 26000, 27000, 28000, 29000, 30000 +}; + +static ssize_t show_pwm_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 regval = + (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + + regval = SENSORS_LIMIT(regval, 0, 15); + + return sprintf(buf, "%u\n", asc7621_pwm_freq_map[regval]); +} + +static ssize_t store_pwm_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + unsigned long reqval; + u8 currval, newval = 255; + int i; + + if (strict_strtoul(buf, 10, &reqval)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(asc7621_pwm_freq_map); i++) { + if (reqval == asc7621_pwm_freq_map[i]) { + newval = i; + break; + } + } + if (newval == 255) + return -EINVAL; + + newval = (newval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static u32 asc7621_pwm_auto_spinup_map[] = { + 0, 100, 250, 400, 700, 1000, 2000, 4000 +}; + +static ssize_t show_pwm_ast(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 regval = + (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + + regval = SENSORS_LIMIT(regval, 0, 7); + + return sprintf(buf, "%u\n", asc7621_pwm_auto_spinup_map[regval]); + +} + +static ssize_t store_pwm_ast(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + u8 currval, newval = 255; + u32 i; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(asc7621_pwm_auto_spinup_map); i++) { + if (reqval == asc7621_pwm_auto_spinup_map[i]) { + newval = i; + break; + } + } + if (newval == 255) + return -EINVAL; + + newval = (newval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static u32 asc7621_temp_smoothing_time_map[] = { + 35000, 17600, 11800, 7000, 4400, 3000, 1600, 800 +}; + +static ssize_t show_temp_st(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_data_param(dev, attr); + u8 regval = + (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + regval = SENSORS_LIMIT(regval, 0, 7); + + return sprintf(buf, "%u\n", asc7621_temp_smoothing_time_map[regval]); +} + +static ssize_t store_temp_st(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_data_param(dev, attr); + long reqval; + u8 currval, newval = 255; + u32 i; + + if (strict_strtol(buf, 10, &reqval)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(asc7621_temp_smoothing_time_map); i++) { + if (reqval == asc7621_temp_smoothing_time_map[i]) { + newval = i; + break; + } + } + + if (newval == 255) + return -EINVAL; + + newval = (newval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * End of data handlers + * + * These defines do nothing more than make the table easier + * to read when wrapped at column 80. + */ + +/* + * Creates a variable length array inititalizer. + * VAA(1,3,5,7) would produce {1,3,5,7} + */ +#define VAA(args...) {args} + +#define PREAD(name, n, pri, rm, rl, m, s, r) \ + {.sda = SENSOR_ATTR(name, S_IRUGO, show_##r, NULL, n), \ + .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \ + .shift[0] = s,} + +#define PWRITE(name, n, pri, rm, rl, m, s, r) \ + {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \ + .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \ + .shift[0] = s,} + +/* + * PWRITEM assumes that the initializers for the .msb, .lsb, .mask and .shift + * were created using the VAA macro. + */ +#define PWRITEM(name, n, pri, rm, rl, m, s, r) \ + {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \ + .priority = pri, .msb = rm, .lsb = rl, .mask = m, .shift = s,} + +static struct asc7621_param asc7621_params[] = { + PREAD(in0_input, 0, PRI_HIGH, 0x20, 0x13, 0, 0, in10), + PREAD(in1_input, 1, PRI_HIGH, 0x21, 0x18, 0, 0, in10), + PREAD(in2_input, 2, PRI_HIGH, 0x22, 0x11, 0, 0, in10), + PREAD(in3_input, 3, PRI_HIGH, 0x23, 0x12, 0, 0, in10), + PREAD(in4_input, 4, PRI_HIGH, 0x24, 0x14, 0, 0, in10), + + PWRITE(in0_min, 0, PRI_LOW, 0x44, 0, 0, 0, in8), + PWRITE(in1_min, 1, PRI_LOW, 0x46, 0, 0, 0, in8), + PWRITE(in2_min, 2, PRI_LOW, 0x48, 0, 0, 0, in8), + PWRITE(in3_min, 3, PRI_LOW, 0x4a, 0, 0, 0, in8), + PWRITE(in4_min, 4, PRI_LOW, 0x4c, 0, 0, 0, in8), + + PWRITE(in0_max, 0, PRI_LOW, 0x45, 0, 0, 0, in8), + PWRITE(in1_max, 1, PRI_LOW, 0x47, 0, 0, 0, in8), + PWRITE(in2_max, 2, PRI_LOW, 0x49, 0, 0, 0, in8), + PWRITE(in3_max, 3, PRI_LOW, 0x4b, 0, 0, 0, in8), + PWRITE(in4_max, 4, PRI_LOW, 0x4d, 0, 0, 0, in8), + + PREAD(in0_alarm, 0, PRI_LOW, 0x41, 0, 0x01, 0, bitmask), + PREAD(in1_alarm, 1, PRI_LOW, 0x41, 0, 0x01, 1, bitmask), + PREAD(in2_alarm, 2, PRI_LOW, 0x41, 0, 0x01, 2, bitmask), + PREAD(in3_alarm, 3, PRI_LOW, 0x41, 0, 0x01, 3, bitmask), + PREAD(in4_alarm, 4, PRI_LOW, 0x42, 0, 0x01, 0, bitmask), + + PREAD(fan1_input, 0, PRI_HIGH, 0x29, 0x28, 0, 0, fan16), + PREAD(fan2_input, 1, PRI_HIGH, 0x2b, 0x2a, 0, 0, fan16), + PREAD(fan3_input, 2, PRI_HIGH, 0x2d, 0x2c, 0, 0, fan16), + PREAD(fan4_input, 3, PRI_HIGH, 0x2f, 0x2e, 0, 0, fan16), + + PWRITE(fan1_min, 0, PRI_LOW, 0x55, 0x54, 0, 0, fan16), + PWRITE(fan2_min, 1, PRI_LOW, 0x57, 0x56, 0, 0, fan16), + PWRITE(fan3_min, 2, PRI_LOW, 0x59, 0x58, 0, 0, fan16), + PWRITE(fan4_min, 3, PRI_LOW, 0x5b, 0x5a, 0, 0, fan16), + + PREAD(fan1_alarm, 0, PRI_LOW, 0x42, 0, 0x01, 0, bitmask), + PREAD(fan2_alarm, 1, PRI_LOW, 0x42, 0, 0x01, 1, bitmask), + PREAD(fan3_alarm, 2, PRI_LOW, 0x42, 0, 0x01, 2, bitmask), + PREAD(fan4_alarm, 3, PRI_LOW, 0x42, 0, 0x01, 3, bitmask), + + PREAD(temp1_input, 0, PRI_HIGH, 0x25, 0x10, 0, 0, temp10), + PREAD(temp2_input, 1, PRI_HIGH, 0x26, 0x15, 0, 0, temp10), + PREAD(temp3_input, 2, PRI_HIGH, 0x27, 0x16, 0, 0, temp10), + PREAD(temp4_input, 3, PRI_HIGH, 0x33, 0x17, 0, 0, temp10), + PREAD(temp5_input, 4, PRI_HIGH, 0xf7, 0xf6, 0, 0, temp10), + PREAD(temp6_input, 5, PRI_HIGH, 0xf9, 0xf8, 0, 0, temp10), + PREAD(temp7_input, 6, PRI_HIGH, 0xfb, 0xfa, 0, 0, temp10), + PREAD(temp8_input, 7, PRI_HIGH, 0xfd, 0xfc, 0, 0, temp10), + + PWRITE(temp1_min, 0, PRI_LOW, 0x4e, 0, 0, 0, temp8), + PWRITE(temp2_min, 1, PRI_LOW, 0x50, 0, 0, 0, temp8), + PWRITE(temp3_min, 2, PRI_LOW, 0x52, 0, 0, 0, temp8), + PWRITE(temp4_min, 3, PRI_LOW, 0x34, 0, 0, 0, temp8), + + PWRITE(temp1_max, 0, PRI_LOW, 0x4f, 0, 0, 0, temp8), + PWRITE(temp2_max, 1, PRI_LOW, 0x51, 0, 0, 0, temp8), + PWRITE(temp3_max, 2, PRI_LOW, 0x53, 0, 0, 0, temp8), + PWRITE(temp4_max, 3, PRI_LOW, 0x35, 0, 0, 0, temp8), + + PREAD(temp1_alarm, 0, PRI_LOW, 0x41, 0, 0x01, 4, bitmask), + PREAD(temp2_alarm, 1, PRI_LOW, 0x41, 0, 0x01, 5, bitmask), + PREAD(temp3_alarm, 2, PRI_LOW, 0x41, 0, 0x01, 6, bitmask), + PREAD(temp4_alarm, 3, PRI_LOW, 0x43, 0, 0x01, 0, bitmask), + + PWRITE(temp1_source, 0, PRI_LOW, 0x02, 0, 0x07, 4, bitmask), + PWRITE(temp2_source, 1, PRI_LOW, 0x02, 0, 0x07, 0, bitmask), + PWRITE(temp3_source, 2, PRI_LOW, 0x03, 0, 0x07, 4, bitmask), + PWRITE(temp4_source, 3, PRI_LOW, 0x03, 0, 0x07, 0, bitmask), + + PWRITE(temp1_smoothing_enable, 0, PRI_LOW, 0x62, 0, 0x01, 3, bitmask), + PWRITE(temp2_smoothing_enable, 1, PRI_LOW, 0x63, 0, 0x01, 7, bitmask), + PWRITE(temp3_smoothing_enable, 2, PRI_LOW, 0x64, 0, 0x01, 3, bitmask), + PWRITE(temp4_smoothing_enable, 3, PRI_LOW, 0x3c, 0, 0x01, 3, bitmask), + + PWRITE(temp1_smoothing_time, 0, PRI_LOW, 0x62, 0, 0x07, 0, temp_st), + PWRITE(temp2_smoothing_time, 1, PRI_LOW, 0x63, 0, 0x07, 4, temp_st), + PWRITE(temp3_smoothing_time, 2, PRI_LOW, 0x63, 0, 0x07, 0, temp_st), + PWRITE(temp4_smoothing_time, 3, PRI_LOW, 0x3c, 0, 0x07, 0, temp_st), + + PWRITE(temp1_auto_point1_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4, + bitmask), + PWRITE(temp2_auto_point1_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0, + bitmask), + PWRITE(temp3_auto_point1_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4, + bitmask), + PWRITE(temp4_auto_point1_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0, + bitmask), + + PREAD(temp1_auto_point2_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4, + bitmask), + PREAD(temp2_auto_point2_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0, + bitmask), + PREAD(temp3_auto_point2_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4, + bitmask), + PREAD(temp4_auto_point2_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0, + bitmask), + + PWRITE(temp1_auto_point1_temp, 0, PRI_LOW, 0x67, 0, 0, 0, temp8), + PWRITE(temp2_auto_point1_temp, 1, PRI_LOW, 0x68, 0, 0, 0, temp8), + PWRITE(temp3_auto_point1_temp, 2, PRI_LOW, 0x69, 0, 0, 0, temp8), + PWRITE(temp4_auto_point1_temp, 3, PRI_LOW, 0x3b, 0, 0, 0, temp8), + + PWRITEM(temp1_auto_point2_temp, 0, PRI_LOW, VAA(0x5f, 0x67), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + PWRITEM(temp2_auto_point2_temp, 1, PRI_LOW, VAA(0x60, 0x68), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + PWRITEM(temp3_auto_point2_temp, 2, PRI_LOW, VAA(0x61, 0x69), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + PWRITEM(temp4_auto_point2_temp, 3, PRI_LOW, VAA(0x3c, 0x3b), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + + PWRITE(temp1_crit, 0, PRI_LOW, 0x6a, 0, 0, 0, temp8), + PWRITE(temp2_crit, 1, PRI_LOW, 0x6b, 0, 0, 0, temp8), + PWRITE(temp3_crit, 2, PRI_LOW, 0x6c, 0, 0, 0, temp8), + PWRITE(temp4_crit, 3, PRI_LOW, 0x3d, 0, 0, 0, temp8), + + PWRITE(temp5_enable, 4, PRI_LOW, 0x0e, 0, 0x01, 0, bitmask), + PWRITE(temp6_enable, 5, PRI_LOW, 0x0e, 0, 0x01, 1, bitmask), + PWRITE(temp7_enable, 6, PRI_LOW, 0x0e, 0, 0x01, 2, bitmask), + PWRITE(temp8_enable, 7, PRI_LOW, 0x0e, 0, 0x01, 3, bitmask), + + PWRITE(remote1_offset, 0, PRI_LOW, 0x1c, 0, 0, 0, temp62), + PWRITE(remote2_offset, 1, PRI_LOW, 0x1d, 0, 0, 0, temp62), + + PWRITE(pwm1, 0, PRI_HIGH, 0x30, 0, 0, 0, u8), + PWRITE(pwm2, 1, PRI_HIGH, 0x31, 0, 0, 0, u8), + PWRITE(pwm3, 2, PRI_HIGH, 0x32, 0, 0, 0, u8), + + PWRITE(pwm1_invert, 0, PRI_LOW, 0x5c, 0, 0x01, 4, bitmask), + PWRITE(pwm2_invert, 1, PRI_LOW, 0x5d, 0, 0x01, 4, bitmask), + PWRITE(pwm3_invert, 2, PRI_LOW, 0x5e, 0, 0x01, 4, bitmask), + + PWRITEM(pwm1_enable, 0, PRI_LOW, VAA(0x5c, 0x5c, 0x62), VAA(0, 0, 0), + VAA(0x07, 0x01, 0x01), VAA(5, 3, 5), pwm_enable), + PWRITEM(pwm2_enable, 1, PRI_LOW, VAA(0x5d, 0x5d, 0x62), VAA(0, 0, 0), + VAA(0x07, 0x01, 0x01), VAA(5, 3, 6), pwm_enable), + PWRITEM(pwm3_enable, 2, PRI_LOW, VAA(0x5e, 0x5e, 0x62), VAA(0, 0, 0), + VAA(0x07, 0x01, 0x01), VAA(5, 3, 7), pwm_enable), + + PWRITEM(pwm1_auto_channels, 0, PRI_LOW, VAA(0x5c, 0x5c), VAA(0, 0), + VAA(0x07, 0x01), VAA(5, 3), pwm_ac), + PWRITEM(pwm2_auto_channels, 1, PRI_LOW, VAA(0x5d, 0x5d), VAA(0, 0), + VAA(0x07, 0x01), VAA(5, 3), pwm_ac), + PWRITEM(pwm3_auto_channels, 2, PRI_LOW, VAA(0x5e, 0x5e), VAA(0, 0), + VAA(0x07, 0x01), VAA(5, 3), pwm_ac), + + PWRITE(pwm1_auto_point1_pwm, 0, PRI_LOW, 0x64, 0, 0, 0, u8), + PWRITE(pwm2_auto_point1_pwm, 1, PRI_LOW, 0x65, 0, 0, 0, u8), + PWRITE(pwm3_auto_point1_pwm, 2, PRI_LOW, 0x66, 0, 0, 0, u8), + + PWRITE(pwm1_auto_point2_pwm, 0, PRI_LOW, 0x38, 0, 0, 0, u8), + PWRITE(pwm2_auto_point2_pwm, 1, PRI_LOW, 0x39, 0, 0, 0, u8), + PWRITE(pwm3_auto_point2_pwm, 2, PRI_LOW, 0x3a, 0, 0, 0, u8), + + PWRITE(pwm1_freq, 0, PRI_LOW, 0x5f, 0, 0x0f, 0, pwm_freq), + PWRITE(pwm2_freq, 1, PRI_LOW, 0x60, 0, 0x0f, 0, pwm_freq), + PWRITE(pwm3_freq, 2, PRI_LOW, 0x61, 0, 0x0f, 0, pwm_freq), + + PREAD(pwm1_auto_zone_assigned, 0, PRI_LOW, 0, 0, 0x03, 2, bitmask), + PREAD(pwm2_auto_zone_assigned, 1, PRI_LOW, 0, 0, 0x03, 4, bitmask), + PREAD(pwm3_auto_zone_assigned, 2, PRI_LOW, 0, 0, 0x03, 6, bitmask), + + PWRITE(pwm1_auto_spinup_time, 0, PRI_LOW, 0x5c, 0, 0x07, 0, pwm_ast), + PWRITE(pwm2_auto_spinup_time, 1, PRI_LOW, 0x5d, 0, 0x07, 0, pwm_ast), + PWRITE(pwm3_auto_spinup_time, 2, PRI_LOW, 0x5e, 0, 0x07, 0, pwm_ast), + + PWRITE(peci_enable, 0, PRI_LOW, 0x40, 0, 0x01, 4, bitmask), + PWRITE(peci_avg, 0, PRI_LOW, 0x36, 0, 0x07, 0, bitmask), + PWRITE(peci_domain, 0, PRI_LOW, 0x36, 0, 0x01, 3, bitmask), + PWRITE(peci_legacy, 0, PRI_LOW, 0x36, 0, 0x01, 4, bitmask), + PWRITE(peci_diode, 0, PRI_LOW, 0x0e, 0, 0x07, 4, bitmask), + PWRITE(peci_4domain, 0, PRI_LOW, 0x0e, 0, 0x01, 4, bitmask), + +}; + +static struct asc7621_data *asc7621_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asc7621_data *data = i2c_get_clientdata(client); + int i; + +/* + * The asc7621 chips guarantee consistent reads of multi-byte values + * regardless of the order of the reads. No special logic is needed + * so we can just read the registers in whatever order they appear + * in the asc7621_params array. + */ + + mutex_lock(&data->update_lock); + + /* Read all the high priority registers */ + + if (!data->valid || + time_after(jiffies, data->last_high_reading + INTERVAL_HIGH)) { + + for (i = 0; i < ARRAY_SIZE(asc7621_register_priorities); i++) { + if (asc7621_register_priorities[i] == PRI_HIGH) { + data->reg[i] = + i2c_smbus_read_byte_data(client, i) & 0xff; + } + } + data->last_high_reading = jiffies; + }; /* last_reading */ + + /* Read all the low priority registers. */ + + if (!data->valid || + time_after(jiffies, data->last_low_reading + INTERVAL_LOW)) { + + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + if (asc7621_register_priorities[i] == PRI_LOW) { + data->reg[i] = + i2c_smbus_read_byte_data(client, i) & 0xff; + } + } + data->last_low_reading = jiffies; + }; /* last_reading */ + + data->valid = 1; + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Standard detection and initialization below + * + * Helper function that checks if an address is valid + * for a particular chip. + */ + +static inline int valid_address_for_chip(int chip_type, int address) +{ + int i; + + for (i = 0; asc7621_chips[chip_type].addresses[i] != I2C_CLIENT_END; + i++) { + if (asc7621_chips[chip_type].addresses[i] == address) + return 1; + } + return 0; +} + +static void asc7621_init_client(struct i2c_client *client) +{ + int value; + + /* Warn if part was not "READY" */ + + value = read_byte(client, 0x40); + + if (value & 0x02) { + dev_err(&client->dev, + "Client (%d,0x%02x) config is locked.\n", + i2c_adapter_id(client->adapter), client->addr); + }; + if (!(value & 0x04)) { + dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n", + i2c_adapter_id(client->adapter), client->addr); + }; + +/* + * Start monitoring + * + * Try to clear LOCK, Set START, save everything else + */ + value = (value & ~0x02) | 0x01; + write_byte(client, 0x40, value & 0xff); + +} + +static int +asc7621_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct asc7621_data *data; + int i, err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = kzalloc(sizeof(struct asc7621_data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + /* Initialize the asc7621 chip */ + asc7621_init_client(client); + + /* Create the sysfs entries */ + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + err = + device_create_file(&client->dev, + &(asc7621_params[i].sda.dev_attr)); + if (err) + goto exit_remove; + } + + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + device_remove_file(&client->dev, + &(asc7621_params[i].sda.dev_attr)); + } + + i2c_set_clientdata(client, NULL); + kfree(data); + return err; +} + +static int asc7621_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int company, verstep, chip_index; + struct device *dev; + + dev = &client->dev; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + for (chip_index = FIRST_CHIP; chip_index <= LAST_CHIP; chip_index++) { + + if (!valid_address_for_chip(chip_index, client->addr)) + continue; + + company = read_byte(client, + asc7621_chips[chip_index].company_reg); + verstep = read_byte(client, + asc7621_chips[chip_index].verstep_reg); + + if (company == asc7621_chips[chip_index].company_id && + verstep == asc7621_chips[chip_index].verstep_id) { + strlcpy(client->name, asc7621_chips[chip_index].name, + I2C_NAME_SIZE); + strlcpy(info->type, asc7621_chips[chip_index].name, + I2C_NAME_SIZE); + + dev_info(&adapter->dev, "Matched %s\n", + asc7621_chips[chip_index].name); + return 0; + } + } + + return -ENODEV; +} + +static int asc7621_remove(struct i2c_client *client) +{ + struct asc7621_data *data = i2c_get_clientdata(client); + int i; + + hwmon_device_unregister(data->class_dev); + + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + device_remove_file(&client->dev, + &(asc7621_params[i].sda.dev_attr)); + } + + i2c_set_clientdata(client, NULL); + kfree(data); + return 0; +} + +static const struct i2c_device_id asc7621_id[] = { + {"asc7621", asc7621}, + {"asc7621a", asc7621a}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, asc7621_id); + +static struct i2c_driver asc7621_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "asc7621", + }, + .probe = asc7621_probe, + .remove = asc7621_remove, + .id_table = asc7621_id, + .detect = asc7621_detect, + .address_list = normal_i2c, +}; + +static int __init sm_asc7621_init(void) +{ + int i, j; +/* + * Collect all the registers needed into a single array. + * This way, if a register isn't actually used for anything, + * we don't retrieve it. + */ + + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + for (j = 0; j < ARRAY_SIZE(asc7621_params[i].msb); j++) + asc7621_register_priorities[asc7621_params[i].msb[j]] = + asc7621_params[i].priority; + for (j = 0; j < ARRAY_SIZE(asc7621_params[i].lsb); j++) + asc7621_register_priorities[asc7621_params[i].lsb[j]] = + asc7621_params[i].priority; + } + return i2c_add_driver(&asc7621_driver); +} + +static void __exit sm_asc7621_exit(void) +{ + i2c_del_driver(&asc7621_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("George Joseph"); +MODULE_DESCRIPTION("Andigilog aSC7621 and aSC7621a driver"); + +module_init(sm_asc7621_init); +module_exit(sm_asc7621_exit); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index fa0728232e71..0627f7a5b9b8 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -267,7 +267,7 @@ struct fschmd_data { struct list_head list; /* member of the watchdog_data_list */ struct kref kref; struct miscdevice watchdog_miscdev; - int kind; + enum chips kind; unsigned long watchdog_is_open; char watchdog_expect_close; char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ @@ -325,8 +325,7 @@ static ssize_t show_in_value(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev); - /* fscher / fschrc - 1 as data->kind is an array index, not a chips */ - if (data->kind == (fscher - 1) || data->kind >= (fschrc - 1)) + if (data->kind == fscher || data->kind >= fschrc) return sprintf(buf, "%d\n", (data->volt[index] * dmi_vref * dmi_mult[index]) / 255 + dmi_offset[index]); else @@ -492,7 +491,7 @@ static ssize_t show_pwm_auto_point1_pwm(struct device *dev, int val = data->fan_min[index]; /* 0 = allow turning off (except on the syl), 1-255 = 50-100% */ - if (val || data->kind == fscsyl - 1) + if (val || data->kind == fscsyl) val = val / 2 + 128; return sprintf(buf, "%d\n", val); @@ -506,7 +505,7 @@ static ssize_t store_pwm_auto_point1_pwm(struct device *dev, unsigned long v = simple_strtoul(buf, NULL, 10); /* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */ - if (v || data->kind == fscsyl - 1) { + if (v || data->kind == fscsyl) { v = SENSORS_LIMIT(v, 128, 255); v = (v - 128) * 2 + 1; } @@ -1037,7 +1036,7 @@ static int fschmd_detect(struct i2c_client *client, else return -ENODEV; - strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE); + strlcpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE); return 0; } @@ -1065,6 +1064,7 @@ static int fschmd_probe(struct i2c_client *client, (where the client is found through a data ptr instead of the otherway around) */ data->client = client; + data->kind = kind; if (kind == fscpos) { /* The Poseidon has hardwired temp limits, fill these @@ -1085,9 +1085,6 @@ static int fschmd_probe(struct i2c_client *client, } } - /* i2c kind goes from 1-6, we want from 0-5 to address arrays */ - data->kind = kind - 1; - /* Read in some never changing registers */ data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); data->global_control = i2c_smbus_read_byte_data(client, diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c index 19c01a49f6be..09ea12e0a551 100644 --- a/drivers/hwmon/g760a.c +++ b/drivers/hwmon/g760a.c @@ -68,7 +68,7 @@ struct g760a_data { #define PWM_FROM_CNT(cnt) (0xff-(cnt)) #define PWM_TO_CNT(pwm) (0xff-(pwm)) -unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) +static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) { return ((val == 0x00) ? 0 : ((clk*30)/(val*div))); } diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0ffe84d190bb..1002befd87d5 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1,40 +1,40 @@ /* - it87.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring. - - The IT8705F is an LPC-based Super I/O part that contains UARTs, a - parallel port, an IR port, a MIDI port, a floppy controller, etc., in - addition to an Environment Controller (Enhanced Hardware Monitor and - Fan Controller) - - This driver supports only the Environment Controller in the IT8705F and - similar parts. The other devices are supported by different drivers. - - Supports: IT8705F Super I/O chip w/LPC interface - IT8712F Super I/O chip w/LPC interface - IT8716F Super I/O chip w/LPC interface - IT8718F Super I/O chip w/LPC interface - IT8720F Super I/O chip w/LPC interface - IT8726F Super I/O chip w/LPC interface - Sis950 A clone of the IT8705F - - Copyright (C) 2001 Chris Gauthron - Copyright (C) 2005-2007 Jean Delvare <khali@linux-fr.org> - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * it87.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring. + * + * The IT8705F is an LPC-based Super I/O part that contains UARTs, a + * parallel port, an IR port, a MIDI port, a floppy controller, etc., in + * addition to an Environment Controller (Enhanced Hardware Monitor and + * Fan Controller) + * + * This driver supports only the Environment Controller in the IT8705F and + * similar parts. The other devices are supported by different drivers. + * + * Supports: IT8705F Super I/O chip w/LPC interface + * IT8712F Super I/O chip w/LPC interface + * IT8716F Super I/O chip w/LPC interface + * IT8718F Super I/O chip w/LPC interface + * IT8720F Super I/O chip w/LPC interface + * IT8726F Super I/O chip w/LPC interface + * Sis950 A clone of the IT8705F + * + * Copyright (C) 2001 Chris Gauthron + * Copyright (C) 2005-2010 Jean Delvare <khali@linux-fr.org> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ #include <linux/module.h> #include <linux/init.h> @@ -128,6 +128,7 @@ superio_exit(void) #define IT87_SIO_GPIO5_REG 0x29 #define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ #define IT87_SIO_VID_REG 0xfc /* VID value */ +#define IT87_SIO_BEEP_PIN_REG 0xf6 /* Beep pin mapping */ /* Update battery voltage after every reading if true */ static int update_vbat; @@ -187,9 +188,13 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; #define IT87_REG_VIN_ENABLE 0x50 #define IT87_REG_TEMP_ENABLE 0x51 +#define IT87_REG_BEEP_ENABLE 0x5c #define IT87_REG_CHIPID 0x58 +#define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) +#define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) + #define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) #define IN_FROM_REG(val) ((val) * 16) @@ -246,6 +251,7 @@ struct it87_sio_data { /* Values read from Super-I/O config space */ u8 revision; u8 vid_value; + u8 beep_pin; /* Features skipped based on config or DMI */ u8 skip_vid; u8 skip_fan; @@ -279,9 +285,21 @@ struct it87_data { u8 vid; /* Register encoding, combined */ u8 vrm; u32 alarms; /* Register encoding, combined */ + u8 beeps; /* Register encoding */ u8 fan_main_ctrl; /* Register value */ u8 fan_ctl; /* Register value */ - u8 manual_pwm_ctl[3]; /* manual PWM value set by user */ + + /* The following 3 arrays correspond to the same registers. The + * meaning of bits 6-0 depends on the value of bit 7, and we want + * to preserve settings on mode changes, so we have to track all + * values separately. */ + u8 pwm_ctrl[3]; /* Register value */ + u8 pwm_duty[3]; /* Manual PWM value set by user (bit 6-0) */ + u8 pwm_temp_map[3]; /* PWM to temp. chan. mapping (bits 1-0) */ + + /* Automatic fan speed control registers */ + u8 auto_pwm[3][4]; /* [nr][3] is hard-coded */ + s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; static inline int has_16bit_fans(const struct it87_data *data) @@ -296,6 +314,15 @@ static inline int has_16bit_fans(const struct it87_data *data) || data->type == it8720; } +static inline int has_old_autopwm(const struct it87_data *data) +{ + /* The old automatic fan speed control interface is implemented + by IT8705F chips up to revision F and IT8712F chips up to + revision G. */ + return (data->type == it87 && data->revision < 0x03) + || (data->type == it8712 && data->revision < 0x08); +} + static int it87_probe(struct platform_device *pdev); static int __devexit it87_remove(struct platform_device *pdev); @@ -352,7 +379,10 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val); @@ -368,7 +398,10 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val); @@ -441,7 +474,10 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + + if (strict_strtol(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); data->temp_high[nr] = TEMP_TO_REG(val); @@ -456,7 +492,10 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + + if (strict_strtol(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); data->temp_low[nr] = TEMP_TO_REG(val); @@ -483,8 +522,9 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - u8 reg = data->sensor; /* In case the value is updated while we use it */ - + u8 reg = data->sensor; /* In case the value is updated while + we use it */ + if (reg & (1 << nr)) return sprintf(buf, "3\n"); /* thermal diode */ if (reg & (8 << nr)) @@ -498,7 +538,10 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + + if (strict_strtol(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); @@ -511,9 +554,9 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, } /* 3 = thermal diode; 4 = thermistor; 0 = disabled */ if (val == 3) - data->sensor |= 1 << nr; + data->sensor |= 1 << nr; else if (val == 4) - data->sensor |= 8 << nr; + data->sensor |= 8 << nr; else if (val != 0) { mutex_unlock(&data->update_lock); return -EINVAL; @@ -531,6 +574,19 @@ show_sensor_offset(2); show_sensor_offset(3); /* 3 Fans */ + +static int pwm_mode(const struct it87_data *data, int nr) +{ + int ctrl = data->fan_main_ctrl & (1 << nr); + + if (ctrl == 0) /* Full speed */ + return 0; + if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ + return 2; + else /* Manual mode */ + return 1; +} + static ssize_t show_fan(struct device *dev, struct device_attribute *attr, char *buf) { @@ -538,7 +594,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, @@ -548,8 +604,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", - FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]))); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) @@ -560,14 +616,14 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); } -static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t show_pwm_enable(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 it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", (data->fan_main_ctrl & (1 << nr)) ? 1 : 0); + return sprintf(buf, "%d\n", pwm_mode(data, nr)); } static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) @@ -576,7 +632,7 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]); + return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm_duty[nr])); } static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, char *buf) @@ -593,15 +649,24 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; u8 reg; + if (strict_strtol(buf, 10, &val) < 0) + return -EINVAL; + mutex_lock(&data->update_lock); reg = it87_read_value(data, IT87_REG_FAN_DIV); switch (nr) { - case 0: data->fan_div[nr] = reg & 0x07; break; - case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break; - case 2: data->fan_div[nr] = (reg & 0x40) ? 3 : 1; break; + case 0: + data->fan_div[nr] = reg & 0x07; + break; + case 1: + data->fan_div[nr] = (reg >> 3) & 0x07; + break; + case 2: + data->fan_div[nr] = (reg & 0x40) ? 3 : 1; + break; } data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); @@ -616,10 +681,13 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; int min; u8 old; + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + mutex_lock(&data->update_lock); old = it87_read_value(data, IT87_REG_FAN_DIV); @@ -651,6 +719,32 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, mutex_unlock(&data->update_lock); return count; } + +/* Returns 0 if OK, -EINVAL otherwise */ +static int check_trip_points(struct device *dev, int nr) +{ + const struct it87_data *data = dev_get_drvdata(dev); + int i, err = 0; + + if (has_old_autopwm(data)) { + for (i = 0; i < 3; i++) { + if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1]) + err = -EINVAL; + } + for (i = 0; i < 2; i++) { + if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1]) + err = -EINVAL; + } + } + + if (err) { + dev_err(dev, "Inconsistent trip points, not switching to " + "automatic mode\n"); + dev_err(dev, "Adjust the trip points and try again\n"); + } + return err; +} + static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -658,7 +752,16 @@ static ssize_t set_pwm_enable(struct device *dev, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + + if (strict_strtol(buf, 10, &val) < 0 || val < 0 || val > 2) + return -EINVAL; + + /* Check trip points before switching to automatic mode */ + if (val == 2) { + if (check_trip_points(dev, nr) < 0) + return -EINVAL; + } mutex_lock(&data->update_lock); @@ -669,16 +772,18 @@ static ssize_t set_pwm_enable(struct device *dev, it87_write_value(data, IT87_REG_FAN_CTL, tmp | (1 << nr)); /* set on/off mode */ data->fan_main_ctrl &= ~(1 << nr); - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); - } else if (val == 1) { + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); + } else { + if (val == 1) /* Manual mode */ + data->pwm_ctrl[nr] = data->pwm_duty[nr]; + else /* Automatic mode */ + data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr]; + it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]); /* set SmartGuardian mode */ data->fan_main_ctrl |= (1 << nr); - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); - /* set saved pwm value, clear FAN_CTLX PWM mode bit */ - it87_write_value(data, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); - } else { - mutex_unlock(&data->update_lock); - return -EINVAL; + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); } mutex_unlock(&data->update_lock); @@ -691,15 +796,19 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; - if (val < 0 || val > 255) + if (strict_strtol(buf, 10, &val) < 0 || val < 0 || val > 255) return -EINVAL; mutex_lock(&data->update_lock); - data->manual_pwm_ctl[nr] = val; - if (data->fan_main_ctrl & (1 << nr)) - it87_write_value(data, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); + data->pwm_duty[nr] = PWM_TO_REG(val); + /* If we are in manual mode, write the duty cycle immediately; + * otherwise, just store it for later use. */ + if (!(data->pwm_ctrl[nr] & 0x80)) { + data->pwm_ctrl[nr] = data->pwm_duty[nr]; + it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]); + } mutex_unlock(&data->update_lock); return count; } @@ -707,9 +816,12 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; int i; + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + /* Search for the nearest available frequency */ for (i = 0; i < 7; i++) { if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2) @@ -724,6 +836,132 @@ static ssize_t set_pwm_freq(struct device *dev, return count; } +static ssize_t show_pwm_temp_map(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 it87_data *data = it87_update_device(dev); + int map; + + if (data->pwm_temp_map[nr] < 3) + map = 1 << data->pwm_temp_map[nr]; + else + map = 0; /* Should never happen */ + return sprintf(buf, "%d\n", map); +} +static ssize_t set_pwm_temp_map(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = dev_get_drvdata(dev); + long val; + u8 reg; + + /* This check can go away if we ever support automatic fan speed + control on newer chips. */ + if (!has_old_autopwm(data)) { + dev_notice(dev, "Mapping change disabled for safety reasons\n"); + return -EINVAL; + } + + if (strict_strtol(buf, 10, &val) < 0) + return -EINVAL; + + switch (val) { + case (1 << 0): + reg = 0x00; + break; + case (1 << 1): + reg = 0x01; + break; + case (1 << 2): + reg = 0x02; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + data->pwm_temp_map[nr] = reg; + /* If we are in automatic mode, write the temp mapping immediately; + * otherwise, just store it for later use. */ + if (data->pwm_ctrl[nr] & 0x80) { + data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr]; + it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]); + } + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_auto_pwm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it87_data *data = it87_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + + return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point])); +} + +static ssize_t set_auto_pwm(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it87_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + long val; + + if (strict_strtol(buf, 10, &val) < 0 || val < 0 || val > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->auto_pwm[nr][point] = PWM_TO_REG(val); + it87_write_value(data, IT87_REG_AUTO_PWM(nr, point), + data->auto_pwm[nr][point]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_auto_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it87_data *data = it87_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->auto_temp[nr][point])); +} + +static ssize_t set_auto_temp(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it87_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + long val; + + if (strict_strtol(buf, 10, &val) < 0 || val < -128000 || val > 127000) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->auto_temp[nr][point] = TEMP_TO_REG(val); + it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), + data->auto_temp[nr][point]); + mutex_unlock(&data->update_lock); + return count; +} #define show_fan_offset(offset) \ static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ @@ -744,7 +982,36 @@ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, offset - 1); \ static DEVICE_ATTR(pwm##offset##_freq, \ (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \ - show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL)); + show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL)); \ +static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels_temp, \ + S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, \ + offset - 1); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_pwm, \ + S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, \ + offset - 1, 0); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point2_pwm, \ + S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, \ + offset - 1, 1); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point3_pwm, \ + S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, \ + offset - 1, 2); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point4_pwm, \ + S_IRUGO, show_auto_pwm, NULL, offset - 1, 3); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_temp, \ + S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ + offset - 1, 1); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_temp_hyst, \ + S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ + offset - 1, 0); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point2_temp, \ + S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ + offset - 1, 2); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point3_temp, \ + S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ + offset - 1, 3); \ +static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point4_temp, \ + S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ + offset - 1, 4); show_pwm_offset(1); show_pwm_offset(2); @@ -775,7 +1042,10 @@ static ssize_t set_fan16_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + + if (strict_strtol(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN16_TO_REG(val); @@ -805,7 +1075,8 @@ show_fan16_offset(4); show_fan16_offset(5); /* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%u\n", data->alarms); @@ -836,27 +1107,78 @@ static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); -static ssize_t -show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_beep(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int bitnr = to_sensor_dev_attr(attr)->index; + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%u\n", (data->beeps >> bitnr) & 1); +} +static ssize_t set_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int bitnr = to_sensor_dev_attr(attr)->index; + struct it87_data *data = dev_get_drvdata(dev); + long val; + + if (strict_strtol(buf, 10, &val) < 0 + || (val != 0 && val != 1)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE); + if (val) + data->beeps |= (1 << bitnr); + else + data->beeps &= ~(1 << bitnr); + it87_write_value(data, IT87_REG_BEEP_ENABLE, data->beeps); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(in0_beep, S_IRUGO | S_IWUSR, + show_beep, set_beep, 1); +static SENSOR_DEVICE_ATTR(in1_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in4_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in5_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in6_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in7_beep, S_IRUGO, show_beep, NULL, 1); +/* fanX_beep writability is set later */ +static SENSOR_DEVICE_ATTR(fan1_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan4_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan5_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR, + show_beep, set_beep, 2); +static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_beep, NULL, 2); + +static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->vrm); } -static ssize_t -store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct it87_data *data = dev_get_drvdata(dev); - u32 val; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; - val = simple_strtoul(buf, NULL, 10); data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -static ssize_t -show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); @@ -931,51 +1253,176 @@ static const struct attribute_group it87_group = { .attrs = it87_attributes, }; -static struct attribute *it87_attributes_opt[] = { +static struct attribute *it87_attributes_beep[] = { + &sensor_dev_attr_in0_beep.dev_attr.attr, + &sensor_dev_attr_in1_beep.dev_attr.attr, + &sensor_dev_attr_in2_beep.dev_attr.attr, + &sensor_dev_attr_in3_beep.dev_attr.attr, + &sensor_dev_attr_in4_beep.dev_attr.attr, + &sensor_dev_attr_in5_beep.dev_attr.attr, + &sensor_dev_attr_in6_beep.dev_attr.attr, + &sensor_dev_attr_in7_beep.dev_attr.attr, + + &sensor_dev_attr_temp1_beep.dev_attr.attr, + &sensor_dev_attr_temp2_beep.dev_attr.attr, + &sensor_dev_attr_temp3_beep.dev_attr.attr, + NULL +}; + +static const struct attribute_group it87_group_beep = { + .attrs = it87_attributes_beep, +}; + +static struct attribute *it87_attributes_fan16[5][3+1] = { { &sensor_dev_attr_fan1_input16.dev_attr.attr, &sensor_dev_attr_fan1_min16.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan2_input16.dev_attr.attr, &sensor_dev_attr_fan2_min16.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan3_input16.dev_attr.attr, &sensor_dev_attr_fan3_min16.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan4_input16.dev_attr.attr, &sensor_dev_attr_fan4_min16.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan5_input16.dev_attr.attr, &sensor_dev_attr_fan5_min16.dev_attr.attr, + &sensor_dev_attr_fan5_alarm.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_fan16[5] = { + { .attrs = it87_attributes_fan16[0] }, + { .attrs = it87_attributes_fan16[1] }, + { .attrs = it87_attributes_fan16[2] }, + { .attrs = it87_attributes_fan16[3] }, + { .attrs = it87_attributes_fan16[4] }, +}; +static struct attribute *it87_attributes_fan[3][4+1] = { { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, &sensor_dev_attr_fan3_div.dev_attr.attr, - - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &sensor_dev_attr_fan4_alarm.dev_attr.attr, - &sensor_dev_attr_fan5_alarm.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_fan[3] = { + { .attrs = it87_attributes_fan[0] }, + { .attrs = it87_attributes_fan[1] }, + { .attrs = it87_attributes_fan[2] }, +}; + +static const struct attribute_group * +it87_get_fan_group(const struct it87_data *data) +{ + return has_16bit_fans(data) ? it87_group_fan16 : it87_group_fan; +} +static struct attribute *it87_attributes_pwm[3][4+1] = { { &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.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, &dev_attr_pwm1_freq.attr, + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, &dev_attr_pwm2_freq.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, &dev_attr_pwm3_freq.attr, + &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_pwm[3] = { + { .attrs = it87_attributes_pwm[0] }, + { .attrs = it87_attributes_pwm[1] }, + { .attrs = it87_attributes_pwm[2] }, +}; +static struct attribute *it87_attributes_autopwm[3][9+1] = { { + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_autopwm[3] = { + { .attrs = it87_attributes_autopwm[0] }, + { .attrs = it87_attributes_autopwm[1] }, + { .attrs = it87_attributes_autopwm[2] }, +}; + +static struct attribute *it87_attributes_fan_beep[] = { + &sensor_dev_attr_fan1_beep.dev_attr.attr, + &sensor_dev_attr_fan2_beep.dev_attr.attr, + &sensor_dev_attr_fan3_beep.dev_attr.attr, + &sensor_dev_attr_fan4_beep.dev_attr.attr, + &sensor_dev_attr_fan5_beep.dev_attr.attr, +}; + +static struct attribute *it87_attributes_vid[] = { &dev_attr_vrm.attr, &dev_attr_cpu0_vid.attr, NULL }; -static const struct attribute_group it87_group_opt = { - .attrs = it87_attributes_opt, +static const struct attribute_group it87_group_vid = { + .attrs = it87_attributes_vid, }; /* SuperIO detection - will change isa_address if a chip is found */ @@ -1035,6 +1482,10 @@ static int __init it87_find(unsigned short *address, if (sio_data->type == it87) { /* The IT8705F doesn't have VID pins at all */ sio_data->skip_vid = 1; + + /* The IT8705F has a different LD number for GPIO */ + superio_select(5); + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else { int reg; @@ -1068,7 +1519,11 @@ static int __init it87_find(unsigned short *address, pr_info("it87: in3 is VCC (+5V)\n"); if (reg & (1 << 1)) pr_info("it87: in7 is VCCH (+5V Stand-By)\n"); + + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } + if (sio_data->beep_pin) + pr_info("it87: Beeping is supported\n"); /* Disable specific features based on DMI strings */ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); @@ -1093,14 +1548,46 @@ exit: return err; } +static void it87_remove_files(struct device *dev) +{ + struct it87_data *data = platform_get_drvdata(pdev); + struct it87_sio_data *sio_data = dev->platform_data; + const struct attribute_group *fan_group = it87_get_fan_group(data); + int i; + + sysfs_remove_group(&dev->kobj, &it87_group); + if (sio_data->beep_pin) + sysfs_remove_group(&dev->kobj, &it87_group_beep); + for (i = 0; i < 5; i++) { + if (!(data->has_fan & (1 << i))) + continue; + sysfs_remove_group(&dev->kobj, &fan_group[i]); + if (sio_data->beep_pin) + sysfs_remove_file(&dev->kobj, + it87_attributes_fan_beep[i]); + } + for (i = 0; i < 3; i++) { + if (sio_data->skip_pwm & (1 << 0)) + continue; + sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); + if (has_old_autopwm(data)) + sysfs_remove_group(&dev->kobj, + &it87_group_autopwm[i]); + } + if (!sio_data->skip_vid) + sysfs_remove_group(&dev->kobj, &it87_group_vid); +} + static int __devinit it87_probe(struct platform_device *pdev) { struct it87_data *data; struct resource *res; struct device *dev = &pdev->dev; struct it87_sio_data *sio_data = dev->platform_data; - int err = 0; + const struct attribute_group *fan_group; + int err = 0, i; int enable_pwm_interface; + int fan_beep_need_rw; static const char *names[] = { "it87", "it8712", @@ -1118,7 +1605,8 @@ static int __devinit it87_probe(struct platform_device *pdev) goto ERROR0; } - if (!(data = kzalloc(sizeof(struct it87_data), GFP_KERNEL))) { + data = kzalloc(sizeof(struct it87_data), GFP_KERNEL); + if (!data) { err = -ENOMEM; goto ERROR1; } @@ -1146,120 +1634,60 @@ static int __devinit it87_probe(struct platform_device *pdev) it87_init_device(pdev); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&dev->kobj, &it87_group))) + err = sysfs_create_group(&dev->kobj, &it87_group); + if (err) goto ERROR2; + if (sio_data->beep_pin) { + err = sysfs_create_group(&dev->kobj, &it87_group_beep); + if (err) + goto ERROR4; + } + /* Do not create fan files for disabled fans */ - if (has_16bit_fans(data)) { - /* 16-bit tachometers */ - if (data->has_fan & (1 << 0)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan1_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 1)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan2_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 2)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan3_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 3)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan4_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan4_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan4_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 4)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan5_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan5_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan5_alarm.dev_attr))) - goto ERROR4; - } - } else { - /* 8-bit tachometers with clock divider */ - if (data->has_fan & (1 << 0)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan1_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 1)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan2_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 2)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan3_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_alarm.dev_attr))) + fan_group = it87_get_fan_group(data); + fan_beep_need_rw = 1; + for (i = 0; i < 5; i++) { + if (!(data->has_fan & (1 << i))) + continue; + err = sysfs_create_group(&dev->kobj, &fan_group[i]); + if (err) + goto ERROR4; + + if (sio_data->beep_pin) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_fan_beep[i]); + if (err) goto ERROR4; + if (!fan_beep_need_rw) + continue; + + /* As we have a single beep enable bit for all fans, + * only the first enabled fan has a writable attribute + * for it. */ + if (sysfs_chmod_file(&dev->kobj, + it87_attributes_fan_beep[i], + S_IRUGO | S_IWUSR)) + dev_dbg(dev, "chmod +w fan%d_beep failed\n", + i + 1); + fan_beep_need_rw = 0; } } if (enable_pwm_interface) { - if (!(sio_data->skip_pwm & (1 << 0))) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm1_enable.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm1.dev_attr)) - || (err = device_create_file(dev, - &dev_attr_pwm1_freq))) - goto ERROR4; - } - if (!(sio_data->skip_pwm & (1 << 1))) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm2_enable.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm2.dev_attr)) - || (err = device_create_file(dev, - &dev_attr_pwm2_freq))) + for (i = 0; i < 3; i++) { + if (sio_data->skip_pwm & (1 << i)) + continue; + err = sysfs_create_group(&dev->kobj, + &it87_group_pwm[i]); + if (err) goto ERROR4; - } - if (!(sio_data->skip_pwm & (1 << 2))) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm3_enable.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm3.dev_attr)) - || (err = device_create_file(dev, - &dev_attr_pwm3_freq))) + + if (!has_old_autopwm(data)) + continue; + err = sysfs_create_group(&dev->kobj, + &it87_group_autopwm[i]); + if (err) goto ERROR4; } } @@ -1268,10 +1696,8 @@ static int __devinit it87_probe(struct platform_device *pdev) data->vrm = vid_which_vrm(); /* VID reading from Super-I/O config space if available */ data->vid = sio_data->vid_value; - if ((err = device_create_file(dev, - &dev_attr_vrm)) - || (err = device_create_file(dev, - &dev_attr_cpu0_vid))) + err = sysfs_create_group(&dev->kobj, &it87_group_vid); + if (err) goto ERROR4; } @@ -1284,8 +1710,7 @@ static int __devinit it87_probe(struct platform_device *pdev) return 0; ERROR4: - sysfs_remove_group(&dev->kobj, &it87_group); - sysfs_remove_group(&dev->kobj, &it87_group_opt); + it87_remove_files(dev); ERROR2: platform_set_drvdata(pdev, NULL); kfree(data); @@ -1300,8 +1725,7 @@ static int __devexit it87_remove(struct platform_device *pdev) struct it87_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &it87_group); - sysfs_remove_group(&pdev->dev.kobj, &it87_group_opt); + it87_remove_files(&pdev->dev); release_region(data->addr, IT87_EC_EXTENT); platform_set_drvdata(pdev, NULL); @@ -1387,15 +1811,18 @@ static void __devinit it87_init_device(struct platform_device *pdev) int tmp, i; u8 mask; - /* initialize to sane defaults: - * - if the chip is in manual pwm mode, this will be overwritten with - * the actual settings on the chip (so in this case, initialization - * is not needed) - * - if in automatic or on/off mode, we could switch to manual mode, - * read the registers and set manual_pwm_ctl accordingly, but currently - * this is not implemented, so we initialize to something sane */ + /* For each PWM channel: + * - If it is in automatic mode, setting to manual mode should set + * the fan to full speed by default. + * - If it is in manual mode, we need a mapping to temperature + * channels to use when later setting to automatic mode later. + * Use a 1:1 mapping by default (we are clueless.) + * In both cases, the value can (and should) be changed by the user + * prior to switching to a different mode. */ for (i = 0; i < 3; i++) { - data->manual_pwm_ctl[i] = 0xff; + data->pwm_temp_map[i] = i; + data->pwm_duty[i] = 0x7f; /* Full speed */ + data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */ } /* Some chips seem to have default value 0xff for all limit @@ -1436,7 +1863,8 @@ static void __devinit it87_init_device(struct platform_device *pdev) if ((data->fan_main_ctrl & mask) == 0) { /* Enable all fan tachometers */ data->fan_main_ctrl |= mask; - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); } data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; @@ -1461,30 +1889,32 @@ static void __devinit it87_init_device(struct platform_device *pdev) /* Fan input pins may be used for alternative functions */ data->has_fan &= ~sio_data->skip_fan; - /* Set current fan mode registers and the default settings for the - * other mode registers */ - for (i = 0; i < 3; i++) { - if (data->fan_main_ctrl & (1 << i)) { - /* pwm mode */ - tmp = it87_read_value(data, IT87_REG_PWM(i)); - if (tmp & 0x80) { - /* automatic pwm - not yet implemented, but - * leave the settings made by the BIOS alone - * until a change is requested via the sysfs - * interface */ - } else { - /* manual pwm */ - data->manual_pwm_ctl[i] = PWM_FROM_REG(tmp); - } - } - } - /* Start monitoring */ it87_write_value(data, IT87_REG_CONFIG, (it87_read_value(data, IT87_REG_CONFIG) & 0x36) | (update_vbat ? 0x41 : 0x01)); } +static void it87_update_pwm_ctrl(struct it87_data *data, int nr) +{ + data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM(nr)); + if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ + data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; + else /* Manual mode */ + data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f; + + if (has_old_autopwm(data)) { + int i; + + for (i = 0; i < 5 ; i++) + data->auto_temp[nr][i] = it87_read_value(data, + IT87_REG_AUTO_TEMP(nr, i)); + for (i = 0; i < 3 ; i++) + data->auto_pwm[nr][i] = it87_read_value(data, + IT87_REG_AUTO_PWM(nr, i)); + } +} + static struct it87_data *it87_update_device(struct device *dev) { struct it87_data *data = dev_get_drvdata(dev); @@ -1494,24 +1924,22 @@ static struct it87_data *it87_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || !data->valid) { - if (update_vbat) { /* Cleared after each update, so reenable. Value - returned by this read will be previous value */ + returned by this read will be previous value */ it87_write_value(data, IT87_REG_CONFIG, - it87_read_value(data, IT87_REG_CONFIG) | 0x40); + it87_read_value(data, IT87_REG_CONFIG) | 0x40); } for (i = 0; i <= 7; i++) { data->in[i] = - it87_read_value(data, IT87_REG_VIN(i)); + it87_read_value(data, IT87_REG_VIN(i)); data->in_min[i] = - it87_read_value(data, IT87_REG_VIN_MIN(i)); + it87_read_value(data, IT87_REG_VIN_MIN(i)); data->in_max[i] = - it87_read_value(data, IT87_REG_VIN_MAX(i)); + it87_read_value(data, IT87_REG_VIN_MAX(i)); } /* in8 (battery) has no limit registers */ - data->in[8] = - it87_read_value(data, IT87_REG_VIN(8)); + data->in[8] = it87_read_value(data, IT87_REG_VIN(8)); for (i = 0; i < 5; i++) { /* Skip disabled fans */ @@ -1519,7 +1947,7 @@ static struct it87_data *it87_update_device(struct device *dev) continue; data->fan_min[i] = - it87_read_value(data, IT87_REG_FAN_MIN[i]); + it87_read_value(data, IT87_REG_FAN_MIN[i]); data->fan[i] = it87_read_value(data, IT87_REG_FAN[i]); /* Add high byte if in 16-bit mode */ @@ -1532,11 +1960,11 @@ static struct it87_data *it87_update_device(struct device *dev) } for (i = 0; i < 3; i++) { data->temp[i] = - it87_read_value(data, IT87_REG_TEMP(i)); + it87_read_value(data, IT87_REG_TEMP(i)); data->temp_high[i] = - it87_read_value(data, IT87_REG_TEMP_HIGH(i)); + it87_read_value(data, IT87_REG_TEMP_HIGH(i)); data->temp_low[i] = - it87_read_value(data, IT87_REG_TEMP_LOW(i)); + it87_read_value(data, IT87_REG_TEMP_LOW(i)); } /* Newer chips don't have clock dividers */ @@ -1551,9 +1979,13 @@ static struct it87_data *it87_update_device(struct device *dev) it87_read_value(data, IT87_REG_ALARM1) | (it87_read_value(data, IT87_REG_ALARM2) << 8) | (it87_read_value(data, IT87_REG_ALARM3) << 16); + data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE); + data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL); + for (i = 0; i < 3; i++) + it87_update_pwm_ctrl(data, i); data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability. @@ -1628,7 +2060,7 @@ exit: static int __init sm_it87_init(void) { int err; - unsigned short isa_address=0; + unsigned short isa_address = 0; struct it87_sio_data sio_data; memset(&sio_data, 0, sizeof(struct it87_sio_data)); @@ -1640,7 +2072,7 @@ static int __init sm_it87_init(void) return err; err = it87_device_add(isa_address, &sio_data); - if (err){ + if (err) { platform_driver_unregister(&it87_driver); return err; } @@ -1661,7 +2093,8 @@ MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0); -MODULE_PARM_DESC(fix_pwm_polarity, "Force PWM polarity to active high (DANGEROUS)"); +MODULE_PARM_DESC(fix_pwm_polarity, + "Force PWM polarity to active high (DANGEROUS)"); MODULE_LICENSE("GPL"); module_init(sm_it87_init); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 7c9bdc167426..7cc2708871ab 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-2009 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org> * * 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 @@ -93,7 +93,8 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646 }; +enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646, + w83l771 }; /* * The LM90 registers @@ -151,6 +152,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info); static int lm90_probe(struct i2c_client *client, const struct i2c_device_id *id); static void lm90_init_client(struct i2c_client *client); +static void lm90_alert(struct i2c_client *client, unsigned int flag); static int lm90_remove(struct i2c_client *client); static struct lm90_data *lm90_update_device(struct device *dev); @@ -173,6 +175,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6659", max6657 }, { "max6680", max6680 }, { "max6681", max6680 }, + { "w83l771", w83l771 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -184,6 +187,7 @@ static struct i2c_driver lm90_driver = { }, .probe = lm90_probe, .remove = lm90_remove, + .alert = lm90_alert, .id_table = lm90_id, .detect = lm90_detect, .address_list = normal_i2c, @@ -201,6 +205,9 @@ struct lm90_data { int kind; int flags; + u8 config_orig; /* Original configuration register value */ + u8 alert_alarms; /* Which alarm bits trigger ALERT# */ + /* registers values */ s8 temp8[4]; /* 0: local low limit 1: local high limit @@ -758,6 +765,14 @@ static int lm90_detect(struct i2c_client *new_client, && reg_convrate <= 0x07) { name = "max6646"; } + } else + if (address == 0x4C + && man_id == 0x5C) { /* Winbond/Nuvoton */ + if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ + && (reg_config1 & 0x2A) == 0x00 + && reg_convrate <= 0x08) { + name = "w83l771"; + } } if (!name) { /* identification failed */ @@ -794,6 +809,19 @@ static int lm90_probe(struct i2c_client *new_client, new_client->flags &= ~I2C_CLIENT_PEC; } + /* Different devices have different alarm bits triggering the + * ALERT# output */ + switch (data->kind) { + case lm90: + case lm99: + case lm86: + data->alert_alarms = 0x7b; + break; + default: + data->alert_alarms = 0x7c; + break; + } + /* Initialize the LM90 chip */ lm90_init_client(new_client); @@ -830,7 +858,7 @@ exit: static void lm90_init_client(struct i2c_client *client) { - u8 config, config_orig; + u8 config; struct lm90_data *data = i2c_get_clientdata(client); /* @@ -842,7 +870,7 @@ static void lm90_init_client(struct i2c_client *client) dev_warn(&client->dev, "Initialization failed!\n"); return; } - config_orig = config; + data->config_orig = config; /* Check Temperature Range Select */ if (data->kind == adt7461) { @@ -860,7 +888,7 @@ static void lm90_init_client(struct i2c_client *client) } config &= 0xBF; /* run */ - if (config != config_orig) /* Only write if changed */ + if (config != data->config_orig) /* Only write if changed */ i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); } @@ -875,10 +903,46 @@ static int lm90_remove(struct i2c_client *client) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); + /* Restore initial configuration */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + data->config_orig); + kfree(data); return 0; } +static void lm90_alert(struct i2c_client *client, unsigned int flag) +{ + struct lm90_data *data = i2c_get_clientdata(client); + u8 config, alarms; + + lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); + if ((alarms & 0x7f) == 0) { + dev_info(&client->dev, "Everything OK\n"); + } else { + if (alarms & 0x61) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 1); + if (alarms & 0x1a) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 2); + if (alarms & 0x04) + dev_warn(&client->dev, + "temp%d diode open, please check!\n", 2); + + /* Disable ALERT# output, because these chips don't implement + SMBus alert correctly; they should only hold the alert line + low briefly. */ + if ((data->kind == adm1032 || data->kind == adt7461) + && (alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + config | 0x80); + } + } +} + static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) { int err; @@ -966,6 +1030,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) } lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); + /* Re-enable ALERT# output if it was originally enabled and + * relevant alarms are all clear */ + if ((data->config_orig & 0x80) == 0 + && (data->alarms & data->alert_alarms) == 0) { + u8 config; + + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + if (config & 0x80) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + i2c_smbus_write_byte_data(client, + LM90_REG_W_CONFIG1, + config & ~0x80); + } + } + data->last_updated = jiffies; data->valid = 1; } diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index a13b30e8d8d8..d14a1af9f550 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -134,7 +134,7 @@ struct tmp401_data { struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ - int kind; + enum chips kind; /* register values */ u8 status; @@ -524,7 +524,7 @@ static int tmp401_detect(struct i2c_client *client, if (reg > 15) return -ENODEV; - strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE); + strlcpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE); return 0; } @@ -572,8 +572,7 @@ static int tmp401_probe(struct i2c_client *client, goto exit_remove; } - dev_info(&client->dev, "Detected TI %s chip\n", - names[data->kind - 1]); + dev_info(&client->dev, "Detected TI %s chip\n", names[data->kind]); return 0; diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 4f7c051e2d7b..738c472ece27 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -61,9 +61,9 @@ static const u8 TMP421_TEMP_LSB[4] = { 0x10, 0x11, 0x12, 0x13 }; #define TMP423_DEVICE_ID 0x23 static const struct i2c_device_id tmp421_id[] = { - { "tmp421", tmp421 }, - { "tmp422", tmp422 }, - { "tmp423", tmp423 }, + { "tmp421", 2 }, + { "tmp422", 3 }, + { "tmp423", 4 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp421_id); @@ -73,21 +73,23 @@ struct tmp421_data { struct mutex update_lock; char valid; unsigned long last_updated; - int kind; + int channels; u8 config; s16 temp[4]; }; static int temp_from_s16(s16 reg) { - int temp = reg; + /* Mask out status bits */ + int temp = reg & ~0xf; return (temp * 1000 + 128) / 256; } static int temp_from_u16(u16 reg) { - int temp = reg; + /* Mask out status bits */ + int temp = reg & ~0xf; /* Add offset for extended temperature range. */ temp -= 64 * 256; @@ -107,7 +109,7 @@ static struct tmp421_data *tmp421_update_device(struct device *dev) data->config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1); - for (i = 0; i <= data->kind; i++) { + for (i = 0; i < data->channels; i++) { data->temp[i] = i2c_smbus_read_byte_data(client, TMP421_TEMP_MSB[i]) << 8; data->temp[i] |= i2c_smbus_read_byte_data(client, @@ -166,7 +168,7 @@ static mode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a, devattr = container_of(a, struct device_attribute, attr); index = to_sensor_dev_attr(devattr)->index; - if (data->kind > index) + if (index < data->channels) return a->mode; return 0; @@ -252,9 +254,9 @@ static int tmp421_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE); + strlcpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", - names[kind - 1], client->addr); + names[kind], client->addr); return 0; } @@ -271,7 +273,7 @@ static int tmp421_probe(struct i2c_client *client, i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - data->kind = id->driver_data; + data->channels = id->driver_data; err = tmp421_init_client(client); if (err) diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 9a2022b67495..9de81a4c15a2 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -3,6 +3,10 @@ Copyright (C) 2006 Winbond Electronics Corp. Yuan Mu Rudolf Marek <r.marek@assembler.cz> + Copyright (C) 2009-2010 Sven Anders <anders@anduras.de>, ANDURAS AG. + Watchdog driver part + (Based partially on fschmd driver, + Copyright 2007-2008 by Hans de Goede) 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 @@ -35,6 +39,16 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/fs.h> +#include <linux/watchdog.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/kref.h> +#include <linux/notifier.h> +#include <linux/reboot.h> + +/* Default values */ +#define WATCHDOG_TIMEOUT 2 /* 2 minute default timeout */ /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -51,6 +65,18 @@ static int reset; module_param(reset, bool, 0); MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); +static int timeout = WATCHDOG_TIMEOUT; /* default timeout in minutes */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in minutes. 2<= timeout <=255 (default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + /* Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved as ID, Bank Select registers @@ -72,6 +98,11 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define W83793_REG_VID_LATCHB 0x08 #define W83793_REG_VID_CTRL 0x59 +#define W83793_REG_WDT_LOCK 0x01 +#define W83793_REG_WDT_ENABLE 0x02 +#define W83793_REG_WDT_STATUS 0x03 +#define W83793_REG_WDT_TIMEOUT 0x04 + static u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f }; #define TEMP_READ 0 @@ -223,8 +254,37 @@ struct w83793_data { u8 tolerance[3]; /* Temp tolerance(Smart Fan I/II) */ u8 sf2_pwm[6][7]; /* Smart FanII: Fan duty cycle */ u8 sf2_temp[6][7]; /* Smart FanII: Temp level point */ + + /* watchdog */ + struct i2c_client *client; + struct mutex watchdog_lock; + struct list_head list; /* member of the watchdog_data_list */ + struct kref kref; + struct miscdevice watchdog_miscdev; + unsigned long watchdog_is_open; + char watchdog_expect_close; + char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ + unsigned int watchdog_caused_reboot; + int watchdog_timeout; /* watchdog timeout in minutes */ }; +/* Somewhat ugly :( global data pointer list with all devices, so that + we can find our device data as when using misc_register. There is no + other method to get to one's device data from the open file-op and + for usage in the reboot notifier callback. */ +static LIST_HEAD(watchdog_data_list); + +/* Note this lock not only protect list access, but also data.kref access */ +static DEFINE_MUTEX(watchdog_data_mutex); + +/* Release our data struct when we're detached from the i2c client *and* all + references to our watchdog device are released */ +static void w83793_release_resources(struct kref *ref) +{ + struct w83793_data *data = container_of(ref, struct w83793_data, kref); + kfree(data); +} + static u8 w83793_read_value(struct i2c_client *client, u16 reg); static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value); static int w83793_probe(struct i2c_client *client, @@ -1063,14 +1123,349 @@ static void w83793_init_client(struct i2c_client *client) /* Start monitoring */ w83793_write_value(client, W83793_REG_CONFIG, w83793_read_value(client, W83793_REG_CONFIG) | 0x01); +} + +/* + * Watchdog routines + */ + +static int watchdog_set_timeout(struct w83793_data *data, int timeout) +{ + int ret, mtimeout; + + mtimeout = DIV_ROUND_UP(timeout, 60); + + if (mtimeout > 255) + return -EINVAL; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + data->watchdog_timeout = mtimeout; + + /* Set Timeout value (in Minutes) */ + w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, + data->watchdog_timeout); + + ret = mtimeout * 60; + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_get_timeout(struct w83793_data *data) +{ + int timeout; + + mutex_lock(&data->watchdog_lock); + timeout = data->watchdog_timeout * 60; + mutex_unlock(&data->watchdog_lock); + + return timeout; +} + +static int watchdog_trigger(struct w83793_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + /* Set Timeout value (in Minutes) */ + w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, + data->watchdog_timeout); + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_enable(struct w83793_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + /* Set initial timeout */ + w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, + data->watchdog_timeout); + + /* Enable Soft Watchdog */ + w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0x55); + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_disable(struct w83793_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + /* Disable Soft Watchdog */ + w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0xAA); + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_open(struct inode *inode, struct file *filp) +{ + struct w83793_data *pos, *data = NULL; + int watchdog_is_open; + + /* We get called from drivers/char/misc.c with misc_mtx hold, and we + call misc_register() from w83793_probe() with watchdog_data_mutex + hold, as misc_register() takes the misc_mtx lock, this is a possible + deadlock, so we use mutex_trylock here. */ + if (!mutex_trylock(&watchdog_data_mutex)) + return -ERESTARTSYS; + list_for_each_entry(pos, &watchdog_data_list, list) { + if (pos->watchdog_miscdev.minor == iminor(inode)) { + data = pos; + break; + } + } + + /* Check, if device is already open */ + watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); + + /* Increase data reference counter (if not already done). + Note we can never not have found data, so we don't check for this */ + if (!watchdog_is_open) + kref_get(&data->kref); + + mutex_unlock(&watchdog_data_mutex); + + /* Check, if device is already open and possibly issue error */ + if (watchdog_is_open) + return -EBUSY; + + /* Enable Soft Watchdog */ + watchdog_enable(data); + + /* Store pointer to data into filp's private data */ + filp->private_data = data; + + return nonseekable_open(inode, filp); +} + +static int watchdog_close(struct inode *inode, struct file *filp) +{ + struct w83793_data *data = filp->private_data; + if (data->watchdog_expect_close) { + watchdog_disable(data); + data->watchdog_expect_close = 0; + } else { + watchdog_trigger(data); + dev_crit(&data->client->dev, + "unexpected close, not stopping watchdog!\n"); + } + + clear_bit(0, &data->watchdog_is_open); + + /* Decrease data reference counter */ + mutex_lock(&watchdog_data_mutex); + kref_put(&data->kref, w83793_release_resources); + mutex_unlock(&watchdog_data_mutex); + + return 0; +} + +static ssize_t watchdog_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + size_t ret; + struct w83793_data *data = filp->private_data; + + if (count) { + if (!nowayout) { + size_t i; + + /* Clear it in case it was set with a previous write */ + data->watchdog_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + data->watchdog_expect_close = 1; + } + } + ret = watchdog_trigger(data); + if (ret < 0) + return ret; + } + return count; +} + +static int watchdog_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_CARDRESET, + .identity = "w83793 watchdog" + }; + + int val, ret = 0; + struct w83793_data *data = filp->private_data; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (!nowayout) + ident.options |= WDIOF_MAGICCLOSE; + if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) + ret = -EFAULT; + break; + + case WDIOC_GETSTATUS: + val = data->watchdog_caused_reboot ? WDIOF_CARDRESET : 0; + ret = put_user(val, (int __user *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_KEEPALIVE: + ret = watchdog_trigger(data); + break; + + case WDIOC_GETTIMEOUT: + val = watchdog_get_timeout(data); + ret = put_user(val, (int __user *)arg); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(val, (int __user *)arg)) { + ret = -EFAULT; + break; + } + ret = watchdog_set_timeout(data, val); + if (ret > 0) + ret = put_user(ret, (int __user *)arg); + break; + + case WDIOC_SETOPTIONS: + if (get_user(val, (int __user *)arg)) { + ret = -EFAULT; + break; + } + + if (val & WDIOS_DISABLECARD) + ret = watchdog_disable(data); + else if (val & WDIOS_ENABLECARD) + ret = watchdog_enable(data); + else + ret = -EINVAL; + + break; + default: + ret = -ENOTTY; + } + + return ret; +} + +static const struct file_operations watchdog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = watchdog_open, + .release = watchdog_close, + .write = watchdog_write, + .ioctl = watchdog_ioctl, +}; + +/* + * Notifier for system down + */ + +static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + struct w83793_data *data = NULL; + + if (code == SYS_DOWN || code == SYS_HALT) { + + /* Disable each registered watchdog */ + mutex_lock(&watchdog_data_mutex); + list_for_each_entry(data, &watchdog_data_list, list) { + if (data->watchdog_miscdev.minor) + watchdog_disable(data); + } + mutex_unlock(&watchdog_data_mutex); + } + + return NOTIFY_DONE; } +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block watchdog_notifier = { + .notifier_call = watchdog_notify_sys, +}; + +/* + * Init / remove routines + */ + static int w83793_remove(struct i2c_client *client) { struct w83793_data *data = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; + int i, tmp; + + /* Unregister the watchdog (if registered) */ + if (data->watchdog_miscdev.minor) { + misc_deregister(&data->watchdog_miscdev); + + if (data->watchdog_is_open) { + dev_warn(&client->dev, + "i2c client detached with watchdog open! " + "Stopping watchdog.\n"); + watchdog_disable(data); + } + + mutex_lock(&watchdog_data_mutex); + list_del(&data->list); + mutex_unlock(&watchdog_data_mutex); + + /* Tell the watchdog code the client is gone */ + mutex_lock(&data->watchdog_lock); + data->client = NULL; + mutex_unlock(&data->watchdog_lock); + } + + /* Reset Configuration Register to Disable Watch Dog Registers */ + tmp = w83793_read_value(client, W83793_REG_CONFIG); + w83793_write_value(client, W83793_REG_CONFIG, tmp & ~0x04); + + unregister_reboot_notifier(&watchdog_notifier); hwmon_device_unregister(data->hwmon_dev); @@ -1099,7 +1494,10 @@ static int w83793_remove(struct i2c_client *client) if (data->lm75[1] != NULL) i2c_unregister_device(data->lm75[1]); - kfree(data); + /* Decrease data reference counter */ + mutex_lock(&watchdog_data_mutex); + kref_put(&data->kref, w83793_release_resources); + mutex_unlock(&watchdog_data_mutex); return 0; } @@ -1203,6 +1601,7 @@ static int w83793_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; struct w83793_data *data; int i, tmp, val, err; int files_fan = ARRAY_SIZE(w83793_left_fan) / 7; @@ -1218,6 +1617,14 @@ static int w83793_probe(struct i2c_client *client, i2c_set_clientdata(client, data); data->bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); mutex_init(&data->update_lock); + mutex_init(&data->watchdog_lock); + INIT_LIST_HEAD(&data->list); + kref_init(&data->kref); + + /* Store client pointer in our data struct for watchdog usage + (where the client is found through a data ptr instead of the + otherway around) */ + data->client = client; err = w83793_detect_subclients(client); if (err) @@ -1380,8 +1787,77 @@ static int w83793_probe(struct i2c_client *client, goto exit_remove; } + /* Watchdog initialization */ + + /* Register boot notifier */ + err = register_reboot_notifier(&watchdog_notifier); + if (err != 0) { + dev_err(&client->dev, + "cannot register reboot notifier (err=%d)\n", err); + goto exit_devunreg; + } + + /* Enable Watchdog registers. + Set Configuration Register to Enable Watch Dog Registers + (Bit 2) = XXXX, X1XX. */ + tmp = w83793_read_value(client, W83793_REG_CONFIG); + w83793_write_value(client, W83793_REG_CONFIG, tmp | 0x04); + + /* Set the default watchdog timeout */ + data->watchdog_timeout = timeout; + + /* Check, if last reboot was caused by watchdog */ + data->watchdog_caused_reboot = + w83793_read_value(data->client, W83793_REG_WDT_STATUS) & 0x01; + + /* Disable Soft Watchdog during initialiation */ + watchdog_disable(data); + + /* We take the data_mutex lock early so that watchdog_open() cannot + run when misc_register() has completed, but we've not yet added + our data to the watchdog_data_list (and set the default timeout) */ + mutex_lock(&watchdog_data_mutex); + for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { + /* Register our watchdog part */ + snprintf(data->watchdog_name, sizeof(data->watchdog_name), + "watchdog%c", (i == 0) ? '\0' : ('0' + i)); + data->watchdog_miscdev.name = data->watchdog_name; + data->watchdog_miscdev.fops = &watchdog_fops; + data->watchdog_miscdev.minor = watchdog_minors[i]; + + err = misc_register(&data->watchdog_miscdev); + if (err == -EBUSY) + continue; + if (err) { + data->watchdog_miscdev.minor = 0; + dev_err(&client->dev, + "Registering watchdog chardev: %d\n", err); + break; + } + + list_add(&data->list, &watchdog_data_list); + + dev_info(&client->dev, + "Registered watchdog chardev major 10, minor: %d\n", + watchdog_minors[i]); + break; + } + if (i == ARRAY_SIZE(watchdog_minors)) { + data->watchdog_miscdev.minor = 0; + dev_warn(&client->dev, "Couldn't register watchdog chardev " + "(due to no free minor)\n"); + } + + mutex_unlock(&watchdog_data_mutex); + return 0; + /* Unregister hwmon device */ + +exit_devunreg: + + hwmon_device_unregister(data->hwmon_dev); + /* Unregister sysfs hooks */ exit_remove: @@ -1628,7 +2104,7 @@ static void __exit sensors_w83793_exit(void) i2c_del_driver(&w83793_driver); } -MODULE_AUTHOR("Yuan Mu"); +MODULE_AUTHOR("Yuan Mu, Sven Anders"); MODULE_DESCRIPTION("w83793 driver"); MODULE_LICENSE("GPL"); |