|
@@ -120,10 +120,7 @@ static inline int TEMP_FROM_REG(s8 val)
|
|
LM78 chips available (well, actually, that is probably never done; but
|
|
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,
|
|
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
|
|
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. */
|
|
|
|
|
|
+ not like to detect it double. */
|
|
|
|
|
|
/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
|
|
/* 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. */
|
|
the driver field to differentiate between I2C and ISA chips. */
|
|
@@ -457,12 +454,24 @@ static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
|
|
/* This function is called when:
|
|
/* This function is called when:
|
|
* lm78_driver is inserted (when this module is loaded), for each
|
|
* lm78_driver is inserted (when this module is loaded), for each
|
|
available adapter
|
|
available adapter
|
|
- * when a new adapter is inserted (and lm78_driver is still present) */
|
|
|
|
|
|
+ * when a new adapter is inserted (and lm78_driver is still present)
|
|
|
|
+ We block updates of the ISA device to minimize the risk of concurrent
|
|
|
|
+ access to the same LM78 chip through different interfaces. */
|
|
static int lm78_attach_adapter(struct i2c_adapter *adapter)
|
|
static int lm78_attach_adapter(struct i2c_adapter *adapter)
|
|
{
|
|
{
|
|
|
|
+ struct lm78_data *data;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
if (!(adapter->class & I2C_CLASS_HWMON))
|
|
if (!(adapter->class & I2C_CLASS_HWMON))
|
|
return 0;
|
|
return 0;
|
|
- return i2c_probe(adapter, &addr_data, lm78_detect);
|
|
|
|
|
|
+
|
|
|
|
+ data = pdev ? platform_get_drvdata(pdev) : NULL;
|
|
|
|
+ if (data)
|
|
|
|
+ mutex_lock(&data->update_lock);
|
|
|
|
+ err = i2c_probe(adapter, &addr_data, lm78_detect);
|
|
|
|
+ if (data)
|
|
|
|
+ mutex_unlock(&data->update_lock);
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
static struct attribute *lm78_attributes[] = {
|
|
static struct attribute *lm78_attributes[] = {
|
|
@@ -531,6 +540,40 @@ static ssize_t show_name(struct device *dev, struct device_attribute
|
|
}
|
|
}
|
|
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
|
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
|
|
|
|
|
|
|
+/* 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)
|
|
|
|
+{
|
|
|
|
+ struct lm78_data *i2c, *isa;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (!pdev) /* No ISA chip */
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ i2c = i2c_get_clientdata(client);
|
|
|
|
+ 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) != lm78_read_value(i2c, i))
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ if (lm78_read_value(isa, LM78_REG_CONFIG) !=
|
|
|
|
+ lm78_read_value(i2c, LM78_REG_CONFIG))
|
|
|
|
+ return 0;
|
|
|
|
+ for (i = 0x43; i <= 0x46; i++) {
|
|
|
|
+ if (lm78_read_value(isa, i) != lm78_read_value(i2c, i))
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
/* This function is called by i2c_probe */
|
|
/* This function is called by i2c_probe */
|
|
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
|
|
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
|
|
{
|
|
{
|
|
@@ -589,6 +632,13 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
|
|
err = -ENODEV;
|
|
err = -ENODEV;
|
|
goto ERROR2;
|
|
goto ERROR2;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (lm78_alias_detect(new_client, i)) {
|
|
|
|
+ dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
|
|
|
|
+ "be the same as ISA device\n", address);
|
|
|
|
+ err = -ENODEV;
|
|
|
|
+ goto ERROR2;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (kind == lm78) {
|
|
if (kind == lm78) {
|
|
@@ -959,14 +1009,12 @@ static int __init sm_lm78_init(void)
|
|
{
|
|
{
|
|
int res;
|
|
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)) {
|
|
if (lm78_isa_found(isa_address)) {
|
|
res = platform_driver_register(&lm78_isa_driver);
|
|
res = platform_driver_register(&lm78_isa_driver);
|
|
if (res)
|
|
if (res)
|
|
- goto exit_unreg_i2c_driver;
|
|
|
|
|
|
+ goto exit;
|
|
|
|
|
|
/* Sets global pdev as a side effect */
|
|
/* Sets global pdev as a side effect */
|
|
res = lm78_isa_device_add(isa_address);
|
|
res = lm78_isa_device_add(isa_address);
|
|
@@ -974,12 +1022,16 @@ static int __init sm_lm78_init(void)
|
|
goto exit_unreg_isa_driver;
|
|
goto exit_unreg_isa_driver;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ res = i2c_add_driver(&lm78_driver);
|
|
|
|
+ if (res)
|
|
|
|
+ goto exit_unreg_isa_device;
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
+ exit_unreg_isa_device:
|
|
|
|
+ platform_device_unregister(pdev);
|
|
exit_unreg_isa_driver:
|
|
exit_unreg_isa_driver:
|
|
platform_driver_unregister(&lm78_isa_driver);
|
|
platform_driver_unregister(&lm78_isa_driver);
|
|
- exit_unreg_i2c_driver:
|
|
|
|
- i2c_del_driver(&lm78_driver);
|
|
|
|
exit:
|
|
exit:
|
|
return res;
|
|
return res;
|
|
}
|
|
}
|