aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2008-10-17 11:51:15 -0400
committerJean Delvare <khali@mahadeva.delvare>2008-10-17 11:51:15 -0400
commit18c73f90421f9a87a0f6bc3a08880d0f1f9b2a74 (patch)
tree3b34751ca992174b957e99fbd973019aadc3b1bd /drivers
parent47c15532ddcd6818f51cb15f914d63864b3ee9ab (diff)
hwmon: (lm78) Detect alias chips
The LM78 and LM79 can be accessed either on the I2C bus or the ISA bus. We must not access the same chip through both interfaces. So far we were relying on the user passing the correct ignore parameter to skip the registration of the I2C interface as suggested by sensors-detect, but this is fragile: the user may load the lm78 driver without running sensors-detect, and the i2c bus numbers are not stable across reboots and hardware changes. So, better detect alias chips in the driver directly, and skip any I2C chip which is obviously an alias of the ISA chip. This is done by comparing the value of 26 selected registers. Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/lm78.c78
1 files changed, 65 insertions, 13 deletions
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 2c96d8a548f7..ec601bbf91b9 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -120,10 +120,7 @@ static inline int TEMP_FROM_REG(s8 val)
120 LM78 chips available (well, actually, that is probably never done; but 120 LM78 chips available (well, actually, that is probably never done; but
121 it is a clean illustration of how to handle a case like that). Finally, 121 it is a clean illustration of how to handle a case like that). Finally,
122 a specific chip may be attached to *both* ISA and SMBus, and we would 122 a specific chip may be attached to *both* ISA and SMBus, and we would
123 not like to detect it double. Fortunately, in the case of the LM78 at 123 not like to detect it double. */
124 least, a register tells us what SMBus address we are on, so that helps
125 a bit - except if there could be more than one SMBus. Groan. No solution
126 for this yet. */
127 124
128/* For ISA chips, we abuse the i2c_client addr and name fields. We also use 125/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
129 the driver field to differentiate between I2C and ISA chips. */ 126 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);
457/* This function is called when: 454/* This function is called when:
458 * lm78_driver is inserted (when this module is loaded), for each 455 * lm78_driver is inserted (when this module is loaded), for each
459 available adapter 456 available adapter
460 * when a new adapter is inserted (and lm78_driver is still present) */ 457 * when a new adapter is inserted (and lm78_driver is still present)
458 We block updates of the ISA device to minimize the risk of concurrent
459 access to the same LM78 chip through different interfaces. */
461static int lm78_attach_adapter(struct i2c_adapter *adapter) 460static int lm78_attach_adapter(struct i2c_adapter *adapter)
462{ 461{
462 struct lm78_data *data;
463 int err;
464
463 if (!(adapter->class & I2C_CLASS_HWMON)) 465 if (!(adapter->class & I2C_CLASS_HWMON))
464 return 0; 466 return 0;
465 return i2c_probe(adapter, &addr_data, lm78_detect); 467
468 data = pdev ? platform_get_drvdata(pdev) : NULL;
469 if (data)
470 mutex_lock(&data->update_lock);
471 err = i2c_probe(adapter, &addr_data, lm78_detect);
472 if (data)
473 mutex_unlock(&data->update_lock);
474 return err;
466} 475}
467 476
468static struct attribute *lm78_attributes[] = { 477static struct attribute *lm78_attributes[] = {
@@ -531,6 +540,40 @@ static ssize_t show_name(struct device *dev, struct device_attribute
531} 540}
532static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 541static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
533 542
543/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
544static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
545{
546 struct lm78_data *i2c, *isa;
547 int i;
548
549 if (!pdev) /* No ISA chip */
550 return 0;
551
552 i2c = i2c_get_clientdata(client);
553 isa = platform_get_drvdata(pdev);
554
555 if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr)
556 return 0; /* Address doesn't match */
557 if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe))
558 return 0; /* Chip type doesn't match */
559
560 /* We compare all the limit registers, the config register and the
561 * interrupt mask registers */
562 for (i = 0x2b; i <= 0x3d; i++) {
563 if (lm78_read_value(isa, i) != lm78_read_value(i2c, i))
564 return 0;
565 }
566 if (lm78_read_value(isa, LM78_REG_CONFIG) !=
567 lm78_read_value(i2c, LM78_REG_CONFIG))
568 return 0;
569 for (i = 0x43; i <= 0x46; i++) {
570 if (lm78_read_value(isa, i) != lm78_read_value(i2c, i))
571 return 0;
572 }
573
574 return 1;
575}
576
534/* This function is called by i2c_probe */ 577/* This function is called by i2c_probe */
535static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) 578static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
536{ 579{
@@ -589,6 +632,13 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
589 err = -ENODEV; 632 err = -ENODEV;
590 goto ERROR2; 633 goto ERROR2;
591 } 634 }
635
636 if (lm78_alias_detect(new_client, i)) {
637 dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
638 "be the same as ISA device\n", address);
639 err = -ENODEV;
640 goto ERROR2;
641 }
592 } 642 }
593 643
594 if (kind == lm78) { 644 if (kind == lm78) {
@@ -959,14 +1009,12 @@ static int __init sm_lm78_init(void)
959{ 1009{
960 int res; 1010 int res;
961 1011
962 res = i2c_add_driver(&lm78_driver); 1012 /* We register the ISA device first, so that we can skip the
963 if (res) 1013 * registration of an I2C interface to the same device. */
964 goto exit;
965
966 if (lm78_isa_found(isa_address)) { 1014 if (lm78_isa_found(isa_address)) {
967 res = platform_driver_register(&lm78_isa_driver); 1015 res = platform_driver_register(&lm78_isa_driver);
968 if (res) 1016 if (res)
969 goto exit_unreg_i2c_driver; 1017 goto exit;
970 1018
971 /* Sets global pdev as a side effect */ 1019 /* Sets global pdev as a side effect */
972 res = lm78_isa_device_add(isa_address); 1020 res = lm78_isa_device_add(isa_address);
@@ -974,12 +1022,16 @@ static int __init sm_lm78_init(void)
974 goto exit_unreg_isa_driver; 1022 goto exit_unreg_isa_driver;
975 } 1023 }
976 1024
1025 res = i2c_add_driver(&lm78_driver);
1026 if (res)
1027 goto exit_unreg_isa_device;
1028
977 return 0; 1029 return 0;
978 1030
1031 exit_unreg_isa_device:
1032 platform_device_unregister(pdev);
979 exit_unreg_isa_driver: 1033 exit_unreg_isa_driver:
980 platform_driver_unregister(&lm78_isa_driver); 1034 platform_driver_unregister(&lm78_isa_driver);
981 exit_unreg_i2c_driver:
982 i2c_del_driver(&lm78_driver);
983 exit: 1035 exit:
984 return res; 1036 return res;
985} 1037}