diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 55 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 4 | ||||
-rw-r--r-- | drivers/hwmon/adcxx.c | 101 | ||||
-rw-r--r-- | drivers/hwmon/adm1021.c | 79 | ||||
-rw-r--r-- | drivers/hwmon/adm1031.c | 40 | ||||
-rw-r--r-- | drivers/hwmon/applesmc.c | 38 | ||||
-rw-r--r-- | drivers/hwmon/coretemp.c | 57 | ||||
-rw-r--r-- | drivers/hwmon/dme1737.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/fscher.c | 680 | ||||
-rw-r--r-- | drivers/hwmon/fscpos.c | 654 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d.c | 9 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d.h | 24 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d_spi.c | 47 | ||||
-rw-r--r-- | drivers/hwmon/lm70.c | 55 | ||||
-rw-r--r-- | drivers/hwmon/ltc4215.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/ltc4245.c | 3 | ||||
-rw-r--r-- | drivers/hwmon/max1111.c | 1 | ||||
-rw-r--r-- | drivers/hwmon/sht15.c | 6 | ||||
-rw-r--r-- | drivers/hwmon/wm831x-hwmon.c | 226 | ||||
-rw-r--r-- | drivers/hwmon/wm8350-hwmon.c | 151 |
20 files changed, 676 insertions, 1558 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 461abb1e273..6857560144b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -325,34 +325,6 @@ config SENSORS_F75375S This driver can also be built as a module. If so, the module will be called f75375s. -config SENSORS_FSCHER - tristate "FSC Hermes (DEPRECATED)" - depends on X86 && I2C - help - This driver is DEPRECATED please use the new merged fschmd - ("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver - instead. - - If you say yes here you get support for Fujitsu Siemens - Computers Hermes sensor chips. - - This driver can also be built as a module. If so, the module - will be called fscher. - -config SENSORS_FSCPOS - tristate "FSC Poseidon (DEPRECATED)" - depends on X86 && I2C - help - This driver is DEPRECATED please use the new merged fschmd - ("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver - instead. - - If you say yes here you get support for Fujitsu Siemens - Computers Poseidon sensor chips. - - This driver can also be built as a module. If so, the module - will be called fscpos. - config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" depends on X86 && I2C @@ -401,12 +373,12 @@ config SENSORS_GL520SM will be called gl520sm. config SENSORS_CORETEMP - tristate "Intel Core (2) Duo/Solo temperature sensor" + tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 && EXPERIMENTAL help If you say yes here you get support for the temperature - sensor inside your CPU. Supported all are all known variants - of Intel Core family. + sensor inside your CPU. Most of the family 6 CPUs + are supported. Check documentation/driver for details. config SENSORS_IBMAEM tristate "IBM Active Energy Manager temperature/power sensors and control" @@ -946,6 +918,27 @@ config SENSORS_W83627EHF This driver can also be built as a module. If so, the module will be called w83627ehf. +config SENSORS_WM831X + tristate "WM831x PMICs" + depends on MFD_WM831X + help + If you say yes here you get support for the hardware + monitoring functionality of the Wolfson Microelectronics + WM831x series of PMICs. + + This driver can also be built as a module. If so, the module + will be called wm831x-hwmon. + +config SENSORS_WM8350 + tristate "Wolfson Microelectronics WM835x" + depends on MFD_WM8350 + help + If you say yes here you get support for the hardware + monitoring features of the WM835x series of PMICs. + + This driver can also be built as a module. If so, the module + will be called wm8350-hwmon. + config SENSORS_ULTRA45 tristate "Sun Ultra45 PIC16F747" depends on SPARC64 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 2e547881bc0..9f46cb019cc 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -42,9 +42,7 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o -obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o -obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o @@ -93,6 +91,8 @@ obj-$(CONFIG_SENSORS_VT8231) += vt8231.o obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o +obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o +obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/hwmon/adcxx.c b/drivers/hwmon/adcxx.c index 242294db3db..5e9e095f113 100644 --- a/drivers/hwmon/adcxx.c +++ b/drivers/hwmon/adcxx.c @@ -43,6 +43,7 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/mutex.h> +#include <linux/mod_devicetable.h> #include <linux/spi/spi.h> #define DRVNAME "adcxx" @@ -157,8 +158,9 @@ static struct sensor_device_attribute ad_input[] = { /*----------------------------------------------------------------------*/ -static int __devinit adcxx_probe(struct spi_device *spi, int channels) +static int __devinit adcxx_probe(struct spi_device *spi) { + int channels = spi_get_device_id(spi)->driver_data; struct adcxx *adc; int status; int i; @@ -204,26 +206,6 @@ out_err: return status; } -static int __devinit adcxx1s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 1); -} - -static int __devinit adcxx2s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 2); -} - -static int __devinit adcxx4s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 4); -} - -static int __devinit adcxx8s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 8); -} - static int __devexit adcxx_remove(struct spi_device *spi) { struct adcxx *adc = dev_get_drvdata(&spi->dev); @@ -241,79 +223,33 @@ static int __devexit adcxx_remove(struct spi_device *spi) return 0; } -static struct spi_driver adcxx1s_driver = { - .driver = { - .name = "adcxx1s", - .owner = THIS_MODULE, - }, - .probe = adcxx1s_probe, - .remove = __devexit_p(adcxx_remove), +static const struct spi_device_id adcxx_ids[] = { + { "adcxx1s", 1 }, + { "adcxx2s", 2 }, + { "adcxx4s", 4 }, + { "adcxx8s", 8 }, + { }, }; +MODULE_DEVICE_TABLE(spi, adcxx_ids); -static struct spi_driver adcxx2s_driver = { +static struct spi_driver adcxx_driver = { .driver = { - .name = "adcxx2s", + .name = "adcxx", .owner = THIS_MODULE, }, - .probe = adcxx2s_probe, - .remove = __devexit_p(adcxx_remove), -}; - -static struct spi_driver adcxx4s_driver = { - .driver = { - .name = "adcxx4s", - .owner = THIS_MODULE, - }, - .probe = adcxx4s_probe, - .remove = __devexit_p(adcxx_remove), -}; - -static struct spi_driver adcxx8s_driver = { - .driver = { - .name = "adcxx8s", - .owner = THIS_MODULE, - }, - .probe = adcxx8s_probe, + .id_table = adcxx_ids, + .probe = adcxx_probe, .remove = __devexit_p(adcxx_remove), }; static int __init init_adcxx(void) { - int status; - status = spi_register_driver(&adcxx1s_driver); - if (status) - goto reg_1_failed; - - status = spi_register_driver(&adcxx2s_driver); - if (status) - goto reg_2_failed; - - status = spi_register_driver(&adcxx4s_driver); - if (status) - goto reg_4_failed; - - status = spi_register_driver(&adcxx8s_driver); - if (status) - goto reg_8_failed; - - return status; - -reg_8_failed: - spi_unregister_driver(&adcxx4s_driver); -reg_4_failed: - spi_unregister_driver(&adcxx2s_driver); -reg_2_failed: - spi_unregister_driver(&adcxx1s_driver); -reg_1_failed: - return status; + return spi_register_driver(&adcxx_driver); } static void __exit exit_adcxx(void) { - spi_unregister_driver(&adcxx1s_driver); - spi_unregister_driver(&adcxx2s_driver); - spi_unregister_driver(&adcxx4s_driver); - spi_unregister_driver(&adcxx8s_driver); + spi_unregister_driver(&adcxx_driver); } module_init(init_adcxx); @@ -322,8 +258,3 @@ module_exit(exit_adcxx); MODULE_AUTHOR("Marc Pignat"); MODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver"); MODULE_LICENSE("GPL"); - -MODULE_ALIAS("adcxx1s"); -MODULE_ALIAS("adcxx2s"); -MODULE_ALIAS("adcxx4s"); -MODULE_ALIAS("adcxx8s"); diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index b11e06f644b..afc59431812 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -83,16 +83,14 @@ struct adm1021_data { struct mutex update_lock; char valid; /* !=0 if following fields are valid */ + char low_power; /* !=0 if device in low power mode */ unsigned long last_updated; /* In jiffies */ - s8 temp_max[2]; /* Register values */ - s8 temp_min[2]; - s8 temp[2]; + int temp_max[2]; /* Register values */ + int temp_min[2]; + int temp[2]; u8 alarms; /* Special values for ADM1023 only */ - u8 remote_temp_prec; - u8 remote_temp_os_prec; - u8 remote_temp_hyst_prec; u8 remote_temp_offset; u8 remote_temp_offset_prec; }; @@ -141,7 +139,7 @@ static ssize_t show_temp(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct adm1021_data *data = adm1021_update_device(dev); - return sprintf(buf, "%d\n", 1000 * data->temp[index]); + return sprintf(buf, "%d\n", data->temp[index]); } static ssize_t show_temp_max(struct device *dev, @@ -150,7 +148,7 @@ static ssize_t show_temp_max(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct adm1021_data *data = adm1021_update_device(dev); - return sprintf(buf, "%d\n", 1000 * data->temp_max[index]); + return sprintf(buf, "%d\n", data->temp_max[index]); } static ssize_t show_temp_min(struct device *dev, @@ -159,7 +157,7 @@ static ssize_t show_temp_min(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct adm1021_data *data = adm1021_update_device(dev); - return sprintf(buf, "%d\n", 1000 * data->temp_min[index]); + return sprintf(buf, "%d\n", data->temp_min[index]); } static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, @@ -216,6 +214,35 @@ static ssize_t set_temp_min(struct device *dev, return count; } +static ssize_t show_low_power(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct adm1021_data *data = adm1021_update_device(dev); + return sprintf(buf, "%d\n", data->low_power); +} + +static ssize_t set_low_power(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1021_data *data = i2c_get_clientdata(client); + int low_power = simple_strtol(buf, NULL, 10) != 0; + + mutex_lock(&data->update_lock); + if (low_power != data->low_power) { + int config = i2c_smbus_read_byte_data( + client, ADM1021_REG_CONFIG_R); + data->low_power = low_power; + i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W, + (config & 0xBF) | (low_power << 6)); + } + mutex_unlock(&data->update_lock); + + return count; +} + + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max, 0); @@ -233,6 +260,7 @@ static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3); static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2); static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(low_power, S_IWUSR | S_IRUGO, show_low_power, set_low_power); static struct attribute *adm1021_attributes[] = { &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -247,6 +275,7 @@ static struct attribute *adm1021_attributes[] = { &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr, &dev_attr_alarms.attr, + &dev_attr_low_power.attr, NULL }; @@ -412,25 +441,27 @@ static struct adm1021_data *adm1021_update_device(struct device *dev) dev_dbg(&client->dev, "Starting adm1021 update\n"); for (i = 0; i < 2; i++) { - data->temp[i] = i2c_smbus_read_byte_data(client, - ADM1021_REG_TEMP(i)); - data->temp_max[i] = i2c_smbus_read_byte_data(client, - ADM1021_REG_TOS_R(i)); - data->temp_min[i] = i2c_smbus_read_byte_data(client, - ADM1021_REG_THYST_R(i)); + data->temp[i] = 1000 * + (s8) i2c_smbus_read_byte_data( + client, ADM1021_REG_TEMP(i)); + data->temp_max[i] = 1000 * + (s8) i2c_smbus_read_byte_data( + client, ADM1021_REG_TOS_R(i)); + data->temp_min[i] = 1000 * + (s8) i2c_smbus_read_byte_data( + client, ADM1021_REG_THYST_R(i)); } data->alarms = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS) & 0x7c; if (data->type == adm1023) { - data->remote_temp_prec = - i2c_smbus_read_byte_data(client, - ADM1023_REG_REM_TEMP_PREC); - data->remote_temp_os_prec = - i2c_smbus_read_byte_data(client, - ADM1023_REG_REM_TOS_PREC); - data->remote_temp_hyst_prec = - i2c_smbus_read_byte_data(client, - ADM1023_REG_REM_THYST_PREC); + /* The ADM1023 provides 3 extra bits of precision for + * the remote sensor in extra registers. */ + data->temp[1] += 125 * (i2c_smbus_read_byte_data( + client, ADM1023_REG_REM_TEMP_PREC) >> 5); + data->temp_max[1] += 125 * (i2c_smbus_read_byte_data( + client, ADM1023_REG_REM_TOS_PREC) >> 5); + data->temp_min[1] += 125 * (i2c_smbus_read_byte_data( + client, ADM1023_REG_REM_THYST_PREC) >> 5); data->remote_temp_offset = i2c_smbus_read_byte_data(client, ADM1023_REG_REM_OFFSET); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 789441830cd..56905955352 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -37,6 +37,7 @@ #define ADM1031_REG_PWM (0x22) #define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr)) +#define ADM1031_REG_TEMP_OFFSET(nr) (0x0d + (nr)) #define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr)) #define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4 * (nr)) #define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4 * (nr)) @@ -93,6 +94,7 @@ struct adm1031_data { u8 auto_temp_min[3]; u8 auto_temp_off[3]; u8 auto_temp_max[3]; + s8 temp_offset[3]; s8 temp_min[3]; s8 temp_max[3]; s8 temp_crit[3]; @@ -145,6 +147,10 @@ adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value) #define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125) +#define TEMP_OFFSET_TO_REG(val) (TEMP_TO_REG(val) & 0x8f) +#define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \ + (val) | 0x70 : (val)) + #define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0) static int FAN_TO_REG(int reg, int div) @@ -585,6 +591,14 @@ static ssize_t show_temp(struct device *dev, (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7)); return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext)); } +static ssize_t show_temp_offset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", + TEMP_OFFSET_FROM_REG(data->temp_offset[nr])); +} static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, char *buf) { @@ -606,6 +620,24 @@ static ssize_t show_temp_crit(struct device *dev, struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr])); } +static ssize_t set_temp_offset(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int nr = to_sensor_dev_attr(attr)->index; + int val; + + val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(val, -15000, 15000); + mutex_lock(&data->update_lock); + data->temp_offset[nr] = TEMP_OFFSET_TO_REG(val); + adm1031_write_value(client, ADM1031_REG_TEMP_OFFSET(nr), + data->temp_offset[nr]); + mutex_unlock(&data->update_lock); + return count; +} static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -661,6 +693,8 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, #define temp_reg(offset) \ static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ show_temp, NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_offset, S_IRUGO | S_IWUSR, \ + show_temp_offset, set_temp_offset, offset - 1); \ static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ show_temp_min, set_temp_min, offset - 1); \ static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ @@ -714,6 +748,7 @@ static struct attribute *adm1031_attributes[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_auto_fan1_channel.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_offset.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -721,6 +756,7 @@ static struct attribute *adm1031_attributes[] = { &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, @@ -757,6 +793,7 @@ static struct attribute *adm1031_attributes_opt[] = { &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_auto_fan2_channel.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, @@ -937,6 +974,9 @@ static struct adm1031_data *adm1031_update_device(struct device *dev) } data->temp[chan] = newh; + data->temp_offset[chan] = + adm1031_read_value(client, + ADM1031_REG_TEMP_OFFSET(chan)); data->temp_min[chan] = adm1031_read_value(client, ADM1031_REG_TEMP_MIN(chan)); diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 753b34885f9..7ea6a8f6605 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -178,6 +178,8 @@ static const int debug; static struct platform_device *pdev; static s16 rest_x; static s16 rest_y; +static u8 backlight_state[2]; + static struct device *hwmon_dev; static struct input_polled_dev *applesmc_idev; @@ -497,17 +499,36 @@ static int applesmc_probe(struct platform_device *dev) return 0; } -static int applesmc_resume(struct platform_device *dev) +/* Synchronize device with memorized backlight state */ +static int applesmc_pm_resume(struct device *dev) { - return applesmc_device_init(); + mutex_lock(&applesmc_lock); + if (applesmc_light) + applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2); + mutex_unlock(&applesmc_lock); + return 0; } +/* Reinitialize device on resume from hibernation */ +static int applesmc_pm_restore(struct device *dev) +{ + int ret = applesmc_device_init(); + if (ret) + return ret; + return applesmc_pm_resume(dev); +} + +static struct dev_pm_ops applesmc_pm_ops = { + .resume = applesmc_pm_resume, + .restore = applesmc_pm_restore, +}; + static struct platform_driver applesmc_driver = { .probe = applesmc_probe, - .resume = applesmc_resume, .driver = { .name = "applesmc", .owner = THIS_MODULE, + .pm = &applesmc_pm_ops, }, }; @@ -804,17 +825,10 @@ static ssize_t applesmc_calibrate_store(struct device *dev, return count; } -/* Store the next backlight value to be written by the work */ -static unsigned int backlight_value; - static void applesmc_backlight_set(struct work_struct *work) { - u8 buffer[2]; - mutex_lock(&applesmc_lock); - buffer[0] = backlight_value; - buffer[1] = 0x00; - applesmc_write_key(BACKLIGHT_KEY, buffer, 2); + applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2); mutex_unlock(&applesmc_lock); } static DECLARE_WORK(backlight_work, &applesmc_backlight_set); @@ -824,7 +838,7 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev, { int ret; - backlight_value = value; + backlight_state[0] = value; ret = queue_work(applesmc_led_wq, &backlight_work); if (debug && (!ret)) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 93c17223b52..caef39cda8c 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -157,17 +157,26 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * /* The 100C is default for both mobile and non mobile CPUs */ int tjmax = 100000; - int ismobile = 1; + int tjmax_ee = 85000; + int usemsr_ee = 1; int err; u32 eax, edx; /* Early chips have no MSR for TjMax */ if ((c->x86_model == 0xf) && (c->x86_mask < 4)) { - ismobile = 0; + usemsr_ee = 0; } - if ((c->x86_model > 0xe) && (ismobile)) { + /* Atoms seems to have TjMax at 90C */ + + if (c->x86_model == 0x1c) { + usemsr_ee = 0; + tjmax = 90000; + } + + if ((c->x86_model > 0xe) && (usemsr_ee)) { + u8 platform_id; /* Now we can detect the mobile CPU using Intel provided table http://softwarecommunity.intel.com/Wiki/Mobility/720.htm @@ -179,13 +188,29 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * dev_warn(dev, "Unable to access MSR 0x17, assuming desktop" " CPU\n"); - ismobile = 0; - } else if (!(eax & 0x10000000)) { - ismobile = 0; + usemsr_ee = 0; + } else if (c->x86_model < 0x17 && !(eax & 0x10000000)) { + /* Trust bit 28 up to Penryn, I could not find any + documentation on that; if you happen to know + someone at Intel please ask */ + usemsr_ee = 0; + } else { + /* Platform ID bits 52:50 (EDX starts at bit 32) */ + platform_id = (edx >> 18) & 0x7; + + /* Mobile Penryn CPU seems to be platform ID 7 or 5 + (guesswork) */ + if ((c->x86_model == 0x17) && + ((platform_id == 5) || (platform_id == 7))) { + /* If MSR EE bit is set, set it to 90 degrees C, + otherwise 105 degrees C */ + tjmax_ee = 90000; + tjmax = 105000; + } } } - if (ismobile) { + if (usemsr_ee) { err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); if (err) { @@ -193,9 +218,11 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * "Unable to access MSR 0xEE, for Tjmax, left" " at default"); } else if (eax & 0x40000000) { - tjmax = 85000; + tjmax = tjmax_ee; } - } else { + /* if we dont use msr EE it means we are desktop CPU (with exeception + of Atom) */ + } else if (tjmax == 100000) { dev_warn(dev, "Using relative temperature scale!\n"); } @@ -248,9 +275,9 @@ static int __devinit coretemp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); /* read the still undocumented IA32_TEMPERATURE_TARGET it exists - on older CPUs but not in this register */ + on older CPUs but not in this register, Atoms don't have it either */ - if (c->x86_model > 0xe) { + if ((c->x86_model > 0xe) && (c->x86_model != 0x1c)) { err = rdmsr_safe_on_cpu(data->id, 0x1a2, &eax, &edx); if (err) { dev_warn(&pdev->dev, "Unable to read" @@ -413,11 +440,15 @@ static int __init coretemp_init(void) for_each_online_cpu(i) { struct cpuinfo_x86 *c = &cpu_data(i); - /* check if family 6, models 0xe, 0xf, 0x16, 0x17, 0x1A */ + /* check if family 6, models 0xe (Pentium M DC), + 0xf (Core 2 DC 65nm), 0x16 (Core 2 SC 65nm), + 0x17 (Penryn 45nm), 0x1a (Nehalem), 0x1c (Atom), + 0x1e (Lynnfield) */ if ((c->cpuid_level < 0) || (c->x86 != 0x6) || !((c->x86_model == 0xe) || (c->x86_model == 0xf) || (c->x86_model == 0x16) || (c->x86_model == 0x17) || - (c->x86_model == 0x1A))) { + (c->x86_model == 0x1a) || (c->x86_model == 0x1c) || + (c->x86_model == 0x1e))) { /* supported CPU not found, but report the unknown family 6 CPU */ diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 9814d51b3af..2c2cb1ec94c 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -1134,7 +1134,7 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, res = PWM_FREQ_FROM_REG(data->pwm_freq[ix]); break; case SYS_PWM_ENABLE: - if (ix > 3) { + if (ix >= 3) { res = 1; /* pwm[5-6] hard-wired to manual mode */ } else { res = PWM_EN_FROM_REG(data->pwm_config[ix]); diff --git a/drivers/hwmon/fscher.c b/drivers/hwmon/fscher.c deleted file mode 100644 index 12c70e402cb..00000000000 --- a/drivers/hwmon/fscher.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - * fscher.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de> - * - * 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. - */ - -/* - * fujitsu siemens hermes chip, - * module based on fscpos.c - * Copyright (C) 2000 Hermann Jung <hej@odn.de> - * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - * and Philip Edelbrock <phil@netroedge.com> - */ - -#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/err.h> -#include <linux/mutex.h> -#include <linux/sysfs.h> - -/* - * Addresses to scan - */ - -static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; - -/* - * Insmod parameters - */ - -I2C_CLIENT_INSMOD_1(fscher); - -/* - * The FSCHER registers - */ - -/* chip identification */ -#define FSCHER_REG_IDENT_0 0x00 -#define FSCHER_REG_IDENT_1 0x01 -#define FSCHER_REG_IDENT_2 0x02 -#define FSCHER_REG_REVISION 0x03 - -/* global control and status */ -#define FSCHER_REG_EVENT_STATE 0x04 -#define FSCHER_REG_CONTROL 0x05 - -/* watchdog */ -#define FSCHER_REG_WDOG_PRESET 0x28 -#define FSCHER_REG_WDOG_STATE 0x23 -#define FSCHER_REG_WDOG_CONTROL 0x21 - -/* fan 0 */ -#define FSCHER_REG_FAN0_MIN 0x55 -#define FSCHER_REG_FAN0_ACT 0x0e -#define FSCHER_REG_FAN0_STATE 0x0d -#define FSCHER_REG_FAN0_RIPPLE 0x0f - -/* fan 1 */ -#define FSCHER_REG_FAN1_MIN 0x65 -#define FSCHER_REG_FAN1_ACT 0x6b -#define FSCHER_REG_FAN1_STATE 0x62 -#define FSCHER_REG_FAN1_RIPPLE 0x6f - -/* fan 2 */ -#define FSCHER_REG_FAN2_MIN 0xb5 -#define FSCHER_REG_FAN2_ACT 0xbb -#define FSCHER_REG_FAN2_STATE 0xb2 -#define FSCHER_REG_FAN2_RIPPLE 0xbf - -/* voltage supervision */ -#define FSCHER_REG_VOLT_12 0x45 -#define FSCHER_REG_VOLT_5 0x42 -#define FSCHER_REG_VOLT_BATT 0x48 - -/* temperature 0 */ -#define FSCHER_REG_TEMP0_ACT 0x64 -#define FSCHER_REG_TEMP0_STATE 0x71 - -/* temperature 1 */ -#define FSCHER_REG_TEMP1_ACT 0x32 -#define FSCHER_REG_TEMP1_STATE 0x81 - -/* temperature 2 */ -#define FSCHER_REG_TEMP2_ACT 0x35 -#define FSCHER_REG_TEMP2_STATE 0x91 - -/* - * Functions declaration - */ - -static int fscher_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int fscher_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info); -static int fscher_remove(struct i2c_client *client); -static struct fscher_data *fscher_update_device(struct device *dev); -static void fscher_init_client(struct i2c_client *client); - -static int fscher_read_value(struct i2c_client *client, u8 reg); -static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id fscher_id[] = { - { "fscher", fscher }, - { } -}; - -static struct i2c_driver fscher_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "fscher", - }, - .probe = fscher_probe, - .remove = fscher_remove, - .id_table = fscher_id, - .detect = fscher_detect, - .address_data = &addr_data, -}; - -/* - * Client data (each client gets its own) - */ - -struct fscher_data { - struct device *hwmon_dev; - struct mutex update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* register values */ - u8 revision; /* revision of chip */ - u8 global_event; /* global event status */ - u8 global_control; /* global control register */ - u8 watchdog[3]; /* watchdog */ - u8 volt[3]; /* 12, 5, battery voltage */ - u8 temp_act[3]; /* temperature */ - u8 temp_status[3]; /* status of sensor */ - u8 fan_act[3]; /* fans revolutions per second */ - u8 fan_status[3]; /* fan status */ - u8 fan_min[3]; /* fan min value for rps */ - u8 fan_ripple[3]; /* divider for rps */ -}; - -/* - * Sysfs stuff - */ - -#define sysfs_r(kind, sub, offset, reg) \ -static ssize_t show_##kind##sub (struct fscher_data *, char *, int); \ -static ssize_t show_##kind##offset##sub (struct device *, struct device_attribute *attr, char *); \ -static ssize_t show_##kind##offset##sub (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct fscher_data *data = fscher_update_device(dev); \ - return show_##kind##sub(data, buf, (offset)); \ -} - -#define sysfs_w(kind, sub, offset, reg) \ -static ssize_t set_##kind##sub (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \ -static ssize_t set_##kind##offset##sub (struct device *, struct device_attribute *attr, const char *, size_t); \ -static ssize_t set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscher_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, (offset), reg); \ -} - -#define sysfs_rw_n(kind, sub, offset, reg) \ -sysfs_r(kind, sub, offset, reg) \ -sysfs_w(kind, sub, offset, reg) \ -static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, show_##kind##offset##sub, set_##kind##offset##sub); - -#define sysfs_rw(kind, sub, reg) \ -sysfs_r(kind, sub, 0, reg) \ -sysfs_w(kind, sub, 0, reg) \ -static DEVICE_ATTR(kind##sub, S_IRUGO | S_IWUSR, show_##kind##0##sub, set_##kind##0##sub); - -#define sysfs_ro_n(kind, sub, offset, reg) \ -sysfs_r(kind, sub, offset, reg) \ -static DEVICE_ATTR(kind##offset##sub, S_IRUGO, show_##kind##offset##sub, NULL); - -#define sysfs_ro(kind, sub, reg) \ -sysfs_r(kind, sub, 0, reg) \ -static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0##sub, NULL); - -#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \ -sysfs_rw_n(pwm, , offset, reg_min) \ -sysfs_rw_n(fan, _status, offset, reg_status) \ -sysfs_rw_n(fan, _div , offset, reg_ripple) \ -sysfs_ro_n(fan, _input , offset, reg_act) - -#define sysfs_temp(offset, reg_status, reg_act) \ -sysfs_rw_n(temp, _status, offset, reg_status) \ -sysfs_ro_n(temp, _input , offset, reg_act) - -#define sysfs_in(offset, reg_act) \ -sysfs_ro_n(in, _input, offset, reg_act) - -#define sysfs_revision(reg_revision) \ -sysfs_ro(revision, , reg_revision) - -#define sysfs_alarms(reg_events) \ -sysfs_ro(alarms, , reg_events) - -#define sysfs_control(reg_control) \ -sysfs_rw(control, , reg_control) - -#define sysfs_watchdog(reg_control, reg_status, reg_preset) \ -sysfs_rw(watchdog, _control, reg_control) \ -sysfs_rw(watchdog, _status , reg_status) \ -sysfs_rw(watchdog, _preset , reg_preset) - -sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN, - FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT) -sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN, - FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT) -sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN, - FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT) - -sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT) -sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT) -sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT) - -sysfs_in(0, FSCHER_REG_VOLT_12) -sysfs_in(1, FSCHER_REG_VOLT_5) -sysfs_in(2, FSCHER_REG_VOLT_BATT) - -sysfs_revision(FSCHER_REG_REVISION) -sysfs_alarms(FSCHER_REG_EVENTS) -sysfs_control(FSCHER_REG_CONTROL) -sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET) - -static struct attribute *fscher_attributes[] = { - &dev_attr_revision.attr, - &dev_attr_alarms.attr, - &dev_attr_control.attr, - - &dev_attr_watchdog_status.attr, - &dev_attr_watchdog_control.attr, - &dev_attr_watchdog_preset.attr, - - &dev_attr_in0_input.attr, - &dev_attr_in1_input.attr, - &dev_attr_in2_input.attr, - - &dev_attr_fan1_status.attr, - &dev_attr_fan1_div.attr, - &dev_attr_fan1_input.attr, - &dev_attr_pwm1.attr, - &dev_attr_fan2_status.attr, - &dev_attr_fan2_div.attr, - &dev_attr_fan2_input.attr, - &dev_attr_pwm2.attr, - &dev_attr_fan3_status.attr, - &dev_attr_fan3_div.attr, - &dev_attr_fan3_input.attr, - &dev_attr_pwm3.attr, - - &dev_attr_temp1_status.attr, - &dev_attr_temp1_input.attr, - &dev_attr_temp2_status.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp3_status.attr, - &dev_attr_temp3_input.attr, - NULL -}; - -static const struct attribute_group fscher_group = { - .attrs = fscher_attributes, -}; - -/* - * Real code - */ - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int fscher_detect(struct i2c_client *new_client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = new_client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - /* Do the remaining detection unless force or force_fscher parameter */ - if (kind < 0) { - if ((i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_0) != 0x48) /* 'H' */ - || (i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_1) != 0x45) /* 'E' */ - || (i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */ - return -ENODEV; - } - - strlcpy(info->type, "fscher", I2C_NAME_SIZE); - - return 0; -} - -static int fscher_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct fscher_data *data; - int err; - - data = kzalloc(sizeof(struct fscher_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - fscher_init_client(new_client); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &fscher_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &fscher_group); -exit_free: - kfree(data); -exit: - return err; -} - -static int fscher_remove(struct i2c_client *client) -{ - struct fscher_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &fscher_group); - - kfree(data); - return 0; -} - -static int fscher_read_value(struct i2c_client *client, u8 reg) -{ - dev_dbg(&client->dev, "read reg 0x%02x\n", reg); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n", - reg, value); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new FSC Hermes. */ -static void fscher_init_client(struct i2c_client *client) -{ - struct fscher_data *data = i2c_get_clientdata(client); - - /* Read revision from chip */ - data->revision = fscher_read_value(client, FSCHER_REG_REVISION); -} - -static struct fscher_data *fscher_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fscher_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - - dev_dbg(&client->dev, "Starting fscher update\n"); - - data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT); - data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT); - data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT); - data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE); - data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE); - data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE); - - data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12); - data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5); - data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT); - - data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT); - data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT); - data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT); - data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE); - data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE); - data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE); - data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN); - data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN); - data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN); - data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE); - data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE); - data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE); - - data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET); - data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE); - data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL); - - data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE); - data->global_control = fscher_read_value(client, - FSCHER_REG_CONTROL); - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} - - - -#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1) - -static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04; - - mutex_lock(&data->update_lock); - data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04); -} - -static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - - mutex_lock(&data->update_lock); - data->fan_min[FAN_INDEX_FROM_NUM(nr)] = v > 0xff ? 0xff : v; - fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_pwm(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]); -} - -static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* supported values: 2, 4, 8 */ - unsigned long v = simple_strtoul(buf, NULL, 10); - - switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; - default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 2, 4 or 8!\n", v); - return -EINVAL; - } - - mutex_lock(&data->update_lock); - - /* bits 2..7 reserved => mask with 0x03 */ - data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03; - data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v; - - fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03)); -} - -#define RPM_FROM_REG(val) (val*60) - -static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)])); -} - - - -#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1) - -static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 2..7 reserved, 0 read only => mask with 0x02 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - mutex_lock(&data->update_lock); - data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03); -} - -#define TEMP_FROM_REG(val) (((val) - 128) * 1000) - -static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)])); -} - -/* - * The final conversion is specified in sensors.conf, as it depends on - * mainboard specific values. We export the registers contents as - * pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much - * sense per se, but it minimizes the conversions count and keeps the - * values within a usual range. - */ -#define VOLT_FROM_REG(val) ((val) * 10) - -static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr])); -} - - - -static ssize_t show_revision(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->revision); -} - - - -static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2, 5..6 reserved => mask with 0x9b */ - return sprintf(buf, "%u\n", data->global_event & 0x9b); -} - - - -static ssize_t set_control(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 1..7 reserved => mask with 0x01 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01; - - mutex_lock(&data->update_lock); - data->global_control = v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_control(struct fscher_data *data, char *buf, int nr) -{ - /* bits 1..7 reserved => mask with 0x01 */ - return sprintf(buf, "%u\n", data->global_control & 0x01); -} - - - -static ssize_t set_watchdog_control(struct i2c_client *client, struct - fscher_data *data, const char *buf, size_t count, - int nr, int reg) -{ - /* bits 0..3 reserved => mask with 0xf0 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; - - mutex_lock(&data->update_lock); - data->watchdog[2] &= ~0xf0; - data->watchdog[2] |= v; - fscher_write_value(client, reg, data->watchdog[2]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */ - return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0); -} - -static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - mutex_lock(&data->update_lock); - data->watchdog[1] &= ~v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - return sprintf(buf, "%u\n", data->watchdog[1] & 0x02); -} - -static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; - - mutex_lock(&data->update_lock); - data->watchdog[0] = v; - fscher_write_value(client, reg, data->watchdog[0]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->watchdog[0]); -} - -static int __init sensors_fscher_init(void) -{ - return i2c_add_driver(&fscher_driver); -} - -static void __exit sensors_fscher_exit(void) -{ - i2c_del_driver(&fscher_driver); -} - -MODULE_AUTHOR("Reinhard Nissl <rnissl@gmx.de>"); -MODULE_DESCRIPTION("FSC Hermes driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_fscher_init); -module_exit(sensors_fscher_exit); diff --git a/drivers/hwmon/fscpos.c b/drivers/hwmon/fscpos.c deleted file mode 100644 index 8a7bcf500b4..00000000000 --- a/drivers/hwmon/fscpos.c +++ /dev/null @@ -1,654 +0,0 @@ -/* - fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips - Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch> - - 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. -*/ - -/* - fujitsu siemens poseidon chip, - module based on the old fscpos module by Hermann Jung <hej@odn.de> and - the fscher module by Reinhard Nissl <rnissl@gmx.de> - - original module based on lm80.c - Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - and Philip Edelbrock <phil@netroedge.com> - - Thanks to Jean Delvare for reviewing my code and suggesting a lot of - improvements. -*/ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/jiffies.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/hwmon.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/sysfs.h> - -/* - * Addresses to scan - */ -static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; - -/* - * Insmod parameters - */ -I2C_CLIENT_INSMOD_1(fscpos); - -/* - * The FSCPOS registers - */ - -/* chip identification */ -#define FSCPOS_REG_IDENT_0 0x00 -#define FSCPOS_REG_IDENT_1 0x01 -#define FSCPOS_REG_IDENT_2 0x02 -#define FSCPOS_REG_REVISION 0x03 - -/* global control and status */ -#define FSCPOS_REG_EVENT_STATE 0x04 -#define FSCPOS_REG_CONTROL 0x05 - -/* watchdog */ -#define FSCPOS_REG_WDOG_PRESET 0x28 -#define FSCPOS_REG_WDOG_STATE 0x23 -#define FSCPOS_REG_WDOG_CONTROL 0x21 - -/* voltages */ -#define FSCPOS_REG_VOLT_12 0x45 -#define FSCPOS_REG_VOLT_5 0x42 -#define FSCPOS_REG_VOLT_BATT 0x48 - -/* fans - the chip does not support minimum speed for fan2 */ -static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 }; -static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab }; -static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 }; -static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf }; - -/* temperatures */ -static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 }; -static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 }; - -/* - * Functions declaration - */ -static int fscpos_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int fscpos_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info); -static int fscpos_remove(struct i2c_client *client); - -static int fscpos_read_value(struct i2c_client *client, u8 reg); -static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value); -static struct fscpos_data *fscpos_update_device(struct device *dev); -static void fscpos_init_client(struct i2c_client *client); - -static void reset_fan_alarm(struct i2c_client *client, int nr); - -/* - * Driver data (common to all clients) - */ -static const struct i2c_device_id fscpos_id[] = { - { "fscpos", fscpos }, - { } -}; - -static struct i2c_driver fscpos_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "fscpos", - }, - .probe = fscpos_probe, - .remove = fscpos_remove, - .id_table = fscpos_id, - .detect = fscpos_detect, - .address_data = &addr_data, -}; - -/* - * Client data (each client gets its own) - */ -struct fscpos_data { - struct device *hwmon_dev; - struct mutex update_lock; - char valid; /* 0 until following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - /* register values */ - u8 revision; /* revision of chip */ - u8 global_event; /* global event status */ - u8 global_control; /* global control register */ - u8 wdog_control; /* watchdog control */ - u8 wdog_state; /* watchdog status */ - u8 wdog_preset; /* watchdog preset */ - u8 volt[3]; /* 12, 5, battery current */ - u8 temp_act[3]; /* temperature */ - u8 temp_status[3]; /* status of sensor */ - u8 fan_act[3]; /* fans revolutions per second */ - u8 fan_status[3]; /* fan status */ - u8 pwm[2]; /* fan min value for rps */ - u8 fan_ripple[3]; /* divider for rps */ -}; - -/* Temperature */ -#define TEMP_FROM_REG(val) (((val) - 128) * 1000) - -static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1])); -} - -static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03); -} - -static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "1\n"); -} - -static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - if (v != 1) { - dev_err(&client->dev, "temp_reset value %ld not supported. " - "Use 1 to reset the alarm!\n", v); - return -EINVAL; - } - - dev_info(&client->dev, "You used the temp_reset feature which has not " - "been proplerly tested. Please report your " - "experience to the module author.\n"); - - /* Supported value: 2 (clears the status) */ - fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr - 1], 2); - return count; -} - -/* Fans */ -#define RPM_FROM_REG(val) ((val) * 60) - -static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04); -} - -static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1])); -} - -static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03); -} - -static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int nr, int reg) -{ - /* supported values: 2, 4, 8 */ - unsigned long v = simple_strtoul(buf, NULL, 10); - - switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; - default: - dev_err(&client->dev, "fan_ripple value %ld not supported. " - "Must be one of 2, 4 or 8!\n", v); - return -EINVAL; - } - - mutex_lock(&data->update_lock); - /* bits 2..7 reserved => mask with 0x03 */ - data->fan_ripple[nr - 1] &= ~0x03; - data->fan_ripple[nr - 1] |= v; - - fscpos_write_value(client, reg, data->fan_ripple[nr - 1]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->pwm[nr - 1]); -} - -static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - - /* Range: 0..255 */ - if (v < 0) v = 0; - if (v > 255) v = 255; - - mutex_lock(&data->update_lock); - data->pwm[nr - 1] = v; - fscpos_write_value(client, reg, data->pwm[nr - 1]); - mutex_unlock(&data->update_lock); - return count; -} - -static void reset_fan_alarm(struct i2c_client *client, int nr) -{ - fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4); -} - -/* Volts */ -#define VOLT_FROM_REG(val, mult) ((val) * (mult) / 255) - -static ssize_t show_volt_12(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200)); -} - -static ssize_t show_volt_5(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600)); -} - -static ssize_t show_volt_batt(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300)); -} - -/* Watchdog */ -static ssize_t show_wdog_control(struct fscpos_data *data, char *buf) -{ - /* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */ - return sprintf(buf, "%u\n", data->wdog_control & 0xb0); -} - -static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - /* bits 0..3 reserved => mask with 0xf0 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; - - mutex_lock(&data->update_lock); - data->wdog_control &= ~0xf0; - data->wdog_control |= v; - fscpos_write_value(client, reg, data->wdog_control); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_wdog_state(struct fscpos_data *data, char *buf) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - return sprintf(buf, "%u\n", data->wdog_state & 0x02); -} - -static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - /* Valid values: 2 (clear) */ - if (v != 2) { - dev_err(&client->dev, "wdog_state value %ld not supported. " - "Must be 2 to clear the state!\n", v); - return -EINVAL; - } - - mutex_lock(&data->update_lock); - data->wdog_state &= ~v; - fscpos_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf) -{ - return sprintf(buf, "%u\n", data->wdog_preset); -} - -static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; - - mutex_lock(&data->update_lock); - data->wdog_preset = v; - fscpos_write_value(client, reg, data->wdog_preset); - mutex_unlock(&data->update_lock); - return count; -} - -/* Event */ -static ssize_t show_event(struct device *dev, struct device_attribute *attr, char *buf) -{ - /* bits 5..7 reserved => mask with 0x1f */ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", data->global_event & 0x9b); -} - -/* - * Sysfs stuff - */ -#define create_getter(kind, sub) \ - static ssize_t sysfs_show_##kind##sub(struct device *dev, struct device_attribute *attr, char *buf) \ - { \ - struct fscpos_data *data = fscpos_update_device(dev); \ - return show_##kind##sub(data, buf); \ - } - -#define create_getter_n(kind, offset, sub) \ - static ssize_t sysfs_show_##kind##offset##sub(struct device *dev, struct device_attribute *attr, char\ - *buf) \ - { \ - struct fscpos_data *data = fscpos_update_device(dev); \ - return show_##kind##sub(data, buf, offset); \ - } - -#define create_setter(kind, sub, reg) \ - static ssize_t sysfs_set_##kind##sub (struct device *dev, struct device_attribute *attr, const char \ - *buf, size_t count) \ - { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscpos_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, reg); \ - } - -#define create_setter_n(kind, offset, sub, reg) \ - static ssize_t sysfs_set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ - { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscpos_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, offset, reg);\ - } - -#define create_sysfs_device_ro(kind, sub, offset) \ - static DEVICE_ATTR(kind##offset##sub, S_IRUGO, \ - sysfs_show_##kind##offset##sub, NULL); - -#define create_sysfs_device_rw(kind, sub, offset) \ - static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, \ - sysfs_show_##kind##offset##sub, sysfs_set_##kind##offset##sub); - -#define sysfs_ro_n(kind, sub, offset) \ - create_getter_n(kind, offset, sub); \ - create_sysfs_device_ro(kind, sub, offset); - -#define sysfs_rw_n(kind, sub, offset, reg) \ - create_getter_n(kind, offset, sub); \ - create_setter_n(kind, offset, sub, reg); \ - create_sysfs_device_rw(kind, sub, offset); - -#define sysfs_rw(kind, sub, reg) \ - create_getter(kind, sub); \ - create_setter(kind, sub, reg); \ - create_sysfs_device_rw(kind, sub,); - -#define sysfs_fan_with_min(offset, reg_status, reg_ripple, reg_min) \ - sysfs_fan(offset, reg_status, reg_ripple); \ - sysfs_rw_n(pwm,, offset, reg_min); - -#define sysfs_fan(offset, reg_status, reg_ripple) \ - sysfs_ro_n(fan, _input, offset); \ - sysfs_ro_n(fan, _status, offset); \ - sysfs_rw_n(fan, _ripple, offset, reg_ripple); - -#define sysfs_temp(offset, reg_status) \ - sysfs_ro_n(temp, _input, offset); \ - sysfs_ro_n(temp, _status, offset); \ - sysfs_rw_n(temp, _reset, offset, reg_status); - -#define sysfs_watchdog(reg_wdog_preset, reg_wdog_state, reg_wdog_control) \ - sysfs_rw(wdog, _control, reg_wdog_control); \ - sysfs_rw(wdog, _preset, reg_wdog_preset); \ - sysfs_rw(wdog, _state, reg_wdog_state); - -sysfs_fan_with_min(1, FSCPOS_REG_FAN_STATE[0], FSCPOS_REG_FAN_RIPPLE[0], - FSCPOS_REG_PWM[0]); -sysfs_fan_with_min(2, FSCPOS_REG_FAN_STATE[1], FSCPOS_REG_FAN_RIPPLE[1], - FSCPOS_REG_PWM[1]); -sysfs_fan(3, FSCPOS_REG_FAN_STATE[2], FSCPOS_REG_FAN_RIPPLE[2]); - -sysfs_temp(1, FSCPOS_REG_TEMP_STATE[0]); -sysfs_temp(2, FSCPOS_REG_TEMP_STATE[1]); -sysfs_temp(3, FSCPOS_REG_TEMP_STATE[2]); - -sysfs_watchdog(FSCPOS_REG_WDOG_PRESET, FSCPOS_REG_WDOG_STATE, - FSCPOS_REG_WDOG_CONTROL); - -static DEVICE_ATTR(event, S_IRUGO, show_event, NULL); -static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL); -static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL); -static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL); - -static struct attribute *fscpos_attributes[] = { - &dev_attr_event.attr, - &dev_attr_in0_input.attr, - &dev_attr_in1_input.attr, - &dev_attr_in2_input.attr, - - &dev_attr_wdog_control.attr, - &dev_attr_wdog_preset.attr, - &dev_attr_wdog_state.attr, - - &dev_attr_temp1_input.attr, - &dev_attr_temp1_status.attr, - &dev_attr_temp1_reset.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp2_status.attr, - &dev_attr_temp2_reset.attr, - &dev_attr_temp3_input.attr, - &dev_attr_temp3_status.attr, - &dev_attr_temp3_reset.attr, - - &dev_attr_fan1_input.attr, - &dev_attr_fan1_status.attr, - &dev_attr_fan1_ripple.attr, - &dev_attr_pwm1.attr, - &dev_attr_fan2_input.attr, - &dev_attr_fan2_status.attr, - &dev_attr_fan2_ripple.attr, - &dev_attr_pwm2.attr, - &dev_attr_fan3_input.attr, - &dev_attr_fan3_status.attr, - &dev_attr_fan3_ripple.attr, - NULL -}; - -static const struct attribute_group fscpos_group = { - .attrs = fscpos_attributes, -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int fscpos_detect(struct i2c_client *new_client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = new_client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - /* Do the remaining detection unless force or force_fscpos parameter */ - if (kind < 0) { - if ((fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) - != 0x50) /* 'P' */ - || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) - != 0x45) /* 'E' */ - || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) - != 0x47))/* 'G' */ - return -ENODEV; - } - - strlcpy(info->type, "fscpos", I2C_NAME_SIZE); - - return 0; -} - -static int fscpos_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct fscpos_data *data; - int err; - - data = kzalloc(sizeof(struct fscpos_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Inizialize the fscpos chip */ - fscpos_init_client(new_client); - - /* Announce that the chip was found */ - dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &fscpos_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &fscpos_group); -exit_free: - kfree(data); -exit: - return err; -} - -static int fscpos_remove(struct i2c_client *client) -{ - struct fscpos_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &fscpos_group); - - kfree(data); - return 0; -} - -static int fscpos_read_value(struct i2c_client *client, u8 reg) -{ - dev_dbg(&client->dev, "Read reg 0x%02x\n", reg); - return i2c_smbus_read_byte_data(client, reg); -} - -static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - dev_dbg(&client->dev, "Write reg 0x%02x, val 0x%02x\n", reg, value); - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new FSCPOS chip */ -static void fscpos_init_client(struct i2c_client *client) -{ - struct fscpos_data *data = i2c_get_clientdata(client); - - /* read revision from chip */ - data->revision = fscpos_read_value(client, FSCPOS_REG_REVISION); -} - -static struct fscpos_data *fscpos_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fscpos_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - int i; - - dev_dbg(&client->dev, "Starting fscpos update\n"); - - for (i = 0; i < 3; i++) { - data->temp_act[i] = fscpos_read_value(client, - FSCPOS_REG_TEMP_ACT[i]); - data->temp_status[i] = fscpos_read_value(client, - FSCPOS_REG_TEMP_STATE[i]); - data->fan_act[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_ACT[i]); - data->fan_status[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_STATE[i]); - data->fan_ripple[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_RIPPLE[i]); - if (i < 2) { - /* fan2_min is not supported by the chip */ - data->pwm[i] = fscpos_read_value(client, - FSCPOS_REG_PWM[i]); - } - /* reset fan status if speed is back to > 0 */ - if (data->fan_status[i] != 0 && data->fan_act[i] > 0) { - reset_fan_alarm(client, i); - } - } - - data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12); - data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5); - data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT); - - data->wdog_preset = fscpos_read_value(client, - FSCPOS_REG_WDOG_PRESET); - data->wdog_state = fscpos_read_value(client, - FSCPOS_REG_WDOG_STATE); - data->wdog_control = fscpos_read_value(client, - FSCPOS_REG_WDOG_CONTROL); - - data->global_event = fscpos_read_value(client, - FSCPOS_REG_EVENT_STATE); - - data->last_updated = jiffies; - data->valid = 1; - } - mutex_unlock(&data->update_lock); - return data; -} - -static int __init sm_fscpos_init(void) -{ - return i2c_add_driver(&fscpos_driver); -} - -static void __exit sm_fscpos_exit(void) -{ - i2c_del_driver(&fscpos_driver); -} - -MODULE_AUTHOR("Stefan Ott <stefan@desire.ch> based on work from Hermann Jung " - "<hej@odn.de>, Frodo Looijaard <frodol@dds.nl>" - " and Philip Edelbrock <phil@netroedge.com>"); -MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver"); -MODULE_LICENSE("GPL"); - -module_init(sm_fscpos_init); -module_exit(sm_fscpos_exit); diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 271338bdb6b..cf5afb9a10a 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -454,6 +454,15 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) (p->click_thresh_y << 4)); } + if (p->wakeup_flags && (dev->whoami == LIS_SINGLE_ID)) { + dev->write(dev, FF_WU_CFG_1, p->wakeup_flags); + dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f); + /* default to 2.5ms for now */ + dev->write(dev, FF_WU_DURATION_1, 1); + /* enable high pass filter for both free-fall units */ + dev->write(dev, CTRL_REG2, HP_FF_WU1 | HP_FF_WU2); + } + if (p->irq_cfg) dev->write(dev, CTRL_REG3, p->irq_cfg); } diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index e320e2f511f..3e1ff46f72d 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -58,15 +58,17 @@ enum lis3_reg { OUTZ_L = 0x2C, OUTZ_H = 0x2D, OUTZ = 0x2D, - FF_WU_CFG = 0x30, - FF_WU_SRC = 0x31, - FF_WU_ACK = 0x32, - FF_WU_THS_L = 0x34, - FF_WU_THS_H = 0x35, - FF_WU_DURATION = 0x36, }; enum lis302d_reg { + FF_WU_CFG_1 = 0x30, + FF_WU_SRC_1 = 0x31, + FF_WU_THS_1 = 0x32, + FF_WU_DURATION_1 = 0x33, + FF_WU_CFG_2 = 0x34, + FF_WU_SRC_2 = 0x35, + FF_WU_THS_2 = 0x36, + FF_WU_DURATION_2 = 0x37, CLICK_CFG = 0x38, CLICK_SRC = 0x39, CLICK_THSY_X = 0x3B, @@ -77,6 +79,12 @@ enum lis302d_reg { }; enum lis3lv02d_reg { + FF_WU_CFG = 0x30, + FF_WU_SRC = 0x31, + FF_WU_ACK = 0x32, + FF_WU_THS_L = 0x34, + FF_WU_THS_H = 0x35, + FF_WU_DURATION = 0x36, DD_CFG = 0x38, DD_SRC = 0x39, DD_ACK = 0x3A, @@ -107,6 +115,10 @@ enum lis3lv02d_ctrl2 { CTRL2_FS = 0x80, /* Full Scale selection */ }; +enum lis302d_ctrl2 { + HP_FF_WU2 = 0x08, + HP_FF_WU1 = 0x04, +}; enum lis3lv02d_ctrl3 { CTRL3_CFS0 = 0x01, diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c index 3827ff04485..ecd739534f6 100644 --- a/drivers/hwmon/lis3lv02d_spi.c +++ b/drivers/hwmon/lis3lv02d_spi.c @@ -66,17 +66,16 @@ static int __devinit lis302dl_spi_probe(struct spi_device *spi) if (ret < 0) return ret; - lis3_dev.bus_priv = spi; - lis3_dev.init = lis3_spi_init; - lis3_dev.read = lis3_spi_read; - lis3_dev.write = lis3_spi_write; - lis3_dev.irq = spi->irq; - lis3_dev.ac = lis3lv02d_axis_normal; - lis3_dev.pdata = spi->dev.platform_data; + lis3_dev.bus_priv = spi; + lis3_dev.init = lis3_spi_init; + lis3_dev.read = lis3_spi_read; + lis3_dev.write = lis3_spi_write; + lis3_dev.irq = spi->irq; + lis3_dev.ac = lis3lv02d_axis_normal; + lis3_dev.pdata = spi->dev.platform_data; spi_set_drvdata(spi, &lis3_dev); - ret = lis3lv02d_init_device(&lis3_dev); - return ret; + return lis3lv02d_init_device(&lis3_dev); } static int __devexit lis302dl_spi_remove(struct spi_device *spi) @@ -87,6 +86,32 @@ static int __devexit lis302dl_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM +static int lis3lv02d_spi_suspend(struct spi_device *spi, pm_message_t mesg) +{ + struct lis3lv02d *lis3 = spi_get_drvdata(spi); + + if (!lis3->pdata->wakeup_flags) + lis3lv02d_poweroff(&lis3_dev); + + return 0; +} + +static int lis3lv02d_spi_resume(struct spi_device *spi) +{ + struct lis3lv02d *lis3 = spi_get_drvdata(spi); + + if (!lis3->pdata->wakeup_flags) + lis3lv02d_poweron(lis3); + + return 0; +} + +#else +#define lis3lv02d_spi_suspend NULL +#define lis3lv02d_spi_resume NULL +#endif + static struct spi_driver lis302dl_spi_driver = { .driver = { .name = DRV_NAME, @@ -94,6 +119,8 @@ static struct spi_driver lis302dl_spi_driver = { }, .probe = lis302dl_spi_probe, .remove = __devexit_p(lis302dl_spi_remove), + .suspend = lis3lv02d_spi_suspend, + .resume = lis3lv02d_spi_resume, }; static int __init lis302dl_init(void) @@ -112,4 +139,4 @@ module_exit(lis302dl_exit); MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); MODULE_DESCRIPTION("lis3lv02d SPI glue layer"); MODULE_LICENSE("GPL"); - +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index ae6204f3321..ab8a5d3c769 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -32,6 +32,7 @@ #include <linux/sysfs.h> #include <linux/hwmon.h> #include <linux/mutex.h> +#include <linux/mod_devicetable.h> #include <linux/spi/spi.h> @@ -130,11 +131,20 @@ static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); /*----------------------------------------------------------------------*/ -static int __devinit common_probe(struct spi_device *spi, int chip) +static int __devinit lm70_probe(struct spi_device *spi) { + int chip = spi_get_device_id(spi)->driver_data; struct lm70 *p_lm70; int status; + /* signaling is SPI_MODE_0 for both LM70 and TMP121 */ + if (spi->mode & (SPI_CPOL | SPI_CPHA)) + return -EINVAL; + + /* 3-wire link (shared SI/SO) for LM70 */ + if (chip == LM70_CHIP_LM70 && !(spi->mode & SPI_3WIRE)) + return -EINVAL; + /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); @@ -170,24 +180,6 @@ out_dev_reg_failed: return status; } -static int __devinit lm70_probe(struct spi_device *spi) -{ - /* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */ - if ((spi->mode & (SPI_CPOL | SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) - return -EINVAL; - - return common_probe(spi, LM70_CHIP_LM70); -} - -static int __devinit tmp121_probe(struct spi_device *spi) -{ - /* signaling is SPI_MODE_0 with only MISO connected */ - if (spi->mode & (SPI_CPOL | SPI_CPHA)) - return -EINVAL; - - return common_probe(spi, LM70_CHIP_TMP121); -} - static int __devexit lm70_remove(struct spi_device *spi) { struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); @@ -201,41 +193,32 @@ static int __devexit lm70_remove(struct spi_device *spi) return 0; } -static struct spi_driver tmp121_driver = { - .driver = { - .name = "tmp121", - .owner = THIS_MODULE, - }, - .probe = tmp121_probe, - .remove = __devexit_p(lm70_remove), + +static const struct spi_device_id lm70_ids[] = { + { "lm70", LM70_CHIP_LM70 }, + { "tmp121", LM70_CHIP_TMP121 }, + { }, }; +MODULE_DEVICE_TABLE(spi, lm70_ids); static struct spi_driver lm70_driver = { .driver = { .name = "lm70", .owner = THIS_MODULE, }, + .id_table = lm70_ids, .probe = lm70_probe, .remove = __devexit_p(lm70_remove), }; static int __init init_lm70(void) { - int ret = spi_register_driver(&lm70_driver); - if (ret) - return ret; - - ret = spi_register_driver(&tmp121_driver); - if (ret) - spi_unregister_driver(&lm70_driver); - - return ret; + return spi_register_driver(&lm70_driver); } static void __exit cleanup_lm70(void) { spi_unregister_driver(&lm70_driver); - spi_unregister_driver(&tmp121_driver); } module_init(init_lm70); diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index 9386e2a3921..6c9a04136e0 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -259,7 +259,7 @@ static int ltc4215_probe(struct i2c_client *client, mutex_init(&data->update_lock); /* Initialize the LTC4215 chip */ - /* TODO */ + i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00); /* Register sysfs hooks */ ret = sysfs_create_group(&client->dev.kobj, <c4215_group); diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 034b2c51584..e3896433361 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -382,7 +382,8 @@ static int ltc4245_probe(struct i2c_client *client, mutex_init(&data->update_lock); /* Initialize the LTC4245 chip */ - /* TODO */ + i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); + i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); /* Register sysfs hooks */ ret = sysfs_create_group(&client->dev.kobj, <c4245_group); diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index bfaa665ccf3..9ac497271ad 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -242,3 +242,4 @@ module_exit(max1111_exit); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); MODULE_DESCRIPTION("MAX1111 ADC Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:max1111"); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 6290a259456..303c02694c3 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -562,7 +562,7 @@ static int __devinit sht15_probe(struct platform_device *pdev) ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group); if (ret) { dev_err(&pdev->dev, "sysfs create failed"); - goto err_free_data; + goto err_release_gpio_data; } ret = request_irq(gpio_to_irq(data->pdata->gpio_data), @@ -581,10 +581,12 @@ static int __devinit sht15_probe(struct platform_device *pdev) data->hwmon_dev = hwmon_device_register(data->dev); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); - goto err_release_gpio_data; + goto err_release_irq; } return 0; +err_release_irq: + free_irq(gpio_to_irq(data->pdata->gpio_data), data); err_release_gpio_data: gpio_free(data->pdata->gpio_data); err_release_gpio_sck: diff --git a/drivers/hwmon/wm831x-hwmon.c b/drivers/hwmon/wm831x-hwmon.c new file mode 100644 index 00000000000..c16e9e74c35 --- /dev/null +++ b/drivers/hwmon/wm831x-hwmon.c @@ -0,0 +1,226 @@ +/* + * drivers/hwmon/wm831x-hwmon.c - Wolfson Microelectronics WM831x PMIC + * hardware monitoring features. + * + * Copyright (C) 2009 Wolfson Microelectronics plc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/auxadc.h> + +struct wm831x_hwmon { + struct wm831x *wm831x; + struct device *classdev; +}; + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "wm831x\n"); +} + +static const char *input_names[] = { + [WM831X_AUX_SYSVDD] = "SYSVDD", + [WM831X_AUX_USB] = "USB", + [WM831X_AUX_BKUP_BATT] = "Backup battery", + [WM831X_AUX_BATT] = "Battery", + [WM831X_AUX_WALL] = "WALL", + [WM831X_AUX_CHIP_TEMP] = "PMIC", + [WM831X_AUX_BATT_TEMP] = "Battery", +}; + + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wm831x_hwmon *hwmon = dev_get_drvdata(dev); + int channel = to_sensor_dev_attr(attr)->index; + int ret; + + ret = wm831x_auxadc_read_uv(hwmon->wm831x, channel); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret, 1000)); +} + +static ssize_t show_chip_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wm831x_hwmon *hwmon = dev_get_drvdata(dev); + int channel = to_sensor_dev_attr(attr)->index; + int ret; + + ret = wm831x_auxadc_read(hwmon->wm831x, channel); + if (ret < 0) + return ret; + + /* Degrees celsius = (512.18-ret) / 1.0983 */ + ret = 512180 - (ret * 1000); + ret = DIV_ROUND_CLOSEST(ret * 10000, 10983); + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int channel = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%s\n", input_names[channel]); +} + +#define WM831X_VOLTAGE(id, name) \ + static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage, \ + NULL, name) + +#define WM831X_NAMED_VOLTAGE(id, name) \ + WM831X_VOLTAGE(id, name); \ + static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \ + NULL, name) + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +WM831X_VOLTAGE(0, WM831X_AUX_AUX1); +WM831X_VOLTAGE(1, WM831X_AUX_AUX2); +WM831X_VOLTAGE(2, WM831X_AUX_AUX3); +WM831X_VOLTAGE(3, WM831X_AUX_AUX4); + +WM831X_NAMED_VOLTAGE(4, WM831X_AUX_SYSVDD); +WM831X_NAMED_VOLTAGE(5, WM831X_AUX_USB); +WM831X_NAMED_VOLTAGE(6, WM831X_AUX_BATT); +WM831X_NAMED_VOLTAGE(7, WM831X_AUX_WALL); +WM831X_NAMED_VOLTAGE(8, WM831X_AUX_BKUP_BATT); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL, + WM831X_AUX_CHIP_TEMP); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, + WM831X_AUX_CHIP_TEMP); +/* Report as a voltage since conversion depends on external components + * and that's what the ABI wants. */ +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL, + WM831X_AUX_BATT_TEMP); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, + WM831X_AUX_BATT_TEMP); + +static struct attribute *wm831x_attributes[] = { + &dev_attr_name.attr, + + &sensor_dev_attr_in0_input.dev_attr.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_in4_label.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_label.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_label.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + + NULL +}; + +static const struct attribute_group wm831x_attr_group = { + .attrs = wm831x_attributes, +}; + +static int __devinit wm831x_hwmon_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_hwmon *hwmon; + int ret; + + hwmon = kzalloc(sizeof(struct wm831x_hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->wm831x = wm831x; + + ret = sysfs_create_group(&pdev->dev.kobj, &wm831x_attr_group); + if (ret) + goto err; + + hwmon->classdev = hwmon_device_register(&pdev->dev); + if (IS_ERR(hwmon->classdev)) { + ret = PTR_ERR(hwmon->classdev); + goto err_sysfs; + } + + platform_set_drvdata(pdev, hwmon); + + return 0; + +err_sysfs: + sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group); +err: + kfree(hwmon); + return ret; +} + +static int __devexit wm831x_hwmon_remove(struct platform_device *pdev) +{ + struct wm831x_hwmon *hwmon = platform_get_drvdata(pdev); + + hwmon_device_unregister(hwmon->classdev); + sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group); + platform_set_drvdata(pdev, NULL); + kfree(hwmon); + + return 0; +} + +static struct platform_driver wm831x_hwmon_driver = { + .probe = wm831x_hwmon_probe, + .remove = __devexit_p(wm831x_hwmon_remove), + .driver = { + .name = "wm831x-hwmon", + .owner = THIS_MODULE, + }, +}; + +static int __init wm831x_hwmon_init(void) +{ + return platform_driver_register(&wm831x_hwmon_driver); +} +module_init(wm831x_hwmon_init); + +static void __exit wm831x_hwmon_exit(void) +{ + platform_driver_unregister(&wm831x_hwmon_driver); +} +module_exit(wm831x_hwmon_exit); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("WM831x Hardware Monitoring"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-hwmon"); diff --git a/drivers/hwmon/wm8350-hwmon.c b/drivers/hwmon/wm8350-hwmon.c new file mode 100644 index 00000000000..13290595ca8 --- /dev/null +++ b/drivers/hwmon/wm8350-hwmon.c @@ -0,0 +1,151 @@ +/* + * drivers/hwmon/wm8350-hwmon.c - Wolfson Microelectronics WM8350 PMIC + * hardware monitoring features. + * + * Copyright (C) 2009 Wolfson Microelectronics plc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#include <linux/mfd/wm8350/core.h> +#include <linux/mfd/wm8350/comparator.h> + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "wm8350\n"); +} + +static const char *input_names[] = { + [WM8350_AUXADC_USB] = "USB", + [WM8350_AUXADC_LINE] = "Line", + [WM8350_AUXADC_BATT] = "Battery", +}; + + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + int channel = to_sensor_dev_attr(attr)->index; + int val; + + val = wm8350_read_auxadc(wm8350, channel, 0, 0) * WM8350_AUX_COEFF; + val = DIV_ROUND_CLOSEST(val, 1000); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int channel = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%s\n", input_names[channel]); +} + +#define WM8350_NAMED_VOLTAGE(id, name) \ + static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage,\ + NULL, name); \ + static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \ + NULL, name) + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +WM8350_NAMED_VOLTAGE(0, WM8350_AUXADC_USB); +WM8350_NAMED_VOLTAGE(1, WM8350_AUXADC_BATT); +WM8350_NAMED_VOLTAGE(2, WM8350_AUXADC_LINE); + +static struct attribute *wm8350_attributes[] = { + &dev_attr_name.attr, + + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group wm8350_attr_group = { + .attrs = wm8350_attributes, +}; + +static int __devinit wm8350_hwmon_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + int ret; + + ret = sysfs_create_group(&pdev->dev.kobj, &wm8350_attr_group); + if (ret) + goto err; + + wm8350->hwmon.classdev = hwmon_device_register(&pdev->dev); + if (IS_ERR(wm8350->hwmon.classdev)) { + ret = PTR_ERR(wm8350->hwmon.classdev); + goto err_group; + } + + return 0; + +err_group: + sysfs_remove_group(&pdev->dev.kobj, &wm8350_attr_group); +err: + return ret; +} + +static int __devexit wm8350_hwmon_remove(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + + hwmon_device_unregister(wm8350->hwmon.classdev); + sysfs_remove_group(&pdev->dev.kobj, &wm8350_attr_group); + + return 0; +} + +static struct platform_driver wm8350_hwmon_driver = { + .probe = wm8350_hwmon_probe, + .remove = __devexit_p(wm8350_hwmon_remove), + .driver = { + .name = "wm8350-hwmon", + .owner = THIS_MODULE, + }, +}; + +static int __init wm8350_hwmon_init(void) +{ + return platform_driver_register(&wm8350_hwmon_driver); +} +module_init(wm8350_hwmon_init); + +static void __exit wm8350_hwmon_exit(void) +{ + platform_driver_unregister(&wm8350_hwmon_driver); +} +module_exit(wm8350_hwmon_exit); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("WM8350 Hardware Monitoring"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-hwmon"); |