diff options
author | Michael Lawnick <ml.lawnick@gmx.de> | 2010-08-11 12:21:02 -0400 |
---|---|---|
committer | Jean Delvare <khali@linux-fr.org> | 2010-08-11 12:21:02 -0400 |
commit | 0826374bff57411d239f2fcb15da3c35af0a93cd (patch) | |
tree | 514d4361cfc9cc546306612de3464def7fe8a7cd /drivers/i2c/i2c-core.c | |
parent | dafc50d141c27959dbd3a1cfe9857a86d23402a7 (diff) |
i2c: Multiplexed I2C bus core support
Add multiplexed bus core support. I2C multiplexer and switches
like pca954x get instantiated as new adapters per port.
Signed-off-by: Michael Lawnick <ml.lawnick@gmx.de>
Acked-by: Rodolfo Giometti <giometti@linux.it>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c/i2c-core.c')
-rw-r--r-- | drivers/i2c/i2c-core.c | 64 |
1 files changed, 57 insertions, 7 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 97f96b66653c..6649176de940 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c | |||
@@ -20,7 +20,9 @@ | |||
20 | /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>. | 20 | /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>. |
21 | All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> | 21 | All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> |
22 | SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and | 22 | SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and |
23 | Jean Delvare <khali@linux-fr.org> */ | 23 | Jean Delvare <khali@linux-fr.org> |
24 | Mux support by Rodolfo Giometti <giometti@enneenne.com> and | ||
25 | Michael Lawnick <michael.lawnick.ext@nsn.com> */ | ||
24 | 26 | ||
25 | #include <linux/module.h> | 27 | #include <linux/module.h> |
26 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
@@ -423,10 +425,48 @@ static int __i2c_check_addr_busy(struct device *dev, void *addrp) | |||
423 | return 0; | 425 | return 0; |
424 | } | 426 | } |
425 | 427 | ||
428 | /* walk up mux tree */ | ||
429 | static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) | ||
430 | { | ||
431 | int result; | ||
432 | |||
433 | result = device_for_each_child(&adapter->dev, &addr, | ||
434 | __i2c_check_addr_busy); | ||
435 | |||
436 | if (!result && i2c_parent_is_i2c_adapter(adapter)) | ||
437 | result = i2c_check_mux_parents( | ||
438 | to_i2c_adapter(adapter->dev.parent), addr); | ||
439 | |||
440 | return result; | ||
441 | } | ||
442 | |||
443 | /* recurse down mux tree */ | ||
444 | static int i2c_check_mux_children(struct device *dev, void *addrp) | ||
445 | { | ||
446 | int result; | ||
447 | |||
448 | if (dev->type == &i2c_adapter_type) | ||
449 | result = device_for_each_child(dev, addrp, | ||
450 | i2c_check_mux_children); | ||
451 | else | ||
452 | result = __i2c_check_addr_busy(dev, addrp); | ||
453 | |||
454 | return result; | ||
455 | } | ||
456 | |||
426 | static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) | 457 | static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) |
427 | { | 458 | { |
428 | return device_for_each_child(&adapter->dev, &addr, | 459 | int result = 0; |
429 | __i2c_check_addr_busy); | 460 | |
461 | if (i2c_parent_is_i2c_adapter(adapter)) | ||
462 | result = i2c_check_mux_parents( | ||
463 | to_i2c_adapter(adapter->dev.parent), addr); | ||
464 | |||
465 | if (!result) | ||
466 | result = device_for_each_child(&adapter->dev, &addr, | ||
467 | i2c_check_mux_children); | ||
468 | |||
469 | return result; | ||
430 | } | 470 | } |
431 | 471 | ||
432 | /** | 472 | /** |
@@ -435,7 +475,10 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) | |||
435 | */ | 475 | */ |
436 | void i2c_lock_adapter(struct i2c_adapter *adapter) | 476 | void i2c_lock_adapter(struct i2c_adapter *adapter) |
437 | { | 477 | { |
438 | rt_mutex_lock(&adapter->bus_lock); | 478 | if (i2c_parent_is_i2c_adapter(adapter)) |
479 | i2c_lock_adapter(to_i2c_adapter(adapter->dev.parent)); | ||
480 | else | ||
481 | rt_mutex_lock(&adapter->bus_lock); | ||
439 | } | 482 | } |
440 | EXPORT_SYMBOL_GPL(i2c_lock_adapter); | 483 | EXPORT_SYMBOL_GPL(i2c_lock_adapter); |
441 | 484 | ||
@@ -445,7 +488,10 @@ EXPORT_SYMBOL_GPL(i2c_lock_adapter); | |||
445 | */ | 488 | */ |
446 | static int i2c_trylock_adapter(struct i2c_adapter *adapter) | 489 | static int i2c_trylock_adapter(struct i2c_adapter *adapter) |
447 | { | 490 | { |
448 | return rt_mutex_trylock(&adapter->bus_lock); | 491 | if (i2c_parent_is_i2c_adapter(adapter)) |
492 | return i2c_trylock_adapter(to_i2c_adapter(adapter->dev.parent)); | ||
493 | else | ||
494 | return rt_mutex_trylock(&adapter->bus_lock); | ||
449 | } | 495 | } |
450 | 496 | ||
451 | /** | 497 | /** |
@@ -454,7 +500,10 @@ static int i2c_trylock_adapter(struct i2c_adapter *adapter) | |||
454 | */ | 500 | */ |
455 | void i2c_unlock_adapter(struct i2c_adapter *adapter) | 501 | void i2c_unlock_adapter(struct i2c_adapter *adapter) |
456 | { | 502 | { |
457 | rt_mutex_unlock(&adapter->bus_lock); | 503 | if (i2c_parent_is_i2c_adapter(adapter)) |
504 | i2c_unlock_adapter(to_i2c_adapter(adapter->dev.parent)); | ||
505 | else | ||
506 | rt_mutex_unlock(&adapter->bus_lock); | ||
458 | } | 507 | } |
459 | EXPORT_SYMBOL_GPL(i2c_unlock_adapter); | 508 | EXPORT_SYMBOL_GPL(i2c_unlock_adapter); |
460 | 509 | ||
@@ -743,10 +792,11 @@ static const struct attribute_group *i2c_adapter_attr_groups[] = { | |||
743 | NULL | 792 | NULL |
744 | }; | 793 | }; |
745 | 794 | ||
746 | static struct device_type i2c_adapter_type = { | 795 | struct device_type i2c_adapter_type = { |
747 | .groups = i2c_adapter_attr_groups, | 796 | .groups = i2c_adapter_attr_groups, |
748 | .release = i2c_adapter_dev_release, | 797 | .release = i2c_adapter_dev_release, |
749 | }; | 798 | }; |
799 | EXPORT_SYMBOL_GPL(i2c_adapter_type); | ||
750 | 800 | ||
751 | #ifdef CONFIG_I2C_COMPAT | 801 | #ifdef CONFIG_I2C_COMPAT |
752 | static struct class_compat *i2c_adapter_compat_class; | 802 | static struct class_compat *i2c_adapter_compat_class; |