From 00cb4739053fa0ce4594a7798a4095007a1c7c79 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 8 May 2007 17:21:59 +0200 Subject: hwmon/smsc47m192: Document the LPC47M292 as supported The new SMSC LPC47M292 Super-I/O chip includes a hardware monitoring block which is compatible with those of the LPC47M192. Signed-off-by: Jean Delvare Cc: Hartmut Rick --- drivers/hwmon/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6d105a1d41b..68ee9702e33 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -437,7 +437,8 @@ config SENSORS_SMSC47M192 select HWMON_VID help If you say yes here you get support for the temperature and - voltage sensors of the SMSC LPC47M192 and LPC47M997 chips. + voltage sensors of the SMSC LPC47M192, LPC47M15x, LPC47M292 + and LPC47M997 chips. The fan monitoring and control capabilities of these chips are supported by another driver, select -- cgit v1.2.3 From 8eccbb6fb97a5b54a9db166399f0d24f33114522 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 8 May 2007 17:21:59 +0200 Subject: hwmon/smsc47m1: Add support for the LPC47M292 The new SMSC LPC47M292 Super-I/O chip is a bit different from the previous ones, it supports a 3rd fan, but unfortunately the pin configuration registers are different. Signed-off-by: Jean Delvare --- Documentation/hwmon/smsc47m1 | 11 ++- drivers/hwmon/Kconfig | 9 +-- drivers/hwmon/smsc47m1.c | 160 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 139 insertions(+), 41 deletions(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/Documentation/hwmon/smsc47m1 b/Documentation/hwmon/smsc47m1 index 04a11124f66..42c8431b3c9 100644 --- a/Documentation/hwmon/smsc47m1 +++ b/Documentation/hwmon/smsc47m1 @@ -14,6 +14,10 @@ Supported chips: http://www.smsc.com/main/datasheets/47m14x.pdf http://www.smsc.com/main/tools/discontinued/47m15x.pdf http://www.smsc.com/main/datasheets/47m192.pdf + * SMSC LPC47M292 + Addresses scanned: none, address read from Super I/O config space + Prefix: 'smsc47m2' + Datasheet: Not public * SMSC LPC47M997 Addresses scanned: none, address read from Super I/O config space Prefix: 'smsc47m1' @@ -32,9 +36,10 @@ Description The Standard Microsystems Corporation (SMSC) 47M1xx Super I/O chips contain monitoring and PWM control circuitry for two fans. -The 47M15x and 47M192 chips contain a full 'hardware monitoring block' -in addition to the fan monitoring and control. The hardware monitoring -block is not supported by the driver. +The LPC47M15x, LPC47M192 and LPC47M292 chips contain a full 'hardware +monitoring block' in addition to the fan monitoring and control. The +hardware monitoring block is not supported by this driver, use the +smsc47m192 driver for that. No documentation is available for the 47M997, but it has the same device ID as the 47M15x and 47M192 chips and seems to be compatible. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 68ee9702e33..399b6a92e9b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -422,11 +422,12 @@ config SENSORS_SMSC47M1 If you say yes here you get support for the integrated fan monitoring and control capabilities of the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x, LPC47M14x, LPC47M15x, - LPC47M192 and LPC47M997 chips. + LPC47M192, LPC47M292 and LPC47M997 chips. - The temperature and voltage sensor features of the LPC47M192 - and LPC47M997 are supported by another driver, select also - "SMSC LPC47M192 and compatibles" below for those. + The temperature and voltage sensor features of the LPC47M15x, + LPC47M192, LPC47M292 and LPC47M997 are supported by another + driver, select also "SMSC LPC47M192 and compatibles" below for + those. This driver can also be built as a module. If so, the module will be called smsc47m1. diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index beb881c4b2e..16762cca87e 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -3,10 +3,11 @@ for hardware monitoring Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x, - LPC47M14x, LPC47M15x, LPC47M192 and LPC47M997 Super-I/O chips. + LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997 + Super-I/O chips. Copyright (C) 2002 Mark D. Studebaker - Copyright (C) 2004 Jean Delvare + Copyright (C) 2004-2007 Jean Delvare Ported to Linux 2.6 by Gabriele Gorla and Jean Delvare @@ -40,6 +41,8 @@ /* Address is autodetected, there is no default value */ static unsigned short address; +static u8 devid; +enum chips { smsc47m1, smsc47m2 }; /* Super-I/0 registers and commands */ @@ -87,10 +90,18 @@ superio_exit(void) #define SMSC47M1_REG_ALARM 0x04 #define SMSC47M1_REG_TPIN(nr) (0x34 - (nr)) #define SMSC47M1_REG_PPIN(nr) (0x36 - (nr)) -#define SMSC47M1_REG_PWM(nr) (0x56 + (nr)) #define SMSC47M1_REG_FANDIV 0x58 -#define SMSC47M1_REG_FAN(nr) (0x59 + (nr)) -#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr)) + +static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b }; +static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c }; +static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 }; + +#define SMSC47M2_REG_ALARM6 0x09 +#define SMSC47M2_REG_TPIN1 0x38 +#define SMSC47M2_REG_TPIN2 0x37 +#define SMSC47M2_REG_TPIN3 0x2d +#define SMSC47M2_REG_PPIN3 0x2c +#define SMSC47M2_REG_FANDIV3 0x6a #define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \ 983040/((192-(reg))*(div))) @@ -103,17 +114,18 @@ superio_exit(void) struct smsc47m1_data { struct i2c_client client; + enum chips type; struct class_device *class_dev; struct mutex lock; struct mutex update_lock; unsigned long last_updated; /* In jiffies */ - u8 fan[2]; /* Register value */ - u8 fan_preload[2]; /* Register value */ - u8 fan_div[2]; /* Register encoding, shifted right */ + u8 fan[3]; /* Register value */ + u8 fan_preload[3]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ u8 alarms; /* Register encoding */ - u8 pwm[2]; /* Register value (bit 7 is enable) */ + u8 pwm[3]; /* Register value (bit 0 is disable) */ }; @@ -200,7 +212,7 @@ static ssize_t set_fan_min(struct device *dev, const char *buf, } data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv); - smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), + smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr], data->fan_preload[nr]); mutex_unlock(&data->update_lock); @@ -234,15 +246,26 @@ static ssize_t set_fan_div(struct device *dev, const char *buf, return -EINVAL; } - tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F; - tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6); - smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp); + switch (nr) { + case 0: + case 1: + tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) + & ~(0x03 << (4 + 2 * nr)); + tmp |= data->fan_div[nr] << (4 + 2 * nr); + smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp); + break; + case 2: + tmp = smsc47m1_read_value(client, SMSC47M2_REG_FANDIV3) & 0xCF; + tmp |= data->fan_div[2] << 4; + smsc47m1_write_value(client, SMSC47M2_REG_FANDIV3, tmp); + break; + } /* Preserve fan min */ tmp = 192 - (old_div * (192 - data->fan_preload[nr]) + new_div / 2) / new_div; data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191); - smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), + smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr], data->fan_preload[nr]); mutex_unlock(&data->update_lock); @@ -263,7 +286,7 @@ static ssize_t set_pwm(struct device *dev, const char *buf, mutex_lock(&data->update_lock); data->pwm[nr] &= 0x81; /* Preserve additional bits */ data->pwm[nr] |= PWM_TO_REG(val); - smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), + smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr], data->pwm[nr]); mutex_unlock(&data->update_lock); @@ -284,7 +307,7 @@ static ssize_t set_pwm_en(struct device *dev, const char *buf, mutex_lock(&data->update_lock); data->pwm[nr] &= 0xFE; /* preserve the other bits */ data->pwm[nr] |= !val; - smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), + smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr], data->pwm[nr]); mutex_unlock(&data->update_lock); @@ -345,6 +368,7 @@ static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ fan_present(1); fan_present(2); +fan_present(3); static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL); @@ -358,11 +382,16 @@ static struct attribute *smsc47m1_attributes[] = { &dev_attr_fan2_input.attr, &dev_attr_fan2_min.attr, &dev_attr_fan2_div.attr, + &dev_attr_fan3_input.attr, + &dev_attr_fan3_min.attr, + &dev_attr_fan3_div.attr, &dev_attr_pwm1.attr, &dev_attr_pwm1_enable.attr, &dev_attr_pwm2.attr, &dev_attr_pwm2_enable.attr, + &dev_attr_pwm3.attr, + &dev_attr_pwm3_enable.attr, &dev_attr_alarms.attr, NULL @@ -377,7 +406,7 @@ static int __init smsc47m1_find(unsigned short *addr) u8 val; superio_enter(); - val = superio_inb(SUPERIO_REG_DEVID); + devid = superio_inb(SUPERIO_REG_DEVID); /* * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x @@ -386,18 +415,29 @@ static int __init smsc47m1_find(unsigned short *addr) * can do much more besides (device id 0x60). * The LPC47M997 is undocumented, but seems to be compatible with * the LPC47M192, and has the same device id. + * The LPC47M292 (device id 0x6B) is somewhat compatible, but it + * supports a 3rd fan, and the pin configuration registers are + * unfortunately different. */ - if (val == 0x51) + switch (devid) { + case 0x51: printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n"); - else if (val == 0x59) + break; + case 0x59: printk(KERN_INFO "smsc47m1: Found SMSC " "LPC47M10x/LPC47M112/LPC47M13x\n"); - else if (val == 0x5F) + break; + case 0x5F: printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n"); - else if (val == 0x60) + break; + case 0x60: printk(KERN_INFO "smsc47m1: Found SMSC " "LPC47M15x/LPC47M192/LPC47M997\n"); - else { + break; + case 0x6B: + printk(KERN_INFO "smsc47m1: Found SMSC LPC47M292\n"); + break; + default: superio_exit(); return -ENODEV; } @@ -421,7 +461,7 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) struct i2c_client *new_client; struct smsc47m1_data *data; int err = 0; - int fan1, fan2, pwm1, pwm2; + int fan1, fan2, fan3, pwm1, pwm2, pwm3; if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.driver.name)) { dev_err(&adapter->dev, "Region 0x%x already in use!\n", address); @@ -433,6 +473,7 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) goto error_release; } + data->type = devid == 0x6B ? smsc47m2 : smsc47m1; new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; @@ -441,20 +482,35 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) new_client->driver = &smsc47m1_driver; new_client->flags = 0; - strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE); + strlcpy(new_client->name, + data->type == smsc47m2 ? "smsc47m2" : "smsc47m1", + I2C_NAME_SIZE); mutex_init(&data->update_lock); /* If no function is properly configured, there's no point in actually registering the chip. */ - fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05) - == 0x05; - fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05) - == 0x05; pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05) == 0x04; pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05) == 0x04; - if (!(fan1 || fan2 || pwm1 || pwm2)) { + if (data->type == smsc47m2) { + fan1 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN1) + & 0x0d) == 0x09; + fan2 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN2) + & 0x0d) == 0x09; + fan3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN3) + & 0x0d) == 0x0d; + pwm3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_PPIN3) + & 0x0d) == 0x08; + } else { + fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) + & 0x05) == 0x05; + fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) + & 0x05) == 0x05; + fan3 = 0; + pwm3 = 0; + } + if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) { dev_warn(&adapter->dev, "Device at 0x%x is not configured, " "will not use\n", new_client->addr); err = -ENODEV; @@ -497,6 +553,18 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, " "skipping\n"); + if (fan3) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_fan3_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan3_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan3_div))) + goto error_remove_files; + } else + dev_dbg(&new_client->dev, "Fan 3 not enabled by hardware, " + "skipping\n"); + if (pwm1) { if ((err = device_create_file(&new_client->dev, &dev_attr_pwm1)) @@ -506,6 +574,7 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) } else dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, " "skipping\n"); + if (pwm2) { if ((err = device_create_file(&new_client->dev, &dev_attr_pwm2)) @@ -516,6 +585,16 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, " "skipping\n"); + if (pwm3) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_pwm3)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm3_enable))) + goto error_remove_files; + } else + dev_dbg(&new_client->dev, "PWM 3 not enabled by hardware, " + "skipping\n"); + if ((err = device_create_file(&new_client->dev, &dev_attr_alarms))) goto error_remove_files; @@ -580,15 +659,16 @@ static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) { - int i; + int i, fan_nr; + fan_nr = data->type == smsc47m2 ? 3 : 2; - for (i = 0; i < 2; i++) { + for (i = 0; i < fan_nr; i++) { data->fan[i] = smsc47m1_read_value(client, - SMSC47M1_REG_FAN(i)); + SMSC47M1_REG_FAN[i]); data->fan_preload[i] = smsc47m1_read_value(client, - SMSC47M1_REG_FAN_PRELOAD(i)); + SMSC47M1_REG_FAN_PRELOAD[i]); data->pwm[i] = smsc47m1_read_value(client, - SMSC47M1_REG_PWM(i)); + SMSC47M1_REG_PWM[i]); } i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV); @@ -601,6 +681,18 @@ static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, if (data->alarms) smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0); + if (fan_nr >= 3) { + data->fan_div[2] = (smsc47m1_read_value(client, + SMSC47M2_REG_FANDIV3) >> 4) & 0x03; + data->alarms |= (smsc47m1_read_value(client, + SMSC47M2_REG_ALARM6) & 0x40) >> 4; + /* Clear alarm if needed */ + if (data->alarms & 0x04) + smsc47m1_write_value(client, + SMSC47M2_REG_ALARM6, + 0x40); + } + data->last_updated = jiffies; } -- cgit v1.2.3 From 787c72b107888805981faf148c8fea96a752d22e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 8 May 2007 17:22:00 +0200 Subject: hwmon/w83627hf: Convert to a platform driver Convert the w83627hf driver from the nonsensical i2c-isa hack to a regular platform driver. Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 3 +- drivers/hwmon/w83627hf.c | 591 +++++++++++++++++++++++++---------------------- 2 files changed, 311 insertions(+), 283 deletions(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 399b6a92e9b..ae3ab9169d5 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -550,8 +550,7 @@ config SENSORS_W83L785TS config SENSORS_W83627HF tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF" - depends on HWMON && I2C - select I2C_ISA + depends on HWMON select HWMON_VID help If you say yes here you get support for the Winbond W836X7 series diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 7738d30a11b..a5b774b07cb 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -5,6 +5,7 @@ Philip Edelbrock , and Mark Studebaker Ported to 2.6 by Bernhard C. Schrenk + Copyright (c) 2007 Jean Delvare This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,8 +43,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -52,8 +52,7 @@ #include #include "lm75.h" -/* The actual ISA address is read from Super-I/O configuration space */ -static unsigned short address; +static struct platform_device *pdev; #define DRVNAME "w83627hf" enum chips { w83627hf, w83627thf, w83697hf, w83637hf, w83687thf }; @@ -157,9 +156,9 @@ superio_exit(void) #define WINB_REGION_OFFSET 5 #define WINB_REGION_SIZE 2 -/* Where are the sensors address/data registers relative to the base address */ -#define W83781D_ADDR_REG_OFFSET 5 -#define W83781D_DATA_REG_OFFSET 6 +/* Where are the sensors address/data registers relative to the region offset */ +#define W83781D_ADDR_REG_OFFSET 0 +#define W83781D_DATA_REG_OFFSET 1 /* The W83781D registers */ /* The W83782D registers for nr=7,8 are in bank 5 */ @@ -290,7 +289,8 @@ static inline u8 DIV_TO_REG(long val) /* For each registered chip, we need to keep some data in memory. The structure is dynamically allocated. */ struct w83627hf_data { - struct i2c_client client; + unsigned short addr; + const char *name; struct class_device *class_dev; struct mutex lock; enum chips type; @@ -325,22 +325,26 @@ struct w83627hf_data { u8 vrm_ovt; /* Register value, 627THF/637HF/687THF only */ }; +struct w83627hf_sio_data { + enum chips type; +}; -static int w83627hf_detect(struct i2c_adapter *adapter); -static int w83627hf_detach_client(struct i2c_client *client); -static int w83627hf_read_value(struct i2c_client *client, u16 reg); -static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value); +static int w83627hf_probe(struct platform_device *pdev); +static int w83627hf_remove(struct platform_device *pdev); + +static int w83627hf_read_value(struct w83627hf_data *data, u16 reg); +static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value); static struct w83627hf_data *w83627hf_update_device(struct device *dev); -static void w83627hf_init_client(struct i2c_client *client); +static void w83627hf_init_device(struct platform_device *pdev); -static struct i2c_driver w83627hf_driver = { +static struct platform_driver w83627hf_driver = { .driver = { .owner = THIS_MODULE, .name = DRVNAME, }, - .attach_adapter = w83627hf_detect, - .detach_client = w83627hf_detach_client, + .probe = w83627hf_probe, + .remove = __devexit_p(w83627hf_remove), }; /* following are the sysfs callback functions */ @@ -358,15 +362,14 @@ show_in_reg(in_max) static ssize_t \ store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \ { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83627hf_data *data = i2c_get_clientdata(client); \ + struct w83627hf_data *data = dev_get_drvdata(dev); \ u32 val; \ \ val = simple_strtoul(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ data->in_##reg[nr] = IN_TO_REG(val); \ - w83627hf_write_value(client, W83781D_REG_IN_##REG(nr), \ + w83627hf_write_value(data, W83781D_REG_IN_##REG(nr), \ data->in_##reg[nr]); \ \ mutex_unlock(&data->update_lock); \ @@ -450,8 +453,7 @@ static ssize_t show_regs_in_max0(struct device *dev, struct device_attribute *at static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); u32 val; val = simple_strtoul(buf, NULL, 10); @@ -470,7 +472,7 @@ static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *a /* use VRM8 (standard) calculation */ data->in_min[0] = IN_TO_REG(val); - w83627hf_write_value(client, W83781D_REG_IN_MIN(0), data->in_min[0]); + w83627hf_write_value(data, W83781D_REG_IN_MIN(0), data->in_min[0]); mutex_unlock(&data->update_lock); return count; } @@ -478,8 +480,7 @@ static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *a static ssize_t store_regs_in_max0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); u32 val; val = simple_strtoul(buf, NULL, 10); @@ -498,7 +499,7 @@ static ssize_t store_regs_in_max0(struct device *dev, struct device_attribute *a /* use VRM8 (standard) calculation */ data->in_max[0] = IN_TO_REG(val); - w83627hf_write_value(client, W83781D_REG_IN_MAX(0), data->in_max[0]); + w83627hf_write_value(data, W83781D_REG_IN_MAX(0), data->in_max[0]); mutex_unlock(&data->update_lock); return count; } @@ -523,8 +524,7 @@ show_fan_reg(fan_min); static ssize_t store_fan_min(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); u32 val; val = simple_strtoul(buf, NULL, 10); @@ -532,7 +532,7 @@ store_fan_min(struct device *dev, const char *buf, size_t count, int nr) mutex_lock(&data->update_lock); data->fan_min[nr - 1] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1])); - w83627hf_write_value(client, W83781D_REG_FAN_MIN(nr), + w83627hf_write_value(data, W83781D_REG_FAN_MIN(nr), data->fan_min[nr - 1]); mutex_unlock(&data->update_lock); @@ -585,8 +585,7 @@ show_temp_reg(temp_max_hyst); static ssize_t \ store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83627hf_data *data = i2c_get_clientdata(client); \ + struct w83627hf_data *data = dev_get_drvdata(dev); \ u32 val; \ \ val = simple_strtoul(buf, NULL, 10); \ @@ -595,11 +594,11 @@ store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ \ if (nr >= 2) { /* TEMP2 and TEMP3 */ \ data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \ - w83627hf_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + w83627hf_write_value(data, W83781D_REG_TEMP_##REG(nr), \ data->temp_##reg##_add[nr-2]); \ } else { /* TEMP1 */ \ data->temp_##reg = TEMP_TO_REG(val); \ - w83627hf_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + w83627hf_write_value(data, W83781D_REG_TEMP_##REG(nr), \ data->temp_##reg); \ } \ \ @@ -657,8 +656,7 @@ show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); u32 val; val = simple_strtoul(buf, NULL, 10); @@ -693,8 +691,7 @@ static ssize_t store_beep_reg(struct device *dev, const char *buf, size_t count, int update_mask) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); u32 val, val2; val = simple_strtoul(buf, NULL, 10); @@ -703,18 +700,18 @@ store_beep_reg(struct device *dev, const char *buf, size_t count, if (update_mask == BEEP_MASK) { /* We are storing beep_mask */ data->beep_mask = BEEP_MASK_TO_REG(val); - w83627hf_write_value(client, W83781D_REG_BEEP_INTS1, + w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, data->beep_mask & 0xff); - w83627hf_write_value(client, W83781D_REG_BEEP_INTS3, + w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, ((data->beep_mask) >> 16) & 0xff); val2 = (data->beep_mask >> 8) & 0x7f; } else { /* We are storing beep_enable */ val2 = - w83627hf_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f; + w83627hf_read_value(data, W83781D_REG_BEEP_INTS2) & 0x7f; data->beep_enable = BEEP_ENABLE_TO_REG(val); } - w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, + w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, val2 | data->beep_enable << 7); mutex_unlock(&data->update_lock); @@ -752,8 +749,7 @@ show_fan_div_reg(struct device *dev, char *buf, int nr) static ssize_t store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); unsigned long min; u8 reg; unsigned long val = simple_strtoul(buf, NULL, 10); @@ -766,19 +762,19 @@ store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) data->fan_div[nr] = DIV_TO_REG(val); - reg = (w83627hf_read_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) + reg = (w83627hf_read_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) & (nr==0 ? 0xcf : 0x3f)) | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); - w83627hf_write_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); + w83627hf_write_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); - reg = (w83627hf_read_value(client, W83781D_REG_VBAT) + reg = (w83627hf_read_value(data, W83781D_REG_VBAT) & ~(1 << (5 + nr))) | ((data->fan_div[nr] & 0x04) << (3 + nr)); - w83627hf_write_value(client, W83781D_REG_VBAT, reg); + w83627hf_write_value(data, W83781D_REG_VBAT, reg); /* Restore fan_min */ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - w83627hf_write_value(client, W83781D_REG_FAN_MIN(nr+1), data->fan_min[nr]); + w83627hf_write_value(data, W83781D_REG_FAN_MIN(nr+1), data->fan_min[nr]); mutex_unlock(&data->update_lock); return count; @@ -812,8 +808,7 @@ show_pwm_reg(struct device *dev, char *buf, int nr) static ssize_t store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); u32 val; val = simple_strtoul(buf, NULL, 10); @@ -823,14 +818,14 @@ store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) if (data->type == w83627thf) { /* bits 0-3 are reserved in 627THF */ data->pwm[nr - 1] = PWM_TO_REG(val) & 0xf0; - w83627hf_write_value(client, + w83627hf_write_value(data, W836X7HF_REG_PWM(data->type, nr), data->pwm[nr - 1] | - (w83627hf_read_value(client, + (w83627hf_read_value(data, W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); } else { data->pwm[nr - 1] = PWM_TO_REG(val); - w83627hf_write_value(client, + w83627hf_write_value(data, W836X7HF_REG_PWM(data->type, nr), data->pwm[nr - 1]); } @@ -866,8 +861,7 @@ show_sensor_reg(struct device *dev, char *buf, int nr) static ssize_t store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); u32 val, tmp; val = simple_strtoul(buf, NULL, 10); @@ -876,31 +870,31 @@ store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) switch (val) { case 1: /* PII/Celeron diode */ - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); - w83627hf_write_value(client, W83781D_REG_SCFG1, + tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); + w83627hf_write_value(data, W83781D_REG_SCFG1, tmp | BIT_SCFG1[nr - 1]); - tmp = w83627hf_read_value(client, W83781D_REG_SCFG2); - w83627hf_write_value(client, W83781D_REG_SCFG2, + tmp = w83627hf_read_value(data, W83781D_REG_SCFG2); + w83627hf_write_value(data, W83781D_REG_SCFG2, tmp | BIT_SCFG2[nr - 1]); data->sens[nr - 1] = val; break; case 2: /* 3904 */ - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); - w83627hf_write_value(client, W83781D_REG_SCFG1, + tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); + w83627hf_write_value(data, W83781D_REG_SCFG1, tmp | BIT_SCFG1[nr - 1]); - tmp = w83627hf_read_value(client, W83781D_REG_SCFG2); - w83627hf_write_value(client, W83781D_REG_SCFG2, + tmp = w83627hf_read_value(data, W83781D_REG_SCFG2); + w83627hf_write_value(data, W83781D_REG_SCFG2, tmp & ~BIT_SCFG2[nr - 1]); data->sens[nr - 1] = val; break; case W83781D_DEFAULT_BETA: /* thermistor */ - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); - w83627hf_write_value(client, W83781D_REG_SCFG1, + tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); + w83627hf_write_value(data, W83781D_REG_SCFG1, tmp & ~BIT_SCFG1[nr - 1]); data->sens[nr - 1] = val; break; default: - dev_err(&client->dev, + dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or %d\n", (long) val, W83781D_DEFAULT_BETA); break; @@ -927,21 +921,52 @@ sysfs_sensor(1); sysfs_sensor(2); sysfs_sensor(3); -static int __init w83627hf_find(int sioaddr, unsigned short *addr) +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static int __init w83627hf_find(int sioaddr, unsigned short *addr, + struct w83627hf_sio_data *sio_data) { int err = -ENODEV; u16 val; + static const __initdata char *names[] = { + "W83627HF", + "W83627THF", + "W83697HF", + "W83637HF", + "W83687THF", + }; + REG = sioaddr; VAL = sioaddr + 1; superio_enter(); val= superio_inb(DEVID); - if(val != W627_DEVID && - val != W627THF_DEVID && - val != W697_DEVID && - val != W637_DEVID && - val != W687THF_DEVID) { + switch (val) { + case W627_DEVID: + sio_data->type = w83627hf; + break; + case W627THF_DEVID: + sio_data->type = w83627thf; + break; + case W697_DEVID: + sio_data->type = w83697hf; + break; + case W637_DEVID: + sio_data->type = w83637hf; + break; + case W687THF_DEVID: + sio_data->type = w83687thf; + break; + default: + pr_debug(DRVNAME ": Unsupported chip (DEVID=0x%x)\n", val); goto exit; } @@ -969,6 +994,8 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr) } err = 0; + pr_info(DRVNAME ": Found %s chip at %#x\n", + names[sio_data->type], *addr); exit: superio_exit(); @@ -1018,6 +1045,7 @@ static struct attribute *w83627hf_attributes[] = { &dev_attr_pwm1.attr, &dev_attr_pwm2.attr, + &dev_attr_name.attr, NULL }; @@ -1054,147 +1082,92 @@ static const struct attribute_group w83627hf_group_opt = { .attrs = w83627hf_attributes_opt, }; -static int w83627hf_detect(struct i2c_adapter *adapter) +static int __devinit w83627hf_probe(struct platform_device *pdev) { - int val, kind; - struct i2c_client *new_client; + struct device *dev = &pdev->dev; + struct w83627hf_sio_data *sio_data = dev->platform_data; struct w83627hf_data *data; - int err = 0; - const char *client_name = ""; + struct resource *res; + int err; - if (!request_region(address + WINB_REGION_OFFSET, WINB_REGION_SIZE, - w83627hf_driver.driver.name)) { + static const char *names[] = { + "w83627hf", + "w83627thf", + "w83697hf", + "w83637hf", + "w83687thf", + }; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start, WINB_REGION_SIZE, DRVNAME)) { + dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", + (unsigned long)res->start, + (unsigned long)(res->start + WINB_REGION_SIZE - 1)); err = -EBUSY; goto ERROR0; } - superio_enter(); - val= superio_inb(DEVID); - if(val == W627_DEVID) - kind = w83627hf; - else if(val == W697_DEVID) - kind = w83697hf; - else if(val == W627THF_DEVID) - kind = w83627thf; - else if(val == W637_DEVID) - kind = w83637hf; - else if (val == W687THF_DEVID) - kind = w83687thf; - else { - dev_info(&adapter->dev, - "Unsupported chip (dev_id=0x%02X).\n", val); - goto ERROR1; - } - superio_exit(); - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access w83627hf_{read,write}_value. */ - if (!(data = kzalloc(sizeof(struct w83627hf_data), GFP_KERNEL))) { err = -ENOMEM; goto ERROR1; } - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; + data->addr = res->start; + data->type = sio_data->type; + data->name = names[sio_data->type]; mutex_init(&data->lock); - new_client->adapter = adapter; - new_client->driver = &w83627hf_driver; - new_client->flags = 0; - - - if (kind == w83627hf) { - client_name = "w83627hf"; - } else if (kind == w83627thf) { - client_name = "w83627thf"; - } else if (kind == w83697hf) { - client_name = "w83697hf"; - } else if (kind == w83637hf) { - client_name = "w83637hf"; - } else if (kind == w83687thf) { - client_name = "w83687thf"; - } - - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - data->type = kind; - data->valid = 0; mutex_init(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; + platform_set_drvdata(pdev, data); /* Initialize the chip */ - w83627hf_init_client(new_client); + w83627hf_init_device(pdev); /* A few vars need to be filled upon startup */ - data->fan_min[0] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(1)); - data->fan_min[1] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(2)); - data->fan_min[2] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(3)); + data->fan_min[0] = w83627hf_read_value(data, W83781D_REG_FAN_MIN(1)); + data->fan_min[1] = w83627hf_read_value(data, W83781D_REG_FAN_MIN(2)); + data->fan_min[2] = w83627hf_read_value(data, W83781D_REG_FAN_MIN(3)); /* Register common device attributes */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &w83627hf_group))) + if ((err = sysfs_create_group(&dev->kobj, &w83627hf_group))) goto ERROR3; /* Register chip-specific device attributes */ - if (kind == w83627hf || kind == w83697hf) - if ((err = device_create_file(&new_client->dev, - &dev_attr_in5_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in5_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in5_max)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in6_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in6_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in6_max))) + if (data->type == w83627hf || data->type == w83697hf) + if ((err = device_create_file(dev, &dev_attr_in5_input)) + || (err = device_create_file(dev, &dev_attr_in5_min)) + || (err = device_create_file(dev, &dev_attr_in5_max)) + || (err = device_create_file(dev, &dev_attr_in6_input)) + || (err = device_create_file(dev, &dev_attr_in6_min)) + || (err = device_create_file(dev, &dev_attr_in6_max))) goto ERROR4; - if (kind != w83697hf) - if ((err = device_create_file(&new_client->dev, - &dev_attr_in1_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in1_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in1_max)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan3_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan3_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan3_div)) - || (err = device_create_file(&new_client->dev, - &dev_attr_temp3_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_temp3_max)) - || (err = device_create_file(&new_client->dev, - &dev_attr_temp3_max_hyst)) - || (err = device_create_file(&new_client->dev, - &dev_attr_temp3_type))) + if (data->type != w83697hf) + if ((err = device_create_file(dev, &dev_attr_in1_input)) + || (err = device_create_file(dev, &dev_attr_in1_min)) + || (err = device_create_file(dev, &dev_attr_in1_max)) + || (err = device_create_file(dev, &dev_attr_fan3_input)) + || (err = device_create_file(dev, &dev_attr_fan3_min)) + || (err = device_create_file(dev, &dev_attr_fan3_div)) + || (err = device_create_file(dev, &dev_attr_temp3_input)) + || (err = device_create_file(dev, &dev_attr_temp3_max)) + || (err = device_create_file(dev, &dev_attr_temp3_max_hyst)) + || (err = device_create_file(dev, &dev_attr_temp3_type))) goto ERROR4; - if (kind != w83697hf && data->vid != 0xff) { + if (data->type != w83697hf && data->vid != 0xff) { /* Convert VID to voltage based on VRM */ data->vrm = vid_which_vrm(); - if ((err = device_create_file(&new_client->dev, - &dev_attr_cpu0_vid)) - || (err = device_create_file(&new_client->dev, - &dev_attr_vrm))) + if ((err = device_create_file(dev, &dev_attr_cpu0_vid)) + || (err = device_create_file(dev, &dev_attr_vrm))) goto ERROR4; } - if (kind == w83627thf || kind == w83637hf || kind == w83687thf) - if ((err = device_create_file(&new_client->dev, - &dev_attr_pwm3))) + if (data->type == w83627thf || data->type == w83637hf + || data->type == w83687thf) + if ((err = device_create_file(dev, &dev_attr_pwm3))) goto ERROR4; - data->class_dev = hwmon_device_register(&new_client->dev); + data->class_dev = hwmon_device_register(dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); goto ERROR4; @@ -1203,47 +1176,37 @@ static int w83627hf_detect(struct i2c_adapter *adapter) return 0; ERROR4: - sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group); - sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group_opt); + sysfs_remove_group(&dev->kobj, &w83627hf_group); + sysfs_remove_group(&dev->kobj, &w83627hf_group_opt); ERROR3: - i2c_detach_client(new_client); - ERROR2: kfree(data); ERROR1: - release_region(address + WINB_REGION_OFFSET, WINB_REGION_SIZE); + release_region(res->start, WINB_REGION_SIZE); ERROR0: return err; } -static int w83627hf_detach_client(struct i2c_client *client) +static int __devexit w83627hf_remove(struct platform_device *pdev) { - struct w83627hf_data *data = i2c_get_clientdata(client); - int err; + struct w83627hf_data *data = platform_get_drvdata(pdev); + struct resource *res; + platform_set_drvdata(pdev, NULL); hwmon_device_unregister(data->class_dev); - sysfs_remove_group(&client->dev.kobj, &w83627hf_group); - sysfs_remove_group(&client->dev.kobj, &w83627hf_group_opt); - - if ((err = i2c_detach_client(client))) - return err; - - release_region(client->addr + WINB_REGION_OFFSET, WINB_REGION_SIZE); + sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group); + sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt); kfree(data); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start, WINB_REGION_SIZE); + return 0; } -/* - ISA access must always be locked explicitly! - We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, - would slow down the W83781D access and should not be necessary. - There are some ugly typecasts here, but the good news is - they should - nowhere else be necessary! */ -static int w83627hf_read_value(struct i2c_client *client, u16 reg) +static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) { - struct w83627hf_data *data = i2c_get_clientdata(client); int res, word_sized; mutex_lock(&data->lock); @@ -1254,29 +1217,29 @@ static int w83627hf_read_value(struct i2c_client *client, u16 reg) || ((reg & 0x00ff) == 0x55)); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); + data->addr + W83781D_ADDR_REG_OFFSET); outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->addr + W83781D_DATA_REG_OFFSET); } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(data->addr + W83781D_DATA_REG_OFFSET); if (word_sized) { outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); + data->addr + W83781D_ADDR_REG_OFFSET); res = - (res << 8) + inb_p(client->addr + + (res << 8) + inb_p(data->addr + W83781D_DATA_REG_OFFSET); } if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + data->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->addr + W83781D_DATA_REG_OFFSET); } mutex_unlock(&data->lock); return res; } -static int w83627thf_read_gpio5(struct i2c_client *client) +static int __devinit w83627thf_read_gpio5(struct platform_device *pdev) { int res = 0xff, sel; @@ -1285,7 +1248,7 @@ static int w83627thf_read_gpio5(struct i2c_client *client) /* Make sure these GPIO pins are enabled */ if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) { - dev_dbg(&client->dev, "GPIO5 disabled, no VID function\n"); + dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); goto exit; } @@ -1293,12 +1256,12 @@ static int w83627thf_read_gpio5(struct i2c_client *client) There must be at least five (VRM 9), and possibly 6 (VRM 10) */ sel = superio_inb(W83627THF_GPIO5_IOSR) & 0x3f; if ((sel & 0x1f) != 0x1f) { - dev_dbg(&client->dev, "GPIO5 not configured for VID " + dev_dbg(&pdev->dev, "GPIO5 not configured for VID " "function\n"); goto exit; } - dev_info(&client->dev, "Reading VID from GPIO5\n"); + dev_info(&pdev->dev, "Reading VID from GPIO5\n"); res = superio_inb(W83627THF_GPIO5_DR) & sel; exit: @@ -1306,7 +1269,7 @@ exit: return res; } -static int w83687thf_read_vid(struct i2c_client *client) +static int __devinit w83687thf_read_vid(struct platform_device *pdev) { int res = 0xff; @@ -1315,13 +1278,13 @@ static int w83687thf_read_vid(struct i2c_client *client) /* Make sure these GPIO pins are enabled */ if (!(superio_inb(W83687THF_VID_EN) & (1 << 2))) { - dev_dbg(&client->dev, "VID disabled, no VID function\n"); + dev_dbg(&pdev->dev, "VID disabled, no VID function\n"); goto exit; } /* Make sure the pins are configured for input */ if (!(superio_inb(W83687THF_VID_CFG) & (1 << 4))) { - dev_dbg(&client->dev, "VID configured as output, " + dev_dbg(&pdev->dev, "VID configured as output, " "no VID function\n"); goto exit; } @@ -1333,9 +1296,8 @@ exit: return res; } -static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value) +static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value) { - struct w83627hf_data *data = i2c_get_clientdata(client); int word_sized; mutex_lock(&data->lock); @@ -1345,31 +1307,31 @@ static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value) || ((reg & 0x00ff) == 0x55)); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); + data->addr + W83781D_ADDR_REG_OFFSET); outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->addr + W83781D_DATA_REG_OFFSET); } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); if (word_sized) { outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->addr + W83781D_DATA_REG_OFFSET); outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); + data->addr + W83781D_ADDR_REG_OFFSET); } outb_p(value & 0xff, - client->addr + W83781D_DATA_REG_OFFSET); + data->addr + W83781D_DATA_REG_OFFSET); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + data->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->addr + W83781D_DATA_REG_OFFSET); } mutex_unlock(&data->lock); return 0; } -static void w83627hf_init_client(struct i2c_client *client) +static void __devinit w83627hf_init_device(struct platform_device *pdev) { - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = platform_get_drvdata(pdev); int i; enum chips type = data->type; u8 tmp; @@ -1380,53 +1342,53 @@ static void w83627hf_init_client(struct i2c_client *client) speed...) so it is now optional. It might even go away if nobody reports it as being useful, as I see very little reason why this would be needed at all. */ - dev_info(&client->dev, "If reset=1 solved a problem you were " + dev_info(&pdev->dev, "If reset=1 solved a problem you were " "having, please report!\n"); /* save this register */ - i = w83627hf_read_value(client, W83781D_REG_BEEP_CONFIG); + i = w83627hf_read_value(data, W83781D_REG_BEEP_CONFIG); /* Reset all except Watchdog values and last conversion values This sets fan-divs to 2, among others */ - w83627hf_write_value(client, W83781D_REG_CONFIG, 0x80); + w83627hf_write_value(data, W83781D_REG_CONFIG, 0x80); /* Restore the register and disable power-on abnormal beep. This saves FAN 1/2/3 input/output values set by BIOS. */ - w83627hf_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); + w83627hf_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80); /* Disable master beep-enable (reset turns it on). Individual beeps should be reset to off but for some reason disabling this bit helps some people not get beeped */ - w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, 0); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, 0); } /* Minimize conflicts with other winbond i2c-only clients... */ /* disable i2c subclients... how to disable main i2c client?? */ /* force i2c address to relatively uncommon address */ - w83627hf_write_value(client, W83781D_REG_I2C_SUBADDR, 0x89); - w83627hf_write_value(client, W83781D_REG_I2C_ADDR, force_i2c); + w83627hf_write_value(data, W83781D_REG_I2C_SUBADDR, 0x89); + w83627hf_write_value(data, W83781D_REG_I2C_ADDR, force_i2c); /* Read VID only once */ if (type == w83627hf || type == w83637hf) { - int lo = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); - int hi = w83627hf_read_value(client, W83781D_REG_CHIPID); + int lo = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); + int hi = w83627hf_read_value(data, W83781D_REG_CHIPID); data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); } else if (type == w83627thf) { - data->vid = w83627thf_read_gpio5(client); + data->vid = w83627thf_read_gpio5(pdev); } else if (type == w83687thf) { - data->vid = w83687thf_read_vid(client); + data->vid = w83687thf_read_vid(pdev); } /* Read VRM & OVT Config only once */ if (type == w83627thf || type == w83637hf || type == w83687thf) { data->vrm_ovt = - w83627hf_read_value(client, W83627THF_REG_VRM_OVT_CFG); + w83627hf_read_value(data, W83627THF_REG_VRM_OVT_CFG); } - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); + tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); for (i = 1; i <= 3; i++) { if (!(tmp & BIT_SCFG1[i - 1])) { data->sens[i - 1] = W83781D_DEFAULT_BETA; } else { if (w83627hf_read_value - (client, + (data, W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) data->sens[i - 1] = 1; else @@ -1438,38 +1400,37 @@ static void w83627hf_init_client(struct i2c_client *client) if(init) { /* Enable temp2 */ - tmp = w83627hf_read_value(client, W83781D_REG_TEMP2_CONFIG); + tmp = w83627hf_read_value(data, W83781D_REG_TEMP2_CONFIG); if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp2, readings " + dev_warn(&pdev->dev, "Enabling temp2, readings " "might not make sense\n"); - w83627hf_write_value(client, W83781D_REG_TEMP2_CONFIG, + w83627hf_write_value(data, W83781D_REG_TEMP2_CONFIG, tmp & 0xfe); } /* Enable temp3 */ if (type != w83697hf) { - tmp = w83627hf_read_value(client, + tmp = w83627hf_read_value(data, W83781D_REG_TEMP3_CONFIG); if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp3, " + dev_warn(&pdev->dev, "Enabling temp3, " "readings might not make sense\n"); - w83627hf_write_value(client, + w83627hf_write_value(data, W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); } } } /* Start monitoring */ - w83627hf_write_value(client, W83781D_REG_CONFIG, - (w83627hf_read_value(client, + w83627hf_write_value(data, W83781D_REG_CONFIG, + (w83627hf_read_value(data, W83781D_REG_CONFIG) & 0xf7) | 0x01); } static struct w83627hf_data *w83627hf_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); + struct w83627hf_data *data = dev_get_drvdata(dev); int i; mutex_lock(&data->update_lock); @@ -1483,23 +1444,23 @@ static struct w83627hf_data *w83627hf_update_device(struct device *dev) && (i == 5 || i == 6))) continue; data->in[i] = - w83627hf_read_value(client, W83781D_REG_IN(i)); + w83627hf_read_value(data, W83781D_REG_IN(i)); data->in_min[i] = - w83627hf_read_value(client, + w83627hf_read_value(data, W83781D_REG_IN_MIN(i)); data->in_max[i] = - w83627hf_read_value(client, + w83627hf_read_value(data, W83781D_REG_IN_MAX(i)); } for (i = 1; i <= 3; i++) { data->fan[i - 1] = - w83627hf_read_value(client, W83781D_REG_FAN(i)); + w83627hf_read_value(data, W83781D_REG_FAN(i)); data->fan_min[i - 1] = - w83627hf_read_value(client, + w83627hf_read_value(data, W83781D_REG_FAN_MIN(i)); } for (i = 1; i <= 3; i++) { - u8 tmp = w83627hf_read_value(client, + u8 tmp = w83627hf_read_value(data, W836X7HF_REG_PWM(data->type, i)); /* bits 0-3 are reserved in 627THF */ if (data->type == w83627thf) @@ -1510,47 +1471,47 @@ static struct w83627hf_data *w83627hf_update_device(struct device *dev) break; } - data->temp = w83627hf_read_value(client, W83781D_REG_TEMP(1)); + data->temp = w83627hf_read_value(data, W83781D_REG_TEMP(1)); data->temp_max = - w83627hf_read_value(client, W83781D_REG_TEMP_OVER(1)); + w83627hf_read_value(data, W83781D_REG_TEMP_OVER(1)); data->temp_max_hyst = - w83627hf_read_value(client, W83781D_REG_TEMP_HYST(1)); + w83627hf_read_value(data, W83781D_REG_TEMP_HYST(1)); data->temp_add[0] = - w83627hf_read_value(client, W83781D_REG_TEMP(2)); + w83627hf_read_value(data, W83781D_REG_TEMP(2)); data->temp_max_add[0] = - w83627hf_read_value(client, W83781D_REG_TEMP_OVER(2)); + w83627hf_read_value(data, W83781D_REG_TEMP_OVER(2)); data->temp_max_hyst_add[0] = - w83627hf_read_value(client, W83781D_REG_TEMP_HYST(2)); + w83627hf_read_value(data, W83781D_REG_TEMP_HYST(2)); if (data->type != w83697hf) { data->temp_add[1] = - w83627hf_read_value(client, W83781D_REG_TEMP(3)); + w83627hf_read_value(data, W83781D_REG_TEMP(3)); data->temp_max_add[1] = - w83627hf_read_value(client, W83781D_REG_TEMP_OVER(3)); + w83627hf_read_value(data, W83781D_REG_TEMP_OVER(3)); data->temp_max_hyst_add[1] = - w83627hf_read_value(client, W83781D_REG_TEMP_HYST(3)); + w83627hf_read_value(data, W83781D_REG_TEMP_HYST(3)); } - i = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); + i = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); data->fan_div[0] = (i >> 4) & 0x03; data->fan_div[1] = (i >> 6) & 0x03; if (data->type != w83697hf) { - data->fan_div[2] = (w83627hf_read_value(client, + data->fan_div[2] = (w83627hf_read_value(data, W83781D_REG_PIN) >> 6) & 0x03; } - i = w83627hf_read_value(client, W83781D_REG_VBAT); + i = w83627hf_read_value(data, W83781D_REG_VBAT); data->fan_div[0] |= (i >> 3) & 0x04; data->fan_div[1] |= (i >> 4) & 0x04; if (data->type != w83697hf) data->fan_div[2] |= (i >> 5) & 0x04; data->alarms = - w83627hf_read_value(client, W83781D_REG_ALARM1) | - (w83627hf_read_value(client, W83781D_REG_ALARM2) << 8) | - (w83627hf_read_value(client, W83781D_REG_ALARM3) << 16); - i = w83627hf_read_value(client, W83781D_REG_BEEP_INTS2); + w83627hf_read_value(data, W83781D_REG_ALARM1) | + (w83627hf_read_value(data, W83781D_REG_ALARM2) << 8) | + (w83627hf_read_value(data, W83781D_REG_ALARM3) << 16); + i = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2); data->beep_enable = i >> 7; data->beep_mask = ((i & 0x7f) << 8) | - w83627hf_read_value(client, W83781D_REG_BEEP_INTS1) | - w83627hf_read_value(client, W83781D_REG_BEEP_INTS3) << 16; + w83627hf_read_value(data, W83781D_REG_BEEP_INTS1) | + w83627hf_read_value(data, W83781D_REG_BEEP_INTS3) << 16; data->last_updated = jiffies; data->valid = 1; } @@ -1560,19 +1521,87 @@ static struct w83627hf_data *w83627hf_update_device(struct device *dev) return data; } +static int __init w83627hf_device_add(unsigned short address, + const struct w83627hf_sio_data *sio_data) +{ + struct resource res = { + .start = address + WINB_REGION_OFFSET, + .end = address + WINB_REGION_OFFSET + WINB_REGION_SIZE - 1, + .name = DRVNAME, + .flags = IORESOURCE_IO, + }; + int err; + + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + printk(KERN_ERR DRVNAME ": Device resource addition failed " + "(%d)\n", err); + goto exit_device_put; + } + + pdev->dev.platform_data = kmalloc(sizeof(struct w83627hf_sio_data), + GFP_KERNEL); + if (!pdev->dev.platform_data) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + goto exit_device_put; + } + memcpy(pdev->dev.platform_data, sio_data, + sizeof(struct w83627hf_sio_data)); + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", + err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit: + return err; +} + static int __init sensors_w83627hf_init(void) { - if (w83627hf_find(0x2e, &address) - && w83627hf_find(0x4e, &address)) { + int err; + unsigned short address; + struct w83627hf_sio_data sio_data; + + if (w83627hf_find(0x2e, &address, &sio_data) + && w83627hf_find(0x4e, &address, &sio_data)) return -ENODEV; - } - return i2c_isa_add_driver(&w83627hf_driver); + err = platform_driver_register(&w83627hf_driver); + if (err) + goto exit; + + /* Sets global pdev as a side effect */ + err = w83627hf_device_add(address, &sio_data); + if (err) + goto exit_driver; + + return 0; + +exit_driver: + platform_driver_unregister(&w83627hf_driver); +exit: + return err; } static void __exit sensors_w83627hf_exit(void) { - i2c_isa_del_driver(&w83627hf_driver); + platform_device_unregister(pdev); + platform_driver_unregister(&w83627hf_driver); } MODULE_AUTHOR("Frodo Looijaard , " -- cgit v1.2.3 From 51f2cca1f72db5e272ed79b678b62fb9472e916e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 8 May 2007 17:22:00 +0200 Subject: hwmon/smsc47m1: Convert to a platform driver Convert the smsc47m1 driver from the nonsensical i2c-isa hack to a regular platform driver. Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 3 +- drivers/hwmon/smsc47m1.c | 327 ++++++++++++++++++++++++++++------------------- 2 files changed, 194 insertions(+), 136 deletions(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ae3ab9169d5..6362d02b18d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -416,8 +416,7 @@ config SENSORS_SIS5595 config SENSORS_SMSC47M1 tristate "SMSC LPC47M10x and compatibles" - depends on HWMON && I2C - select I2C_ISA + depends on HWMON help If you say yes here you get support for the integrated fan monitoring and control capabilities of the SMSC LPC47B27x, diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index f219d7c6a98..223708897e8 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -30,8 +30,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -39,9 +38,9 @@ #include #include -/* Address is autodetected, there is no default value */ -static unsigned short address; -static u8 devid; +static struct platform_device *pdev; + +#define DRVNAME "smsc47m1" enum chips { smsc47m1, smsc47m2 }; /* Super-I/0 registers and commands */ @@ -113,7 +112,8 @@ static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 }; #define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E) struct smsc47m1_data { - struct i2c_client client; + unsigned short addr; + const char *name; enum chips type; struct class_device *class_dev; @@ -127,30 +127,34 @@ struct smsc47m1_data { u8 pwm[3]; /* Register value (bit 0 is disable) */ }; +struct smsc47m1_sio_data { + enum chips type; +}; -static int smsc47m1_detect(struct i2c_adapter *adapter); -static int smsc47m1_detach_client(struct i2c_client *client); + +static int smsc47m1_probe(struct platform_device *pdev); +static int smsc47m1_remove(struct platform_device *pdev); static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, int init); -static inline int smsc47m1_read_value(struct i2c_client *client, u8 reg) +static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg) { - return inb_p(client->addr + reg); + return inb_p(data->addr + reg); } -static inline void smsc47m1_write_value(struct i2c_client *client, u8 reg, +static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg, u8 value) { - outb_p(value, client->addr + reg); + outb_p(value, data->addr + reg); } -static struct i2c_driver smsc47m1_driver = { +static struct platform_driver smsc47m1_driver = { .driver = { .owner = THIS_MODULE, - .name = "smsc47m1", + .name = DRVNAME, }, - .attach_adapter = smsc47m1_detect, - .detach_client = smsc47m1_detach_client, + .probe = smsc47m1_probe, + .remove = __devexit_p(smsc47m1_remove), }; /* nr is 0 or 1 in the callback functions below */ @@ -204,8 +208,7 @@ static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, cha static ssize_t set_fan_min(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); + struct smsc47m1_data *data = dev_get_drvdata(dev); long rpmdiv, val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -217,7 +220,7 @@ static ssize_t set_fan_min(struct device *dev, const char *buf, } data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv); - smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr], + smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr], data->fan_preload[nr]); mutex_unlock(&data->update_lock); @@ -231,8 +234,7 @@ static ssize_t set_fan_min(struct device *dev, const char *buf, static ssize_t set_fan_div(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); + struct smsc47m1_data *data = dev_get_drvdata(dev); long new_div = simple_strtol(buf, NULL, 10), tmp; u8 old_div = DIV_FROM_REG(data->fan_div[nr]); @@ -254,15 +256,15 @@ static ssize_t set_fan_div(struct device *dev, const char *buf, switch (nr) { case 0: case 1: - tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) + tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV) & ~(0x03 << (4 + 2 * nr)); tmp |= data->fan_div[nr] << (4 + 2 * nr); - smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp); + smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp); break; case 2: - tmp = smsc47m1_read_value(client, SMSC47M2_REG_FANDIV3) & 0xCF; + tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF; tmp |= data->fan_div[2] << 4; - smsc47m1_write_value(client, SMSC47M2_REG_FANDIV3, tmp); + smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp); break; } @@ -270,7 +272,7 @@ static ssize_t set_fan_div(struct device *dev, const char *buf, tmp = 192 - (old_div * (192 - data->fan_preload[nr]) + new_div / 2) / new_div; data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191); - smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr], + smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr], data->fan_preload[nr]); mutex_unlock(&data->update_lock); @@ -280,8 +282,7 @@ static ssize_t set_fan_div(struct device *dev, const char *buf, static ssize_t set_pwm(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); + struct smsc47m1_data *data = dev_get_drvdata(dev); long val = simple_strtol(buf, NULL, 10); @@ -291,7 +292,7 @@ static ssize_t set_pwm(struct device *dev, const char *buf, mutex_lock(&data->update_lock); data->pwm[nr] &= 0x81; /* Preserve additional bits */ data->pwm[nr] |= PWM_TO_REG(val); - smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr], + smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr], data->pwm[nr]); mutex_unlock(&data->update_lock); @@ -301,8 +302,7 @@ static ssize_t set_pwm(struct device *dev, const char *buf, static ssize_t set_pwm_en(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); + struct smsc47m1_data *data = dev_get_drvdata(dev); long val = simple_strtol(buf, NULL, 10); @@ -312,7 +312,7 @@ static ssize_t set_pwm_en(struct device *dev, const char *buf, mutex_lock(&data->update_lock); data->pwm[nr] &= 0xFE; /* preserve the other bits */ data->pwm[nr] |= !val; - smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr], + smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr], data->pwm[nr]); mutex_unlock(&data->update_lock); @@ -377,6 +377,15 @@ fan_present(3); static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL); +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct smsc47m1_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + /* Almost all sysfs files may or may not be created depending on the chip setup so we create them individually. It is still convenient to define a group to remove them all at once. */ @@ -399,6 +408,7 @@ static struct attribute *smsc47m1_attributes[] = { &dev_attr_pwm3_enable.attr, &dev_attr_alarms.attr, + &dev_attr_name.attr, NULL }; @@ -406,12 +416,13 @@ static const struct attribute_group smsc47m1_group = { .attrs = smsc47m1_attributes, }; -static int __init smsc47m1_find(unsigned short *addr) +static int __init smsc47m1_find(unsigned short *addr, + struct smsc47m1_sio_data *sio_data) { u8 val; superio_enter(); - devid = superio_inb(SUPERIO_REG_DEVID); + val = superio_inb(SUPERIO_REG_DEVID); /* * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x @@ -424,23 +435,28 @@ static int __init smsc47m1_find(unsigned short *addr) * supports a 3rd fan, and the pin configuration registers are * unfortunately different. */ - switch (devid) { + switch (val) { case 0x51: printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n"); + sio_data->type = smsc47m1; break; case 0x59: printk(KERN_INFO "smsc47m1: Found SMSC " "LPC47M10x/LPC47M112/LPC47M13x\n"); + sio_data->type = smsc47m1; break; case 0x5F: printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n"); + sio_data->type = smsc47m1; break; case 0x60: printk(KERN_INFO "smsc47m1: Found SMSC " "LPC47M15x/LPC47M192/LPC47M997\n"); + sio_data->type = smsc47m1; break; case 0x6B: printk(KERN_INFO "smsc47m1: Found SMSC LPC47M292\n"); + sio_data->type = smsc47m2; break; default: superio_exit(); @@ -461,15 +477,25 @@ static int __init smsc47m1_find(unsigned short *addr) return 0; } -static int smsc47m1_detect(struct i2c_adapter *adapter) +static int __devinit smsc47m1_probe(struct platform_device *pdev) { - struct i2c_client *new_client; + struct device *dev = &pdev->dev; + struct smsc47m1_sio_data *sio_data = dev->platform_data; struct smsc47m1_data *data; + struct resource *res; int err = 0; int fan1, fan2, fan3, pwm1, pwm2, pwm3; - if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.driver.name)) { - dev_err(&adapter->dev, "Region 0x%x already in use!\n", address); + static const char *names[] = { + "smsc47m1", + "smsc47m2", + }; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) { + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + (unsigned long)res->start, + (unsigned long)res->end); return -EBUSY; } @@ -478,131 +504,99 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) goto error_release; } - data->type = devid == 0x6B ? smsc47m2 : smsc47m1; - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &smsc47m1_driver; - new_client->flags = 0; - - strlcpy(new_client->name, - data->type == smsc47m2 ? "smsc47m2" : "smsc47m1", - I2C_NAME_SIZE); + data->addr = res->start; + data->type = sio_data->type; + data->name = names[sio_data->type]; mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); /* If no function is properly configured, there's no point in actually registering the chip. */ - pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05) + pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05) == 0x04; - pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05) + pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05) == 0x04; if (data->type == smsc47m2) { - fan1 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN1) + fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1) & 0x0d) == 0x09; - fan2 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN2) + fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2) & 0x0d) == 0x09; - fan3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN3) + fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3) & 0x0d) == 0x0d; - pwm3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_PPIN3) + pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3) & 0x0d) == 0x08; } else { - fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) + fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0)) & 0x05) == 0x05; - fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) + fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1)) & 0x05) == 0x05; fan3 = 0; pwm3 = 0; } if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) { - dev_warn(&adapter->dev, "Device at 0x%x is not configured, " - "will not use\n", new_client->addr); + dev_warn(dev, "Device not configured, will not use\n"); err = -ENODEV; goto error_free; } - if ((err = i2c_attach_client(new_client))) - goto error_free; - /* Some values (fan min, clock dividers, pwm registers) may be needed before any update is triggered, so we better read them at least once here. We don't usually do it that way, but in this particular case, manually reading 5 registers out of 8 doesn't make much sense and we're better using the existing function. */ - smsc47m1_update_device(&new_client->dev, 1); + smsc47m1_update_device(dev, 1); /* Register sysfs hooks */ if (fan1) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_fan1_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan1_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan1_div))) + if ((err = device_create_file(dev, &dev_attr_fan1_input)) + || (err = device_create_file(dev, &dev_attr_fan1_min)) + || (err = device_create_file(dev, &dev_attr_fan1_div))) goto error_remove_files; } else - dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, " - "skipping\n"); + dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n"); if (fan2) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_fan2_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan2_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan2_div))) + if ((err = device_create_file(dev, &dev_attr_fan2_input)) + || (err = device_create_file(dev, &dev_attr_fan2_min)) + || (err = device_create_file(dev, &dev_attr_fan2_div))) goto error_remove_files; } else - dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, " - "skipping\n"); + dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n"); if (fan3) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_fan3_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan3_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan3_div))) + if ((err = device_create_file(dev, &dev_attr_fan3_input)) + || (err = device_create_file(dev, &dev_attr_fan3_min)) + || (err = device_create_file(dev, &dev_attr_fan3_div))) goto error_remove_files; } else - dev_dbg(&new_client->dev, "Fan 3 not enabled by hardware, " - "skipping\n"); + dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n"); if (pwm1) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_pwm1)) - || (err = device_create_file(&new_client->dev, - &dev_attr_pwm1_enable))) + if ((err = device_create_file(dev, &dev_attr_pwm1)) + || (err = device_create_file(dev, &dev_attr_pwm1_enable))) goto error_remove_files; } else - dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, " - "skipping\n"); + dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n"); if (pwm2) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_pwm2)) - || (err = device_create_file(&new_client->dev, - &dev_attr_pwm2_enable))) + if ((err = device_create_file(dev, &dev_attr_pwm2)) + || (err = device_create_file(dev, &dev_attr_pwm2_enable))) goto error_remove_files; } else - dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, " - "skipping\n"); + dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n"); if (pwm3) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_pwm3)) - || (err = device_create_file(&new_client->dev, - &dev_attr_pwm3_enable))) + if ((err = device_create_file(dev, &dev_attr_pwm3)) + || (err = device_create_file(dev, &dev_attr_pwm3_enable))) goto error_remove_files; } else - dev_dbg(&new_client->dev, "PWM 3 not enabled by hardware, " - "skipping\n"); + dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n"); - if ((err = device_create_file(&new_client->dev, &dev_attr_alarms))) + if ((err = device_create_file(dev, &dev_attr_alarms))) goto error_remove_files; - data->class_dev = hwmon_device_register(&new_client->dev); + data->class_dev = hwmon_device_register(dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); goto error_remove_files; @@ -611,27 +605,25 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) return 0; error_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group); - i2c_detach_client(new_client); + sysfs_remove_group(&dev->kobj, &smsc47m1_group); error_free: kfree(data); error_release: - release_region(address, SMSC_EXTENT); + release_region(res->start, SMSC_EXTENT); return err; } -static int smsc47m1_detach_client(struct i2c_client *client) +static int __devexit smsc47m1_remove(struct platform_device *pdev) { - struct smsc47m1_data *data = i2c_get_clientdata(client); - int err; + struct smsc47m1_data *data = platform_get_drvdata(pdev); + struct resource *res; + platform_set_drvdata(pdev, NULL); hwmon_device_unregister(data->class_dev); - sysfs_remove_group(&client->dev.kobj, &smsc47m1_group); - - if ((err = i2c_detach_client(client))) - return err; + sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); - release_region(client->addr, SMSC_EXTENT); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start, SMSC_EXTENT); kfree(data); return 0; @@ -640,8 +632,7 @@ static int smsc47m1_detach_client(struct i2c_client *client) static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, int init) { - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); + struct smsc47m1_data *data = dev_get_drvdata(dev); mutex_lock(&data->update_lock); @@ -650,32 +641,32 @@ static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, fan_nr = data->type == smsc47m2 ? 3 : 2; for (i = 0; i < fan_nr; i++) { - data->fan[i] = smsc47m1_read_value(client, + data->fan[i] = smsc47m1_read_value(data, SMSC47M1_REG_FAN[i]); - data->fan_preload[i] = smsc47m1_read_value(client, + data->fan_preload[i] = smsc47m1_read_value(data, SMSC47M1_REG_FAN_PRELOAD[i]); - data->pwm[i] = smsc47m1_read_value(client, + data->pwm[i] = smsc47m1_read_value(data, SMSC47M1_REG_PWM[i]); } - i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV); + i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV); data->fan_div[0] = (i >> 4) & 0x03; data->fan_div[1] = i >> 6; - data->alarms = smsc47m1_read_value(client, + data->alarms = smsc47m1_read_value(data, SMSC47M1_REG_ALARM) >> 6; /* Clear alarms if needed */ if (data->alarms) - smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0); + smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0); if (fan_nr >= 3) { - data->fan_div[2] = (smsc47m1_read_value(client, + data->fan_div[2] = (smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) >> 4) & 0x03; - data->alarms |= (smsc47m1_read_value(client, + data->alarms |= (smsc47m1_read_value(data, SMSC47M2_REG_ALARM6) & 0x40) >> 4; /* Clear alarm if needed */ if (data->alarms & 0x04) - smsc47m1_write_value(client, + smsc47m1_write_value(data, SMSC47M2_REG_ALARM6, 0x40); } @@ -687,18 +678,86 @@ static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, return data; } +static int __init smsc47m1_device_add(unsigned short address, + const struct smsc47m1_sio_data *sio_data) +{ + struct resource res = { + .start = address, + .end = address + SMSC_EXTENT - 1, + .name = DRVNAME, + .flags = IORESOURCE_IO, + }; + int err; + + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + printk(KERN_ERR DRVNAME ": Device resource addition failed " + "(%d)\n", err); + goto exit_device_put; + } + + pdev->dev.platform_data = kmalloc(sizeof(struct smsc47m1_sio_data), + GFP_KERNEL); + if (!pdev->dev.platform_data) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + goto exit_device_put; + } + memcpy(pdev->dev.platform_data, sio_data, + sizeof(struct smsc47m1_sio_data)); + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", + err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit: + return err; +} + static int __init sm_smsc47m1_init(void) { - if (smsc47m1_find(&address)) { + int err; + unsigned short address; + struct smsc47m1_sio_data sio_data; + + if (smsc47m1_find(&address, &sio_data)) return -ENODEV; - } - return i2c_isa_add_driver(&smsc47m1_driver); + err = platform_driver_register(&smsc47m1_driver); + if (err) + goto exit; + + /* Sets global pdev as a side effect */ + err = smsc47m1_device_add(address, &sio_data); + if (err) + goto exit_driver; + + return 0; + +exit_driver: + platform_driver_unregister(&smsc47m1_driver); +exit: + return err; } static void __exit sm_smsc47m1_exit(void) { - i2c_isa_del_driver(&smsc47m1_driver); + platform_device_unregister(pdev); + platform_driver_unregister(&smsc47m1_driver); } MODULE_AUTHOR("Mark D. Studebaker "); -- cgit v1.2.3 From d20620de0c3de622a9d6a841725bafaed6d1aec2 Mon Sep 17 00:00:00 2001 From: Hans-Juergen Koch Date: Tue, 8 May 2007 17:22:00 +0200 Subject: hwmon: New max6650 driver This driver supports the Maxim MAX6650 and MAX6651 fan speed monitoring and control chips. Signed-off-by: Hans J. Koch Signed-off-by: Jean Delvare --- Documentation/hwmon/max6650 | 53 ++++ MAINTAINERS | 6 + drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/max6650.c | 693 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 763 insertions(+) create mode 100644 Documentation/hwmon/max6650 create mode 100644 drivers/hwmon/max6650.c (limited to 'drivers/hwmon/Kconfig') diff --git a/Documentation/hwmon/max6650 b/Documentation/hwmon/max6650 new file mode 100644 index 00000000000..8be7beb9e3e --- /dev/null +++ b/Documentation/hwmon/max6650 @@ -0,0 +1,53 @@ +Kernel driver max6650 +===================== + +Supported chips: + * Maxim 6650 / 6651 + Prefix: 'max6650' + Addresses scanned: I2C 0x1b, 0x1f, 0x48, 0x4b + Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf + +Authors: + Hans J. Koch + John Morris + Claus Gindhart + +Description +----------- + +This driver implements support for the Maxim 6650/6651 + +The 2 devices are very similar, but the Maxim 6550 has a reduced feature +set, e.g. only one fan-input, instead of 4 for the 6651. + +The driver is not able to distinguish between the 2 devices. + +The driver provides the following sensor accesses in sysfs: + +fan1_input ro fan tachometer speed in RPM +fan2_input ro " +fan3_input ro " +fan4_input ro " +fan1_target rw desired fan speed in RPM (closed loop mode only) +pwm1_enable rw regulator mode, 0=full on, 1=open loop, 2=closed loop +pwm1 rw relative speed (0-255), 255=max. speed. + Used in open loop mode only. +fan1_div rw sets the speed range the inputs can handle. Legal + values are 1, 2, 4, and 8. Use lower values for + faster fans. + +Module parameters +----------------- + +If your board has a BIOS that initializes the MAX6650/6651 correctly, you can +simply load your module without parameters. It won't touch the configuration +registers then. If your board BIOS doesn't initialize the chip, or you want +different settings, you can set the following parameters: + +voltage_12V: 5=5V fan, 12=12V fan, 0=don't change +prescaler: Possible values are 1,2,4,8,16, or 0 for don't change +clock: The clock frequency in Hz of the chip the driver should assume [254000] + +Please have a look at the MAX6650/6651 data sheet and make sure that you fully +understand the meaning of these parameters before you attempt to change them. + diff --git a/MAINTAINERS b/MAINTAINERS index 6d665ac13f9..a328862731c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2304,6 +2304,12 @@ M: vandrove@vc.cvut.cz L: linux-fbdev-devel@lists.sourceforge.net (subscribers-only) S: Maintained +MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER +P: Hans J. Koch +M: hjk@linutronix.de +L: lm-sensors@lm-sensors.org +S: Maintained + MEGARAID SCSI DRIVERS P: Neela Syam Kolli M: Neela.Kolli@engenio.com diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6362d02b18d..d89bd5eb50a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -375,6 +375,16 @@ config SENSORS_MAX1619 This driver can also be built as a module. If so, the module will be called max1619. +config SENSORS_MAX6650 + tristate "Maxim MAX6650 sensor chip" + depends on HWMON && I2C && EXPERIMENTAL + help + If you say yes here you get support for the MAX6650 / MAX6651 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6650. + config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" depends on HWMON && I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4165c27a2f2..bbbe86bf5da 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o +obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c new file mode 100644 index 00000000000..8415664f33c --- /dev/null +++ b/drivers/hwmon/max6650.c @@ -0,0 +1,693 @@ +/* + * max6650.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring. + * + * (C) 2007 by Hans J. Koch + * + * based on code written by John Morris + * Copyright (c) 2003 Spirent Communications + * and Claus Gindhart + * + * This module has only been tested with the MAX6650 chip. It should + * also work with the MAX6651. It does not distinguish max6650 and max6651 + * chips. + * + * Tha datasheet was last seen at: + * + * http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses to scan. There are four disjoint possibilities, by pin config. + */ + +static unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b, I2C_CLIENT_END}; + +/* + * Insmod parameters + */ + +/* fan_voltage: 5=5V fan, 12=12V fan, 0=don't change */ +static int fan_voltage; +/* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */ +static int prescaler; +/* clock: The clock frequency of the chip the driver should assume */ +static int clock = 254000; + +module_param(fan_voltage, int, S_IRUGO); +module_param(prescaler, int, S_IRUGO); +module_param(clock, int, S_IRUGO); + +I2C_CLIENT_INSMOD_1(max6650); + +/* + * MAX 6650/6651 registers + */ + +#define MAX6650_REG_SPEED 0x00 +#define MAX6650_REG_CONFIG 0x02 +#define MAX6650_REG_GPIO_DEF 0x04 +#define MAX6650_REG_DAC 0x06 +#define MAX6650_REG_ALARM_EN 0x08 +#define MAX6650_REG_ALARM 0x0A +#define MAX6650_REG_TACH0 0x0C +#define MAX6650_REG_TACH1 0x0E +#define MAX6650_REG_TACH2 0x10 +#define MAX6650_REG_TACH3 0x12 +#define MAX6650_REG_GPIO_STAT 0x14 +#define MAX6650_REG_COUNT 0x16 + +/* + * Config register bits + */ + +#define MAX6650_CFG_V12 0x08 +#define MAX6650_CFG_PRESCALER_MASK 0x07 +#define MAX6650_CFG_PRESCALER_2 0x01 +#define MAX6650_CFG_PRESCALER_4 0x02 +#define MAX6650_CFG_PRESCALER_8 0x03 +#define MAX6650_CFG_PRESCALER_16 0x04 +#define MAX6650_CFG_MODE_MASK 0x30 +#define MAX6650_CFG_MODE_ON 0x00 +#define MAX6650_CFG_MODE_OFF 0x10 +#define MAX6650_CFG_MODE_CLOSED_LOOP 0x20 +#define MAX6650_CFG_MODE_OPEN_LOOP 0x30 +#define MAX6650_COUNT_MASK 0x03 + +/* Minimum and maximum values of the FAN-RPM */ +#define FAN_RPM_MIN 240 +#define FAN_RPM_MAX 30000 + +#define DIV_FROM_REG(reg) (1 << (reg & 7)) + +static int max6650_attach_adapter(struct i2c_adapter *adapter); +static int max6650_detect(struct i2c_adapter *adapter, int address, int kind); +static int max6650_init_client(struct i2c_client *client); +static int max6650_detach_client(struct i2c_client *client); +static struct max6650_data *max6650_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver max6650_driver = { + .driver = { + .name = "max6650", + }, + .attach_adapter = max6650_attach_adapter, + .detach_client = max6650_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct max6650_data +{ + struct i2c_client client; + struct class_device *class_dev; + struct mutex update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 speed; + u8 config; + u8 tach[4]; + u8 count; + u8 dac; +}; + +static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max6650_data *data = max6650_update_device(dev); + int rpm; + + /* + * Calculation details: + * + * Each tachometer counts over an interval given by the "count" + * register (0.25, 0.5, 1 or 2 seconds). This module assumes + * that the fans produce two pulses per revolution (this seems + * to be the most common). + */ + + rpm = ((data->tach[attr->index] * 120) / DIV_FROM_REG(data->count)); + return sprintf(buf, "%d\n", rpm); +} + +/* + * Set the fan speed to the specified RPM (or read back the RPM setting). + * This works in closed loop mode only. Use pwm1 for open loop speed setting. + * + * The MAX6650/1 will automatically control fan speed when in closed loop + * mode. + * + * Assumptions: + * + * 1) The MAX6650/1 internal 254kHz clock frequency is set correctly. Use + * the clock module parameter if you need to fine tune this. + * + * 2) The prescaler (low three bits of the config register) has already + * been set to an appropriate value. Use the prescaler module parameter + * if your BIOS doesn't initialize the chip properly. + * + * The relevant equations are given on pages 21 and 22 of the datasheet. + * + * From the datasheet, the relevant equation when in regulation is: + * + * [fCLK / (128 x (KTACH + 1))] = 2 x FanSpeed / KSCALE + * + * where: + * + * fCLK is the oscillator frequency (either the 254kHz internal + * oscillator or the externally applied clock) + * + * KTACH is the value in the speed register + * + * FanSpeed is the speed of the fan in rps + * + * KSCALE is the prescaler value (1, 2, 4, 8, or 16) + * + * When reading, we need to solve for FanSpeed. When writing, we need to + * solve for KTACH. + * + * Note: this tachometer is completely separate from the tachometers + * used to measure the fan speeds. Only one fan's speed (fan1) is + * controlled. + */ + +static ssize_t get_target(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct max6650_data *data = max6650_update_device(dev); + int kscale, ktach, rpm; + + /* + * Use the datasheet equation: + * + * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] + * + * then multiply by 60 to give rpm. + */ + + kscale = DIV_FROM_REG(data->config); + ktach = data->speed; + rpm = 60 * kscale * clock / (256 * (ktach + 1)); + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t set_target(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = i2c_get_clientdata(client); + int rpm = simple_strtoul(buf, NULL, 10); + int kscale, ktach; + + rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX); + + /* + * Divide the required speed by 60 to get from rpm to rps, then + * use the datasheet equation: + * + * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 + */ + + mutex_lock(&data->update_lock); + + kscale = DIV_FROM_REG(data->config); + ktach = ((clock * kscale) / (256 * rpm / 60)) - 1; + if (ktach < 0) + ktach = 0; + if (ktach > 255) + ktach = 255; + data->speed = ktach; + + i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED, data->speed); + + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * Get/set the fan speed in open loop mode using pwm1 sysfs file. + * Speed is given as a relative value from 0 to 255, where 255 is maximum + * speed. Note that this is done by writing directly to the chip's DAC, + * it won't change the closed loop speed set by fan1_target. + * Also note that due to rounding errors it is possible that you don't read + * back exactly the value you have set. + */ + +static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + int pwm; + struct max6650_data *data = max6650_update_device(dev); + + /* Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans. + Lower DAC values mean higher speeds. */ + if (data->config & MAX6650_CFG_V12) + pwm = 255 - (255 * (int)data->dac)/180; + else + pwm = 255 - (255 * (int)data->dac)/76; + + if (pwm < 0) + pwm = 0; + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = i2c_get_clientdata(client); + int pwm = simple_strtoul(buf, NULL, 10); + + pwm = SENSORS_LIMIT(pwm, 0, 255); + + mutex_lock(&data->update_lock); + + if (data->config & MAX6650_CFG_V12) + data->dac = 180 - (180 * pwm)/255; + else + data->dac = 76 - (76 * pwm)/255; + + i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); + + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * Get/Set controller mode: + * Possible values: + * 0 = Fan always on + * 1 = Open loop, Voltage is set according to speed, not regulated. + * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer + */ + +static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct max6650_data *data = max6650_update_device(dev); + int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4; + int sysfs_modes[4] = {0, 1, 2, 1}; + + return sprintf(buf, "%d\n", sysfs_modes[mode]); +} + +static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = i2c_get_clientdata(client); + int mode = simple_strtoul(buf, NULL, 10); + int max6650_modes[3] = {0, 3, 2}; + + if ((mode < 0)||(mode > 2)) { + dev_err(&client->dev, + "illegal value for pwm1_enable (%d)\n", mode); + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + data->config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); + data->config = (data->config & ~MAX6650_CFG_MODE_MASK) + | (max6650_modes[mode] << 4); + + i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config); + + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * Read/write functions for fan1_div sysfs file. The MAX6650 has no such + * divider. We handle this by converting between divider and counttime: + * + * (counttime == k) <==> (divider == 2^k), k = 0, 1, 2, or 3 + * + * Lower values of k allow to connect a faster fan without the risk of + * counter overflow. The price is lower resolution. You can also set counttime + * using the module parameter. Note that the module parameter "prescaler" also + * influences the behaviour. Unfortunately, there's no sysfs attribute + * defined for that. See the data sheet for details. + */ + +static ssize_t get_div(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct max6650_data *data = max6650_update_device(dev); + + return sprintf(buf, "%d\n", DIV_FROM_REG(data->count)); +} + +static ssize_t set_div(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = i2c_get_clientdata(client); + int div = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + switch (div) { + case 1: + data->count = 0; + break; + case 2: + data->count = 1; + break; + case 4: + data->count = 2; + break; + case 8: + data->count = 3; + break; + default: + dev_err(&client->dev, + "illegal value for fan divider (%d)\n", div); + return -EINVAL; + } + + i2c_smbus_write_byte_data(client, MAX6650_REG_COUNT, data->count); + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3); +static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target); +static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div); +static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable); +static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm); + + +static struct attribute *max6650_attrs[] = { + &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, + &dev_attr_fan1_target.attr, + &dev_attr_fan1_div.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1.attr, + NULL +}; + +static struct attribute_group max6650_attr_grp = { + .attrs = max6650_attrs, +}; + +/* + * Real code + */ + +static int max6650_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) { + dev_dbg(&adapter->dev, + "FATAL: max6650_attach_adapter class HWMON not set\n"); + return 0; + } + + return i2c_probe(adapter, &addr_data, max6650_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ + +static int max6650_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct max6650_data *data; + int err = -ENODEV; + + dev_dbg(&adapter->dev, "max6650_detect called, kind = %d\n", kind); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support " + "byte read mode, skipping.\n"); + return 0; + } + + if (!(data = kzalloc(sizeof(struct max6650_data), GFP_KERNEL))) { + dev_err(&adapter->dev, "max6650: out of memory.\n"); + return -ENOMEM; + } + + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; + client->adapter = adapter; + client->driver = &max6650_driver; + + /* + * Now we do the remaining detection. A negative kind means that + * the driver was loaded with no force parameter (default), so we + * must both detect and identify the chip (actually there is only + * one possible kind of chip for now, max6650). A zero kind means that + * the driver was loaded with the force parameter, the detection + * step shall be skipped. A positive kind means that the driver + * was loaded with the force parameter and a given kind of chip is + * requested, so both the detection and the identification steps + * are skipped. + * + * Currently I can find no way to distinguish between a MAX6650 and + * a MAX6651. This driver has only been tried on the former. + */ + + if ((kind < 0) && + ( (i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0) + ||(i2c_smbus_read_byte_data(client, MAX6650_REG_GPIO_STAT) & 0xE0) + ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN) & 0xE0) + ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM) & 0xE0) + ||(i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT) & 0xFC))) { + dev_dbg(&adapter->dev, + "max6650: detection failed at 0x%02x.\n", address); + goto err_free; + } + + dev_info(&adapter->dev, "max6650: chip found at 0x%02x.\n", address); + + strlcpy(client->name, "max6650", I2C_NAME_SIZE); + mutex_init(&data->update_lock); + + if ((err = i2c_attach_client(client))) { + dev_err(&adapter->dev, "max6650: failed to attach client.\n"); + goto err_free; + } + + /* + * Initialize the max6650 chip + */ + if (max6650_init_client(client)) + goto err_detach; + + err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp); + if (err) + goto err_detach; + + data->class_dev = hwmon_device_register(&client->dev); + if (!IS_ERR(data->class_dev)) + return 0; + + err = PTR_ERR(data->class_dev); + dev_err(&client->dev, "error registering hwmon device.\n"); + sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); +err_detach: + i2c_detach_client(client); +err_free: + kfree(data); + return err; +} + +static int max6650_detach_client(struct i2c_client *client) +{ + struct max6650_data *data = i2c_get_clientdata(client); + int err; + + sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); + hwmon_device_unregister(data->class_dev); + err = i2c_detach_client(client); + if (!err) + kfree(data); + return err; +} + +static int max6650_init_client(struct i2c_client *client) +{ + struct max6650_data *data = i2c_get_clientdata(client); + int config; + int err = -EIO; + + config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); + + if (config < 0) { + dev_err(&client->dev, "Error reading config, aborting.\n"); + return err; + } + + switch (fan_voltage) { + case 0: + break; + case 5: + config &= ~MAX6650_CFG_V12; + break; + case 12: + config |= MAX6650_CFG_V12; + break; + default: + dev_err(&client->dev, + "illegal value for fan_voltage (%d)\n", + fan_voltage); + } + + dev_info(&client->dev, "Fan voltage is set to %dV.\n", + (config & MAX6650_CFG_V12) ? 12 : 5); + + switch (prescaler) { + case 0: + break; + case 1: + config &= ~MAX6650_CFG_PRESCALER_MASK; + break; + case 2: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_2; + break; + case 4: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_4; + break; + case 8: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_8; + break; + case 16: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_16; + break; + default: + dev_err(&client->dev, + "illegal value for prescaler (%d)\n", + prescaler); + } + + dev_info(&client->dev, "Prescaler is set to %d.\n", + 1 << (config & MAX6650_CFG_PRESCALER_MASK)); + + /* If mode is set to "full off", we change it to "open loop" and + * set DAC to 255, which has the same effect. We do this because + * there's no "full off" mode defined in hwmon specifcations. + */ + + if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) { + dev_dbg(&client->dev, "Change mode to open loop, full off.\n"); + config = (config & ~MAX6650_CFG_MODE_MASK) + | MAX6650_CFG_MODE_OPEN_LOOP; + if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) { + dev_err(&client->dev, "DAC write error, aborting.\n"); + return err; + } + } + + if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { + dev_err(&client->dev, "Config write error, aborting.\n"); + return err; + } + + data->config = config; + data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT); + + return 0; +} + +static const u8 tach_reg[] = { + MAX6650_REG_TACH0, + MAX6650_REG_TACH1, + MAX6650_REG_TACH2, + MAX6650_REG_TACH3, +}; + +static struct max6650_data *max6650_update_device(struct device *dev) +{ + int i; + struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->speed = i2c_smbus_read_byte_data(client, + MAX6650_REG_SPEED); + data->config = i2c_smbus_read_byte_data(client, + MAX6650_REG_CONFIG); + for (i = 0; i < 4; i++) { + data->tach[i] = i2c_smbus_read_byte_data(client, + tach_reg[i]); + } + data->count = i2c_smbus_read_byte_data(client, + MAX6650_REG_COUNT); + data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init sensors_max6650_init(void) +{ + return i2c_add_driver(&max6650_driver); +} + +static void __exit sensors_max6650_exit(void) +{ + i2c_del_driver(&max6650_driver); +} + +MODULE_AUTHOR("Hans J. Koch"); +MODULE_DESCRIPTION("MAX6650 sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_max6650_init); +module_exit(sensors_max6650_exit); -- cgit v1.2.3 From c40769fee13c1ab43e1fb10320d6fbc29f582d8e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 8 May 2007 17:22:00 +0200 Subject: hwmon/lm78: No longer use i2c-isa Reimplement the ISA device support as a platform driver, so that we no longer rely on i2c-isa. Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 1 - drivers/hwmon/lm78.c | 377 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 267 insertions(+), 111 deletions(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d89bd5eb50a..659a2abb3e9 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -291,7 +291,6 @@ config SENSORS_LM77 config SENSORS_LM78 tristate "National Semiconductor LM78 and compatibles" depends on HWMON && I2C - select I2C_ISA select HWMON_VID help If you say yes here you get support for National Semiconductor LM78, diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 886786c3391..cfb08978459 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -2,6 +2,7 @@ lm78.c - Part of lm_sensors, Linux kernel modules for hardware monitoring Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (c) 2007 Jean Delvare This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,13 +24,17 @@ #include #include #include -#include +#include +#include #include #include #include #include #include +/* ISA device, if found */ +static struct platform_device *pdev; + /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, @@ -121,12 +126,8 @@ static inline int TEMP_FROM_REG(s8 val) a bit - except if there could be more than one SMBus. Groan. No solution for this yet. */ -/* This module may seem overly long and complicated. In fact, it is not so - bad. Quite a lot of bookkeeping is done. A real driver can often cut - some corners. */ - -/* For each registered chip, we need to keep some data in memory. - The structure is dynamically allocated. */ +/* For ISA chips, we abuse the i2c_client addr and name fields. We also use + the driver field to differentiate between I2C and ISA chips. */ struct lm78_data { struct i2c_client client; struct class_device *class_dev; @@ -152,10 +153,12 @@ struct lm78_data { static int lm78_attach_adapter(struct i2c_adapter *adapter); -static int lm78_isa_attach_adapter(struct i2c_adapter *adapter); static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); static int lm78_detach_client(struct i2c_client *client); +static int __devinit lm78_isa_probe(struct platform_device *pdev); +static int __devexit lm78_isa_remove(struct platform_device *pdev); + static int lm78_read_value(struct i2c_client *client, u8 reg); static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value); static struct lm78_data *lm78_update_device(struct device *dev); @@ -171,13 +174,13 @@ static struct i2c_driver lm78_driver = { .detach_client = lm78_detach_client, }; -static struct i2c_driver lm78_isa_driver = { +static struct platform_driver lm78_isa_driver = { .driver = { .owner = THIS_MODULE, - .name = "lm78-isa", + .name = "lm78", }, - .attach_adapter = lm78_isa_attach_adapter, - .detach_client = lm78_detach_client, + .probe = lm78_isa_probe, + .remove = lm78_isa_remove, }; @@ -203,8 +206,8 @@ static ssize_t show_in_max(struct device *dev, char *buf, int nr) static ssize_t set_in_min(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); + struct lm78_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -217,8 +220,8 @@ static ssize_t set_in_min(struct device *dev, const char *buf, static ssize_t set_in_max(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); + struct lm78_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -284,8 +287,8 @@ static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); + struct lm78_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -303,8 +306,8 @@ static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); + struct lm78_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -338,8 +341,8 @@ static ssize_t show_fan_min(struct device *dev, char *buf, int nr) static ssize_t set_fan_min(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); + struct lm78_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -362,8 +365,8 @@ static ssize_t show_fan_div(struct device *dev, char *buf, int nr) static ssize_t set_fan_div(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); + struct lm78_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; unsigned long val = simple_strtoul(buf, NULL, 10); unsigned long min; u8 reg; @@ -378,7 +381,7 @@ static ssize_t set_fan_div(struct device *dev, const char *buf, case 4: data->fan_div[nr] = 2; break; case 8: data->fan_div[nr] = 3; break; default: - dev_err(&client->dev, "fan_div value %ld not " + dev_err(dev, "fan_div value %ld not " "supported. Choose one of 1, 2, 4 or 8!\n", val); mutex_unlock(&data->update_lock); return -EINVAL; @@ -475,11 +478,6 @@ static int lm78_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, lm78_detect); } -static int lm78_isa_attach_adapter(struct i2c_adapter *adapter) -{ - return lm78_detect(adapter, isa_address, -1); -} - static struct attribute *lm78_attributes[] = { &dev_attr_in0_input.attr, &dev_attr_in0_min.attr, @@ -524,6 +522,17 @@ static const struct attribute_group lm78_group = { .attrs = lm78_attributes, }; +/* I2C devices get this name attribute automatically, but for ISA devices + we must create it by ourselves. */ +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct lm78_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->client.name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + /* This function is called by i2c_probe */ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -531,54 +540,10 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) struct i2c_client *new_client; struct lm78_data *data; const char *client_name = ""; - int is_isa = i2c_is_isa_adapter(adapter); - if (!is_isa && - !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { err = -ENODEV; - goto ERROR0; - } - - /* Reserve the ISA region */ - if (is_isa) - if (!request_region(address, LM78_EXTENT, - lm78_isa_driver.driver.name)) { - err = -EBUSY; - goto ERROR0; - } - - /* Probe whether there is anything available on this address. Already - done for SMBus clients */ - if (kind < 0) { - if (is_isa) { - -#define REALLY_SLOW_IO - /* We need the timeouts for at least some LM78-like - chips. But only if we read 'undefined' registers. */ - i = inb_p(address + 1); - if (inb_p(address + 2) != i) { - err = -ENODEV; - goto ERROR1; - } - if (inb_p(address + 3) != i) { - err = -ENODEV; - goto ERROR1; - } - if (inb_p(address + 7) != i) { - err = -ENODEV; - goto ERROR1; - } -#undef REALLY_SLOW_IO - - /* Let's just hope nothing breaks here */ - i = inb_p(address + 5) & 0x7f; - outb_p(~i & 0x7f, address + 5); - if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { - outb_p(i, address + 5); - err = -ENODEV; - goto ERROR1; - } - } + goto ERROR1; } /* OK. For now, we presume we have a valid client. We now create the @@ -591,13 +556,10 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) } new_client = &data->client; - if (is_isa) - mutex_init(&data->lock); i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; - new_client->driver = is_isa ? &lm78_isa_driver : &lm78_driver; - new_client->flags = 0; + new_client->driver = &lm78_driver; /* Now, we do the remaining detection. */ if (kind < 0) { @@ -605,8 +567,8 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) err = -ENODEV; goto ERROR2; } - if (!is_isa && (lm78_read_value( - new_client, LM78_REG_I2C_ADDR) != address)) { + if (lm78_read_value(new_client, LM78_REG_I2C_ADDR) != + address) { err = -ENODEV; goto ERROR2; } @@ -641,9 +603,6 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) strlcpy(new_client->name, client_name, I2C_NAME_SIZE); data->type = kind; - data->valid = 0; - mutex_init(&data->update_lock); - /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) goto ERROR2; @@ -651,12 +610,6 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) /* Initialize the LM78 chip */ lm78_init_client(new_client); - /* A few vars need to be filled upon startup */ - for (i = 0; i < 3; i++) { - data->fan_min[i] = lm78_read_value(new_client, - LM78_REG_FAN_MIN(i)); - } - /* Register sysfs hooks */ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group))) goto ERROR3; @@ -676,9 +629,6 @@ ERROR3: ERROR2: kfree(data); ERROR1: - if (is_isa) - release_region(address, LM78_EXTENT); -ERROR0: return err; } @@ -693,9 +643,77 @@ static int lm78_detach_client(struct i2c_client *client) if ((err = i2c_detach_client(client))) return err; - if(i2c_is_isa_client(client)) - release_region(client->addr, LM78_EXTENT); + kfree(data); + + return 0; +} + +static int __devinit lm78_isa_probe(struct platform_device *pdev) +{ + int err; + struct lm78_data *data; + struct resource *res; + const char *name; + + /* Reserve the ISA region */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start, LM78_EXTENT, "lm78")) { + err = -EBUSY; + goto exit; + } + + if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit_release_region; + } + mutex_init(&data->lock); + data->client.addr = res->start; + i2c_set_clientdata(&data->client, data); + platform_set_drvdata(pdev, data); + + if (lm78_read_value(&data->client, LM78_REG_CHIPID) & 0x80) { + data->type = lm79; + name = "lm79"; + } else { + data->type = lm78; + name = "lm78"; + } + strlcpy(data->client.name, name, I2C_NAME_SIZE); + + /* Initialize the LM78 chip */ + lm78_init_client(&data->client); + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) + || (err = device_create_file(&pdev->dev, &dev_attr_name))) + goto exit_remove_files; + + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; + } + + return 0; + + exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &lm78_group); + device_remove_file(&pdev->dev, &dev_attr_name); + kfree(data); + exit_release_region: + release_region(res->start, LM78_EXTENT); + exit: + return err; +} + +static int __devexit lm78_isa_remove(struct platform_device *pdev) +{ + struct lm78_data *data = platform_get_drvdata(pdev); + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&pdev->dev.kobj, &lm78_group); + device_remove_file(&pdev->dev, &dev_attr_name); + release_region(data->client.addr, LM78_EXTENT); kfree(data); return 0; @@ -709,7 +727,7 @@ static int lm78_detach_client(struct i2c_client *client) static int lm78_read_value(struct i2c_client *client, u8 reg) { int res; - if (i2c_is_isa_client(client)) { + if (!client->driver) { /* ISA device */ struct lm78_data *data = i2c_get_clientdata(client); mutex_lock(&data->lock); outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); @@ -729,7 +747,7 @@ static int lm78_read_value(struct i2c_client *client, u8 reg) nowhere else be necessary! */ static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) { - if (i2c_is_isa_client(client)) { + if (!client->driver) { /* ISA device */ struct lm78_data *data = i2c_get_clientdata(client); mutex_lock(&data->lock); outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); @@ -742,18 +760,29 @@ static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) static void lm78_init_client(struct i2c_client *client) { - u8 config = lm78_read_value(client, LM78_REG_CONFIG); + struct lm78_data *data = i2c_get_clientdata(client); + u8 config; + int i; /* Start monitoring */ - if (!(config & 0x01)) + config = lm78_read_value(client, LM78_REG_CONFIG); + if ((config & 0x09) != 0x01) lm78_write_value(client, LM78_REG_CONFIG, (config & 0xf7) | 0x01); + + /* A few vars need to be filled upon startup */ + for (i = 0; i < 3; i++) { + data->fan_min[i] = lm78_read_value(client, + LM78_REG_FAN_MIN(i)); + } + + mutex_init(&data->update_lock); } static struct lm78_data *lm78_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); + struct lm78_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; int i; mutex_lock(&data->update_lock); @@ -761,7 +790,7 @@ static struct lm78_data *lm78_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || !data->valid) { - dev_dbg(&client->dev, "Starting lm78 update\n"); + dev_dbg(dev, "Starting lm78 update\n"); for (i = 0; i <= 6; i++) { data->in[i] = @@ -805,26 +834,154 @@ static struct lm78_data *lm78_update_device(struct device *dev) return data; } +/* return 1 if a supported chip is found, 0 otherwise */ +static int __init lm78_isa_found(unsigned short address) +{ + int val, save, found = 0; + + if (!request_region(address, LM78_EXTENT, "lm78")) + return 0; + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like + chips. But only if we read 'undefined' registers. */ + val = inb_p(address + 1); + if (inb_p(address + 2) != val + || inb_p(address + 3) != val + || inb_p(address + 7) != val) + goto release; +#undef REALLY_SLOW_IO + + /* We should be able to change the 7 LSB of the address port. The + MSB (busy flag) should be clear initially, set after the write. */ + save = inb_p(address + LM78_ADDR_REG_OFFSET); + if (save & 0x80) + goto release; + val = ~save & 0x7f; + outb_p(val, address + LM78_ADDR_REG_OFFSET); + if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) { + outb_p(save, address + LM78_ADDR_REG_OFFSET); + goto release; + } + + /* We found a device, now see if it could be an LM78 */ + outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET); + val = inb_p(address + LM78_DATA_REG_OFFSET); + if (val & 0x80) + goto release; + outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET); + val = inb_p(address + LM78_DATA_REG_OFFSET); + if (val < 0x03 || val > 0x77) /* Not a valid I2C address */ + goto release; + + /* The busy flag should be clear again */ + if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80) + goto release; + + /* Explicitly prevent the misdetection of Winbond chips */ + outb_p(0x4f, address + LM78_ADDR_REG_OFFSET); + val = inb_p(address + LM78_DATA_REG_OFFSET); + if (val == 0xa3 || val == 0x5c) + goto release; + + /* Explicitly prevent the misdetection of ITE chips */ + outb_p(0x58, address + LM78_ADDR_REG_OFFSET); + val = inb_p(address + LM78_DATA_REG_OFFSET); + if (val == 0x90) + goto release; + + /* Determine the chip type */ + outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET); + val = inb_p(address + LM78_DATA_REG_OFFSET); + if (val == 0x00 /* LM78 */ + || val == 0x40 /* LM78-J */ + || (val & 0xfe) == 0xc0) /* LM79 */ + found = 1; + + if (found) + pr_info("lm78: Found an %s chip at %#x\n", + val & 0x80 ? "LM79" : "LM78", (int)address); + + release: + release_region(address, LM78_EXTENT); + return found; +} + +static int __init lm78_isa_device_add(unsigned short address) +{ + struct resource res = { + .start = address, + .end = address + LM78_EXTENT, + .name = "lm78", + .flags = IORESOURCE_IO, + }; + int err; + + pdev = platform_device_alloc("lm78", address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR "lm78: Device allocation failed\n"); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + printk(KERN_ERR "lm78: Device resource addition failed " + "(%d)\n", err); + goto exit_device_put; + } + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR "lm78: Device addition failed (%d)\n", + err); + goto exit_device_put; + } + + return 0; + + exit_device_put: + platform_device_put(pdev); + exit: + pdev = NULL; + return err; +} + static int __init sm_lm78_init(void) { int res; res = i2c_add_driver(&lm78_driver); if (res) - return res; + goto exit; + + if (lm78_isa_found(isa_address)) { + res = platform_driver_register(&lm78_isa_driver); + if (res) + goto exit_unreg_i2c_driver; - /* Don't exit if this one fails, we still want the I2C variants - to work! */ - if (i2c_isa_add_driver(&lm78_isa_driver)) - isa_address = 0; + /* Sets global pdev as a side effect */ + res = lm78_isa_device_add(isa_address); + if (res) + goto exit_unreg_isa_driver; + } return 0; + + exit_unreg_isa_driver: + platform_driver_unregister(&lm78_isa_driver); + exit_unreg_i2c_driver: + i2c_del_driver(&lm78_driver); + exit: + return res; } static void __exit sm_lm78_exit(void) { - if (isa_address) - i2c_isa_del_driver(&lm78_isa_driver); + if (pdev) { + platform_device_unregister(pdev); + platform_driver_unregister(&lm78_isa_driver); + } i2c_del_driver(&lm78_driver); } -- cgit v1.2.3 From bebe467823c0d8eeb7f49115c255d8a235a20ddb Mon Sep 17 00:00:00 2001 From: Rudolf Marek Date: Tue, 8 May 2007 17:22:02 +0200 Subject: hwmon: New coretemp driver Add the support for the digital temperature sensor found in recent Intel Core CPUs. Signed-off-by: Rudolf Marek Signed-off-by: Jean Delvare --- MAINTAINERS | 6 + drivers/hwmon/Kconfig | 8 + drivers/hwmon/Makefile | 1 + drivers/hwmon/coretemp.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 421 insertions(+) create mode 100644 drivers/hwmon/coretemp.c (limited to 'drivers/hwmon/Kconfig') diff --git a/MAINTAINERS b/MAINTAINERS index a328862731c..1561f33e15b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1031,6 +1031,12 @@ P: Simon Arlott M: cxacru@fire.lp0.eu S: Maintained +CORETEMP HARDWARE MONITORING DRIVER +P: Rudolf Marek +M: r.marek@assembler.cz +L: lm-sensors@lm-sensors.org +S: Maintained + COSA/SRP SYNC SERIAL DRIVER P: Jan "Yenya" Kasprzak M: kas@fi.muni.cz diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 659a2abb3e9..e488d691da4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -229,6 +229,14 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. +config SENSORS_CORETEMP + tristate "Intel Core (2) Duo/Solo temperature sensor" + depends on HWMON && 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. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" depends on HWMON && I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index bbbe86bf5da..33780678bd4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o +obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_FSCHER) += fscher.o diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c new file mode 100644 index 00000000000..03b1f650d1c --- /dev/null +++ b/drivers/hwmon/coretemp.c @@ -0,0 +1,406 @@ +/* + * coretemp.c - Linux kernel module for hardware monitoring + * + * Copyright (C) 2007 Rudolf Marek + * + * Inspired from many hwmon drivers + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "coretemp" + +typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_LABEL, SHOW_NAME } SHOW; + +/* + * Functions declaration + */ + +static struct coretemp_data *coretemp_update_device(struct device *dev); + +struct coretemp_data { + struct class_device *class_dev; + struct mutex update_lock; + const char *name; + u32 id; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + int temp; + int tjmax; + u8 alarm; +}; + +static struct coretemp_data *coretemp_update_device(struct device *dev); + +/* + * Sysfs stuff + */ + +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + int ret; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct coretemp_data *data = dev_get_drvdata(dev); + + if (attr->index == SHOW_NAME) + ret = sprintf(buf, "%s\n", data->name); + else /* show label */ + ret = sprintf(buf, "Core %d\n", data->id); + return ret; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct coretemp_data *data = coretemp_update_device(dev); + /* read the Out-of-spec log, never clear */ + return sprintf(buf, "%d\n", data->alarm); +} + +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 coretemp_data *data = coretemp_update_device(dev); + int err; + + if (attr->index == SHOW_TEMP) + err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN; + else + err = sprintf(buf, "%d\n", data->tjmax); + + return err; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, + SHOW_TEMP); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, + SHOW_TJMAX); +static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME); + +static struct attribute *coretemp_attributes[] = { + &sensor_dev_attr_name.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &dev_attr_temp1_crit_alarm.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + NULL +}; + +static const struct attribute_group coretemp_group = { + .attrs = coretemp_attributes, +}; + +static struct coretemp_data *coretemp_update_device(struct device *dev) +{ + struct coretemp_data *data = dev_get_drvdata(dev); + + mutex_lock(&data->update_lock); + + if (!data->valid || time_after(jiffies, data->last_updated + HZ)) { + u32 eax, edx; + + data->valid = 0; + rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); + data->alarm = (eax >> 5) & 1; + /* update only if data has been valid */ + if (eax & 0x80000000) { + data->temp = data->tjmax - (((eax >> 16) + & 0x7f) * 1000); + data->valid = 1; + } else { + dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax); + } + data->last_updated = jiffies; + } + + mutex_unlock(&data->update_lock); + return data; +} + +static int __devinit coretemp_probe(struct platform_device *pdev) +{ + struct coretemp_data *data; + struct cpuinfo_x86 *c = &(cpu_data)[pdev->id]; + int err; + u32 eax, edx; + + if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) { + err = -ENOMEM; + dev_err(&pdev->dev, "Out of memory\n"); + goto exit; + } + + data->id = pdev->id; + data->name = "coretemp"; + mutex_init(&data->update_lock); + /* Tjmax default is 100 degrees C */ + data->tjmax = 100000; + + /* test if we can access the THERM_STATUS MSR */ + err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); + if (err) { + dev_err(&pdev->dev, + "Unable to access THERM_STATUS MSR, giving up\n"); + goto exit_free; + } + + /* Some processors have Tjmax 85 following magic should detect it + Intel won't disclose the information without signed NDA, but + individuals cannot sign it. Catch(ed) 22. + */ + + if (((c->x86_model == 0xf) && (c->x86_mask > 3)) || + (c->x86_model == 0xe)) { + err = rdmsr_safe_on_cpu(data->id, 0xee, &eax, &edx); + if (err) { + dev_warn(&pdev->dev, + "Unable to access MSR 0xEE, Tjmax left at %d " + "degrees C\n", data->tjmax/1000); + } else if (eax & 0x40000000) { + data->tjmax = 85000; + } + } + + platform_set_drvdata(pdev, data); + + if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group))) + goto exit_free; + + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + dev_err(&pdev->dev, "Class registration failed (%d)\n", + err); + goto exit_class; + } + + return 0; + +exit_class: + sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); +exit_free: + kfree(data); +exit: + return err; +} + +static int __devexit coretemp_remove(struct platform_device *pdev) +{ + struct coretemp_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); + platform_set_drvdata(pdev, NULL); + kfree(data); + return 0; +} + +static struct platform_driver coretemp_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = coretemp_probe, + .remove = __devexit_p(coretemp_remove), +}; + +struct pdev_entry { + struct list_head list; + struct platform_device *pdev; + unsigned int cpu; +}; + +static LIST_HEAD(pdev_list); +static DEFINE_MUTEX(pdev_list_mutex); + +static int __cpuinit coretemp_device_add(unsigned int cpu) +{ + int err; + struct platform_device *pdev; + struct pdev_entry *pdev_entry; + + pdev = platform_device_alloc(DRVNAME, cpu); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + goto exit; + } + + pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); + if (!pdev_entry) { + err = -ENOMEM; + goto exit_device_put; + } + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", + err); + goto exit_device_free; + } + + pdev_entry->pdev = pdev; + pdev_entry->cpu = cpu; + mutex_lock(&pdev_list_mutex); + list_add_tail(&pdev_entry->list, &pdev_list); + mutex_unlock(&pdev_list_mutex); + + return 0; + +exit_device_free: + kfree(pdev_entry); +exit_device_put: + platform_device_put(pdev); +exit: + return err; +} + +#ifdef CONFIG_HOTPLUG_CPU +void coretemp_device_remove(unsigned int cpu) +{ + struct pdev_entry *p, *n; + mutex_lock(&pdev_list_mutex); + list_for_each_entry_safe(p, n, &pdev_list, list) { + if (p->cpu == cpu) { + platform_device_unregister(p->pdev); + list_del(&p->list); + kfree(p); + } + } + mutex_unlock(&pdev_list_mutex); +} + +static int coretemp_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long) hcpu; + + switch (action) { + case CPU_ONLINE: + coretemp_device_add(cpu); + break; + case CPU_DEAD: + coretemp_device_remove(cpu); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata coretemp_cpu_notifier = { + .notifier_call = coretemp_cpu_callback, +}; +#endif /* !CONFIG_HOTPLUG_CPU */ + +static int __init coretemp_init(void) +{ + int i, err = -ENODEV; + struct pdev_entry *p, *n; + + printk(KERN_NOTICE DRVNAME ": This driver uses undocumented features " + "of Core CPU. Temperature might be wrong!\n"); + + /* quick check if we run Intel */ + if (cpu_data[0].x86_vendor != X86_VENDOR_INTEL) + goto exit; + + err = platform_driver_register(&coretemp_driver); + if (err) + goto exit; + + for_each_online_cpu(i) { + struct cpuinfo_x86 *c = &(cpu_data)[i]; + + /* check if family 6, models e, f */ + if ((c->cpuid_level < 0) || (c->x86 != 0x6) || + !((c->x86_model == 0xe) || (c->x86_model == 0xf))) { + + /* supported CPU not found, but report the unknown + family 6 CPU */ + if ((c->x86 == 0x6) && (c->x86_model > 0xf)) + printk(KERN_WARNING DRVNAME ": Unknown CPU " + "model %x\n", c->x86_model); + continue; + } + + err = coretemp_device_add(i); + if (err) + goto exit_devices_unreg; + } + if (list_empty(&pdev_list)) { + err = -ENODEV; + goto exit_driver_unreg; + } + +#ifdef CONFIG_HOTPLUG_CPU + register_hotcpu_notifier(&coretemp_cpu_notifier); +#endif + return 0; + +exit_devices_unreg: + mutex_lock(&pdev_list_mutex); + list_for_each_entry_safe(p, n, &pdev_list, list) { + platform_device_unregister(p->pdev); + list_del(&p->list); + kfree(p); + } + mutex_unlock(&pdev_list_mutex); +exit_driver_unreg: + platform_driver_unregister(&coretemp_driver); +exit: + return err; +} + +static void __exit coretemp_exit(void) +{ + struct pdev_entry *p, *n; +#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&coretemp_cpu_notifier); +#endif + mutex_lock(&pdev_list_mutex); + list_for_each_entry_safe(p, n, &pdev_list, list) { + platform_device_unregister(p->pdev); + list_del(&p->list); + kfree(p); + } + mutex_unlock(&pdev_list_mutex); + platform_driver_unregister(&coretemp_driver); +} + +MODULE_AUTHOR("Rudolf Marek "); +MODULE_DESCRIPTION("Intel Core temperature monitor"); +MODULE_LICENSE("GPL"); + +module_init(coretemp_init) +module_exit(coretemp_exit) -- cgit v1.2.3 From 2d8dd65fc14287f2c004dd755e517ba0f45d446e Mon Sep 17 00:00:00 2001 From: Alessandro Zummo Date: Tue, 8 May 2007 17:22:02 +0200 Subject: hwmon: New AD7416, AD7417 and AD7418 driver A driver for the Analog Devices AD7416, AD7417 and AD7418 chips. Signed-off-by: Alessandro Zummo Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ad7418.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 384 insertions(+) create mode 100644 drivers/hwmon/ad7418.c (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e488d691da4..1691c6c7bbc 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -39,6 +39,16 @@ config SENSORS_ABITUGURU This driver can also be built as a module. If so, the module will be called abituguru. +config SENSORS_AD7418 + tristate "Analog Devices AD7416, AD7417 and AD7418" + depends on HWMON && I2C && EXPERIMENTAL + help + If you say yes here you get support for the Analog Devices + AD7416, AD7417 and AD7418 temperature monitoring chips. + + This driver can also be built as a module. If so, the module + will be called ad7418. + config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" depends on HWMON && I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 33780678bd4..d83f2380e08 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o +obj-$(CONFIG_SENSORS_AD7418) += ad7418.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c new file mode 100644 index 00000000000..cc8b624a1e5 --- /dev/null +++ b/drivers/hwmon/ad7418.c @@ -0,0 +1,373 @@ +/* + * An hwmon driver for the Analog Devices AD7416/17/18 + * Copyright (C) 2006-07 Tower Technologies + * + * Author: Alessandro Zummo + * + * Based on lm75.c + * Copyright (C) 1998-99 Frodo Looijaard + * + * 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 - version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lm75.h" + +#define DRV_VERSION "0.3" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x28, I2C_CLIENT_END }; +/* Insmod parameters */ +I2C_CLIENT_INSMOD_3(ad7416, ad7417, ad7418); + +/* AD7418 registers */ +#define AD7418_REG_TEMP_IN 0x00 +#define AD7418_REG_CONF 0x01 +#define AD7418_REG_TEMP_HYST 0x02 +#define AD7418_REG_TEMP_OS 0x03 +#define AD7418_REG_ADC 0x04 +#define AD7418_REG_CONF2 0x05 + +#define AD7418_REG_ADC_CH(x) ((x) << 5) +#define AD7418_CH_TEMP AD7418_REG_ADC_CH(0) + +static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN, + AD7418_REG_TEMP_HYST, + AD7418_REG_TEMP_OS }; + +struct ad7418_data { + struct i2c_client client; + struct class_device *class_dev; + struct attribute_group attrs; + enum chips type; + struct mutex lock; + int adc_max; /* number of ADC channels */ + char valid; + unsigned long last_updated; /* In jiffies */ + s16 temp[3]; /* Register values */ + u16 in[4]; +}; + +static int ad7418_attach_adapter(struct i2c_adapter *adapter); +static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind); +static int ad7418_detach_client(struct i2c_client *client); + +static struct i2c_driver ad7418_driver = { + .driver = { + .name = "ad7418", + }, + .attach_adapter = ad7418_attach_adapter, + .detach_client = ad7418_detach_client, +}; + +/* All registers are word-sized, except for the configuration registers. + * AD7418 uses a high-byte first convention. Do NOT use those functions to + * access the configuration registers CONF and CONF2, as they are byte-sized. + */ +static inline int ad7418_read(struct i2c_client *client, u8 reg) +{ + return swab16(i2c_smbus_read_word_data(client, reg)); +} + +static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, swab16(value)); +} + +static void ad7418_init_client(struct i2c_client *client) +{ + struct ad7418_data *data = i2c_get_clientdata(client); + + int reg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF); + if (reg < 0) { + dev_err(&client->dev, "cannot read configuration register\n"); + } else { + dev_info(&client->dev, "configuring for mode 1\n"); + i2c_smbus_write_byte_data(client, AD7418_REG_CONF, reg & 0xfe); + + if (data->type == ad7417 || data->type == ad7418) + i2c_smbus_write_byte_data(client, + AD7418_REG_CONF2, 0x00); + } +} + +static struct ad7418_data *ad7418_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ad7418_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + u8 cfg; + int i, ch; + + /* read config register and clear channel bits */ + cfg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF); + cfg &= 0x1F; + + i2c_smbus_write_byte_data(client, AD7418_REG_CONF, + cfg | AD7418_CH_TEMP); + udelay(30); + + for (i = 0; i < 3; i++) { + data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]); + } + + for (i = 0, ch = 4; i < data->adc_max; i++, ch--) { + i2c_smbus_write_byte_data(client, + AD7418_REG_CONF, + cfg | AD7418_REG_ADC_CH(ch)); + + udelay(15); + data->in[data->adc_max - 1 - i] = + ad7418_read(client, AD7418_REG_ADC); + } + + /* restore old configuration value */ + ad7418_write(client, AD7418_REG_CONF, cfg); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->lock); + + return data; +} + +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 ad7418_data *data = ad7418_update_device(dev); + return sprintf(buf, "%d\n", + LM75_TEMP_FROM_REG(data->temp[attr->index])); +} + +static ssize_t show_adc(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct ad7418_data *data = ad7418_update_device(dev); + + return sprintf(buf, "%d\n", + ((data->in[attr->index] >> 6) * 2500 + 512) / 1024); +} + +static ssize_t set_temp(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 ad7418_data *data = i2c_get_clientdata(client); + int temp = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->lock); + data->temp[attr->index] = LM75_TEMP_TO_REG(temp); + ad7418_write(client, AD7418_REG_TEMP[attr->index], data->temp[attr->index]); + mutex_unlock(&data->lock); + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + show_temp, set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + show_temp, set_temp, 2); + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 3); + +static int ad7418_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_probe(adapter, &addr_data, ad7418_detect); +} + +static struct attribute *ad7416_attributes[] = { + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL +}; + +static struct attribute *ad7417_attributes[] = { + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_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, + NULL +}; + +static struct attribute *ad7418_attributes[] = { + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + NULL +}; + +static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct ad7418_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + if (!(data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + client = &data->client; + client->addr = address; + client->adapter = adapter; + client->driver = &ad7418_driver; + + i2c_set_clientdata(client, data); + + mutex_init(&data->lock); + + /* AD7418 has a curious behaviour on registers 6 and 7. They + * both always read 0xC071 and are not documented on the datasheet. + * We use them to detect the chip. + */ + if (kind <= 0) { + int reg, reg6, reg7; + + /* the AD7416 lies within this address range, but I have + * no means to check. + */ + if (address >= 0x48 && address <= 0x4f) { + /* XXX add tests for AD7416 here */ + /* data->type = ad7416; */ + } + /* here we might have AD7417 or AD7418 */ + else if (address >= 0x28 && address <= 0x2f) { + reg6 = i2c_smbus_read_word_data(client, 0x06); + reg7 = i2c_smbus_read_word_data(client, 0x07); + + if (address == 0x28 && reg6 == 0xC071 && reg7 == 0xC071) + data->type = ad7418; + + /* XXX add tests for AD7417 here */ + + + /* both AD7417 and AD7418 have bits 0-5 of + * the CONF2 register at 0 + */ + reg = i2c_smbus_read_byte_data(client, + AD7418_REG_CONF2); + if (reg & 0x3F) + data->type = any_chip; /* detection failed */ + } + } else { + dev_dbg(&adapter->dev, "detection forced\n"); + } + + if (kind > 0) + data->type = kind; + else if (kind < 0 && data->type == any_chip) { + err = -ENODEV; + goto exit_free; + } + + switch (data->type) { + case any_chip: + case ad7416: + data->adc_max = 0; + data->attrs.attrs = ad7416_attributes; + strlcpy(client->name, "ad7416", I2C_NAME_SIZE); + break; + + case ad7417: + data->adc_max = 4; + data->attrs.attrs = ad7417_attributes; + strlcpy(client->name, "ad7417", I2C_NAME_SIZE); + break; + + case ad7418: + data->adc_max = 1; + data->attrs.attrs = ad7418_attributes; + strlcpy(client->name, "ad7418", I2C_NAME_SIZE); + break; + } + + if ((err = i2c_attach_client(client))) + goto exit_free; + + dev_info(&client->dev, "%s chip found\n", client->name); + + /* Initialize the AD7418 chip */ + ad7418_init_client(client); + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs))) + goto exit_detach; + + 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: + sysfs_remove_group(&client->dev.kobj, &data->attrs); +exit_detach: + i2c_detach_client(client); +exit_free: + kfree(data); +exit: + return err; +} + +static int ad7418_detach_client(struct i2c_client *client) +{ + struct ad7418_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &data->attrs); + i2c_detach_client(client); + kfree(data); + return 0; +} + +static int __init ad7418_init(void) +{ + return i2c_add_driver(&ad7418_driver); +} + +static void __exit ad7418_exit(void) +{ + i2c_del_driver(&ad7418_driver); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("AD7416/17/18 driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(ad7418_init); +module_exit(ad7418_exit); -- cgit v1.2.3 From 7666c13c627fdc65e8057013893c183c3bafe59e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 8 May 2007 17:22:02 +0200 Subject: hwmon/w83781d: No longer use i2c-isa Reimplement the ISA device support as a platform driver, so that we no longer rely on i2c-isa. Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 1 - drivers/hwmon/w83781d.c | 571 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 377 insertions(+), 195 deletions(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1691c6c7bbc..065851a77cc 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -523,7 +523,6 @@ config SENSORS_VT8231 config SENSORS_W83781D tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" depends on HWMON && I2C - select I2C_ISA select HWMON_VID help If you say yes here you get support for the Winbond W8378x series diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index a47da3ec547..96338ddd74a 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -2,8 +2,9 @@ w83781d.c - Part of lm_sensors, Linux kernel modules for hardware monitoring Copyright (c) 1998 - 2001 Frodo Looijaard , - Philip Edelbrock , - and Mark Studebaker + Philip Edelbrock , + and Mark Studebaker + Copyright (c) 2007 Jean Delvare This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,7 +39,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -47,6 +49,9 @@ #include #include "lm75.h" +/* ISA device, if found */ +static struct platform_device *pdev; + /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, @@ -221,8 +226,8 @@ DIV_TO_REG(long val, enum chips type) a bit - except if there could be more than one SMBus. Groan. No solution for this yet. */ -/* For each registered chip, we need to keep some data in memory. - The structure is dynamically allocated. */ +/* For ISA chips, we abuse the i2c_client addr and name fields. We also use + the driver field to differentiate between I2C and ISA chips. */ struct w83781d_data { struct i2c_client client; struct class_device *class_dev; @@ -263,14 +268,16 @@ struct w83781d_data { }; static int w83781d_attach_adapter(struct i2c_adapter *adapter); -static int w83781d_isa_attach_adapter(struct i2c_adapter *adapter); static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); static int w83781d_detach_client(struct i2c_client *client); +static int __devinit w83781d_isa_probe(struct platform_device *pdev); +static int __devexit w83781d_isa_remove(struct platform_device *pdev); + static int w83781d_read_value(struct i2c_client *client, u16 reg); static int w83781d_write_value(struct i2c_client *client, u16 reg, u16 value); static struct w83781d_data *w83781d_update_device(struct device *dev); -static void w83781d_init_client(struct i2c_client *client); +static void w83781d_init_device(struct device *dev); static struct i2c_driver w83781d_driver = { .driver = { @@ -281,13 +288,13 @@ static struct i2c_driver w83781d_driver = { .detach_client = w83781d_detach_client, }; -static struct i2c_driver w83781d_isa_driver = { +static struct platform_driver w83781d_isa_driver = { .driver = { .owner = THIS_MODULE, - .name = "w83781d-isa", + .name = "w83781d", }, - .attach_adapter = w83781d_isa_attach_adapter, - .detach_client = w83781d_detach_client, + .probe = w83781d_isa_probe, + .remove = w83781d_isa_remove, }; @@ -305,8 +312,8 @@ show_in_reg(in_max); #define store_in_reg(REG, reg) \ static ssize_t store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \ { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83781d_data *data = i2c_get_clientdata(client); \ + struct w83781d_data *data = dev_get_drvdata(dev); \ + struct i2c_client *client = &data->client; \ u32 val; \ \ val = simple_strtoul(buf, NULL, 10) / 10; \ @@ -368,8 +375,8 @@ show_fan_reg(fan_min); static ssize_t store_fan_min(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; u32 val; val = simple_strtoul(buf, NULL, 10); @@ -427,8 +434,8 @@ show_temp_reg(temp_max_hyst); #define store_temp_reg(REG, reg) \ static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83781d_data *data = i2c_get_clientdata(client); \ + struct w83781d_data *data = dev_get_drvdata(dev); \ + struct i2c_client *client = &data->client; \ s32 val; \ \ val = simple_strtol(buf, NULL, 10); \ @@ -498,8 +505,7 @@ show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); u32 val; val = simple_strtoul(buf, NULL, 10); @@ -539,8 +545,8 @@ static ssize_t store_beep_reg(struct device *dev, const char *buf, size_t count, int update_mask) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; u32 val, val2; val = simple_strtoul(buf, NULL, 10); @@ -599,8 +605,8 @@ show_fan_div_reg(struct device *dev, char *buf, int nr) static ssize_t store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; unsigned long min; u8 reg; unsigned long val = simple_strtoul(buf, NULL, 10); @@ -666,8 +672,8 @@ show_pwmenable_reg(struct device *dev, char *buf, int nr) static ssize_t store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; u32 val; val = simple_strtoul(buf, NULL, 10); @@ -682,8 +688,8 @@ store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) static ssize_t store_pwmenable_reg(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; u32 val, reg; val = simple_strtoul(buf, NULL, 10); @@ -755,8 +761,8 @@ show_sensor_reg(struct device *dev, char *buf, int nr) static ssize_t store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; u32 val, tmp; val = simple_strtoul(buf, NULL, 10); @@ -813,6 +819,16 @@ sysfs_sensor(1); sysfs_sensor(2); sysfs_sensor(3); +/* I2C devices get this name attribute automatically, but for ISA devices + we must create it by ourselves. */ +static ssize_t +show_name(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct w83781d_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->client.name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + /* This function is called when: * w83781d_driver is inserted (when this module is loaded), for each available adapter @@ -825,12 +841,6 @@ w83781d_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, w83781d_detect); } -static int -w83781d_isa_attach_adapter(struct i2c_adapter *adapter) -{ - return w83781d_detect(adapter, isa_address, -1); -} - /* Assumes that adapter is of I2C, not ISA variety. * OTHERWISE DON'T CALL THIS */ @@ -994,77 +1004,85 @@ static const struct attribute_group w83781d_group_opt = { .attrs = w83781d_attributes_opt, }; +/* No clean up is done on error, it's up to the caller */ static int -w83781d_detect(struct i2c_adapter *adapter, int address, int kind) +w83781d_create_files(struct device *dev, int kind, int is_isa) { - int i = 0, val1 = 0, val2; - struct i2c_client *client; - struct device *dev; - struct w83781d_data *data; int err; - const char *client_name = ""; - int is_isa = i2c_is_isa_adapter(adapter); - enum vendor { winbond, asus } vendid; - if (!is_isa - && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -EINVAL; - goto ERROR0; + if ((err = sysfs_create_group(&dev->kobj, &w83781d_group))) + return err; + + if (kind != w83783s) { + if ((err = device_create_file(dev, &dev_attr_in1_input)) + || (err = device_create_file(dev, &dev_attr_in1_min)) + || (err = device_create_file(dev, &dev_attr_in1_max))) + return err; + } + if (kind != as99127f && kind != w83781d && kind != w83783s) { + if ((err = device_create_file(dev, &dev_attr_in7_input)) + || (err = device_create_file(dev, &dev_attr_in7_min)) + || (err = device_create_file(dev, &dev_attr_in7_max)) + || (err = device_create_file(dev, &dev_attr_in8_input)) + || (err = device_create_file(dev, &dev_attr_in8_min)) + || (err = device_create_file(dev, &dev_attr_in8_max))) + return err; + } + if (kind != w83783s) { + if ((err = device_create_file(dev, &dev_attr_temp3_input)) + || (err = device_create_file(dev, &dev_attr_temp3_max)) + || (err = device_create_file(dev, + &dev_attr_temp3_max_hyst))) + return err; } - /* Prevent users from forcing a kind for a bus it isn't supposed - to possibly be on */ - if (is_isa && (kind == as99127f || kind == w83783s)) { - dev_err(&adapter->dev, - "Cannot force I2C-only chip for ISA address 0x%02x.\n", - address); - err = -EINVAL; - goto ERROR0; + if (kind != w83781d && kind != as99127f) { + if ((err = device_create_file(dev, &dev_attr_pwm1)) + || (err = device_create_file(dev, &dev_attr_pwm2)) + || (err = device_create_file(dev, &dev_attr_pwm2_enable))) + return err; } - - if (is_isa) - if (!request_region(address, W83781D_EXTENT, - w83781d_isa_driver.driver.name)) { - dev_dbg(&adapter->dev, "Request of region " - "0x%x-0x%x for w83781d failed\n", address, - address + W83781D_EXTENT - 1); - err = -EBUSY; - goto ERROR0; + if (kind == w83782d && !is_isa) { + if ((err = device_create_file(dev, &dev_attr_pwm3)) + || (err = device_create_file(dev, &dev_attr_pwm4))) + return err; + } + + if (kind != as99127f && kind != w83781d) { + if ((err = device_create_file(dev, &dev_attr_temp1_type)) + || (err = device_create_file(dev, + &dev_attr_temp2_type))) + return err; + if (kind != w83783s) { + if ((err = device_create_file(dev, + &dev_attr_temp3_type))) + return err; } + } - /* Probe whether there is anything available on this address. Already - done for SMBus clients */ - if (kind < 0) { - if (is_isa) { + if (is_isa) { + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + return err; + } -#define REALLY_SLOW_IO - /* We need the timeouts for at least some LM78-like - chips. But only if we read 'undefined' registers. */ - i = inb_p(address + 1); - if (inb_p(address + 2) != i - || inb_p(address + 3) != i - || inb_p(address + 7) != i) { - dev_dbg(&adapter->dev, "Detection of w83781d " - "chip failed at step 1\n"); - err = -ENODEV; - goto ERROR1; - } -#undef REALLY_SLOW_IO + return 0; +} - /* Let's just hope nothing breaks here */ - i = inb_p(address + 5) & 0x7f; - outb_p(~i & 0x7f, address + 5); - val2 = inb_p(address + 5) & 0x7f; - if (val2 != (~i & 0x7f)) { - outb_p(i, address + 5); - dev_dbg(&adapter->dev, "Detection of w83781d " - "chip failed at step 2 (0x%x != " - "0x%x at 0x%x)\n", val2, ~i & 0x7f, - address + 5); - err = -ENODEV; - goto ERROR1; - } - } +static int +w83781d_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int val1 = 0, val2; + struct i2c_client *client; + struct device *dev; + struct w83781d_data *data; + int err; + const char *client_name = ""; + enum vendor { winbond, asus } vendid; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + err = -EINVAL; + goto ERROR1; } /* OK. For now, we presume we have a valid client. We now create the @@ -1081,8 +1099,7 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) client->addr = address; mutex_init(&data->lock); client->adapter = adapter; - client->driver = is_isa ? &w83781d_isa_driver : &w83781d_driver; - client->flags = 0; + client->driver = &w83781d_driver; dev = &client->dev; /* Now, we do the remaining detection. */ @@ -1111,8 +1128,8 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) } /* If Winbond SMBus, check address at 0x48. Asus doesn't support, except for as99127f rev.2 */ - if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || - ((val1 & 0x80) && (val2 == 0x5c)))) { + if ((!(val1 & 0x80) && (val2 == 0xa3)) || + ((val1 & 0x80) && (val2 == 0x5c))) { if (w83781d_read_value (client, W83781D_REG_I2C_ADDR) != address) { dev_dbg(&adapter->dev, "Detection of w83781d " @@ -1149,12 +1166,11 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) kind = w83781d; else if (val1 == 0x30 && vendid == winbond) kind = w83782d; - else if (val1 == 0x40 && vendid == winbond && !is_isa - && address == 0x2d) + else if (val1 == 0x40 && vendid == winbond && address == 0x2d) kind = w83783s; else if (val1 == 0x21 && vendid == winbond) kind = w83627hf; - else if (val1 == 0x31 && !is_isa && address >= 0x28) + else if (val1 == 0x31 && address >= 0x28) kind = as99127f; else { if (kind == 0) @@ -1182,86 +1198,23 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) strlcpy(client->name, client_name, I2C_NAME_SIZE); data->type = kind; - data->valid = 0; - mutex_init(&data->update_lock); - /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(client))) goto ERROR2; /* attach secondary i2c lm75-like clients */ - if (!is_isa) { - if ((err = w83781d_detect_subclients(adapter, address, - kind, client))) - goto ERROR3; - } else { - data->lm75[0] = NULL; - data->lm75[1] = NULL; - } + if ((err = w83781d_detect_subclients(adapter, address, + kind, client))) + goto ERROR3; /* Initialize the chip */ - w83781d_init_client(client); - - /* A few vars need to be filled upon startup */ - for (i = 1; i <= 3; i++) { - data->fan_min[i - 1] = w83781d_read_value(client, - W83781D_REG_FAN_MIN(i)); - } - if (kind != w83781d && kind != as99127f) - for (i = 0; i < 4; i++) - data->pwmenable[i] = 1; + w83781d_init_device(dev); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&dev->kobj, &w83781d_group))) + err = w83781d_create_files(dev, kind, 0); + if (err) goto ERROR4; - if (kind != w83783s) { - if ((err = device_create_file(dev, &dev_attr_in1_input)) - || (err = device_create_file(dev, &dev_attr_in1_min)) - || (err = device_create_file(dev, &dev_attr_in1_max))) - goto ERROR4; - } - if (kind != as99127f && kind != w83781d && kind != w83783s) { - if ((err = device_create_file(dev, &dev_attr_in7_input)) - || (err = device_create_file(dev, &dev_attr_in7_min)) - || (err = device_create_file(dev, &dev_attr_in7_max)) - || (err = device_create_file(dev, &dev_attr_in8_input)) - || (err = device_create_file(dev, &dev_attr_in8_min)) - || (err = device_create_file(dev, &dev_attr_in8_max))) - goto ERROR4; - } - if (kind != w83783s) { - if ((err = device_create_file(dev, &dev_attr_temp3_input)) - || (err = device_create_file(dev, &dev_attr_temp3_max)) - || (err = device_create_file(dev, - &dev_attr_temp3_max_hyst))) - goto ERROR4; - } - - if (kind != w83781d && kind != as99127f) { - if ((err = device_create_file(dev, &dev_attr_pwm1)) - || (err = device_create_file(dev, &dev_attr_pwm2)) - || (err = device_create_file(dev, &dev_attr_pwm2_enable))) - goto ERROR4; - } - if (kind == w83782d && !is_isa) { - if ((err = device_create_file(dev, &dev_attr_pwm3)) - || (err = device_create_file(dev, &dev_attr_pwm4))) - goto ERROR4; - } - - if (kind != as99127f && kind != w83781d) { - if ((err = device_create_file(dev, &dev_attr_temp1_type)) - || (err = device_create_file(dev, - &dev_attr_temp2_type))) - goto ERROR4; - if (kind != w83783s) { - if ((err = device_create_file(dev, - &dev_attr_temp3_type))) - goto ERROR4; - } - } - data->class_dev = hwmon_device_register(dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); @@ -1287,9 +1240,6 @@ ERROR3: ERROR2: kfree(data); ERROR1: - if (is_isa) - release_region(address, W83781D_EXTENT); -ERROR0: return err; } @@ -1305,8 +1255,6 @@ w83781d_detach_client(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &w83781d_group); sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt); } - if (i2c_is_isa_client(client)) - release_region(client->addr, W83781D_EXTENT); if ((err = i2c_detach_client(client))) return err; @@ -1322,6 +1270,88 @@ w83781d_detach_client(struct i2c_client *client) return 0; } +static int __devinit +w83781d_isa_probe(struct platform_device *pdev) +{ + int err, reg; + struct w83781d_data *data; + struct resource *res; + const char *name; + + /* Reserve the ISA region */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start, W83781D_EXTENT, "w83781d")) { + err = -EBUSY; + goto exit; + } + + if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit_release_region; + } + mutex_init(&data->lock); + data->client.addr = res->start; + i2c_set_clientdata(&data->client, data); + platform_set_drvdata(pdev, data); + + reg = w83781d_read_value(&data->client, W83781D_REG_WCHIPID); + switch (reg) { + case 0x21: + data->type = w83627hf; + name = "w83627hf"; + break; + case 0x30: + data->type = w83782d; + name = "w83782d"; + break; + default: + data->type = w83781d; + name = "w83781d"; + } + strlcpy(data->client.name, name, I2C_NAME_SIZE); + + /* Initialize the W83781D chip */ + w83781d_init_device(&pdev->dev); + + /* Register sysfs hooks */ + err = w83781d_create_files(&pdev->dev, data->type, 1); + if (err) + goto exit_remove_files; + + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; + } + + return 0; + + exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + kfree(data); + exit_release_region: + release_region(res->start, W83781D_EXTENT); + exit: + return err; +} + +static int __devexit +w83781d_isa_remove(struct platform_device *pdev) +{ + struct w83781d_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + release_region(data->client.addr, W83781D_EXTENT); + kfree(data); + + return 0; +} + /* The SMBus locks itself, usually, but nothing may access the Winbond between bank switches. ISA access must always be locked explicitly! We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, @@ -1336,7 +1366,7 @@ w83781d_read_value(struct i2c_client *client, u16 reg) struct i2c_client *cl; mutex_lock(&data->lock); - if (i2c_is_isa_client(client)) { + if (!client->driver) { /* ISA device */ word_sized = (((reg & 0xff00) == 0x100) || ((reg & 0xff00) == 0x200)) && (((reg & 0x00ff) == 0x50) @@ -1405,7 +1435,7 @@ w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) struct i2c_client *cl; mutex_lock(&data->lock); - if (i2c_is_isa_client(client)) { + if (!client->driver) { /* ISA device */ word_sized = (((reg & 0xff00) == 0x100) || ((reg & 0xff00) == 0x200)) && (((reg & 0x00ff) == 0x53) @@ -1462,9 +1492,10 @@ w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) } static void -w83781d_init_client(struct i2c_client *client) +w83781d_init_device(struct device *dev) { - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; int i, p; int type = data->type; u8 tmp; @@ -1477,7 +1508,7 @@ w83781d_init_client(struct i2c_client *client) It might even go away if nobody reports it as being useful, as I see very little reason why this would be needed at all. */ - dev_info(&client->dev, "If reset=1 solved a problem you were " + dev_info(dev, "If reset=1 solved a problem you were " "having, please report!\n"); /* save these registers */ @@ -1527,7 +1558,7 @@ w83781d_init_client(struct i2c_client *client) /* Enable temp2 */ tmp = w83781d_read_value(client, W83781D_REG_TEMP2_CONFIG); if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp2, readings " + dev_warn(dev, "Enabling temp2, readings " "might not make sense\n"); w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, tmp & 0xfe); @@ -1538,7 +1569,7 @@ w83781d_init_client(struct i2c_client *client) tmp = w83781d_read_value(client, W83781D_REG_TEMP3_CONFIG); if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp3, " + dev_warn(dev, "Enabling temp3, " "readings might not make sense\n"); w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); @@ -1551,12 +1582,23 @@ w83781d_init_client(struct i2c_client *client) (w83781d_read_value(client, W83781D_REG_CONFIG) & 0xf7) | 0x01); + + /* A few vars need to be filled upon startup */ + for (i = 1; i <= 3; i++) { + data->fan_min[i - 1] = w83781d_read_value(client, + W83781D_REG_FAN_MIN(i)); + } + if (type != w83781d && type != as99127f) + for (i = 0; i < 4; i++) + data->pwmenable[i] = 1; + + mutex_init(&data->update_lock); } static struct w83781d_data *w83781d_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); + struct w83781d_data *data = dev_get_drvdata(dev); + struct i2c_client *client = &data->client; int i; mutex_lock(&data->update_lock); @@ -1589,8 +1631,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) data->pwm[i - 1] = w83781d_read_value(client, W83781D_REG_PWM(i)); - if ((data->type != w83782d - || i2c_is_isa_client(client)) + if ((data->type != w83782d || !client->driver) && i == 2) break; } @@ -1672,6 +1713,133 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) return data; } +/* return 1 if a supported chip is found, 0 otherwise */ +static int __init +w83781d_isa_found(unsigned short address) +{ + int val, save, found = 0; + + if (!request_region(address, W83781D_EXTENT, "w83781d")) + return 0; + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some W83781D-like + chips. But only if we read 'undefined' registers. */ + val = inb_p(address + 1); + if (inb_p(address + 2) != val + || inb_p(address + 3) != val + || inb_p(address + 7) != val) { + pr_debug("w83781d: Detection failed at step 1\n"); + goto release; + } +#undef REALLY_SLOW_IO + + /* We should be able to change the 7 LSB of the address port. The + MSB (busy flag) should be clear initially, set after the write. */ + save = inb_p(address + W83781D_ADDR_REG_OFFSET); + if (save & 0x80) { + pr_debug("w83781d: Detection failed at step 2\n"); + goto release; + } + val = ~save & 0x7f; + outb_p(val, address + W83781D_ADDR_REG_OFFSET); + if (inb_p(address + W83781D_ADDR_REG_OFFSET) != (val | 0x80)) { + outb_p(save, address + W83781D_ADDR_REG_OFFSET); + pr_debug("w83781d: Detection failed at step 3\n"); + goto release; + } + + /* We found a device, now see if it could be a W83781D */ + outb_p(W83781D_REG_CONFIG, address + W83781D_ADDR_REG_OFFSET); + val = inb_p(address + W83781D_DATA_REG_OFFSET); + if (val & 0x80) { + pr_debug("w83781d: Detection failed at step 4\n"); + goto release; + } + outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET); + save = inb_p(address + W83781D_DATA_REG_OFFSET); + outb_p(W83781D_REG_CHIPMAN, address + W83781D_ADDR_REG_OFFSET); + val = inb_p(address + W83781D_DATA_REG_OFFSET); + if ((!(save & 0x80) && (val != 0xa3)) + || ((save & 0x80) && (val != 0x5c))) { + pr_debug("w83781d: Detection failed at step 5\n"); + goto release; + } + outb_p(W83781D_REG_I2C_ADDR, address + W83781D_ADDR_REG_OFFSET); + val = inb_p(address + W83781D_DATA_REG_OFFSET); + if (val < 0x03 || val > 0x77) { /* Not a valid I2C address */ + pr_debug("w83781d: Detection failed at step 6\n"); + goto release; + } + + /* The busy flag should be clear again */ + if (inb_p(address + W83781D_ADDR_REG_OFFSET) & 0x80) { + pr_debug("w83781d: Detection failed at step 7\n"); + goto release; + } + + /* Determine the chip type */ + outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET); + save = inb_p(address + W83781D_DATA_REG_OFFSET); + outb_p(save & 0xf8, address + W83781D_DATA_REG_OFFSET); + outb_p(W83781D_REG_WCHIPID, address + W83781D_ADDR_REG_OFFSET); + val = inb_p(address + W83781D_DATA_REG_OFFSET); + if ((val & 0xfe) == 0x10 /* W83781D */ + || val == 0x30 /* W83782D */ + || val == 0x21) /* W83627HF */ + found = 1; + + if (found) + pr_info("w83781d: Found a %s chip at %#x\n", + val == 0x21 ? "W83627HF" : + val == 0x30 ? "W83782D" : "W83781D", (int)address); + + release: + release_region(address, W83781D_EXTENT); + return found; +} + +static int __init +w83781d_isa_device_add(unsigned short address) +{ + struct resource res = { + .start = address, + .end = address + W83781D_EXTENT, + .name = "w83781d", + .flags = IORESOURCE_IO, + }; + int err; + + pdev = platform_device_alloc("w83781d", address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR "w83781d: Device allocation failed\n"); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + printk(KERN_ERR "w83781d: Device resource addition failed " + "(%d)\n", err); + goto exit_device_put; + } + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR "w83781d: Device addition failed (%d)\n", + err); + goto exit_device_put; + } + + return 0; + + exit_device_put: + platform_device_put(pdev); + exit: + pdev = NULL; + return err; +} + static int __init sensors_w83781d_init(void) { @@ -1679,21 +1847,36 @@ sensors_w83781d_init(void) res = i2c_add_driver(&w83781d_driver); if (res) - return res; + goto exit; + + if (w83781d_isa_found(isa_address)) { + res = platform_driver_register(&w83781d_isa_driver); + if (res) + goto exit_unreg_i2c_driver; - /* Don't exit if this one fails, we still want the I2C variants - to work! */ - if (i2c_isa_add_driver(&w83781d_isa_driver)) - isa_address = 0; + /* Sets global pdev as a side effect */ + res = w83781d_isa_device_add(isa_address); + if (res) + goto exit_unreg_isa_driver; + } return 0; + + exit_unreg_isa_driver: + platform_driver_unregister(&w83781d_isa_driver); + exit_unreg_i2c_driver: + i2c_del_driver(&w83781d_driver); + exit: + return res; } static void __exit sensors_w83781d_exit(void) { - if (isa_address) - i2c_isa_del_driver(&w83781d_isa_driver); + if (pdev) { + platform_device_unregister(pdev); + platform_driver_unregister(&w83781d_isa_driver); + } i2c_del_driver(&w83781d_driver); } -- cgit v1.2.3 From 292fc1a5ff44d477ff335a343a48d2b67bbc70e3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 8 May 2007 17:22:03 +0200 Subject: hwmon/smsc47b397: Convert to a platform driver Convert the smsc47b397 driver from the nonsensical i2c-isa hack to a regular platform driver. Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 3 +- drivers/hwmon/smsc47b397.c | 164 +++++++++++++++++++++++++++++---------------- 2 files changed, 109 insertions(+), 58 deletions(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 065851a77cc..5216da02b8b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -477,8 +477,7 @@ config SENSORS_SMSC47M192 config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" - depends on HWMON && I2C && EXPERIMENTAL - select I2C_ISA + depends on HWMON && EXPERIMENTAL help If you say yes here you get support for the SMSC LPC47B397-NC sensor chip. diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index 72b0e2d8650..09df56118e1 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -30,16 +30,16 @@ #include #include #include -#include -#include +#include #include #include #include #include #include -/* Address is autodetected, there is no default value */ -static unsigned short address; +static struct platform_device *pdev; + +#define DRVNAME "smsc47b397" /* Super-I/0 registers and commands */ @@ -91,7 +91,8 @@ static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80}; #define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr)) struct smsc47b397_data { - struct i2c_client client; + unsigned short addr; + const char *name; struct class_device *class_dev; struct mutex lock; @@ -104,45 +105,43 @@ struct smsc47b397_data { u8 temp[4]; }; -static int smsc47b397_read_value(struct i2c_client *client, u8 reg) +static int smsc47b397_read_value(struct smsc47b397_data* data, u8 reg) { - struct smsc47b397_data *data = i2c_get_clientdata(client); int res; mutex_lock(&data->lock); - outb(reg, client->addr); - res = inb_p(client->addr + 1); + outb(reg, data->addr); + res = inb_p(data->addr + 1); mutex_unlock(&data->lock); return res; } static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct smsc47b397_data *data = i2c_get_clientdata(client); + struct smsc47b397_data *data = dev_get_drvdata(dev); int i; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - dev_dbg(&client->dev, "starting device update...\n"); + dev_dbg(dev, "starting device update...\n"); /* 4 temperature inputs, 4 fan inputs */ for (i = 0; i < 4; i++) { - data->temp[i] = smsc47b397_read_value(client, + data->temp[i] = smsc47b397_read_value(data, SMSC47B397_REG_TEMP(i)); /* must read LSB first */ - data->fan[i] = smsc47b397_read_value(client, + data->fan[i] = smsc47b397_read_value(data, SMSC47B397_REG_FAN_LSB(i)); - data->fan[i] |= smsc47b397_read_value(client, + data->fan[i] |= smsc47b397_read_value(data, SMSC47B397_REG_FAN_MSB(i)) << 8; } data->last_updated = jiffies; data->valid = 1; - dev_dbg(&client->dev, "... device update complete\n"); + dev_dbg(dev, "... device update complete\n"); } mutex_unlock(&data->update_lock); @@ -202,6 +201,14 @@ sysfs_fan(2); sysfs_fan(3); sysfs_fan(4); +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct smsc47b397_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + static struct attribute *smsc47b397_attributes[] = { &dev_attr_temp1_input.attr, &dev_attr_temp2_input.attr, @@ -212,6 +219,7 @@ static struct attribute *smsc47b397_attributes[] = { &dev_attr_fan3_input.attr, &dev_attr_fan4_input.attr, + &dev_attr_name.attr, NULL }; @@ -219,44 +227,44 @@ static const struct attribute_group smsc47b397_group = { .attrs = smsc47b397_attributes, }; -static int smsc47b397_detach_client(struct i2c_client *client) +static int __devexit smsc47b397_remove(struct platform_device *pdev) { - struct smsc47b397_data *data = i2c_get_clientdata(client); - int err; + struct smsc47b397_data *data = platform_get_drvdata(pdev); + struct resource *res; hwmon_device_unregister(data->class_dev); - sysfs_remove_group(&client->dev.kobj, &smsc47b397_group); - - if ((err = i2c_detach_client(client))) - return err; - - release_region(client->addr, SMSC_EXTENT); + sysfs_remove_group(&pdev->dev.kobj, &smsc47b397_group); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start, SMSC_EXTENT); kfree(data); return 0; } -static int smsc47b397_detect(struct i2c_adapter *adapter); +static int smsc47b397_probe(struct platform_device *pdev); -static struct i2c_driver smsc47b397_driver = { +static struct platform_driver smsc47b397_driver = { .driver = { .owner = THIS_MODULE, - .name = "smsc47b397", + .name = DRVNAME, }, - .attach_adapter = smsc47b397_detect, - .detach_client = smsc47b397_detach_client, + .probe = smsc47b397_probe, + .remove = __devexit_p(smsc47b397_remove), }; -static int smsc47b397_detect(struct i2c_adapter *adapter) +static int __devinit smsc47b397_probe(struct platform_device *pdev) { - struct i2c_client *new_client; + struct device *dev = &pdev->dev; struct smsc47b397_data *data; + struct resource *res; int err = 0; - if (!request_region(address, SMSC_EXTENT, + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start, SMSC_EXTENT, smsc47b397_driver.driver.name)) { - dev_err(&adapter->dev, "Region 0x%x already in use!\n", - address); + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + (unsigned long)res->start, + (unsigned long)res->start + SMSC_EXTENT - 1); return -EBUSY; } @@ -265,25 +273,16 @@ static int smsc47b397_detect(struct i2c_adapter *adapter) goto error_release; } - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; + data->addr = res->start; + data->name = "smsc47b397"; mutex_init(&data->lock); - new_client->adapter = adapter; - new_client->driver = &smsc47b397_driver; - new_client->flags = 0; - - strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE); - mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); - if ((err = i2c_attach_client(new_client))) + if ((err = sysfs_create_group(&dev->kobj, &smsc47b397_group))) goto error_free; - if ((err = sysfs_create_group(&new_client->dev.kobj, &smsc47b397_group))) - goto error_detach; - - data->class_dev = hwmon_device_register(&new_client->dev); + data->class_dev = hwmon_device_register(dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); goto error_remove; @@ -292,13 +291,50 @@ static int smsc47b397_detect(struct i2c_adapter *adapter) return 0; error_remove: - sysfs_remove_group(&new_client->dev.kobj, &smsc47b397_group); -error_detach: - i2c_detach_client(new_client); + sysfs_remove_group(&dev->kobj, &smsc47b397_group); error_free: kfree(data); error_release: - release_region(address, SMSC_EXTENT); + release_region(res->start, SMSC_EXTENT); + return err; +} + +static int __init smsc47b397_device_add(unsigned short address) +{ + struct resource res = { + .start = address, + .end = address + SMSC_EXTENT - 1, + .name = DRVNAME, + .flags = IORESOURCE_IO, + }; + int err; + + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + printk(KERN_ERR DRVNAME ": Device resource addition failed " + "(%d)\n", err); + goto exit_device_put; + } + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", + err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit: return err; } @@ -320,7 +356,7 @@ static int __init smsc47b397_find(unsigned short *addr) *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) | superio_inb(SUPERIO_REG_BASE_LSB); - printk(KERN_INFO "smsc47b397: found SMSC %s " + printk(KERN_INFO DRVNAME ": found SMSC %s " "(base address 0x%04x, revision %u)\n", id == 0x81 ? "SCH5307-NS" : "LPC47B397-NC", *addr, rev); @@ -330,17 +366,33 @@ static int __init smsc47b397_find(unsigned short *addr) static int __init smsc47b397_init(void) { + unsigned short address; int ret; if ((ret = smsc47b397_find(&address))) return ret; - return i2c_isa_add_driver(&smsc47b397_driver); + ret = platform_driver_register(&smsc47b397_driver); + if (ret) + goto exit; + + /* Sets global pdev as a side effect */ + ret = smsc47b397_device_add(address); + if (ret) + goto exit_driver; + + return 0; + +exit_driver: + platform_driver_unregister(&smsc47b397_driver); +exit: + return ret; } static void __exit smsc47b397_exit(void) { - i2c_isa_del_driver(&smsc47b397_driver); + platform_device_unregister(pdev); + platform_driver_unregister(&smsc47b397_driver); } MODULE_AUTHOR("Mark M. Hoffman "); -- cgit v1.2.3 From 1d72acf91abb327e25137ad2e371c1a788b34e45 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 8 May 2007 17:22:04 +0200 Subject: Use menuconfig objects - hwmon Change Kconfig objects from "menu, config" into "menuconfig" so that the user can disable the whole feature without having to enter the menu first. Signed-off-by: Jan Engelhardt Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 107 ++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 55 deletions(-) (limited to 'drivers/hwmon/Kconfig') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5216da02b8b..a853eb2dda7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2,9 +2,7 @@ # Hardware monitoring chip drivers configuration # -menu "Hardware Monitoring support" - -config HWMON +menuconfig HWMON tristate "Hardware Monitoring support" default y help @@ -23,13 +21,15 @@ config HWMON This support can also be built as a module. If so, the module will be called hwmon. +if HWMON + config HWMON_VID tristate default n config SENSORS_ABITUGURU tristate "Abit uGuru" - depends on HWMON && EXPERIMENTAL + depends on EXPERIMENTAL help If you say yes here you get support for the Abit uGuru chips sensor part. The voltage and frequency control parts of the Abit @@ -41,7 +41,7 @@ config SENSORS_ABITUGURU config SENSORS_AD7418 tristate "Analog Devices AD7416, AD7417 and AD7418" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Analog Devices AD7416, AD7417 and AD7418 temperature monitoring chips. @@ -51,7 +51,7 @@ config SENSORS_AD7418 config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for Analog Devices ADM1021 and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, @@ -63,7 +63,7 @@ config SENSORS_ADM1021 config SENSORS_ADM1025 tristate "Analog Devices ADM1025 and compatibles" - depends on HWMON && I2C + depends on I2C select HWMON_VID help If you say yes here you get support for Analog Devices ADM1025 @@ -74,7 +74,7 @@ config SENSORS_ADM1025 config SENSORS_ADM1026 tristate "Analog Devices ADM1026 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for Analog Devices ADM1026 @@ -85,7 +85,7 @@ config SENSORS_ADM1026 config SENSORS_ADM1029 tristate "Analog Devices ADM1029" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL help If you say yes here you get support for Analog Devices ADM1029 sensor chip. @@ -96,7 +96,7 @@ config SENSORS_ADM1029 config SENSORS_ADM1031 tristate "Analog Devices ADM1031 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL help If you say yes here you get support for Analog Devices ADM1031 and ADM1030 sensor chips. @@ -106,7 +106,7 @@ config SENSORS_ADM1031 config SENSORS_ADM9240 tristate "Analog Devices ADM9240 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for Analog Devices ADM9240, @@ -117,7 +117,7 @@ config SENSORS_ADM9240 config SENSORS_K8TEMP tristate "AMD Athlon64/FX or Opteron temperature sensor" - depends on HWMON && X86 && PCI && EXPERIMENTAL + depends on X86 && PCI && EXPERIMENTAL help If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported is whole AMD K8 @@ -129,7 +129,7 @@ config SENSORS_K8TEMP config SENSORS_AMS tristate "Apple Motion Sensor driver" - depends on HWMON && PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL + depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL help Support for the motion sensor included in PowerBooks. Includes implementations for PMU and I2C. @@ -154,7 +154,7 @@ config SENSORS_AMS_I2C config SENSORS_ASB100 tristate "Asus ASB100 Bach" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for the ASB100 Bach sensor @@ -165,7 +165,7 @@ config SENSORS_ASB100 config SENSORS_ATXP1 tristate "Attansic ATXP1 VID controller" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for the Attansic ATXP1 VID @@ -179,7 +179,7 @@ config SENSORS_ATXP1 config SENSORS_DS1621 tristate "Dallas Semiconductor DS1621 and DS1625" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for Dallas Semiconductor DS1621 and DS1625 sensor chips. @@ -189,7 +189,7 @@ config SENSORS_DS1621 config SENSORS_F71805F tristate "Fintek F71805F/FG and F71872F/FG" - depends on HWMON && EXPERIMENTAL + depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring features of the Fintek F71805F/FG and F71872F/FG Super-I/O @@ -200,7 +200,7 @@ config SENSORS_F71805F config SENSORS_FSCHER tristate "FSC Hermes" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for Fujitsu Siemens Computers Hermes sensor chips. @@ -210,7 +210,7 @@ config SENSORS_FSCHER config SENSORS_FSCPOS tristate "FSC Poseidon" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for Fujitsu Siemens Computers Poseidon sensor chips. @@ -220,7 +220,7 @@ config SENSORS_FSCPOS config SENSORS_GL518SM tristate "Genesys Logic GL518SM" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for Genesys Logic GL518SM sensor chips. @@ -230,7 +230,7 @@ config SENSORS_GL518SM config SENSORS_GL520SM tristate "Genesys Logic GL520SM" - depends on HWMON && I2C + depends on I2C select HWMON_VID help If you say yes here you get support for Genesys Logic GL520SM @@ -241,7 +241,7 @@ config SENSORS_GL520SM config SENSORS_CORETEMP tristate "Intel Core (2) Duo/Solo temperature sensor" - depends on HWMON && X86 && EXPERIMENTAL + 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 @@ -249,7 +249,7 @@ config SENSORS_CORETEMP config SENSORS_IT87 tristate "ITE IT87xx and compatibles" - depends on HWMON && I2C + depends on I2C select I2C_ISA select HWMON_VID help @@ -261,7 +261,7 @@ config SENSORS_IT87 config SENSORS_LM63 tristate "National Semiconductor LM63" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for the National Semiconductor LM63 remote diode digital temperature sensor with integrated fan @@ -273,7 +273,7 @@ config SENSORS_LM63 config SENSORS_LM70 tristate "National Semiconductor LM70" - depends on HWMON && SPI_MASTER && EXPERIMENTAL + depends on SPI_MASTER && EXPERIMENTAL help If you say yes here you get support for the National Semiconductor LM70 digital temperature sensor chip. @@ -283,7 +283,7 @@ config SENSORS_LM70 config SENSORS_LM75 tristate "National Semiconductor LM75 and compatibles" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for National Semiconductor LM75 sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in @@ -298,7 +298,7 @@ config SENSORS_LM75 config SENSORS_LM77 tristate "National Semiconductor LM77" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for National Semiconductor LM77 sensor chips. @@ -308,7 +308,7 @@ config SENSORS_LM77 config SENSORS_LM78 tristate "National Semiconductor LM78 and compatibles" - depends on HWMON && I2C + depends on I2C select HWMON_VID help If you say yes here you get support for National Semiconductor LM78, @@ -319,7 +319,7 @@ config SENSORS_LM78 config SENSORS_LM80 tristate "National Semiconductor LM80" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL help If you say yes here you get support for National Semiconductor LM80 sensor chips. @@ -329,7 +329,7 @@ config SENSORS_LM80 config SENSORS_LM83 tristate "National Semiconductor LM83 and compatibles" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for National Semiconductor LM82 and LM83 sensor chips. @@ -339,7 +339,7 @@ config SENSORS_LM83 config SENSORS_LM85 tristate "National Semiconductor LM85 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for National Semiconductor LM85 @@ -350,7 +350,7 @@ config SENSORS_LM85 config SENSORS_LM87 tristate "National Semiconductor LM87" - depends on HWMON && I2C + depends on I2C select HWMON_VID help If you say yes here you get support for National Semiconductor LM87 @@ -361,7 +361,7 @@ config SENSORS_LM87 config SENSORS_LM90 tristate "National Semiconductor LM90 and compatibles" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and @@ -375,7 +375,7 @@ config SENSORS_LM90 config SENSORS_LM92 tristate "National Semiconductor LM92 and compatibles" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for National Semiconductor LM92 and Maxim MAX6635 sensor chips. @@ -385,7 +385,7 @@ config SENSORS_LM92 config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" - depends on HWMON && I2C + depends on I2C help If you say yes here you get support for MAX1619 sensor chip. @@ -394,7 +394,7 @@ config SENSORS_MAX1619 config SENSORS_MAX6650 tristate "Maxim MAX6650 sensor chip" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL help If you say yes here you get support for the MAX6650 / MAX6651 sensor chips. @@ -404,7 +404,7 @@ config SENSORS_MAX6650 config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select I2C_ISA select HWMON_VID help @@ -419,7 +419,7 @@ config SENSORS_PC87360 config SENSORS_PC87427 tristate "National Semiconductor PC87427" - depends on HWMON && EXPERIMENTAL + depends on EXPERIMENTAL help If you say yes here you get access to the hardware monitoring functions of the National Semiconductor PC87427 Super-I/O chip. @@ -432,7 +432,7 @@ config SENSORS_PC87427 config SENSORS_SIS5595 tristate "Silicon Integrated Systems Corp. SiS5595" - depends on HWMON && I2C && PCI && EXPERIMENTAL + depends on I2C && PCI && EXPERIMENTAL select I2C_ISA help If you say yes here you get support for the integrated sensors in @@ -443,7 +443,6 @@ config SENSORS_SIS5595 config SENSORS_SMSC47M1 tristate "SMSC LPC47M10x and compatibles" - depends on HWMON help If you say yes here you get support for the integrated fan monitoring and control capabilities of the SMSC LPC47B27x, @@ -460,7 +459,7 @@ config SENSORS_SMSC47M1 config SENSORS_SMSC47M192 tristate "SMSC LPC47M192 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for the temperature and @@ -477,7 +476,7 @@ config SENSORS_SMSC47M192 config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" - depends on HWMON && EXPERIMENTAL + depends on EXPERIMENTAL help If you say yes here you get support for the SMSC LPC47B397-NC sensor chip. @@ -487,7 +486,7 @@ config SENSORS_SMSC47B397 config SENSORS_VIA686A tristate "VIA686A" - depends on HWMON && I2C && PCI + depends on I2C && PCI select I2C_ISA help If you say yes here you get support for the integrated sensors in @@ -498,7 +497,7 @@ config SENSORS_VIA686A config SENSORS_VT1211 tristate "VIA VT1211" - depends on HWMON && EXPERIMENTAL + depends on EXPERIMENTAL select HWMON_VID help If you say yes here then you get support for hardware monitoring @@ -509,7 +508,7 @@ config SENSORS_VT1211 config SENSORS_VT8231 tristate "VIA VT8231" - depends on HWMON && I2C && PCI && EXPERIMENTAL + depends on I2C && PCI && EXPERIMENTAL select HWMON_VID select I2C_ISA help @@ -521,7 +520,7 @@ config SENSORS_VT8231 config SENSORS_W83781D tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" - depends on HWMON && I2C + depends on I2C select HWMON_VID help If you say yes here you get support for the Winbond W8378x series @@ -533,7 +532,7 @@ config SENSORS_W83781D config SENSORS_W83791D tristate "Winbond W83791D" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for the Winbond W83791D chip. @@ -543,7 +542,7 @@ config SENSORS_W83791D config SENSORS_W83792D tristate "Winbond W83792D" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Winbond W83792D chip. @@ -552,7 +551,7 @@ config SENSORS_W83792D config SENSORS_W83793 tristate "Winbond W83793" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select HWMON_VID help If you say yes here you get support for the Winbond W83793 @@ -563,7 +562,7 @@ config SENSORS_W83793 config SENSORS_W83L785TS tristate "Winbond W83L785TS-S" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Winbond W83L785TS-S sensor chip, which is used on the Asus A7N8X, among other @@ -574,7 +573,6 @@ config SENSORS_W83L785TS config SENSORS_W83627HF tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF" - depends on HWMON select HWMON_VID help If you say yes here you get support for the Winbond W836X7 series @@ -586,7 +584,7 @@ config SENSORS_W83627HF config SENSORS_W83627EHF tristate "Winbond W83627EHF" - depends on HWMON && I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL select I2C_ISA help If you say yes here you get preliminary support for the hardware @@ -602,7 +600,7 @@ config SENSORS_W83627EHF config SENSORS_HDAPS tristate "IBM Hard Drive Active Protection System (hdaps)" - depends on HWMON && INPUT && X86 + depends on INPUT && X86 default n help This driver provides support for the IBM Hard Drive Active Protection @@ -621,7 +619,6 @@ config SENSORS_HDAPS config HWMON_DEBUG_CHIP bool "Hardware Monitoring Chip debugging messages" - depends on HWMON default n help Say Y here if you want the I2C chip drivers to produce a bunch of @@ -629,4 +626,4 @@ config HWMON_DEBUG_CHIP a problem with I2C support and want to see more of what is going on. -endmenu +endif # HWMON -- cgit v1.2.3