as99127f 7 3 0 3 0x31 0x12c3 yes no
as99127f rev.2 (type_name = as99127f) 0x31 0x5ca3 yes no
w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes
- w83627hf 9 3 2 3 0x21 0x5ca3 yes yes(LPC)
w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes
w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no
static struct platform_device *pdev;
/* Addresses to scan */
-static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
- 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
- 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
+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_5(w83781d, w83782d, w83783s, w83627hf, as99127f);
+I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f);
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
#define W83781D_REG_ALARM1 0x41
#define W83781D_REG_ALARM2 0x42
-/* Real-time status (W83782D, W83783S, W83627HF) */
+/* Real-time status (W83782D, W83783S) */
#define W83782D_REG_ALARM1 0x459
#define W83782D_REG_ALARM2 0x45A
#define W83782D_REG_ALARM3 0x45B
#define W83781D_DEFAULT_BETA 3435
-/* RT Table registers */
-#define W83781D_REG_RT_IDX 0x50
-#define W83781D_REG_RT_VAL 0x51
-
/* Conversions */
#define IN_TO_REG(val) SENSORS_LIMIT(((val) + 8) / 16, 0, 255)
#define IN_FROM_REG(val) ((val) * 16)
#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))
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. */
+ not like to detect it double. */
/* 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. */
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.
.driver = {
.name = "w83781d",
},
- .id = I2C_DRIVERID_W83781D,
.attach_adapter = w83781d_attach_adapter,
.detach_client = w83781d_detach_client,
};
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,
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);
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)
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)
unsigned long val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
-
+
/* Save fan_min */
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
/* 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) */
+ * when a new adapter is inserted (and w83781d_driver is still present)
+ We block updates of the ISA device to minimize the risk of concurrent
+ access to the same W83781D chip through different interfaces. */
static int
w83781d_attach_adapter(struct i2c_adapter *adapter)
{
+ struct w83781d_data *data;
+ int err;
+
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
- return i2c_probe(adapter, &addr_data, w83781d_detect);
+
+ data = pdev ? platform_get_drvdata(pdev) : NULL;
+ if (data)
+ mutex_lock(&data->update_lock);
+ err = i2c_probe(adapter, &addr_data, w83781d_detect);
+ if (data)
+ mutex_unlock(&data->update_lock);
+ return err;
}
/* Assumes that adapter is of I2C, not ISA variety.
client_name = "w83782d subclient";
else if (kind == w83783s)
client_name = "w83783s subclient";
- else if (kind == w83627hf)
- client_name = "w83627hf subclient";
else if (kind == as99127f)
client_name = "as99127f subclient";
#define IN_UNIT_ATTRS(X) \
&sensor_dev_attr_in##X##_input.dev_attr.attr, \
&sensor_dev_attr_in##X##_min.dev_attr.attr, \
- &sensor_dev_attr_in##X##_max.dev_attr.attr, \
+ &sensor_dev_attr_in##X##_max.dev_attr.attr, \
&sensor_dev_attr_in##X##_alarm.dev_attr.attr, \
&sensor_dev_attr_in##X##_beep.dev_attr.attr
&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 = {
.attrs = w83781d_attributes_opt,
};
+/* 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 *i2c, *isa;
+ int i;
+
+ if (!pdev) /* No ISA chip */
+ return 0;
+
+ i2c = i2c_get_clientdata(client);
+ 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) != w83781d_read_value(i2c, i))
+ return 0;
+ }
+ if (w83781d_read_value(isa, W83781D_REG_CONFIG) !=
+ w83781d_read_value(i2c, W83781D_REG_CONFIG))
+ return 0;
+ for (i = 0x43; i <= 0x46; i++) {
+ if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i))
+ return 0;
+ }
+
+ return 1;
+}
+
/* No clean up is done on error, it's up to the caller */
static int
w83781d_create_files(struct device *dev, int kind, int is_isa)
kind = w83782d;
else if (val1 == 0x40 && vendid == winbond && address == 0x2d)
kind = w83783s;
- else if (val1 == 0x21 && vendid == winbond)
- kind = w83627hf;
- else if (val1 == 0x31 && address >= 0x28)
+ else if (val1 == 0x31)
kind = as99127f;
else {
if (kind == 0)
err = -EINVAL;
goto ERROR2;
}
+
+ 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);
+ err = -ENODEV;
+ goto ERROR2;
+ }
}
if (kind == w83781d) {
client_name = "w83782d";
} else if (kind == w83783s) {
client_name = "w83783s";
- } else if (kind == w83627hf) {
- client_name = "w83627hf";
} else if (kind == as99127f) {
client_name = "as99127f";
}
/* Reserve the ISA region */
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!request_region(res->start, W83781D_EXTENT, "w83781d")) {
+ if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2,
+ "w83781d")) {
err = -EBUSY;
goto exit;
}
reg = w83781d_read_value(data, W83781D_REG_WCHIPID);
switch (reg) {
- case 0x21:
- data->type = w83627hf;
- name = "w83627hf";
- break;
case 0x30:
data->type = w83782d;
name = "w83782d";
device_remove_file(&pdev->dev, &dev_attr_name);
kfree(data);
exit_release_region:
- release_region(res->start, W83781D_EXTENT);
+ release_region(res->start + W83781D_ADDR_REG_OFFSET, 2);
exit:
return err;
}
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group);
sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt);
device_remove_file(&pdev->dev, &dev_attr_name);
- release_region(data->client.addr, W83781D_EXTENT);
+ release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2);
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!
+ 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.
+ 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
int type = data->type;
u8 tmp;
- if (type == w83627hf)
- dev_info(dev, "The W83627HF chip is better supported by the "
- "w83627hf driver, support will be dropped from the "
- "w83781d driver soon\n");
-
if (reset && type != as99127f) { /* this resets registers we don't have
documentation for on the as99127f */
/* Resetting the chip has been the default for a long time,
w83781d_read_value(data, W83781D_REG_IN_MIN(i));
data->in_max[i] =
w83781d_read_value(data, W83781D_REG_IN_MAX(i));
- if ((data->type != w83782d)
- && (data->type != w83627hf) && (i == 6))
+ if ((data->type != w83782d) && (i == 6))
break;
}
for (i = 0; i < 3; i++) {
data->fan_div[1] |= (i >> 4) & 0x04;
data->fan_div[2] |= (i >> 5) & 0x04;
}
- if ((data->type == w83782d) || (data->type == w83627hf)) {
+ if (data->type == w83782d) {
data->alarms = w83781d_read_value(data,
W83782D_REG_ALARM1)
| (w83781d_read_value(data,
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 |=
{
int val, save, found = 0;
- if (!request_region(address, W83781D_EXTENT, "w83781d"))
+ /* 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, "w83781d")) {
+ pr_debug("w83781d: Failed to request low part of region\n");
return 0;
+ }
+ if (!request_region(address + 4, 4, "w83781d")) {
+ pr_debug("w83781d: 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 W83781D-like
outb_p(W83781D_REG_WCHIPID, address + W83781D_ADDR_REG_OFFSET);
val = inb_p(address + W83781D_DATA_REG_OFFSET);
if ((val & 0xfe) == 0x10 /* W83781D */
- || val == 0x30 /* W83782D */
- || val == 0x21) /* W83627HF */
+ || val == 0x30) /* W83782D */
found = 1;
if (found)
pr_info("w83781d: Found a %s chip at %#x\n",
- val == 0x21 ? "W83627HF" :
val == 0x30 ? "W83782D" : "W83781D", (int)address);
release:
- release_region(address, W83781D_EXTENT);
+ release_region(address + 4, 4);
+ release_region(address, 4);
return found;
}
{
int res;
- res = i2c_add_driver(&w83781d_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 (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);
goto exit_unreg_isa_driver;
}
+ res = i2c_add_driver(&w83781d_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(&w83781d_isa_driver);
- exit_unreg_i2c_driver:
- i2c_del_driver(&w83781d_driver);
exit:
return res;
}