aboutsummaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig8
-rw-r--r--drivers/hwmon/adm1026.c17
-rw-r--r--drivers/hwmon/adm1029.c5
-rw-r--r--drivers/hwmon/ams/ams-core.c82
-rw-r--r--drivers/hwmon/ams/ams-i2c.c60
-rw-r--r--drivers/hwmon/ams/ams-input.c63
-rw-r--r--drivers/hwmon/ams/ams-pmu.c18
-rw-r--r--drivers/hwmon/ams/ams.h4
-rw-r--r--drivers/hwmon/ibmaem.c7
-rw-r--r--drivers/hwmon/ibmpex.c6
-rw-r--r--drivers/hwmon/it87.c11
-rw-r--r--drivers/hwmon/lm78.c270
-rw-r--r--drivers/hwmon/lm85.c352
-rw-r--r--drivers/hwmon/lm87.c33
-rw-r--r--drivers/hwmon/lm90.c444
-rw-r--r--drivers/hwmon/max1619.c17
-rw-r--r--drivers/hwmon/w83781d.c976
-rw-r--r--drivers/hwmon/w83791d.c324
18 files changed, 1654 insertions, 1043 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index ebacc0af40f..6de1e0ffd39 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -510,11 +510,9 @@ config SENSORS_LM90
depends on I2C
help
If you say yes here you get support for National Semiconductor LM90,
- LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657,
- MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips.
-
- The Analog Devices ADT7461 sensor chip is also supported, but only
- if found in ADM1032 compatibility mode.
+ LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim
+ MAX6646, MAX6647, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680 and
+ MAX6681 sensor chips.
This driver can also be built as a module. If so, the module
will be called lm90.
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index 7fe2441fc84..ff7de40b6e3 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -279,7 +279,6 @@ struct adm1026_data {
u8 fan_min[8]; /* Register value */
u8 fan_div[8]; /* Decoded value */
struct pwm_data pwm1; /* Pwm control values */
- int vid; /* Decoded value */
u8 vrm; /* VRM version */
u8 analog_out; /* Register value (DAC) */
long alarms; /* Register encoding, combined */
@@ -455,7 +454,7 @@ static void adm1026_print_gpio(struct i2c_client *client)
struct adm1026_data *data = i2c_get_clientdata(client);
int i;
- dev_dbg(&client->dev, "GPIO config is:");
+ dev_dbg(&client->dev, "GPIO config is:\n");
for (i = 0;i <= 7;++i) {
if (data->config2 & (1 << i)) {
dev_dbg(&client->dev, "\t%sGP%s%d\n",
@@ -697,8 +696,6 @@ static struct adm1026_data *adm1026_update_device(struct device *dev)
data->last_config = jiffies;
}; /* last_config */
- dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n");
- data->vid = (data->gpio >> 11) & 0x1f;
data->valid = 1;
mutex_unlock(&data->update_lock);
return data;
@@ -1215,7 +1212,10 @@ static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg,
static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
- return sprintf(buf, "%d\n", vid_from_reg(data->vid & 0x3f, data->vrm));
+ int vid = (data->gpio >> 11) & 0x1f;
+
+ dev_dbg(dev, "Setting VID from GPIO11-15.\n");
+ return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
@@ -1681,17 +1681,16 @@ static int adm1026_detect(struct i2c_client *client, int kind,
kind = adm1026;
} else if (company == ADM1026_COMPANY_ANALOG_DEV
&& (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, ": Unrecognized stepping "
+ dev_err(&adapter->dev, "Unrecognized stepping "
"0x%02x. Defaulting to ADM1026.\n", verstep);
kind = adm1026;
} else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, ": Found version/stepping "
+ dev_err(&adapter->dev, "Found version/stepping "
"0x%02x. Assuming generic ADM1026.\n",
verstep);
kind = any_chip;
} else {
- dev_dbg(&adapter->dev, ": Autodetection "
- "failed\n");
+ dev_dbg(&adapter->dev, "Autodetection failed\n");
/* Not an ADM1026 ... */
if (kind == 0) { /* User used force=x,y */
dev_err(&adapter->dev, "Generic ADM1026 not "
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
index ba84ca5923f..36718150b47 100644
--- a/drivers/hwmon/adm1029.c
+++ b/drivers/hwmon/adm1029.c
@@ -179,7 +179,8 @@ show_fan(struct device *dev, struct device_attribute *devattr, char *buf)
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adm1029_data *data = adm1029_update_device(dev);
u16 val;
- if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0
+ if (data->fan[attr->index] == 0
+ || (data->fan_div[attr->index] & 0xC0) == 0
|| data->fan[attr->index] == 255) {
return sprintf(buf, "0\n");
}
@@ -194,7 +195,7 @@ show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adm1029_data *data = adm1029_update_device(dev);
- if (data->fan_div[attr->index] == 0)
+ if ((data->fan_div[attr->index] & 0xC0) == 0)
return sprintf(buf, "0\n");
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
}
diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c
index fbefa82a015..6c9ace1b76f 100644
--- a/drivers/hwmon/ams/ams-core.c
+++ b/drivers/hwmon/ams/ams-core.c
@@ -99,39 +99,31 @@ static struct pmf_irq_client ams_shock_client = {
*/
static void ams_worker(struct work_struct *work)
{
- mutex_lock(&ams_info.lock);
-
- if (ams_info.has_device) {
- unsigned long flags;
+ unsigned long flags;
+ u8 irqs_to_clear;
- spin_lock_irqsave(&ams_info.irq_lock, flags);
+ mutex_lock(&ams_info.lock);
- if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
- if (verbose)
- printk(KERN_INFO "ams: freefall detected!\n");
+ spin_lock_irqsave(&ams_info.irq_lock, flags);
+ irqs_to_clear = ams_info.worker_irqs;
- ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
+ if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
+ if (verbose)
+ printk(KERN_INFO "ams: freefall detected!\n");
- /* we must call this with interrupts enabled */
- spin_unlock_irqrestore(&ams_info.irq_lock, flags);
- ams_info.clear_irq(AMS_IRQ_FREEFALL);
- spin_lock_irqsave(&ams_info.irq_lock, flags);
- }
+ ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
+ }
- if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
- if (verbose)
- printk(KERN_INFO "ams: shock detected!\n");
+ if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
+ if (verbose)
+ printk(KERN_INFO "ams: shock detected!\n");
- ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
+ ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
+ }
- /* we must call this with interrupts enabled */
- spin_unlock_irqrestore(&ams_info.irq_lock, flags);
- ams_info.clear_irq(AMS_IRQ_SHOCK);
- spin_lock_irqsave(&ams_info.irq_lock, flags);
- }
+ spin_unlock_irqrestore(&ams_info.irq_lock, flags);
- spin_unlock_irqrestore(&ams_info.irq_lock, flags);
- }
+ ams_info.clear_irq(irqs_to_clear);
mutex_unlock(&ams_info.lock);
}
@@ -223,34 +215,28 @@ int __init ams_init(void)
void ams_exit(void)
{
- mutex_lock(&ams_info.lock);
-
- if (ams_info.has_device) {
- /* Remove input device */
- ams_input_exit();
+ /* Remove input device */
+ ams_input_exit();
- /* Shut down implementation */
- ams_info.exit();
-
- /* Flush interrupt worker
- *
- * We do this after ams_info.exit(), because an interrupt might
- * have arrived before disabling them.
- */
- flush_scheduled_work();
+ /* Remove attributes */
+ device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
- /* Remove attributes */
- device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
+ /* Shut down implementation */
+ ams_info.exit();
- /* Remove device */
- of_device_unregister(ams_info.of_dev);
+ /* Flush interrupt worker
+ *
+ * We do this after ams_info.exit(), because an interrupt might
+ * have arrived before disabling them.
+ */
+ flush_scheduled_work();
- /* Remove handler */
- pmf_unregister_irq_client(&ams_shock_client);
- pmf_unregister_irq_client(&ams_freefall_client);
- }
+ /* Remove device */
+ of_device_unregister(ams_info.of_dev);
- mutex_unlock(&ams_info.lock);
+ /* Remove handler */
+ pmf_unregister_irq_client(&ams_shock_client);
+ pmf_unregister_irq_client(&ams_freefall_client);
}
MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c
index 957760536a4..2cbf8a6506c 100644
--- a/drivers/hwmon/ams/ams-i2c.c
+++ b/drivers/hwmon/ams/ams-i2c.c
@@ -60,26 +60,34 @@ enum ams_i2c_cmd {
AMS_CMD_START,
};
-static int ams_i2c_attach(struct i2c_adapter *adapter);
-static int ams_i2c_detach(struct i2c_adapter *adapter);
+static int ams_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int ams_i2c_remove(struct i2c_client *client);
+
+static const struct i2c_device_id ams_id[] = {
+ { "ams", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ams_id);
static struct i2c_driver ams_i2c_driver = {
.driver = {
.name = "ams",
.owner = THIS_MODULE,
},
- .attach_adapter = ams_i2c_attach,
- .detach_adapter = ams_i2c_detach,
+ .probe = ams_i2c_probe,
+ .remove = ams_i2c_remove,
+ .id_table = ams_id,
};
static s32 ams_i2c_read(u8 reg)
{
- return i2c_smbus_read_byte_data(&ams_info.i2c_client, reg);
+ return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
}
static int ams_i2c_write(u8 reg, u8 value)
{
- return i2c_smbus_write_byte_data(&ams_info.i2c_client, reg, value);
+ return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
}
static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
@@ -152,9 +160,9 @@ static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
*z = ams_i2c_read(AMS_DATAZ);
}
-static int ams_i2c_attach(struct i2c_adapter *adapter)
+static int ams_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- unsigned long bus;
int vmaj, vmin;
int result;
@@ -162,17 +170,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter)
if (unlikely(ams_info.has_device))
return -ENODEV;
- if (strncmp(adapter->name, "uni-n", 5))
- return -ENODEV;
-
- bus = simple_strtoul(adapter->name + 6, NULL, 10);
- if (bus != ams_info.i2c_bus)
- return -ENODEV;
-
- ams_info.i2c_client.addr = ams_info.i2c_address;
- ams_info.i2c_client.adapter = adapter;
- ams_info.i2c_client.driver = &ams_i2c_driver;
- strcpy(ams_info.i2c_client.name, "Apple Motion Sensor");
+ ams_info.i2c_client = client;
if (ams_i2c_cmd(AMS_CMD_RESET)) {
printk(KERN_INFO "ams: Failed to reset the device\n");
@@ -237,7 +235,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter)
return 0;
}
-static int ams_i2c_detach(struct i2c_adapter *adapter)
+static int ams_i2c_remove(struct i2c_client *client)
{
if (ams_info.has_device) {
/* Disable interrupts */
@@ -261,11 +259,7 @@ static void ams_i2c_exit(void)
int __init ams_i2c_init(struct device_node *np)
{
- char *tmp_bus;
int result;
- const u32 *prop;
-
- mutex_lock(&ams_info.lock);
/* Set implementation stuff */
ams_info.of_node = np;
@@ -275,25 +269,7 @@ int __init ams_i2c_init(struct device_node *np)
ams_info.clear_irq = ams_i2c_clear_irq;
ams_info.bustype = BUS_I2C;
- /* look for bus either using "reg" or by path */
- prop = of_get_property(ams_info.of_node, "reg", NULL);
- if (!prop) {
- result = -ENODEV;
-
- goto exit;
- }
-
- tmp_bus = strstr(ams_info.of_node->full_name, "/i2c-bus@");
- if (tmp_bus)
- ams_info.i2c_bus = *(tmp_bus + 9) - '0';
- else
- ams_info.i2c_bus = ((*prop) >> 8) & 0x0f;
- ams_info.i2c_address = ((*prop) & 0xff) >> 1;
-
result = i2c_add_driver(&ams_i2c_driver);
-exit:
- mutex_unlock(&ams_info.lock);
-
return result;
}
diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c
index 7b81e0c2c2d..8a712392cd3 100644
--- a/drivers/hwmon/ams/ams-input.c
+++ b/drivers/hwmon/ams/ams-input.c
@@ -20,13 +20,15 @@
#include "ams.h"
static unsigned int joystick;
-module_param(joystick, bool, 0644);
+module_param(joystick, bool, S_IRUGO);
MODULE_PARM_DESC(joystick, "Enable the input class device on module load");
static unsigned int invert;
-module_param(invert, bool, 0644);
+module_param(invert, bool, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
+static DEFINE_MUTEX(ams_input_mutex);
+
static void ams_idev_poll(struct input_polled_dev *dev)
{
struct input_dev *idev = dev->input;
@@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev)
}
/* Call with ams_info.lock held! */
-static void ams_input_enable(void)
+static int ams_input_enable(void)
{
struct input_dev *input;
s8 x, y, z;
-
- if (ams_info.idev)
- return;
+ int error;
ams_sensors(&x, &y, &z);
ams_info.xcalib = x;
@@ -65,7 +65,7 @@ static void ams_input_enable(void)
ams_info.idev = input_allocate_polled_device();
if (!ams_info.idev)
- return;
+ return -ENOMEM;
ams_info.idev->poll = ams_idev_poll;
ams_info.idev->poll_interval = 25;
@@ -84,14 +84,18 @@ static void ams_input_enable(void)
set_bit(EV_KEY, input->evbit);
set_bit(BTN_TOUCH, input->keybit);
- if (input_register_polled_device(ams_info.idev)) {
+ error = input_register_polled_device(ams_info.idev);
+ if (error) {
input_free_polled_device(ams_info.idev);
ams_info.idev = NULL;
- return;
+ return error;
}
+
+ joystick = 1;
+
+ return 0;
}
-/* Call with ams_info.lock held! */
static void ams_input_disable(void)
{
if (ams_info.idev) {
@@ -99,6 +103,8 @@ static void ams_input_disable(void)
input_free_polled_device(ams_info.idev);
ams_info.idev = NULL;
}
+
+ joystick = 0;
}
static ssize_t ams_input_show_joystick(struct device *dev,
@@ -110,39 +116,42 @@ static ssize_t ams_input_show_joystick(struct device *dev,
static ssize_t ams_input_store_joystick(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- if (sscanf(buf, "%d\n", &joystick) != 1)
+ unsigned long enable;
+ int error = 0;
+
+ if (strict_strtoul(buf, 0, &enable) || enable > 1)
return -EINVAL;
- mutex_lock(&ams_info.lock);
+ mutex_lock(&ams_input_mutex);
- if (joystick)
- ams_input_enable();
- else
- ams_input_disable();
+ if (enable != joystick) {
+ if (enable)
+ error = ams_input_enable();
+ else
+ ams_input_disable();
+ }
- mutex_unlock(&ams_info.lock);
+ mutex_unlock(&ams_input_mutex);
- return count;
+ return error ? error : count;
}
static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR,
ams_input_show_joystick, ams_input_store_joystick);
-/* Call with ams_info.lock held! */
int ams_input_init(void)
{
- int result;
-
- result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick);
-
- if (!result && joystick)
+ if (joystick)
ams_input_enable();
- return result;
+
+ return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick);
}
-/* Call with ams_info.lock held! */
void ams_input_exit(void)
{
- ams_input_disable();
device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick);
+
+ mutex_lock(&ams_input_mutex);
+ ams_input_disable();
+ mutex_unlock(&ams_input_mutex);
}
diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c
index 9463e9768f6..fb18b3d3162 100644
--- a/drivers/hwmon/ams/ams-pmu.c
+++ b/drivers/hwmon/ams/ams-pmu.c
@@ -149,8 +149,6 @@ int __init ams_pmu_init(struct device_node *np)
const u32 *prop;
int result;
- mutex_lock(&ams_info.lock);
-
/* Set implementation stuff */
ams_info.of_node = np;
ams_info.exit = ams_pmu_exit;
@@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np)
/* Get PMU command, should be 0x4e, but we can never know */
prop = of_get_property(ams_info.of_node, "reg", NULL);
- if (!prop) {
- result = -ENODEV;
- goto exit;
- }
+ if (!prop)
+ return -ENODEV;
+
ams_pmu_cmd = ((*prop) >> 8) & 0xff;
/* Disable interrupts */
@@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np)
result = ams_sensor_attach();
if (result < 0)
- goto exit;
+ return result;
/* Set default values */
ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
@@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np)
printk(KERN_INFO "ams: Found PMU based motion sensor\n");
- result = 0;
-
-exit:
- mutex_unlock(&ams_info.lock);
-
- return result;
+ return 0;
}
diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h
index 221ef6915a5..5ed387b0bd9 100644
--- a/drivers/hwmon/ams/ams.h
+++ b/drivers/hwmon/ams/ams.h
@@ -46,9 +46,7 @@ struct ams {
#ifdef CONFIG_SENSORS_AMS_I2C
/* I2C properties */
- int i2c_bus;
- int i2c_address;
- struct i2c_client i2c_client;
+ struct i2c_client *i2c_client;
#endif
/* Joystick emulation */
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index 0f70dc20410..7b0ed5dea39 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -1118,3 +1118,10 @@ MODULE_LICENSE("GPL");
module_init(aem_init);
module_exit(aem_exit);
+
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
index 4e9b19c6732..537d9fb2ff8 100644
--- a/drivers/hwmon/ibmpex.c
+++ b/drivers/hwmon/ibmpex.c
@@ -608,3 +608,9 @@ MODULE_LICENSE("GPL");
module_init(ibmpex_init);
module_exit(ibmpex_exit);
+
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index d793cc01199..b74c95735f9 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -477,7 +477,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
if (reg & (1 << nr))
return sprintf(buf, "3\n"); /* thermal diode */
if (reg & (8 << nr))
- return sprintf(buf, "2\n"); /* thermistor */
+ return sprintf(buf, "4\n"); /* thermistor */
return sprintf(buf, "0\n"); /* disabled */
}
static ssize_t set_sensor(struct device *dev, struct device_attribute *attr,
@@ -493,10 +493,15 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr,
data->sensor &= ~(1 << nr);
data->sensor &= ~(8 << nr);
- /* 3 = thermal diode; 2 = thermistor; 0 = disabled */
+ if (val == 2) { /* backwards compatibility */
+ dev_warn(dev, "Sensor type 2 is deprecated, please use 4 "
+ "instead\n");
+ val = 4;
+ }
+ /* 3 = thermal diode; 4 = thermistor; 0 = disabled */
if (val == 3)
data->sensor |= 1 << nr;
- else if (val == 2)
+ else if (val == 4)
data->sensor |= 8 << nr;
else if (val != 0) {
mutex_unlock(&data->update_lock);
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index ed7859f0e16..b5e3b285169 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -114,25 +114,16 @@ static inline int TEMP_FROM_REG(s8 val)
#define DIV_FROM_REG(val) (1 << (val))
-/* There are some complications in a module like this. First off, LM78 chips
- may be both present on the SMBus and the ISA bus, and we have to handle
- those cases separately at some places. Second, there might be several
- LM78 chips available (well, actually, that is probably never done; but
- it is a clean illustration of how to handle a case like that). Finally,
- a specific chip may be attached to *both* ISA and SMBus, and we would
- not like to detect it double. Fortunately, in the case of the LM78 at
- least, a register tells us what SMBus address we are on, so that helps
- a bit - except if there could be more than one SMBus. Groan. No solution
- for this yet. */
-
-/* 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 i2c_client *client;
struct device *hwmon_dev;
struct mutex lock;
enum chips type;
+ /* For ISA device only */
+ const char *name;
+ int isa_addr;
+
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
@@ -151,9 +142,11 @@ struct lm78_data {
};
-static int lm78_attach_adapter(struct i2c_adapter *adapter);
-static int lm78_detect(struct i2c_adapter *adapter, int address, int kind);
-static int lm78_detach_client(struct i2c_client *client);
+static int lm78_i2c_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info);
+static int lm78_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int lm78_i2c_remove(struct i2c_client *client);
static int __devinit lm78_isa_probe(struct platform_device *pdev);
static int __devexit lm78_isa_remove(struct platform_device *pdev);
@@ -164,12 +157,23 @@ static struct lm78_data *lm78_update_device(struct device *dev);
static void lm78_init_device(struct lm78_data *data);
+static const struct i2c_device_id lm78_i2c_id[] = {
+ { "lm78", lm78 },
+ { "lm79", lm79 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
+
static struct i2c_driver lm78_driver = {
+ .class = I2C_CLASS_HWMON,
.driver = {
.name = "lm78",
},
- .attach_adapter = lm78_attach_adapter,
- .detach_client = lm78_detach_client,
+ .probe = lm78_i2c_probe,
+ .remove = lm78_i2c_remove,
+ .id_table = lm78_i2c_id,
+ .detect = lm78_i2c_detect,
+ .address_data = &addr_data,
};
static struct platform_driver lm78_isa_driver = {
@@ -454,17 +458,6 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
-/* This function is called when:
- * lm78_driver is inserted (when this module is loaded), for each
- available adapter
- * when a new adapter is inserted (and lm78_driver is still present) */
-static int lm78_attach_adapter(struct i2c_adapter *adapter)
-{
- if (!(adapter->class & I2C_CLASS_HWMON))
- return 0;
- return i2c_probe(adapter, &addr_data, lm78_detect);
-}
-
static struct attribute *lm78_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in0_min.dev_attr.attr,
@@ -527,54 +520,77 @@ static ssize_t show_name(struct device *dev, struct device_attribute
{
struct lm78_data *data = dev_get_drvdata(dev);
- return sprintf(buf, "%s\n", data->client.name);
+ return sprintf(buf, "%s\n", data->name);
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-/* This function is called by i2c_probe */
-static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
+/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
+static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
{
- int i, err;
- struct i2c_client *new_client;
- struct lm78_data *data;
- const char *client_name = "";
+ struct lm78_data *isa;
+ int i;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- err = -ENODEV;
- goto ERROR1;
+ if (!pdev) /* No ISA chip */
+ return 0;
+ isa = platform_get_drvdata(pdev);
+
+ if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr)
+ return 0; /* Address doesn't match */
+ if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe))
+ return 0; /* Chip type doesn't match */
+
+ /* We compare all the limit registers, the config register and the
+ * interrupt mask registers */
+ for (i = 0x2b; i <= 0x3d; i++) {
+ if (lm78_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
}
+ if (lm78_read_value(isa, LM78_REG_CONFIG) !=
+ i2c_smbus_read_byte_data(client, LM78_REG_CONFIG))
+ return 0;
+ for (i = 0x43; i <= 0x46; i++) {
+ if (lm78_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
+ }
+
+ return 1;
+}
- /* OK. For now, we presume we have a valid client. We now create the
- client structure, even though we cannot fill it completely yet.
- But it allows us to access lm78_{read,write}_value. */
+static int lm78_i2c_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ int i;
+ struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL;
+ const char *client_name;
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
- if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
- err = -ENOMEM;
- goto ERROR1;
- }
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
- new_client = &data->client;
- i2c_set_clientdata(new_client, data);
- new_client->addr = address;
- new_client->adapter = adapter;
- new_client->driver = &lm78_driver;
+ /* We block updates of the ISA device to minimize the risk of
+ concurrent access to the same LM78 chip through different
+ interfaces. */
+ if (isa)
+ mutex_lock(&isa->update_lock);
- /* Now, we do the remaining detection. */
if (kind < 0) {
- if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) {
- err = -ENODEV;
- goto ERROR2;
- }
- if (lm78_read_value(data, LM78_REG_I2C_ADDR) !=
- address) {
- err = -ENODEV;
- goto ERROR2;
- }
+ if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
+ || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR)
+ != address)
+ goto err_nodev;
+
+ /* Explicitly prevent the misdetection of Winbond chips */
+ i = i2c_smbus_read_byte_data(client, 0x4f);
+ if (i == 0xa3 || i == 0x5c)
+ goto err_nodev;
}
/* Determine the chip type. */
if (kind <= 0) {
- i = lm78_read_value(data, LM78_REG_CHIPID);
+ i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
if (i == 0x00 || i == 0x20 /* LM78 */
|| i == 0x40) /* LM78-J */
kind = lm78;
@@ -586,33 +602,59 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
"parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
- err = -ENODEV;
- goto ERROR2;
+ goto err_nodev;
+ }
+
+ if (lm78_alias_detect(client, i)) {
+ dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
+ "be the same as ISA device\n", address);
+ goto err_nodev;
}
}
- if (kind == lm78) {
- client_name = "lm78";
- } else if (kind == lm79) {
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+
+ switch (kind) {
+ case lm79:
client_name = "lm79";
+ break;
+ default:
+ client_name = "lm78";
}
+ strlcpy(info->type, client_name, I2C_NAME_SIZE);
+
+ return 0;
+
+ err_nodev:
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+ return -ENODEV;
+}
+
+static int lm78_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lm78_data *data;
+ int err;
- /* 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 = kzalloc(sizeof(struct lm78_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- /* Tell the I2C layer a new client has arrived */
- if ((err = i2c_attach_client(new_client)))
- goto ERROR2;
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ data->type = id->driver_data;
/* Initialize the LM78 chip */
lm78_init_device(data);
/* Register sysfs hooks */
- if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group)))
+ err = sysfs_create_group(&client->dev.kobj, &lm78_group);
+ if (err)
goto ERROR3;
- data->hwmon_dev = hwmon_device_register(&new_client->dev);
+ data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto ERROR4;
@@ -621,26 +663,18 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
return 0;
ERROR4:
- sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
+ sysfs_remove_group(&client->dev.kobj, &lm78_group);
ERROR3:
- i2c_detach_client(new_client);
-ERROR2:
kfree(data);
-ERROR1:
return err;
}
-static int lm78_detach_client(struct i2c_client *client)
+static int lm78_i2c_remove(struct i2c_client *client)
{
struct lm78_data *data = i2c_get_clientdata(client);
- int err;
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm78_group);
-
- if ((err = i2c_detach_client(client)))
- return err;
-
kfree(data);
return 0;
@@ -651,11 +685,10 @@ 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")) {
+ if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) {
err = -EBUSY;
goto exit;
}
@@ -665,18 +698,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
goto exit_release_region;
}
mutex_init(&data->lock);
- data->client.addr = res->start;
- i2c_set_clientdata(&data->client, data);
+ data->isa_addr = res->start;
platform_set_drvdata(pdev, data);
if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
data->type = lm79;
- name = "lm79";
+ data->name = "lm79";
} else {
data->type = lm78;
- name = "lm78";
+ data->name = "lm78";
}
- strlcpy(data->client.name, name, I2C_NAME_SIZE);
/* Initialize the LM78 chip */
lm78_init_device(data);
@@ -699,7 +730,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
device_remove_file(&pdev->dev, &dev_attr_name);
kfree(data);
exit_release_region:
- release_region(res->start, LM78_EXTENT);
+ release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
exit:
return err;
}
@@ -707,13 +738,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev)
static int __devexit lm78_isa_remove(struct platform_device *pdev)
{
struct lm78_data *data = platform_get_drvdata(pdev);
+ struct resource *res;
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
device_remove_file(&pdev->dev, &dev_attr_name);
- release_region(data->client.addr, LM78_EXTENT);
kfree(data);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
+
return 0;
}
@@ -724,13 +758,13 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev)
would slow down the LM78 access and should not be necessary. */
static int lm78_read_value(struct lm78_data *data, u8 reg)
{
- struct i2c_client *client = &data->client;
+ struct i2c_client *client = data->client;
- if (!client->driver) { /* ISA device */
+ if (!client) { /* ISA device */
int res;
mutex_lock(&data->lock);
- outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
- res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
+ outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
+ res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return res;
} else
@@ -746,12 +780,12 @@ static int lm78_read_value(struct lm78_data *data, u8 reg)
nowhere else be necessary! */
static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
{
- struct i2c_client *client = &data->client;
+ struct i2c_client *client = data->client;
- if (!client->driver) { /* ISA device */
+ if (!client) { /* ISA device */
mutex_lock(&data->lock);
- outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
- outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
+ outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
+ outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return 0;
} else
@@ -837,8 +871,17 @@ static int __init lm78_isa_found(unsigned short address)
{
int val, save, found = 0;
- if (!request_region(address, LM78_EXTENT, "lm78"))
+ /* We have to request the region in two parts because some
+ boards declare base+4 to base+7 as a PNP device */
+ if (!request_region(address, 4, "lm78")) {
+ pr_debug("lm78: Failed to request low part of region\n");
return 0;
+ }
+ if (!request_region(address + 4, 4, "lm78")) {
+ pr_debug("lm78: Failed to request high part of region\n");
+ release_region(address, 4);
+ return 0;
+ }
#define REALLY_SLOW_IO
/* We need the timeouts for at least some LM78-like
@@ -901,7 +944,8 @@ static int __init lm78_isa_found(unsigned short address)
val & 0x80 ? "LM79" : "LM78", (int)address);
release:
- release_region(address, LM78_EXTENT);
+ release_region(address + 4, 4);
+ release_region(address, 4);
return found;
}
@@ -949,14 +993,12 @@ static int __init sm_lm78_init(void)
{
int res;
- res = i2c_add_driver(&lm78_driver);
- if (res)
- goto exit;
-
+ /* We register the ISA device first, so that we can skip the
+ * registration of an I2C interface to the same device. */
if (lm78_isa_found(isa_address)) {
res = platform_driver_register(&lm78_isa_driver);
if (res)
- goto exit_unreg_i2c_driver;
+ goto exit;
/* Sets global pdev as a side effect */
res = lm78_isa_device_add(isa_address);
@@ -964,12 +1006,16 @@ static int __init sm_lm78_init(void)
goto exit_unreg_isa_driver;
}
+ res = i2c_add_driver(&lm78_driver);
+ if (res)
+ goto exit_unreg_isa_device;
+
return 0;
+ exit_unreg_isa_device:
+ platform_device_unregister(pdev);
exit_unreg_isa_driver:
platform_driver_unregister(&lm78_isa_driver);
- exit_unreg_i2c_driver:
- i2c_del_driver(&lm78_driver);
exit:
return res;
}
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 12d446f54f9..3ff0285396f 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -5,6 +5,7 @@
Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
+ Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
Chip details at <http://www.national.com/ds/LM/LM85.pdf>
@@ -173,40 +174,39 @@ static int RANGE_TO_REG(int range)
{
int i;
- if (range >= lm85_range_map[15])
- return 15;
-
/* Find the closest match */
- for (i = 14; i >= 0; --i) {
- if (range >= lm85_range_map[i]) {
- if ((lm85_range_map[i + 1] - range) <
- (range - lm85_range_map[i]))
- return i + 1;
- return i;
- }
+ for (i = 0; i < 15; ++i) {
+ if (range <= (lm85_range_map[i] + lm85_range_map[i + 1]) / 2)
+ break;
}
- return 0;
+ return i;
}
#define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]
/* These are the PWM frequency encodings */
-static const int lm85_freq_map[] = { /* .1 Hz */
- 100, 150, 230, 300, 380, 470, 620, 940
+static const int lm85_freq_map[8] = { /* 1 Hz */
+ 10, 15, 23, 30, 38, 47, 61, 94
+};
+static const int adm1027_freq_map[8] = { /* 1 Hz */
+ 11, 15, 22, 29, 35, 44, 59, 88
};
-static int FREQ_TO_REG(int freq)
+static int FREQ_TO_REG(const int *map, int freq)
{
int i;
- if (freq >= lm85_freq_map[7])
- return 7;
+ /* Find the closest match */
for (i = 0; i < 7; ++i)
- if (freq <= lm85_freq_map[i])
+ if (freq <= (map[i] + map[i + 1]) / 2)
break;
return i;
}
-#define FREQ_FROM_REG(val) lm85_freq_map[(val) & 0x07]
+
+static int FREQ_FROM_REG(const int *map, u8 reg)
+{
+ return map[reg & 0x07];
+}
/* Since we can't use strings, I'm abusing these numbers
* to stand in for the following meanings:
@@ -275,7 +275,6 @@ struct lm85_zone {
struct lm85_autofan {
u8 config; /* Register value */
- u8 freq; /* PWM frequency, encoded */
u8 min_pwm; /* Minimum PWM value, encoded */
u8 min_off; /* Min PWM or OFF below "limit", flag */
};
@@ -283,8 +282,8 @@ struct lm85_autofan {
/* For each registered chip, we need to keep some data in memory.
The structure is dynamically allocated. */
struct lm85_data {
- struct i2c_client client;
struct device *hwmon_dev;
+ const int *freq_map;
enum chips type;
struct mutex update_lock;
@@ -301,6 +300,7 @@ struct lm85_data {
u16 fan[4]; /* Register value */
u16 fan_min[4]; /* Register value */
u8 pwm[3]; /* Register value */
+ u8 pwm_freq[3]; /* Register encoding */
u8 temp_ext[3]; /* Decoded values */
u8 in_ext[8]; /* Decoded values */
u8 vid; /* Register value */
@@ -310,22 +310,40 @@ struct lm85_data {
struct lm85_zone zone[3];
};
-static int lm85_attach_adapter(struct i2c_adapter *adapter);
-static int lm85_detect(struct i2c_adapter *adapter, int address,
- int kind);
-static int lm85_detach_client(struct i2c_client *client);
+static int lm85_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info);
+static int lm85_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int lm85_remove(struct i2c_client *client);
static int lm85_read_value(struct i2c_client *client, u8 reg);
static void lm85_write_value(struct i2c_client *client, u8 reg, int value);
static struct lm85_data *lm85_update_device(struct device *dev);
+static const struct i2c_device_id lm85_id[] = {
+ { "adm1027", adm1027 },
+ { "adt7463", adt7463 },
+ { "lm85", any_chip },
+ { "lm85b", lm85b },
+ { "lm85c", lm85c },
+ { "emc6d100", emc6d100 },
+ { "emc6d101", emc6d100 },
+ { "emc6d102", emc6d102 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm85_id);
+
static struct i2c_driver lm85_driver = {
+ .class = I2C_CLASS_HWMON,
.driver = {
.name = "lm85",
},
- .attach_adapter = lm85_attach_adapter,
- .detach_client = lm85_detach_client,
+ .probe = lm85_probe,
+ .remove = lm85_remove,
+ .id_table = lm85_id,
+ .detect = lm85_detect,
+ .address_data = &addr_data,
};
@@ -528,11 +546,39 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
return count;
}
+static ssize_t show_pwm_freq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map,
+ data->pwm_freq[nr]));
+}
+
+static ssize_t set_pwm_freq(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm85_data *data = i2c_get_clientdata(client);
+ long val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val);
+ lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+ (data->zone[nr].range << 4)
+ | data->pwm_freq[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
#define show_pwm_reg(offset) \
static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
show_pwm, set_pwm, offset - 1); \
static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
- show_pwm_enable, set_pwm_enable, offset - 1)
+ show_pwm_enable, set_pwm_enable, offset - 1); \
+static SENSOR_DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR, \
+ show_pwm_freq, set_pwm_freq, offset - 1)
show_pwm_reg(1);
show_pwm_reg(2);
@@ -761,31 +807,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev,
return count;
}
-static ssize_t show_pwm_auto_pwm_freq(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int nr = to_sensor_dev_attr(attr)->index;
- struct lm85_data *data = lm85_update_device(dev);
- return sprintf(buf, "%d\n", FREQ_FROM_REG(data->autofan[nr].freq));
-}
-
-static ssize_t set_pwm_auto_pwm_freq(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int nr = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct lm85_data *data = i2c_get_clientdata(client);
- long val = simple_strtol(buf, NULL, 10);
-
- mutex_lock(&data->update_lock);
- data->autofan[nr].freq = FREQ_TO_REG(val);
- lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
- (data->zone[nr].range << 4)
- | data->autofan[nr].freq);
- mutex_unlock(&data->update_lock);
- return count;
-}
-
#define pwm_auto(offset) \
static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \
S_IRUGO | S_IWUSR, show_pwm_auto_channels, \
@@ -795,10 +816,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \
set_pwm_auto_pwm_min, offset - 1); \
static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \
S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \
- set_pwm_auto_pwm_minctl, offset - 1); \
-static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_freq, \
- S_IRUGO | S_IWUSR, show_pwm_auto_pwm_freq, \
- set_pwm_auto_pwm_freq, offset - 1);
+ set_pwm_auto_pwm_minctl, offset - 1)
pwm_auto(1);
pwm_auto(2);
@@ -867,7 +885,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev,
TEMP_FROM_REG(data->zone[nr].limit));
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4)
- | (data->autofan[nr].freq & 0x07));
+ | (data->pwm_freq[nr] & 0x07));
/* Update temp_auto_hyst and temp_auto_off */
data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG(
@@ -910,7 +928,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev,
val - min);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4)
- | (data->autofan[nr].freq & 0x07));
+ | (data->pwm_freq[nr] & 0x07));
mutex_unlock(&data->update_lock);
return count;
}
@@ -957,13 +975,6 @@ temp_auto(1);
temp_auto(2);
temp_auto(3);
-static int lm85_attach_adapter(struct i2c_adapter *adapter)
-{
- if (!(adapter->class & I2C_CLASS_HWMON))
- return 0;
- return i2c_probe(adapter, &addr_data, lm85_detect);
-}
-
static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
@@ -984,6 +995,9 @@ static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
@@ -1026,9 +1040,6 @@ static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr,
- &sensor_dev_attr_pwm1_auto_pwm_freq.dev_attr.attr,
- &sensor_dev_attr_pwm2_auto_pwm_freq.dev_attr.attr,
- &sensor_dev_attr_pwm3_auto_pwm_freq.dev_attr.attr,
&sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr,
@@ -1103,109 +1114,74 @@ static void lm85_init_client(struct i2c_client *client)
dev_warn(&client->dev, "Device is not ready\n");
}
-static int lm85_detect(struct i2c_adapter *adapter, int address,
- int kind)
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm85_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
{
- int company, verstep;
- struct i2c_client *client;
- struct lm85_data *data;
- int err = 0;
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
const char *type_name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
/* We need to be able to do byte I/O */
- goto ERROR0;
+ return -ENODEV;
}
- /* OK. For now, we presume we have a valid client. We now create the
- client structure, even though we cannot fill it completely yet.
- But it allows us to access lm85_{read,write}_value. */
+ /* If auto-detecting, determine the chip type */
+ if (kind < 0) {
+ int company = lm85_read_value(client, LM85_REG_COMPANY);
+ int verstep = lm85_read_value(client, LM85_REG_VERSTEP);
- if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) {
- err = -ENOMEM;
- goto ERROR0;
- }
+ dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
+ "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+ address, company, verstep);
- client = &data->client;
- i2c_set_clientdata(client, data);
- client->addr = address;
- client->adapter = adapter;
- client->driver = &lm85_driver;
-
- /* Now, we do the remaining detection. */
-
- company = lm85_read_value(client, LM85_REG_COMPANY);
- verstep = lm85_read_value(client, LM85_REG_VERSTEP);
-
- dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with"
- " COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
- i2c_adapter_id(client->adapter), client->addr,
- company, verstep);
-
- /* If auto-detecting, Determine the chip type. */
- if (kind <= 0) {
- dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x ...\n",
- i2c_adapter_id(adapter), address);
- if (company == LM85_COMPANY_NATIONAL
- && verstep == LM85_VERSTEP_LM85C) {
- kind = lm85c;
- } else if (company == LM85_COMPANY_NATIONAL
- && verstep == LM85_VERSTEP_LM85B) {
- kind = lm85b;
- } else if (company == LM85_COMPANY_NATIONAL
- && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x"
- " Defaulting to LM85.\n", verstep);
- kind = any_chip;
- } else if (company == LM85_COMPANY_ANALOG_DEV
- && verstep == LM85_VERSTEP_ADM1027) {
- kind = adm1027;
- } else if (company == LM85_COMPANY_ANALOG_DEV
- && (verstep == LM85_VERSTEP_ADT7463
- || verstep == LM85_VERSTEP_ADT7463C)) {
- kind = adt7463;
- } else if (company == LM85_COMPANY_ANALOG_DEV
- && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x"
- " Defaulting to Generic LM85.\n", verstep);
- kind = any_chip;
- } else if (company == LM85_COMPANY_SMSC
- && (verstep == LM85_VERSTEP_EMC6D100_A0
- || verstep == LM85_VERSTEP_EMC6D100_A1)) {
- /* Unfortunately, we can't tell a '100 from a '101
- * from the registers. Since a '101 is a '100
- * in a package with fewer pins and therefore no
- * 3.3V, 1.5V or 1.8V inputs, perhaps if those
- * inputs read 0, then it's a '101.
- */
- kind = emc6d100;
- } else if (company == LM85_COMPANY_SMSC
- && verstep == LM85_VERSTEP_EMC6D102) {
- kind = emc6d102;
- } else if (company == LM85_COMPANY_SMSC
- && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "lm85: Detected SMSC chip\n");
- dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x"
- " Defaulting to Generic LM85.\n", verstep);
- kind = any_chip;
- } else if (kind == any_chip
- && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n");
- /* Leave kind as "any_chip" */
- } else {
- dev_dbg(&adapter->dev, "Autodetection failed\n");
- /* Not an LM85... */
- if (kind == any_chip) { /* User used force=x,y */
- dev_err(&adapter->dev, "Generic LM85 Version 6 not"
- " found at %d,0x%02x. Try force_lm85c.\n",
- i2c_adapter_id(adapter), address);
+ /* All supported chips have the version in common */
+ if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) {
+ dev_dbg(&adapter->dev, "Autodetection failed: "
+ "unsupported version\n");
+ return -ENODEV;
+ }
+ kind = any_chip;
+
+ /* Now, refine the detection */
+ if (company == LM85_COMPANY_NATIONAL) {
+ switch (verstep) {
+ case LM85_VERSTEP_LM85C:
+ kind = lm85c;
+ break;
+ case LM85_VERSTEP_LM85B:
+ kind = lm85b;
+ break;
+ }
+ } else if (company == LM85_COMPANY_ANALOG_DEV) {
+ switch (verstep) {
+ case LM85_VERSTEP_ADM1027:
+ kind = adm1027;
+ break;
+ case LM85_VERSTEP_ADT7463:
+ case LM85_VERSTEP_ADT7463C:
+ kind = adt7463;
+ break;
+ }
+ } else if (company == LM85_COMPANY_SMSC) {
+ switch (verstep) {
+ case LM85_VERSTEP_EMC6D100_A0:
+ case LM85_VERSTEP_EMC6D100_A1:
+ /* Note: we can't tell a '100 from a '101 */
+ kind = emc6d100;
+ break;
+ case LM85_VERSTEP_EMC6D102:
+ kind = emc6d102;
+ break;
}
- err = 0;
- goto ERROR1;
+ } else {
+ dev_dbg(&adapter->dev, "Autodetection failed: "
+ "unknown vendor\n");
+ return -ENODEV;
}
}
- /* Fill in the chip specific driver values */
switch (kind) {
case lm85b:
type_name = "lm85b";
@@ -1228,16 +1204,36 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
default:
type_name = "lm85";
}
- strlcpy(client->name, type_name, I2C_NAME_SIZE);
+ strlcpy(info->type, type_name, I2C_NAME_SIZE);
+
+ return 0;
+}
- /* Fill in the remaining client fields */
- data->type = kind;
+static int lm85_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lm85_data *data;
+ int err;
+
+ data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->type = id->driver_data;
mutex_init(&data->update_lock);
- /* Tell the I2C layer a new client has arrived */
- err = i2c_attach_client(client);
- if (err)
- goto ERROR1;
+ /* Fill in the chip specific driver values */
+ switch (data->type) {
+ case adm1027:
+ case adt7463:
+ case emc6d100:
+ case emc6d102:
+ data->freq_map = adm1027_freq_map;
+ break;
+ default:
+ data->freq_map = lm85_freq_map;
+ }
/* Set the VRM version */
data->vrm = vid_which_vrm();
@@ -1248,45 +1244,42 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &lm85_group);
if (err)
- goto ERROR2;
+ goto err_kfree;
/* The ADT7463 has an optional VRM 10 mode where pin 21 is used
as a sixth digital VID input rather than an analog input. */
data->vid = lm85_read_value(client, LM85_REG_VID);
- if (!(kind == adt7463 && (data->vid & 0x80)))
+ if (!(data->type == adt7463 && (data->vid & 0x80)))
if ((err = sysfs_create_group(&client->dev.kobj,
&lm85_group_in4)))
- goto ERROR3;
+ goto err_remove_files;
/* The EMC6D100 has 3 additional voltage inputs */
- if (kind == emc6d100)
+ if (data->type == emc6d100)
if ((err = sysfs_create_group(&client->dev.kobj,
&lm85_group_in567)))
- goto ERROR3;
+ goto err_remove_files;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
- goto ERROR3;
+ goto err_remove_files;
}
return 0;
/* Error out and cleanup code */
- ERROR3:
+ err_remove_files:
sysfs_remove_group(&client->dev.kobj, &lm85_group);
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
- if (kind == emc6d100)
+ if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
- ERROR2:
- i2c_detach_client(client);
- ERROR1:
+ err_kfree:
kfree(data);
- ERROR0:
return err;
}
-static int lm85_detach_client(struct i2c_client *client)
+static int lm85_remove(struct i2c_client *client)
{
struct lm85_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
@@ -1294,7 +1287,6 @@ static int lm85_detach_client(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
- i2c_detach_client(client);
kfree(data);
return 0;
}
@@ -1481,7 +1473,7 @@ static struct lm85_data *lm85_update_device(struct device *dev)
data->autofan[i].config =
lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
- data->autofan[i].freq = val & 0x07;
+ data->pwm_freq[i] = val & 0x07;
data->zone[i].range = val >> 4;
data->autofan[i].min_pwm =
lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 21970f0d53a..2e4a3cea95f 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -21,11 +21,10 @@
* http://www.national.com/pf/LM/LM87.html
*
* Some functions share pins, so not all functions are available at the same
- * time. Which are depends on the hardware setup. This driver assumes that
- * the BIOS configured the chip correctly. In that respect, it differs from
- * the original driver (from lm_sensors for Linux 2.4), which would force the
- * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual
- * chipset wiring.
+ * time. Which are depends on the hardware setup. This driver normally
+ * assumes that firmware configured the chip correctly. Where this is not
+ * the case, platform code must set the I2C client's platform_data to point
+ * to a u8 value to be written to the channel register.
* For reference, here is the list of exclusive functions:
* - in0+in5 (default) or temp3
* - fan1 (default) or in6
@@ -199,6 +198,7 @@ struct lm87_data {
unsigned long last_updated; /* In jiffies */
u8 channel; /* register value */
+ u8 config; /* original register value */
u8 in[8]; /* register value */
u8 in_max[8]; /* register value */
@@ -832,6 +832,7 @@ exit_remove:
sysfs_remove_group(&new_client->dev.kobj, &lm87_group);
sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt);
exit_free:
+ lm87_write_value(new_client, LM87_REG_CONFIG, data->config);
kfree(data);
exit:
return err;
@@ -840,12 +841,17 @@ exit:
static void lm87_init_client(struct i2c_client *client)
{
struct lm87_data *data = i2c_get_clientdata(client);
- u8 config;
- data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
+ if (client->dev.platform_data) {
+ data->channel = *(u8 *)client->dev.platform_data;
+ lm87_write_value(client,
+ LM87_REG_CHANNEL_MODE, data->channel);
+ } else {
+ data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
+ }
+ data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F;
- config = lm87_read_value(client, LM87_REG_CONFIG);
- if (!(config & 0x01)) {
+ if (!(data->config & 0x01)) {
int i;
/* Limits are left uninitialized after power-up */
@@ -867,11 +873,11 @@ static void lm87_init_client(struct i2c_client *client)
lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
}
}
- if ((config & 0x81) != 0x01) {
- /* Start monitoring */
+
+ /* Make sure Start is set and INT#_Clear is clear */
+ if ((data->config & 0x09) != 0x01)
lm87_write_value(client, LM87_REG_CONFIG,
- (config & 0xF7) | 0x01);
- }
+ (data->config & 0x77) | 0x01);
}
static int lm87_remove(struct i2c_client *client)
@@ -882,6 +888,7 @@ static int lm87_remove(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj, &lm87_group);
sysfs_remove_group(&client->dev.kobj, &lm87_group_opt);
+ lm87_write_value(client, LM87_REG_CONFIG, data->config);
kfree(data);
return 0;
}
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index c24fe36ac78..3edeebc0b83 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1,14 +1,12 @@
/*
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm83 driver. The LM90 is a sensor chip made by National
* Semiconductor. It reports up to two temperatures (its own plus up to
* one external one) with a 0.125 deg resolution (1 deg for local
- * temperature) and a 3-4 deg accuracy. Complete datasheet can be
- * obtained from National's website at:
- * http://www.national.com/pf/LM/LM90.html
+ * temperature) and a 3-4 deg accuracy.
*
* This driver also supports the LM89 and LM99, two other sensor chips
* made by National Semiconductor. Both have an increased remote
@@ -16,48 +14,38 @@
* additionally shifts remote temperatures (measured and limits) by 16
* degrees, which allows for higher temperatures measurement. The
* driver doesn't handle it since it can be done easily in user-space.
- * Complete datasheets can be obtained from National's website at:
- * http://www.national.com/pf/LM/LM89.html
- * http://www.national.com/pf/LM/LM99.html
* Note that there is no way to differentiate between both chips.
*
* This driver also supports the LM86, another sensor chip made by
* National Semiconductor. It is exactly similar to the LM90 except it
* has a higher accuracy.
- * Complete datasheet can be obtained from National's website at:
- * http://www.national.com/pf/LM/LM86.html
*
* This driver also supports the ADM1032, a sensor chip made by Analog
* Devices. That chip is similar to the LM90, with a few differences
- * that are not handled by this driver. Complete datasheet can be
- * obtained from Analog's website at:
- * http://www.analog.com/en/prod/0,2877,ADM1032,00.html
- * Among others, it has a higher accuracy than the LM90, much like the
- * LM86 does.
+ * that are not handled by this driver. Among others, it has a higher
+ * accuracy than the LM90, much like the LM86 does.
*
* This driver also supports the MAX6657, MAX6658 and MAX6659 sensor
- * chips made by Maxim. These chips are similar to the LM86. Complete
- * datasheet can be obtained at Maxim's website at:
- * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
+ * chips made by Maxim. These chips are similar to the LM86.
* Note that there is no easy way to differentiate between the three
* variants. The extra address and features of the MAX6659 are not
* supported by this driver. These chips lack the remote temperature
* offset feature.
*
+ * This driver also supports the MAX6646, MAX6647 and MAX6649 chips
+ * made by Maxim. These are again similar to the LM86, but they use
+ * unsigned temperature values and can report temperatures from 0 to
+ * 145 degrees.
+ *
* This driver also supports the MAX6680 and MAX6681, two other sensor
* chips made by Maxim. These are quite similar to the other Maxim
- * chips. Complete datasheet can be obtained at:
- * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370
- * The MAX6680 and MAX6681 only differ in the pinout so they can be
- * treated identically.
+ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can
+ * be treated identically.
*
- * This driver also supports the ADT7461 chip from Analog Devices but
- * only in its "compatability mode". If an ADT7461 chip is found but
- * is configured in non-compatible mode (where its temperature
- * register values are decoded differently) it is ignored by this
- * driver. Complete datasheet can be obtained from Analog's website
- * at:
- * http://www.analog.com/en/prod/0,2877,ADT7461,00.html
+ * This driver also supports the ADT7461 chip from Analog Devices.
+ * It's supported in both compatibility and extended mode. It is mostly
+ * compatible with LM90 except for a data format difference for the
+ * temperature value registers.
*
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
@@ -93,9 +81,10 @@
* Addresses to scan
* Address is fully defined internally and cannot be changed except for
* MAX6659, MAX6680 and MAX6681.
- * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658
- * have address 0x4c.
- * ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d.
+ * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657
+ * and MAX6658 have address 0x4c.
+ * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d.
+ * MAX6647 has address 0x4e.
* MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported).
* MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
* 0x4c, 0x4d or 0x4e.
@@ -108,7 +97,8 @@ static const unsigned short normal_i2c[] = {
* Insmod parameters
*/
-I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
+I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680,
+ max6646);
/*
* The LM90 registers
@@ -149,39 +139,14 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
#define LM90_REG_R_TCRIT_HYST 0x21
#define LM90_REG_W_TCRIT_HYST 0x21
-/*
- * Conversions and various macros
- * For local temperatures and limits, critical limits and the hysteresis
- * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
- * For remote temperatures and limits, it uses signed 11-bit values with
- * LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
- */
+/* MAX6646/6647/6649/6657/6658/6659 registers */
-#define TEMP1_FROM_REG(val) ((val) * 1000)
-#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
- (val) >= 127000 ? 127 : \
- (val) < 0 ? ((val) - 500) / 1000 : \
- ((val) + 500) / 1000)
-#define TEMP2_FROM_REG(val) ((val) / 32 * 125)
-#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
- (val) >= 127875 ? 0x7FE0 : \
- (val) < 0 ? ((val) - 62) / 125 * 32 : \
- ((val) + 62) / 125 * 32)
-#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \
- ((val) + 500) / 1000)
-
-/*
- * ADT7461 is almost identical to LM90 except that attempts to write
- * values that are outside the range 0 < temp < 127 are treated as
- * the boundary value.
- */
+#define MAX6657_REG_R_LOCAL_TEMPL 0x11
-#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
- (val) >= 127000 ? 127 : \
- ((val) + 500) / 1000)
-#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
- (val) >= 127750 ? 0x7FC0 : \
- ((val) + 125) / 250 * 64)
+/*
+ * Device flags
+ */
+#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */
/*
* Functions declaration
@@ -206,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = {
{ "lm86", lm86 },
{ "lm89", lm99 },
{ "lm99", lm99 }, /* Missing temperature offset */
+ { "max6646", max6646 },
+ { "max6647", max6646 },
+ { "max6649", max6646 },
{ "max6657", max6657 },
{ "max6658", max6657 },
{ "max6659", max6657 },
@@ -237,22 +205,150 @@ struct lm90_data {
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
int kind;
+ int flags;
/* registers values */
- s8 temp8[5]; /* 0: local input
- 1: local low limit
- 2: local high limit
- 3: local critical limit
- 4: remote critical limit */
- s16 temp11[4]; /* 0: remote input
+ s8 temp8[4]; /* 0: local low limit
+ 1: local high limit
+ 2: local critical limit
+ 3: remote critical limit */
+ s16 temp11[5]; /* 0: remote input
1: remote low limit
2: remote high limit
- 3: remote offset (except max6657) */
+ 3: remote offset (except max6646 and max6657)
+ 4: local input */
u8 temp_hyst;
u8 alarms; /* bitvector */
};
/*
+ * Conversions
+ * For local temperatures and limits, critical limits and the hysteresis
+ * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
+ * For remote temperatures and limits, it uses signed 11-bit values with
+ * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some
+ * Maxim chips use unsigned values.
+ */
+
+static inline int temp_from_s8(s8 val)
+{
+ return val * 1000;
+}
+
+static inline int temp_from_u8(u8 val)
+{
+ return val * 1000;
+}
+
+static inline int temp_from_s16(s16 val)
+{
+ return val / 32 * 125;
+}
+
+static inline int temp_from_u16(u16 val)
+{
+ return val / 32 * 125;
+}
+
+static s8 temp_to_s8(long val)
+{
+ if (val <= -128000)
+ return -128;
+ if (val >= 127000)
+ return 127;
+ if (val < 0)
+ return (val - 500) / 1000;
+ return (val + 500) / 1000;
+}
+
+static u8 temp_to_u8(long val)
+{
+ if (val <= 0)
+ return 0;
+ if (val >= 255000)
+ return 255;
+ return (val + 500) / 1000;
+}
+
+static s16 temp_to_s16(long val)
+{
+ if (val <= -128000)
+ return 0x8000;
+ if (val >= 127875)
+ return 0x7FE0;
+ if (val < 0)
+ return (val - 62) / 125 * 32;
+ return (val + 62) / 125 * 32;
+}
+
+static u8 hyst_to_reg(long val)
+{
+ if (val <= 0)
+ return 0;
+ if (val >= 30500)
+ return 31;
+ return (val + 500) / 1000;
+}
+
+/*
+ * ADT7461 in compatibility mode is almost identical to LM90 except that
+ * attempts to write values that are outside the range 0 < temp < 127 are
+ * treated as the boundary value.
+ *
+ * ADT7461 in "extended mode" operation uses unsigned integers offset by
+ * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC.
+ */
+static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val)
+{
+ if (data->flags & LM90_FLAG_ADT7461_EXT)
+ return (val - 64) * 1000;
+ else
+ return temp_from_s8(val);
+}
+
+static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val)
+{
+ if (data->flags & LM90_FLAG_ADT7461_EXT)
+ return (val - 0x4000) / 64 * 250;
+ else
+ return temp_from_s16(val);
+}
+
+static u8 temp_to_u8_adt7461(struct lm90_data *data, long val)
+{
+ if (data->flags & LM90_FLAG_ADT7461_EXT) {
+ if (val <= -64000)
+ return 0;
+ if (val >= 191000)
+ return 0xFF;
+ return (val + 500 + 64000) / 1000;
+ } else {
+ if (val <= 0)
+ return 0;
+ if (val >= 127000)
+ return 127;
+ return (val + 500) / 1000;
+ }
+}
+
+static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
+{
+ if (data->flags & LM90_FLAG_ADT7461_EXT) {
+ if (val <= -64000)
+ return 0;
+ if (val >= 191750)
+ return 0xFFC0;
+ return (val + 64000 + 125) / 250 * 64;
+ } else {
+ if (val <= 0)
+ return 0;
+ if (val >= 127750)
+ return 0x7FC0;
+ return (val + 125) / 250 * 64;
+ }
+}
+
+/*
* Sysfs stuff
*/
@@ -261,7 +357,16 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev);
- return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]));
+ int temp;
+
+ if (data->kind == adt7461)
+ temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
+ else if (data->kind == max6646)
+ temp = temp_from_u8(data->temp8[attr->index]);
+ else
+ temp = temp_from_s8(data->temp8[attr->index]);
+
+ return sprintf(buf, "%d\n", temp);
}
static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
@@ -282,10 +387,12 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->update_lock);
if (data->kind == adt7461)
- data->temp8[nr] = TEMP1_TO_REG_ADT7461(val);
+ data->temp8[nr] = temp_to_u8_adt7461(data, val);
+ else if (data->kind == max6646)
+ data->temp8[nr] = temp_to_u8(val);
else
- data->temp8[nr] = TEMP1_TO_REG(val);
- i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]);
+ data->temp8[nr] = temp_to_s8(val);
+ i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]);
mutex_unlock(&data->update_lock);
return count;
}
@@ -295,7 +402,16 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev);
- return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index]));
+ int temp;
+
+ if (data->kind == adt7461)
+ temp = temp_from_u16_adt7461(data, data->temp11[attr->index]);
+ else if (data->kind == max6646)
+ temp = temp_from_u16(data->temp11[attr->index]);
+ else
+ temp = temp_from_s16(data->temp11[attr->index]);
+
+ return sprintf(buf, "%d\n", temp);
}
static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
@@ -318,13 +434,20 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->update_lock);
if (data->kind == adt7461)
- data->temp11[nr] = TEMP2_TO_REG_ADT7461(val);
+ data->temp11[nr] = temp_to_u16_adt7461(data, val);
+ else if (data->kind == max6657 || data->kind == max6680)
+ data->temp11[nr] = temp_to_s8(val) << 8;
+ else if (data->kind == max6646)
+ data->temp11[nr] = temp_to_u8(val) << 8;
else
- data->temp11[nr] = TEMP2_TO_REG(val);
+ data->temp11[nr] = temp_to_s16(val);
+
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
data->temp11[nr] >> 8);
- i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
- data->temp11[nr] & 0xff);
+ if (data->kind != max6657 && data->kind != max6680
+ && data->kind != max6646)
+ i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
+ data->temp11[nr] & 0xff);
mutex_unlock(&data->update_lock);
return count;
}
@@ -334,8 +457,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev);
- return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])
- - TEMP1_FROM_REG(data->temp_hyst));
+ int temp;
+
+ if (data->kind == adt7461)
+ temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
+ else
+ temp = temp_from_s8(data->temp8[attr->index]);
+
+ return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst));
}
static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
@@ -347,9 +476,9 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
long hyst;
mutex_lock(&data->update_lock);
- hyst = TEMP1_FROM_REG(data->temp8[3]) - val;
+ hyst = temp_from_s8(data->temp8[2]) - val;
i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
- HYST_TO_REG(hyst));
+ hyst_to_reg(hyst));
mutex_unlock(&data->update_lock);
return count;
}
@@ -371,23 +500,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, 1);
+ set_temp8, 0);
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 1);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, 2);
+ set_temp8, 1);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 2);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, 3);
+ set_temp8, 2);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, 4);
+ set_temp8, 3);
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
- set_temphyst, 3);
-static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4);
+ set_temphyst, 2);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3);
static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 3);
@@ -568,7 +697,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
kind = adm1032;
} else
if (chip_id == 0x51 /* ADT7461 */
- && (reg_config1 & 0x1F) == 0x00 /* check compat mode */
+ && (reg_config1 & 0x1B) == 0x00
&& reg_convrate <= 0x0A) {
kind = adt7461;
}
@@ -599,13 +728,23 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
&& (reg_config1 & 0x03) == 0x00
&& reg_convrate <= 0x07) {
kind = max6680;
+ } else
+ /* The chip_id register of the MAX6646/6647/6649
+ * holds the revision of the chip.
+ * The lowest 6 bits of the config1 register are
+ * unused and should return zero when read.
+ */
+ if (chip_id == 0x59
+ && (reg_config1 & 0x3f) == 0x00
+ && reg_convrate <= 0x07) {
+ kind = max6646;
}
}
if (kind <= 0) { /* identification failed */
- dev_info(&adapter->dev,
- "Unsupported chip (man_id=0x%02X, "
- "chip_id=0x%02X).\n", man_id, chip_id);
+ dev_dbg(&adapter->dev,
+ "Unsupported chip at 0x%02x (man_id=0x%02X, "
+ "chip_id=0x%02X)\n", address, man_id, chip_id);
return -ENODEV;
}
}
@@ -629,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
name = "max6680";
} else if (kind == adt7461) {
name = "adt7461";
+ } else if (kind == max6646) {
+ name = "max6646";
}
strlcpy(info->type, name, I2C_NAME_SIZE);
@@ -668,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client,
&dev_attr_pec)))
goto exit_remove_files;
}
- if (data->kind != max6657) {
+ if (data->kind != max6657 && data->kind != max6646) {
if ((err = device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_offset.dev_attr)))
goto exit_remove_files;
@@ -707,6 +848,12 @@ static void lm90_init_client(struct i2c_client *client)
}
config_orig = config;
+ /* Check Temperature Range Select */
+ if (data->kind == adt7461) {
+ if (config & 0x04)
+ data->flags |= LM90_FLAG_ADT7461_EXT;
+ }
+
/*
* Put MAX6680/MAX8881 into extended resolution (bit 0x10,
* 0.125 degree resolution) and range (0x08, extend range
@@ -728,7 +875,7 @@ static int lm90_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm90_group);
device_remove_file(&client->dev, &dev_attr_pec);
- if (data->kind != max6657)
+ if (data->kind != max6657 && data->kind != max6646)
device_remove_file(&client->dev,
&sensor_dev_attr_temp2_offset.dev_attr);
@@ -736,6 +883,38 @@ static int lm90_remove(struct i2c_client *client)
return 0;
}
+static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
+{
+ int err;
+ u8 oldh, newh, l;
+
+ /*
+ * There is a trick here. We have to read two registers to have the
+ * sensor temperature, but we have to beware a conversion could occur
+ * inbetween the readings. The datasheet says we should either use
+ * the one-shot conversion register, which we don't want to do
+ * (disables hardware monitoring) or monitor the busy bit, which is
+ * impossible (we can't read the values and monitor that bit at the
+ * exact same time). So the solution used here is to read the high
+ * byte once, then the low byte, then the high byte again. If the new
+ * high byte matches the old one, then we have a valid reading. Else
+ * we have to read the low byte again, and now we believe we have a
+ * correct reading.
+ */
+ if ((err = lm90_read_reg(client, regh, &oldh))
+ || (err = lm90_read_reg(client, regl, &l))
+ || (err = lm90_read_reg(client, regh, &newh)))
+ return err;
+ if (oldh != newh) {
+ err = lm90_read_reg(client, regl, &l);
+ if (err)
+ return err;
+ }
+ *value = (newh << 8) | l;
+
+ return 0;
+}
+
static struct lm90_data *lm90_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -744,49 +923,50 @@ static struct lm90_data *lm90_update_device(struct device *dev)
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
- u8 oldh, newh, l;
+ u8 h, l;
dev_dbg(&client->dev, "Updating lm90 data.\n");
- lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]);
- lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[1]);
- lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[2]);
- lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[3]);
- lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]);
+ lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]);
+ lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]);
+ lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]);
+ lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);
lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
- /*
- * There is a trick here. We have to read two registers to
- * have the remote sensor temperature, but we have to beware
- * a conversion could occur inbetween the readings. The
- * datasheet says we should either use the one-shot
- * conversion register, which we don't want to do (disables
- * hardware monitoring) or monitor the busy bit, which is
- * impossible (we can't read the values and monitor that bit
- * at the exact same time). So the solution used here is to
- * read the high byte once, then the low byte, then the high
- * byte again. If the new high byte matches the old one,
- * then we have a valid reading. Else we have to read the low
- * byte again, and now we believe we have a correct reading.
- */
- if (lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &oldh) == 0
- && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0
- && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &newh) == 0
- && (newh == oldh
- || lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0))
- data->temp11[0] = (newh << 8) | l;
-
- if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &newh) == 0
- && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0)
- data->temp11[1] = (newh << 8) | l;
- if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &newh) == 0
- && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0)
- data->temp11[2] = (newh << 8) | l;
- if (data->kind != max6657) {
+ if (data->kind == max6657 || data->kind == max6646) {
+ lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
+ MAX6657_REG_R_LOCAL_TEMPL,
+ &data->temp11[4]);
+ } else {
+ if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
+ &h) == 0)
+ data->temp11[4] = h << 8;
+ }
+ lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
+ LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]);
+
+ if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) {
+ data->temp11[1] = h << 8;
+ if (data->kind != max6657 && data->kind != max6680
+ && data->kind != max6646
+ && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL,
+ &l) == 0)
+ data->temp11[1] |= l;
+ }
+ if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) {
+ data->temp11[2] = h << 8;
+ if (data->kind != max6657 && data->kind != max6680
+ && data->kind != max6646
+ && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL,
+ &l) == 0)
+ data->temp11[2] |= l;
+ }
+
+ if (data->kind != max6657 && data->kind != max6646) {
if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH,
- &newh) == 0
+ &h) == 0
&& lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL,
&l) == 0)
- data->temp11[3] = (newh << 8) | l;
+ data->temp11[3] = (h << 8) | l;
}
lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms);
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index 1ab1cacad59..7897754f3a5 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -69,11 +69,18 @@ I2C_CLIENT_INSMOD_1(max1619);
#define MAX1619_REG_W_TCRIT_HYST 0x13
/*
- * Conversions and various macros
+ * Conversions
*/
-#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000)
-#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000)
+static int temp_from_reg(int val)
+{
+ return (val & 0x80 ? val-0x100 : val) * 1000;
+}
+
+static int temp_to_reg(int val)
+{
+ return (val < 0 ? val+0x100*1000 : val) / 1000;
+}
/*
* Functions declaration
@@ -135,7 +142,7 @@ struct max1619_data {
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct max1619_data *data = max1619_update_device(dev); \
- return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
+ return sprintf(buf, "%d\n", temp_from_reg(data->value)); \
}
show_temp(temp_input1);
show_temp(temp_input2);
@@ -153,7 +160,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co
long val = simple_strtol(buf, NULL, 10); \
\
mutex_lock(&data->update_lock); \
- data->value = TEMP_TO_REG(val); \
+ data->value = temp_to_reg(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \
mutex_unlock(&data->update_lock); \
return count; \
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index f942ecdd47c..d4d1b859d4f 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -4,7 +4,7 @@
Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
and Mark Studebaker <mdsxyz123@yahoo.com>
- Copyright (c) 2007 Jean Delvare <khali@linux-fr.org>
+ Copyright (c) 2007 - 2008 Jean Delvare <khali@linux-fr.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,25 +38,24 @@
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <linux/ioport.h>
#include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
#include <linux/hwmon-sysfs.h>
#include <linux/sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
+
+#ifdef CONFIG_ISA
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
#include <asm/io.h>
-#include "lm75.h"
+#endif
-/* ISA device, if found */
-static struct platform_device *pdev;
+#include "lm75.h"
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, I2C_CLIENT_END };
-static unsigned short isa_address = 0x290;
-
/* Insmod parameters */
I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f);
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
@@ -178,9 +177,9 @@ FAN_FROM_REG(u8 val, int div)
#define TEMP_FROM_REG(val) ((val) * 1000)
#define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \
- (val) ^ 0x7fff : (val))
+ (~(val)) & 0x7fff : (val) & 0xff7fff)
#define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \
- (~(val)) & 0x7fff : (val) & 0xffffff)
+ (~(val)) & 0x7fff : (val) & 0xff7fff)
#define DIV_FROM_REG(val) (1 << (val))
@@ -199,25 +198,16 @@ DIV_TO_REG(long val, enum chips type)
return i;
}
-/* There are some complications in a module like this. First off, W83781D chips
- may be both present on the SMBus and the ISA bus, and we have to handle
- those cases separately at some places. Second, there might be several
- W83781D chips available (well, actually, that is probably never done; but
- it is a clean illustration of how to handle a case like that). Finally,
- a specific chip may be attached to *both* ISA and SMBus, and we would
- not like to detect it double. Fortunately, in the case of the W83781D at
- least, a register tells us what SMBus address we are on, so that helps
- a bit - except if there could be more than one SMBus. Groan. No solution
- for this yet. */
-
-/* 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 i2c_client *client;
struct device *hwmon_dev;
struct mutex lock;
enum chips type;
+ /* For ISA device only */
+ const char *name;
+ int isa_addr;
+
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
@@ -240,7 +230,6 @@ struct w83781d_data {
u8 vid; /* Register encoding, combined */
u32 alarms; /* Register encoding, combined */
u32 beep_mask; /* Register encoding, combined */
- u8 beep_enable; /* Boolean */
u8 pwm[4]; /* Register value */
u8 pwm2_enable; /* Boolean */
u16 sens[3]; /* 782D/783S only.
@@ -249,36 +238,14 @@ struct w83781d_data {
u8 vrm;
};
-static int w83781d_attach_adapter(struct i2c_adapter *adapter);
-static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind);
-static int w83781d_detach_client(struct i2c_client *client);
-
-static int __devinit w83781d_isa_probe(struct platform_device *pdev);
-static int __devexit w83781d_isa_remove(struct platform_device *pdev);
+static struct w83781d_data *w83781d_data_if_isa(void);
+static int w83781d_alias_detect(struct i2c_client *client, u8 chipid);
static int w83781d_read_value(struct w83781d_data *data, u16 reg);
static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value);
static struct w83781d_data *w83781d_update_device(struct device *dev);
static void w83781d_init_device(struct device *dev);
-static struct i2c_driver w83781d_driver = {
- .driver = {
- .name = "w83781d",
- },
- .attach_adapter = w83781d_attach_adapter,
- .detach_client = w83781d_detach_client,
-};
-
-static struct platform_driver w83781d_isa_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "w83781d",
- },
- .probe = w83781d_isa_probe,
- .remove = w83781d_isa_remove,
-};
-
-
/* following are the sysfs callback functions */
#define show_in_reg(reg) \
static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \
@@ -513,11 +480,6 @@ static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr
return sprintf(buf, "%ld\n",
(long)BEEP_MASK_FROM_REG(data->beep_mask, data->type));
}
-static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct w83781d_data *data = w83781d_update_device(dev);
- return sprintf(buf, "%ld\n", (long)data->beep_enable);
-}
static ssize_t
store_beep_mask(struct device *dev, struct device_attribute *attr,
@@ -529,12 +491,12 @@ store_beep_mask(struct device *dev, struct device_attribute *attr,
val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
- data->beep_mask = BEEP_MASK_TO_REG(val, data->type);
+ data->beep_mask &= 0x8000; /* preserve beep enable */
+ data->beep_mask |= BEEP_MASK_TO_REG(val, data->type);
w83781d_write_value(data, W83781D_REG_BEEP_INTS1,
data->beep_mask & 0xff);
w83781d_write_value(data, W83781D_REG_BEEP_INTS2,
- ((data->beep_mask >> 8) & 0x7f)
- | data->beep_enable << 7);
+ (data->beep_mask >> 8) & 0xff);
if (data->type != w83781d && data->type != as99127f) {
w83781d_write_value(data, W83781D_REG_BEEP_INTS3,
((data->beep_mask) >> 16) & 0xff);
@@ -544,31 +506,8 @@ store_beep_mask(struct device *dev, struct device_attribute *attr,
return count;
}
-static ssize_t
-store_beep_enable(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct w83781d_data *data = dev_get_drvdata(dev);
- u32 val;
-
- val = simple_strtoul(buf, NULL, 10);
- if (val != 0 && val != 1)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
- data->beep_enable = val;
- val = w83781d_read_value(data, W83781D_REG_BEEP_INTS2) & 0x7f;
- val |= data->beep_enable << 7;
- w83781d_write_value(data, W83781D_REG_BEEP_INTS2, val);
- mutex_unlock(&data->update_lock);
-
- return count;
-}
-
static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR,
show_beep_mask, store_beep_mask);
-static DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR,
- show_beep_enable, store_beep_enable);
static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -663,6 +602,8 @@ static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR,
show_beep, store_beep, 5);
static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO,
show_temp3_beep, store_beep, 13);
+static SENSOR_DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 15);
static ssize_t
show_fan_div(struct device *dev, struct device_attribute *da, char *buf)
@@ -866,45 +807,19 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR,
static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR,
show_sensor, store_sensor, 2);
-/* 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
- * when a new adapter is inserted (and w83781d_driver is still present) */
-static int
-w83781d_attach_adapter(struct i2c_adapter *adapter)
-{
- if (!(adapter->class & I2C_CLASS_HWMON))
- return 0;
- return i2c_probe(adapter, &addr_data, w83781d_detect);
-}
-
/* Assumes that adapter is of I2C, not ISA variety.
* OTHERWISE DON'T CALL THIS
*/
static int
-w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
- struct i2c_client *new_client)
+w83781d_detect_subclients(struct i2c_client *new_client)
{
int i, val1 = 0, id;
int err;
- const char *client_name = "";
+ int address = new_client->addr;
+ unsigned short sc_addr[2];
+ struct i2c_adapter *adapter = new_client->adapter;
struct w83781d_data *data = i2c_get_clientdata(new_client);
-
- data->lm75[0] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!(data->lm75[0])) {
- err = -ENOMEM;
- goto ERROR_SC_0;
- }
+ enum chips kind = data->type;
id = i2c_adapter_id(adapter);
@@ -922,55 +837,35 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
w83781d_write_value(data, W83781D_REG_I2C_SUBADDR,
(force_subclients[2] & 0x07) |
((force_subclients[3] & 0x07) << 4));
- data->lm75[0]->addr = force_subclients[2];
+ sc_addr[0] = force_subclients[2];
} else {
val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR);
- data->lm75[0]->addr = 0x48 + (val1 & 0x07);
+ sc_addr[0] = 0x48 + (val1 & 0x07);
}
if (kind != w83783s) {
- data->lm75[1] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!(data->lm75[1])) {
- err = -ENOMEM;
- goto ERROR_SC_1;
- }
-
if (force_subclients[0] == id &&
force_subclients[1] == address) {
- data->lm75[1]->addr = force_subclients[3];
+ sc_addr[1] = force_subclients[3];
} else {
- data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07);
+ sc_addr[1] = 0x48 + ((val1 >> 4) & 0x07);
}
- if (data->lm75[0]->addr == data->lm75[1]->addr) {
+ if (sc_addr[0] == sc_addr[1]) {
dev_err(&new_client->dev,
"Duplicate addresses 0x%x for subclients.\n",
- data->lm75[0]->addr);
+ sc_addr[0]);
err = -EBUSY;
goto ERROR_SC_2;
}
}
- if (kind == w83781d)
- client_name = "w83781d subclient";
- else if (kind == w83782d)
- client_name = "w83782d subclient";
- else if (kind == w83783s)
- client_name = "w83783s subclient";
- else if (kind == as99127f)
- client_name = "as99127f subclient";
-
for (i = 0; i <= 1; i++) {
- /* store all data in w83781d */
- i2c_set_clientdata(data->lm75[i], NULL);
- data->lm75[i]->adapter = adapter;
- data->lm75[i]->driver = &w83781d_driver;
- data->lm75[i]->flags = 0;
- strlcpy(data->lm75[i]->name, client_name,
- I2C_NAME_SIZE);
- if ((err = i2c_attach_client(data->lm75[i]))) {
+ data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]);
+ if (!data->lm75[i]) {
dev_err(&new_client->dev, "Subclient %d "
"registration at address 0x%x "
- "failed.\n", i, data->lm75[i]->addr);
+ "failed.\n", i, sc_addr[i]);
+ err = -ENOMEM;
if (i == 1)
goto ERROR_SC_3;
goto ERROR_SC_2;
@@ -983,12 +878,9 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
/* Undo inits in case of errors */
ERROR_SC_3:
- i2c_detach_client(data->lm75[0]);
+ i2c_unregister_device(data->lm75[0]);
ERROR_SC_2:
- kfree(data->lm75[1]);
ERROR_SC_1:
- kfree(data->lm75[0]);
-ERROR_SC_0:
return err;
}
@@ -1029,7 +921,7 @@ static struct attribute* w83781d_attributes[] = {
&dev_attr_vrm.attr,
&dev_attr_alarms.attr,
&dev_attr_beep_mask.attr,
- &dev_attr_beep_enable.attr,
+ &sensor_dev_attr_beep_enable.dev_attr.attr,
NULL
};
static const struct attribute_group w83781d_group = {
@@ -1151,96 +1043,74 @@ w83781d_create_files(struct device *dev, int kind, int is_isa)
}
}
- if (is_isa) {
- err = device_create_file(&pdev->dev, &dev_attr_name);
- if (err)
- return err;
- }
-
return 0;
}
+/* Return 0 if detection is successful, -ENODEV otherwise */
static int
-w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
+w83781d_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
{
int val1 = 0, val2;
- struct i2c_client *client;
- struct device *dev;
- struct w83781d_data *data;
- int err;
+ struct w83781d_data *isa = w83781d_data_if_isa();
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
const char *client_name = "";
enum vendor { winbond, asus } vendid;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- err = -EINVAL;
- goto ERROR1;
- }
-
- /* OK. For now, we presume we have a valid client. We now create the
- client structure, even though we cannot fill it completely yet.
- But it allows us to access w83781d_{read,write}_value. */
-
- if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) {
- err = -ENOMEM;
- goto ERROR1;
- }
-
- client = &data->client;
- i2c_set_clientdata(client, data);
- client->addr = address;
- mutex_init(&data->lock);
- client->adapter = adapter;
- client->driver = &w83781d_driver;
- dev = &client->dev;
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
- /* Now, we do the remaining detection. */
+ /* We block updates of the ISA device to minimize the risk of
+ concurrent access to the same W83781D chip through different
+ interfaces. */
+ if (isa)
+ mutex_lock(&isa->update_lock);
/* The w8378?d may be stuck in some other bank than bank 0. This may
make reading other information impossible. Specify a force=... or
force_*=... parameter, and the Winbond will be reset to the right
bank. */
if (kind < 0) {
- if (w83781d_read_value(data, W83781D_REG_CONFIG) & 0x80) {
+ if (i2c_smbus_read_byte_data
+ (client, W83781D_REG_CONFIG) & 0x80) {
dev_dbg(&adapter->dev, "Detection of w83781d chip "
"failed at step 3\n");
- err = -ENODEV;
- goto ERROR2;
+ goto err_nodev;
}
- val1 = w83781d_read_value(data, W83781D_REG_BANK);
- val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN);
+ val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK);
+ val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
/* Check for Winbond or Asus ID if in bank 0 */
if ((!(val1 & 0x07)) &&
(((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
|| ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) {
dev_dbg(&adapter->dev, "Detection of w83781d chip "
"failed at step 4\n");
- err = -ENODEV;
- goto ERROR2;
+ goto err_nodev;
}
/* If Winbond SMBus, check address at 0x48.
Asus doesn't support, except for as99127f rev.2 */
if ((!(val1 & 0x80) && (val2 == 0xa3)) ||
((val1 & 0x80) && (val2 == 0x5c))) {
- if (w83781d_read_value
- (data, W83781D_REG_I2C_ADDR) != address) {
+ if (i2c_smbus_read_byte_data
+ (client, W83781D_REG_I2C_ADDR) != address) {
dev_dbg(&adapter->dev, "Detection of w83781d "
"chip failed at step 5\n");
- err = -ENODEV;
- goto ERROR2;
+ goto err_nodev;
}
}
}
/* We have either had a force parameter, or we have already detected the
Winbond. Put it now into bank 0 and Vendor ID High Byte */
- w83781d_write_value(data, W83781D_REG_BANK,
- (w83781d_read_value(data, W83781D_REG_BANK)
- & 0x78) | 0x80);
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ (i2c_smbus_read_byte_data(client, W83781D_REG_BANK)
+ & 0x78) | 0x80);
/* Determine the chip type. */
if (kind <= 0) {
/* get vendor ID */
- val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN);
+ val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
if (val2 == 0x5c)
vendid = winbond;
else if (val2 == 0x12)
@@ -1248,11 +1118,10 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
else {
dev_dbg(&adapter->dev, "w83781d chip vendor is "
"neither Winbond nor Asus\n");
- err = -ENODEV;
- goto ERROR2;
+ goto err_nodev;
}
- val1 = w83781d_read_value(data, W83781D_REG_WCHIPID);
+ val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID);
if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
kind = w83781d;
else if (val1 == 0x30 && vendid == winbond)
@@ -1266,11 +1135,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
dev_warn(&adapter->dev, "Ignoring 'force' "
"parameter for unknown chip at "
"address 0x%02x\n", address);
- err = -EINVAL;
- goto ERROR2;
+ goto err_nodev;
+ }
+
+ if ((kind == w83781d || kind == w83782d)
+ && w83781d_alias_detect(client, val1)) {
+ dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
+ "be the same as ISA device\n", address);
+ goto err_nodev;
}
}
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+
if (kind == w83781d) {
client_name = "w83781d";
} else if (kind == w83782d) {
@@ -1281,24 +1159,46 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
client_name = "as99127f";
}
- /* Fill in the remaining client fields and put into the global list */
- strlcpy(client->name, client_name, I2C_NAME_SIZE);
- data->type = kind;
+ strlcpy(info->type, client_name, I2C_NAME_SIZE);
+
+ return 0;
+
+ err_nodev:
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+ return -ENODEV;
+}
+
+static int
+w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct w83781d_data *data;
+ int err;
+
+ data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto ERROR1;
+ }
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
- /* Tell the I2C layer a new client has arrived */
- if ((err = i2c_attach_client(client)))
- goto ERROR2;
+ data->type = id->driver_data;
+ data->client = client;
/* attach secondary i2c lm75-like clients */
- if ((err = w83781d_detect_subclients(adapter, address,
- kind, client)))
+ err = w83781d_detect_subclients(client);
+ if (err)
goto ERROR3;
/* Initialize the chip */
w83781d_init_device(dev);
/* Register sysfs hooks */
- err = w83781d_create_files(dev, kind, 0);
+ err = w83781d_create_files(dev, data->type, 0);
if (err)
goto ERROR4;
@@ -1314,264 +1214,113 @@ ERROR4:
sysfs_remove_group(&dev->kobj, &w83781d_group);
sysfs_remove_group(&dev->kobj, &w83781d_group_opt);
- if (data->lm75[1]) {
- i2c_detach_client(data->lm75[1]);
- kfree(data->lm75[1]);
- }
- if (data->lm75[0]) {
- i2c_detach_client(data->lm75[0]);
- kfree(data->lm75[0]);
- }
+ if (data->lm75[0])
+ i2c_unregister_device(data->lm75[0]);
+ if (data->lm75[1])
+ i2c_unregister_device(data->lm75[1]);
ERROR3:
- i2c_detach_client(client);
-ERROR2:
+ i2c_set_clientdata(client, NULL);
kfree(data);
ERROR1:
return err;
}
static int
-w83781d_detach_client(struct i2c_client *client)
+w83781d_remove(struct i2c_client *client)
{
struct w83781d_data *data = i2c_get_clientdata(client);
- int err;
-
- /* main client */
- if (data) {
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &w83781d_group);
- sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt);
- }
-
- if ((err = i2c_detach_client(client)))
- return err;
+ struct device *dev = &client->dev;
- /* main client */
- if (data)
- kfree(data);
-
- /* subclient */
- else
- kfree(client);
-
- return 0;
-}
-
-static int __devinit
-w83781d_isa_probe(struct platform_device *pdev)
-{
- int err, reg;
- struct w83781d_data *data;
- struct resource *res;
- const char *name;
-
- /* Reserve the ISA region */
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2,
- "w83781d")) {
- err = -EBUSY;
- goto exit;
- }
-
- if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) {
- err = -ENOMEM;
- goto exit_release_region;
- }
- mutex_init(&data->lock);
- data->client.addr = res->start;
- i2c_set_clientdata(&data->client, data);
- platform_set_drvdata(pdev, data);
-
- reg = w83781d_read_value(data, W83781D_REG_WCHIPID);
- switch (reg) {
- case 0x30:
- data->type = w83782d;
- name = "w83782d";
- break;
- default:
- data->type = w83781d;
- name = "w83781d";
- }
- strlcpy(data->client.name, name, I2C_NAME_SIZE);
-
- /* Initialize the W83781D chip */
- w83781d_init_device(&pdev->dev);
-
- /* Register sysfs hooks */
- err = w83781d_create_files(&pdev->dev, data->type, 1);
- if (err)
- goto exit_remove_files;
-
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto exit_remove_files;
- }
-
- return 0;
+ hwmon_device_unregister(data->hwmon_dev);
- exit_remove_files:
- sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
- sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
- device_remove_file(&pdev->dev, &dev_attr_name);
- kfree(data);
- exit_release_region:
- release_region(res->start + W83781D_ADDR_REG_OFFSET, 2);
- exit:
- return err;
-}
+ sysfs_remove_group(&dev->kobj, &w83781d_group);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_opt);
-static int __devexit
-w83781d_isa_remove(struct platform_device *pdev)
-{
- struct w83781d_data *data = platform_get_drvdata(pdev);
+ if (data->lm75[0])
+ i2c_unregister_device(data->lm75[0]);
+ if (data->lm75[1])
+ i2c_unregister_device(data->lm75[1]);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
- sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
- device_remove_file(&pdev->dev, &dev_attr_name);
- release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2);
+ i2c_set_clientdata(client, NULL);
kfree(data);
return 0;
}
-/* The SMBus locks itself, usually, but nothing may access the Winbond between
- bank switches. ISA access must always be locked explicitly!
- We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
- would slow down the W83781D access and should not be necessary.
- There are some ugly typecasts here, but the good news is - they should
- nowhere else be necessary! */
static int
-w83781d_read_value(struct w83781d_data *data, u16 reg)
+w83781d_read_value_i2c(struct w83781d_data *data, u16 reg)
{
- struct i2c_client *client = &data->client;
- int res, word_sized, bank;
+ struct i2c_client *client = data->client;
+ int res, bank;
struct i2c_client *cl;
- mutex_lock(&data->lock);
- if (!client->driver) { /* ISA device */
- word_sized = (((reg & 0xff00) == 0x100)
- || ((reg & 0xff00) == 0x200))
- && (((reg & 0x00ff) == 0x50)
- || ((reg & 0x00ff) == 0x53)
- || ((reg & 0x00ff) == 0x55));
- if (reg & 0xff00) {
- outb_p(W83781D_REG_BANK,
- client->addr + W83781D_ADDR_REG_OFFSET);
- outb_p(reg >> 8,
- client->addr + W83781D_DATA_REG_OFFSET);
- }
- outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
- res = inb_p(client->addr + W83781D_DATA_REG_OFFSET);
- if (word_sized) {
- outb_p((reg & 0xff) + 1,
- client->addr + W83781D_ADDR_REG_OFFSET);
- res =
- (res << 8) + inb_p(client->addr +
- W83781D_DATA_REG_OFFSET);
- }
- if (reg & 0xff00) {
- outb_p(W83781D_REG_BANK,
- client->addr + W83781D_ADDR_REG_OFFSET);
- outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
- }
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ bank);
+ if (bank == 0 || bank > 2) {
+ res = i2c_smbus_read_byte_data(client, reg & 0xff);
} else {
- bank = (reg >> 8) & 0x0f;
- if (bank > 2)
- /* switch banks */
- i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
- bank);
- if (bank == 0 || bank > 2) {
- res = i2c_smbus_read_byte_data(client, reg & 0xff);
- } else {
- /* switch to subclient */
- cl = data->lm75[bank - 1];
- /* convert from ISA to LM75 I2C addresses */
- switch (reg & 0xff) {
- case 0x50: /* TEMP */
- res = swab16(i2c_smbus_read_word_data(cl, 0));
- break;
- case 0x52: /* CONFIG */
- res = i2c_smbus_read_byte_data(cl, 1);
- break;
- case 0x53: /* HYST */
- res = swab16(i2c_smbus_read_word_data(cl, 2));
- break;
- case 0x55: /* OVER */
- default:
- res = swab16(i2c_smbus_read_word_data(cl, 3));
- break;
- }
+ /* switch to subclient */
+ cl = data->lm75[bank - 1];
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x50: /* TEMP */
+ res = swab16(i2c_smbus_read_word_data(cl, 0));
+ break;
+ case 0x52: /* CONFIG */
+ res = i2c_smbus_read_byte_data(cl, 1);
+ break;
+ case 0x53: /* HYST */
+ res = swab16(i2c_smbus_read_word_data(cl, 2));
+ break;
+ case 0x55: /* OVER */
+ default:
+ res = swab16(i2c_smbus_read_word_data(cl, 3));
+ break;
}
- if (bank > 2)
- i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
}
- mutex_unlock(&data->lock);
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
+
return res;
}
static int
-w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value)
+w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value)
{
- struct i2c_client *client = &data->client;
- int word_sized, bank;
+ struct i2c_client *client = data->client;
+ int bank;
struct i2c_client *cl;
- mutex_lock(&data->lock);
- if (!client->driver) { /* ISA device */
- word_sized = (((reg & 0xff00) == 0x100)
- || ((reg & 0xff00) == 0x200))
- && (((reg & 0x00ff) == 0x53)
- || ((reg & 0x00ff) == 0x55));
- if (reg & 0xff00) {
- outb_p(W83781D_REG_BANK,
- client->addr + W83781D_ADDR_REG_OFFSET);
- outb_p(reg >> 8,
- client->addr + W83781D_DATA_REG_OFFSET);
- }
- outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
- if (word_sized) {
- outb_p(value >> 8,
- client->addr + W83781D_DATA_REG_OFFSET);
- outb_p((reg & 0xff) + 1,
- client->addr + W83781D_ADDR_REG_OFFSET);
- }
- outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET);
- if (reg & 0xff00) {
- outb_p(W83781D_REG_BANK,
- client->addr + W83781D_ADDR_REG_OFFSET);
- outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
- }
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ bank);
+ if (bank == 0 || bank > 2) {
+ i2c_smbus_write_byte_data(client, reg & 0xff,
+ value & 0xff);
} else {
- bank = (reg >> 8) & 0x0f;
- if (bank > 2)
- /* switch banks */
- i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
- bank);
- if (bank == 0 || bank > 2) {
- i2c_smbus_write_byte_data(client, reg & 0xff,
- value & 0xff);
- } else {
- /* switch to subclient */
- cl = data->lm75[bank - 1];
- /* convert from ISA to LM75 I2C addresses */
- switch (reg & 0xff) {
- case 0x52: /* CONFIG */
- i2c_smbus_write_byte_data(cl, 1, value & 0xff);
- break;
- case 0x53: /* HYST */
- i2c_smbus_write_word_data(cl, 2, swab16(value));
- break;
- case 0x55: /* OVER */
- i2c_smbus_write_word_data(cl, 3, swab16(value));
- break;
- }
+ /* switch to subclient */
+ cl = data->lm75[bank - 1];
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x52: /* CONFIG */
+ i2c_smbus_write_byte_data(cl, 1, value & 0xff);
+ break;
+ case 0x53: /* HYST */
+ i2c_smbus_write_word_data(cl, 2, swab16(value));
+ break;
+ case 0x55: /* OVER */
+ i2c_smbus_write_word_data(cl, 3, swab16(value));
+ break;
}
- if (bank > 2)
- i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
}
- mutex_unlock(&data->lock);
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
+
return 0;
}
@@ -1678,7 +1427,7 @@ w83781d_init_device(struct device *dev)
static struct w83781d_data *w83781d_update_device(struct device *dev)
{
struct w83781d_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = &data->client;
+ struct i2c_client *client = data->client;
int i;
mutex_lock(&data->update_lock);
@@ -1775,8 +1524,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev)
W83781D_REG_ALARM2) << 8);
}
i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2);
- data->beep_enable = i >> 7;
- data->beep_mask = ((i & 0x7f) << 8) +
+ data->beep_mask = (i << 8) +
w83781d_read_value(data, W83781D_REG_BEEP_INTS1);
if ((data->type != w83781d) && (data->type != as99127f)) {
data->beep_mask |=
@@ -1792,6 +1540,275 @@ static struct w83781d_data *w83781d_update_device(struct device *dev)
return data;
}
+static const struct i2c_device_id w83781d_ids[] = {
+ { "w83781d", w83781d, },
+ { "w83782d", w83782d, },
+ { "w83783s", w83783s, },
+ { "as99127f", as99127f },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, w83781d_ids);
+
+static struct i2c_driver w83781d_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "w83781d",
+ },
+ .probe = w83781d_probe,
+ .remove = w83781d_remove,
+ .id_table = w83781d_ids,
+ .detect = w83781d_detect,
+ .address_data = &addr_data,
+};
+
+/*
+ * ISA related code
+ */
+#ifdef CONFIG_ISA
+
+/* ISA device, if found */
+static struct platform_device *pdev;
+
+static unsigned short isa_address = 0x290;
+
+/* I2C devices get this name attribute automatically, but for ISA devices
+ we must create it by ourselves. */
+static ssize_t
+show_name(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", data->name);
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct w83781d_data *w83781d_data_if_isa(void)
+{
+ return pdev ? platform_get_drvdata(pdev) : NULL;
+}
+
+/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
+static int w83781d_alias_detect(struct i2c_client *client, u8 chipid)
+{
+ struct w83781d_data *isa;
+ int i;
+
+ if (!pdev) /* No ISA chip */
+ return 0;
+
+ isa = platform_get_drvdata(pdev);
+
+ if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr)
+ return 0; /* Address doesn't match */
+ if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid)
+ return 0; /* Chip type doesn't match */
+
+ /* We compare all the limit registers, the config register and the
+ * interrupt mask registers */
+ for (i = 0x2b; i <= 0x3d; i++) {
+ if (w83781d_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
+ }
+ if (w83781d_read_value(isa, W83781D_REG_CONFIG) !=
+ i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG))
+ return 0;
+ for (i = 0x43; i <= 0x46; i++) {
+ if (w83781d_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+w83781d_read_value_isa(struct w83781d_data *data, u16 reg)
+{
+ int word_sized, res;
+
+ word_sized = (((reg & 0xff00) == 0x100)
+ || ((reg & 0xff00) == 0x200))
+ && (((reg & 0x00ff) == 0x50)
+ || ((reg & 0x00ff) == 0x53)
+ || ((reg & 0x00ff) == 0x55));
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(reg >> 8,
+ data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+ outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ res = inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET);
+ if (word_sized) {
+ outb_p((reg & 0xff) + 1,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ res =
+ (res << 8) + inb_p(data->isa_addr +
+ W83781D_DATA_REG_OFFSET);
+ }
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+ return res;
+}
+
+static void
+w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value)
+{
+ int word_sized;
+
+ word_sized = (((reg & 0xff00) == 0x100)
+ || ((reg & 0xff00) == 0x200))
+ && (((reg & 0x00ff) == 0x53)
+ || ((reg & 0x00ff) == 0x55));
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(reg >> 8,
+ data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+ outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ if (word_sized) {
+ outb_p(value >> 8,
+ data->isa_addr + W83781D_DATA_REG_OFFSET);
+ outb_p((reg & 0xff) + 1,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ }
+ outb_p(value & 0xff, data->isa_addr + W83781D_DATA_REG_OFFSET);
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+}
+
+/* The SMBus locks itself, usually, but nothing may access the Winbond between
+ bank switches. ISA access must always be locked explicitly!
+ We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
+ would slow down the W83781D access and should not be necessary.
+ There are some ugly typecasts here, but the good news is - they should
+ nowhere else be necessary! */
+static int
+w83781d_read_value(struct w83781d_data *data, u16 reg)
+{
+ struct i2c_client *client = data->client;
+ int res;
+
+ mutex_lock(&data->lock);
+ if (client)
+ res = w83781d_read_value_i2c(data, reg);
+ else
+ res = w83781d_read_value_isa(data, reg);
+ mutex_unlock(&data->lock);
+ return res;
+}
+
+static int
+w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value)
+{
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->lock);
+ if (client)
+ w83781d_write_value_i2c(data, reg, value);
+ else
+ w83781d_write_value_isa(data, reg, value);
+ mutex_unlock(&data->lock);
+ return 0;
+}
+
+static int __devinit
+w83781d_isa_probe(struct platform_device *pdev)
+{
+ int err, reg;
+ struct w83781d_data *data;
+ struct resource *res;
+
+ /* Reserve the ISA region */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2,
+ "w83781d")) {
+ err = -EBUSY;
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit_release_region;
+ }
+ mutex_init(&data->lock);
+ data->isa_addr = res->start;
+ platform_set_drvdata(pdev, data);
+
+ reg = w83781d_read_value(data, W83781D_REG_WCHIPID);
+ switch (reg) {
+ case 0x30:
+ data->type = w83782d;
+ data->name = "w83782d";
+ break;
+ default:
+ data->type = w83781d;
+ data->name = "w83781d";
+ }
+
+ /* Initialize the W83781D chip */
+ w83781d_init_device(&pdev->dev);
+
+ /* Register sysfs hooks */
+ err = w83781d_create_files(&pdev->dev, data->type, 1);
+ if (err)
+ goto exit_remove_files;
+
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
+ goto exit_remove_files;
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+ exit_remove_files:
+ sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
+ sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ kfree(data);
+ exit_release_region:
+ release_region(res->start + W83781D_ADDR_REG_OFFSET, 2);
+ exit:
+ return err;
+}
+
+static int __devexit
+w83781d_isa_remove(struct platform_device *pdev)
+{
+ struct w83781d_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
+ sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ release_region(data->isa_addr + W83781D_ADDR_REG_OFFSET, 2);
+ kfree(data);
+
+ return 0;
+}
+
+static struct platform_driver w83781d_isa_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "w83781d",
+ },
+ .probe = w83781d_isa_probe,
+ .remove = __devexit_p(w83781d_isa_remove),
+};
+
/* return 1 if a supported chip is found, 0 otherwise */
static int __init
w83781d_isa_found(unsigned short address)
@@ -1928,18 +1945,14 @@ w83781d_isa_device_add(unsigned short address)
}
static int __init
-sensors_w83781d_init(void)
+w83781d_isa_register(void)
{
int res;
- res = i2c_add_driver(&w83781d_driver);
- if (res)
- goto exit;
-
if (w83781d_isa_found(isa_address)) {
res = platform_driver_register(&w83781d_isa_driver);
if (res)
- goto exit_unreg_i2c_driver;
+ goto exit;
/* Sets global pdev as a side effect */
res = w83781d_isa_device_add(isa_address);
@@ -1949,21 +1962,94 @@ sensors_w83781d_init(void)
return 0;
- exit_unreg_isa_driver:
+exit_unreg_isa_driver:
platform_driver_unregister(&w83781d_isa_driver);
- exit_unreg_i2c_driver:
- i2c_del_driver(&w83781d_driver);
- exit:
+exit:
return res;
}
static void __exit
-sensors_w83781d_exit(void)
+w83781d_isa_unregister(void)
{
if (pdev) {
platform_device_unregister(pdev);
platform_driver_unregister(&w83781d_isa_driver);
}
+}
+#else /* !CONFIG_ISA */
+
+static struct w83781d_data *w83781d_data_if_isa(void)
+{
+ return NULL;
+}
+
+static int
+w83781d_alias_detect(struct i2c_client *client, u8 chipid)
+{
+ return 0;
+}
+
+static int
+w83781d_read_value(struct w83781d_data *data, u16 reg)
+{
+ int res;
+
+ mutex_lock(&data->lock);
+ res = w83781d_read_value_i2c(data, reg);
+ mutex_unlock(&data->lock);
+
+ return res;
+}
+
+static int
+w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value)
+{
+ mutex_lock(&data->lock);
+ w83781d_write_value_i2c(data, reg, value);
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static int __init
+w83781d_isa_register(void)
+{
+ return 0;
+}
+
+static void __exit
+w83781d_isa_unregister(void)
+{
+}
+#endif /* CONFIG_ISA */
+
+static int __init
+sensors_w83781d_init(void)
+{
+ int res;
+
+ /* We register the ISA device first, so that we can skip the
+ * registration of an I2C interface to the same device. */
+ res = w83781d_isa_register();
+ if (res)
+ goto exit;
+
+ res = i2c_add_driver(&w83781d_driver);
+ if (res)
+ goto exit_unreg_isa;
+
+ return 0;
+
+ exit_unreg_isa:
+ w83781d_isa_unregister();
+ exit:
+ return res;
+}
+
+static void __exit
+sensors_w83781d_exit(void)
+{
+ w83781d_isa_unregister();
i2c_del_driver(&w83781d_driver);
}
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index de21142d106..5768def8a4f 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -23,7 +23,7 @@
Supports following chips:
Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
- w83791d 10 5 3 3 0x71 0x5ca3 yes no
+ w83791d 10 5 5 3 0x71 0x5ca3 yes no
The w83791d chip appears to be part way between the 83781d and the
83792d. Thus, this file is derived from both the w83792d.c and
@@ -45,6 +45,7 @@
#define NUMBER_OF_VIN 10
#define NUMBER_OF_FANIN 5
#define NUMBER_OF_TEMPIN 3
+#define NUMBER_OF_PWM 5
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
@@ -116,6 +117,25 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = {
0xBD, /* FAN 5 Count Low Limit in DataSheet */
};
+static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = {
+ 0x81, /* PWM 1 duty cycle register in DataSheet */
+ 0x83, /* PWM 2 duty cycle register in DataSheet */
+ 0x94, /* PWM 3 duty cycle register in DataSheet */
+ 0xA0, /* PWM 4 duty cycle register in DataSheet */
+ 0xA1, /* PWM 5 duty cycle register in DataSheet */
+};
+
+static const u8 W83791D_REG_TEMP_TARGET[3] = {
+ 0x85, /* PWM 1 target temperature for temp 1 */
+ 0x86, /* PWM 2 target temperature for temp 2 */
+ 0x96, /* PWM 3 target temperature for temp 3 */
+};
+
+static const u8 W83791D_REG_TEMP_TOL[2] = {
+ 0x87, /* PWM 1/2 temperature tolerance */
+ 0x97, /* PWM 3 temperature tolerance */
+};
+
static const u8 W83791D_REG_FAN_CFG[2] = {
0x84, /* FAN 1/2 configuration */
0x95, /* FAN 3 configuration */
@@ -160,6 +180,7 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = {
0xA3, /* BEEP Control Register 3 */
};
+#define W83791D_REG_GPIO 0x15
#define W83791D_REG_CONFIG 0x40
#define W83791D_REG_VID_FANDIV 0x47
#define W83791D_REG_DID_VID4 0x49
@@ -224,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div)
(val) < 0 ? ((val) - 250) / 500 * 128 : \
((val) + 250) / 500 * 128)
+/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */
+#define TARGET_TEMP_TO_REG(val) ((val) < 0 ? 0 : \
+ (val) >= 127000 ? 127 : \
+ ((val) + 500) / 1000)
+
+/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */
+#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \
+ (val) >= 15000 ? 15 : \
+ ((val) + 500) / 1000)
#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
#define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff)
@@ -275,6 +305,14 @@ struct w83791d_data {
two sensors with three values
(cur, over, hyst) */
+ /* PWMs */
+ u8 pwm[5]; /* pwm duty cycle */
+ u8 pwm_enable[3]; /* pwm enable status for fan 1-3
+ (fan 4-5 only support manual mode) */
+
+ u8 temp_target[3]; /* pwm 1-3 target temperature */
+ u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */
+
/* Misc */
u32 alarms; /* realtime status register encoding,combined */
u8 beep_enable; /* Global beep enable */
@@ -652,6 +690,217 @@ static struct sensor_device_attribute sda_fan_alarm[] = {
SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22),
};
+/* read/write PWMs */
+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%u\n", data->pwm[nr]);
+}
+
+static ssize_t store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = SENSORS_LIMIT(val, 0, 255);
+ w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_pwm[] = {
+ SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 0),
+ SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 1),
+ SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 2),
+ SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 3),
+ SENSOR_ATTR(pwm5, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 4),
+};
+
+static ssize_t show_pwmenable(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%u\n", data->pwm_enable[nr] + 1);
+}
+
+static ssize_t store_pwmenable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long val;
+ u8 reg_cfg_tmp;
+ u8 reg_idx = 0;
+ u8 val_shift = 0;
+ u8 keep_mask = 0;
+
+ int ret = strict_strtoul(buf, 10, &val);
+
+ if (ret || val < 1 || val > 3)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm_enable[nr] = val - 1;
+ switch (nr) {
+ case 0:
+ reg_idx = 0;
+ val_shift = 2;
+ keep_mask = 0xf3;
+ break;
+ case 1:
+ reg_idx = 0;
+ val_shift = 4;
+ keep_mask = 0xcf;
+ break;
+ case 2:
+ reg_idx = 1;
+ val_shift = 2;
+ keep_mask = 0xf3;
+ break;
+ }
+
+ reg_cfg_tmp = w83791d_read(client, W83791D_REG_FAN_CFG[reg_idx]);
+ reg_cfg_tmp = (reg_cfg_tmp & keep_mask) |
+ data->pwm_enable[nr] << val_shift;
+
+ w83791d_write(client, W83791D_REG_FAN_CFG[reg_idx], reg_cfg_tmp);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+static struct sensor_device_attribute sda_pwmenable[] = {
+ SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 0),
+ SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 1),
+ SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 2),
+};
+
+/* For Smart Fan I / Thermal Cruise */
+static ssize_t show_temp_target(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ int nr = sensor_attr->index;
+ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr]));
+}
+
+static ssize_t store_temp_target(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long val;
+ u8 target_mask;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->temp_target[nr] = TARGET_TEMP_TO_REG(val);
+ target_mask = w83791d_read(client,
+ W83791D_REG_TEMP_TARGET[nr]) & 0x80;
+ w83791d_write(client, W83791D_REG_TEMP_TARGET[nr],
+ data->temp_target[nr] | target_mask);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_temp_target[] = {
+ SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO,
+ show_temp_target, store_temp_target, 0),
+ SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO,
+ show_temp_target, store_temp_target, 1),
+ SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO,
+ show_temp_target, store_temp_target, 2),
+};
+
+static ssize_t show_temp_tolerance(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ int nr = sensor_attr->index;
+ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr]));
+}
+
+static ssize_t store_temp_tolerance(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long val;
+ u8 target_mask;
+ u8 reg_idx = 0;
+ u8 val_shift = 0;
+ u8 keep_mask = 0;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ switch (nr) {
+ case 0:
+ reg_idx = 0;
+ val_shift = 0;
+ keep_mask = 0xf0;
+ break;
+ case 1:
+ reg_idx = 0;
+ val_shift = 4;
+ keep_mask = 0x0f;
+ break;
+ case 2:
+ reg_idx = 1;
+ val_shift = 0;
+ keep_mask = 0xf0;
+ break;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val);
+ target_mask = w83791d_read(client,
+ W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask;
+ w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx],
+ (data->temp_tolerance[nr] << val_shift) | target_mask);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_temp_tolerance[] = {
+ SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 0),
+ SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 1),
+ SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 2),
+};
+
/* read/write the temperature1, includes measured value and limits */
static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr,
char *buf)
@@ -908,8 +1157,6 @@ static struct attribute *w83791d_attributes[] = {
FAN_UNIT_ATTRS(0),
FAN_UNIT_ATTRS(1),
FAN_UNIT_ATTRS(2),
- FAN_UNIT_ATTRS(3),
- FAN_UNIT_ATTRS(4),
TEMP_UNIT_ATTRS(0),
TEMP_UNIT_ATTRS(1),
TEMP_UNIT_ATTRS(2),
@@ -918,6 +1165,18 @@ static struct attribute *w83791d_attributes[] = {
&sda_beep_ctrl[1].dev_attr.attr,
&dev_attr_cpu0_vid.attr,
&dev_attr_vrm.attr,
+ &sda_pwm[0].dev_attr.attr,
+ &sda_pwm[1].dev_attr.attr,
+ &sda_pwm[2].dev_attr.attr,
+ &sda_pwmenable[0].dev_attr.attr,
+ &sda_pwmenable[1].dev_attr.attr,
+ &sda_pwmenable[2].dev_attr.attr,
+ &sda_temp_target[0].dev_attr.attr,
+ &sda_temp_target[1].dev_attr.attr,
+ &sda_temp_target[2].dev_attr.attr,
+ &sda_temp_tolerance[0].dev_attr.attr,
+ &sda_temp_tolerance[1].dev_attr.attr,
+ &sda_temp_tolerance[2].dev_attr.attr,
NULL
};
@@ -925,6 +1184,20 @@ static const struct attribute_group w83791d_group = {
.attrs = w83791d_attributes,
};
+/* Separate group of attributes for fan/pwm 4-5. Their pins can also be
+ in use for GPIO in which case their sysfs-interface should not be made
+ available */
+static struct attribute *w83791d_attributes_fanpwm45[] = {
+ FAN_UNIT_ATTRS(3),
+ FAN_UNIT_ATTRS(4),
+ &sda_pwm[3].dev_attr.attr,
+ &sda_pwm[4].dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group w83791d_group_fanpwm45 = {
+ .attrs = w83791d_attributes_fanpwm45,
+};
static int w83791d_detect_subclients(struct i2c_client *client)
{
@@ -1056,6 +1329,7 @@ static int w83791d_probe(struct i2c_client *client,
struct w83791d_data *data;
struct device *dev = &client->dev;
int i, err;
+ u8 has_fanpwm45;
#ifdef DEBUG
int val1;
@@ -1090,15 +1364,27 @@ static int w83791d_probe(struct i2c_client *client,
if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group)))
goto error3;
+ /* Check if pins of fan/pwm 4-5 are in use as GPIO */
+ has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10;
+ if (has_fanpwm45) {
+ err = sysfs_create_group(&client->dev.kobj,
+ &w83791d_group_fanpwm45);
+ if (err)
+ goto error4;
+ }
+
/* Everything is ready, now register the working device */
data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
- goto error4;
+ goto error5;
}
return 0;
+error5:
+ if (has_fanpwm45)
+ sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45);
error4:
sysfs_remove_group(&client->dev.kobj, &w83791d_group);
error3:
@@ -1236,6 +1522,36 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
for (i = 0; i < 3; i++)
data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04;
+ /* Update PWM duty cycle */
+ for (i = 0; i < NUMBER_OF_PWM; i++) {
+ data->pwm[i] = w83791d_read(client,
+ W83791D_REG_PWM[i]);
+ }
+
+ /* Update PWM enable status */
+ for (i = 0; i < 2; i++) {
+ reg_array_tmp[i] = w83791d_read(client,
+ W83791D_REG_FAN_CFG[i]);
+ }
+ data->pwm_enable[0] = (reg_array_tmp[0] >> 2) & 0x03;
+ data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03;
+ data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03;
+
+ /* Update PWM target temperature */
+ for (i = 0; i < 3; i++) {
+ data->temp_target[i] = w83791d_read(client,
+ W83791D_REG_TEMP_TARGET[i]) & 0x7f;
+ }
+
+ /* Update PWM temperature tolerance */
+ for (i = 0; i < 2; i++) {
+ reg_array_tmp[i] = w83791d_read(client,
+ W83791D_REG_TEMP_TOL[i]);
+ }
+ data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f;
+ data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f;
+ data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f;
+
/* Update the first temperature sensor */
for (i = 0; i < 3; i++) {
data->temp1[i] = w83791d_read(client,