aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/extcon
diff options
context:
space:
mode:
authorMyungJoo Ham <myungjoo.ham@samsung.com>2012-04-20 01:16:26 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-20 12:24:03 -0400
commitbde68e60b18208978c50c6fb9bdf29826d2887f3 (patch)
tree62b48807d48c2454c374bede680c9e9c6cf71a0d /drivers/extcon
parent806d9dd71ff52ef09764585baaeec23afbb98560 (diff)
Extcon: support mutually exclusive relation between cables.
There could be cables that t recannot be attaches simulatenously. Extcon device drivers may express such information via mutually_exclusive in struct extcon_dev. For example, for an extcon device with 16 cables (bits 0 to 15 are available), if mutually_exclusive = { 0x7, 0xC0, 0x81, 0 }, then, the following attachments are prohibitted. {0, 1} {0, 2} {1, 2} {6, 7} {0, 7} and every attachment set that are superset of one of the above. For the detail, please refer to linux/include/linux/extcon.h. The concept is suggested by NeilBrown <neilb@suse.de> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> -- Changes from V5: - Updated sysfs format Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/extcon')
-rw-r--r--drivers/extcon/extcon_class.c123
1 files changed, 115 insertions, 8 deletions
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 403933bc3e9c..3bc4b8af46cf 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -72,6 +72,39 @@ static struct class_compat *switch_class;
72static LIST_HEAD(extcon_dev_list); 72static LIST_HEAD(extcon_dev_list);
73static DEFINE_MUTEX(extcon_dev_list_lock); 73static DEFINE_MUTEX(extcon_dev_list_lock);
74 74
75/**
76 * check_mutually_exclusive - Check if new_state violates mutually_exclusive
77 * condition.
78 * @edev: the extcon device
79 * @new_state: new cable attach status for @edev
80 *
81 * Returns 0 if nothing violates. Returns the index + 1 for the first
82 * violated condition.
83 */
84static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
85{
86 int i = 0;
87
88 if (!edev->mutually_exclusive)
89 return 0;
90
91 for (i = 0; edev->mutually_exclusive[i]; i++) {
92 int count = 0, j;
93 u32 correspondants = new_state & edev->mutually_exclusive[i];
94 u32 exp = 1;
95
96 for (j = 0; j < 32; j++) {
97 if (exp & correspondants)
98 count++;
99 if (count > 1)
100 return i + 1;
101 exp <<= 1;
102 }
103 }
104
105 return 0;
106}
107
75static ssize_t state_show(struct device *dev, struct device_attribute *attr, 108static ssize_t state_show(struct device *dev, struct device_attribute *attr,
76 char *buf) 109 char *buf)
77{ 110{
@@ -100,7 +133,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
100 return count; 133 return count;
101} 134}
102 135
103void extcon_set_state(struct extcon_dev *edev, u32 state); 136int extcon_set_state(struct extcon_dev *edev, u32 state);
104static ssize_t state_store(struct device *dev, struct device_attribute *attr, 137static ssize_t state_store(struct device *dev, struct device_attribute *attr,
105 const char *buf, size_t count) 138 const char *buf, size_t count)
106{ 139{
@@ -112,7 +145,7 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
112 if (ret == 0) 145 if (ret == 0)
113 ret = -EINVAL; 146 ret = -EINVAL;
114 else 147 else
115 extcon_set_state(edev, state); 148 ret = extcon_set_state(edev, state);
116 149
117 if (ret < 0) 150 if (ret < 0)
118 return ret; 151 return ret;
@@ -191,7 +224,7 @@ static ssize_t cable_state_store(struct device *dev,
191 * Note that the notifier provides which bits are changed in the state 224 * Note that the notifier provides which bits are changed in the state
192 * variable with the val parameter (second) to the callback. 225 * variable with the val parameter (second) to the callback.
193 */ 226 */
194void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) 227int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
195{ 228{
196 char name_buf[120]; 229 char name_buf[120];
197 char state_buf[120]; 230 char state_buf[120];
@@ -206,6 +239,12 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
206 if (edev->state != ((edev->state & ~mask) | (state & mask))) { 239 if (edev->state != ((edev->state & ~mask) | (state & mask))) {
207 u32 old_state = edev->state; 240 u32 old_state = edev->state;
208 241
242 if (check_mutually_exclusive(edev, (edev->state & ~mask) |
243 (state & mask))) {
244 spin_unlock_irqrestore(&edev->lock, flags);
245 return -EPERM;
246 }
247
209 edev->state &= ~mask; 248 edev->state &= ~mask;
210 edev->state |= state & mask; 249 edev->state |= state & mask;
211 250
@@ -247,6 +286,8 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
247 /* No changes */ 286 /* No changes */
248 spin_unlock_irqrestore(&edev->lock, flags); 287 spin_unlock_irqrestore(&edev->lock, flags);
249 } 288 }
289
290 return 0;
250} 291}
251EXPORT_SYMBOL_GPL(extcon_update_state); 292EXPORT_SYMBOL_GPL(extcon_update_state);
252 293
@@ -258,9 +299,9 @@ EXPORT_SYMBOL_GPL(extcon_update_state);
258 * Note that notifier provides which bits are changed in the state 299 * Note that notifier provides which bits are changed in the state
259 * variable with the val parameter (second) to the callback. 300 * variable with the val parameter (second) to the callback.
260 */ 301 */
261void extcon_set_state(struct extcon_dev *edev, u32 state) 302int extcon_set_state(struct extcon_dev *edev, u32 state)
262{ 303{
263 extcon_update_state(edev, 0xffffffff, state); 304 return extcon_update_state(edev, 0xffffffff, state);
264} 305}
265EXPORT_SYMBOL_GPL(extcon_set_state); 306EXPORT_SYMBOL_GPL(extcon_set_state);
266 307
@@ -334,8 +375,7 @@ int extcon_set_cable_state_(struct extcon_dev *edev,
334 return -EINVAL; 375 return -EINVAL;
335 376
336 state = cable_state ? (1 << index) : 0; 377 state = cable_state ? (1 << index) : 0;
337 extcon_update_state(edev, 1 << index, state); 378 return extcon_update_state(edev, 1 << index, state);
338 return 0;
339} 379}
340EXPORT_SYMBOL_GPL(extcon_set_cable_state_); 380EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
341 381
@@ -511,6 +551,14 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip)
511 if (!skip && get_device(edev->dev)) { 551 if (!skip && get_device(edev->dev)) {
512 int index; 552 int index;
513 553
554 if (edev->mutually_exclusive && edev->max_supported) {
555 for (index = 0; edev->mutually_exclusive[index];
556 index++)
557 kfree(edev->d_attrs_muex[index].attr.name);
558 kfree(edev->d_attrs_muex);
559 kfree(edev->attrs_muex);
560 }
561
514 for (index = 0; index < edev->max_supported; index++) 562 for (index = 0; index < edev->max_supported; index++)
515 kfree(edev->cables[index].attr_g.name); 563 kfree(edev->cables[index].attr_g.name);
516 564
@@ -533,6 +581,7 @@ static void extcon_dev_release(struct device *dev)
533 extcon_cleanup(edev, true); 581 extcon_cleanup(edev, true);
534} 582}
535 583
584static const char *muex_name = "mutually_exclusive";
536static void dummy_sysfs_dev_release(struct device *dev) 585static void dummy_sysfs_dev_release(struct device *dev)
537{ 586{
538} 587}
@@ -625,10 +674,58 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
625 } 674 }
626 } 675 }
627 676
677 if (edev->max_supported && edev->mutually_exclusive) {
678 char buf[80];
679 char *name;
680
681 /* Count the size of mutually_exclusive array */
682 for (index = 0; edev->mutually_exclusive[index]; index++)
683 ;
684
685 edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
686 (index + 1), GFP_KERNEL);
687 if (!edev->attrs_muex) {
688 ret = -ENOMEM;
689 goto err_muex;
690 }
691
692 edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
693 index, GFP_KERNEL);
694 if (!edev->d_attrs_muex) {
695 ret = -ENOMEM;
696 kfree(edev->attrs_muex);
697 goto err_muex;
698 }
699
700 for (index = 0; edev->mutually_exclusive[index]; index++) {
701 sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
702 name = kzalloc(sizeof(char) * (strlen(buf) + 1),
703 GFP_KERNEL);
704 if (!name) {
705 for (index--; index >= 0; index--) {
706 kfree(edev->d_attrs_muex[index].attr.
707 name);
708 }
709 kfree(edev->d_attrs_muex);
710 kfree(edev->attrs_muex);
711 ret = -ENOMEM;
712 goto err_muex;
713 }
714 strcpy(name, buf);
715 edev->d_attrs_muex[index].attr.name = name;
716 edev->d_attrs_muex[index].attr.mode = 0000;
717 edev->attrs_muex[index] = &edev->d_attrs_muex[index]
718 .attr;
719 }
720 edev->attr_g_muex.name = muex_name;
721 edev->attr_g_muex.attrs = edev->attrs_muex;
722
723 }
724
628 if (edev->max_supported) { 725 if (edev->max_supported) {
629 edev->extcon_dev_type.groups = 726 edev->extcon_dev_type.groups =
630 kzalloc(sizeof(struct attribute_group *) * 727 kzalloc(sizeof(struct attribute_group *) *
631 (edev->max_supported + 1), GFP_KERNEL); 728 (edev->max_supported + 2), GFP_KERNEL);
632 if (!edev->extcon_dev_type.groups) { 729 if (!edev->extcon_dev_type.groups) {
633 ret = -ENOMEM; 730 ret = -ENOMEM;
634 goto err_alloc_groups; 731 goto err_alloc_groups;
@@ -640,6 +737,9 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
640 for (index = 0; index < edev->max_supported; index++) 737 for (index = 0; index < edev->max_supported; index++)
641 edev->extcon_dev_type.groups[index] = 738 edev->extcon_dev_type.groups[index] =
642 &edev->cables[index].attr_g; 739 &edev->cables[index].attr_g;
740 if (edev->mutually_exclusive)
741 edev->extcon_dev_type.groups[index] =
742 &edev->attr_g_muex;
643 743
644 edev->dev->type = &edev->extcon_dev_type; 744 edev->dev->type = &edev->extcon_dev_type;
645 } 745 }
@@ -672,6 +772,13 @@ err_dev:
672 if (edev->max_supported) 772 if (edev->max_supported)
673 kfree(edev->extcon_dev_type.groups); 773 kfree(edev->extcon_dev_type.groups);
674err_alloc_groups: 774err_alloc_groups:
775 if (edev->max_supported && edev->mutually_exclusive) {
776 for (index = 0; edev->mutually_exclusive[index]; index++)
777 kfree(edev->d_attrs_muex[index].attr.name);
778 kfree(edev->d_attrs_muex);
779 kfree(edev->attrs_muex);
780 }
781err_muex:
675 for (index = 0; index < edev->max_supported; index++) 782 for (index = 0; index < edev->max_supported; index++)
676 kfree(edev->cables[index].attr_g.name); 783 kfree(edev->cables[index].attr_g.name);
677err_alloc_cables: 784err_alloc_cables: