extcon: Add the support for extcon property according to extcon type
[cascardo/linux.git] / drivers / extcon / extcon.c
1 /*
2  *  drivers/extcon/extcon.c - External Connector (extcon) framework.
3  *
4  *  External connector (extcon) class driver
5  *
6  * Copyright (C) 2015 Samsung Electronics
7  * Author: Chanwoo Choi <cw00.choi@samsung.com>
8  *
9  * Copyright (C) 2012 Samsung Electronics
10  * Author: Donggeun Kim <dg77.kim@samsung.com>
11  * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
12  *
13  * based on android/drivers/switch/switch_class.c
14  * Copyright (C) 2008 Google, Inc.
15  * Author: Mike Lockwood <lockwood@android.com>
16  *
17  * This software is licensed under the terms of the GNU General Public
18  * License version 2, as published by the Free Software Foundation, and
19  * may be copied, distributed, and modified under those terms.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  */
26
27 #include <linux/module.h>
28 #include <linux/types.h>
29 #include <linux/init.h>
30 #include <linux/device.h>
31 #include <linux/fs.h>
32 #include <linux/err.h>
33 #include <linux/extcon.h>
34 #include <linux/of.h>
35 #include <linux/slab.h>
36 #include <linux/sysfs.h>
37
38 #define SUPPORTED_CABLE_MAX     32
39 #define CABLE_NAME_MAX          30
40
41 struct __extcon_info {
42         unsigned int type;
43         unsigned int id;
44         const char *name;
45
46 } extcon_info[] = {
47         [EXTCON_NONE] = {
48                 .type = EXTCON_TYPE_MISC,
49                 .id = EXTCON_NONE,
50                 .name = "NONE",
51         },
52
53         /* USB external connector */
54         [EXTCON_USB] = {
55                 .type = EXTCON_TYPE_USB,
56                 .id = EXTCON_USB,
57                 .name = "USB",
58         },
59         [EXTCON_USB_HOST] = {
60                 .type = EXTCON_TYPE_USB,
61                 .id = EXTCON_USB_HOST,
62                 .name = "USB_HOST",
63         },
64
65         /* Charging external connector */
66         [EXTCON_CHG_USB_SDP] = {
67                 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
68                 .id = EXTCON_CHG_USB_SDP,
69                 .name = "SDP",
70         },
71         [EXTCON_CHG_USB_DCP] = {
72                 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
73                 .id = EXTCON_CHG_USB_DCP,
74                 .name = "DCP",
75         },
76         [EXTCON_CHG_USB_CDP] = {
77                 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
78                 .id = EXTCON_CHG_USB_CDP,
79                 .name = "CDP",
80         },
81         [EXTCON_CHG_USB_ACA] = {
82                 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
83                 .id = EXTCON_CHG_USB_ACA,
84                 .name = "ACA",
85         },
86         [EXTCON_CHG_USB_FAST] = {
87                 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
88                 .id = EXTCON_CHG_USB_FAST,
89                 .name = "FAST-CHARGER",
90         },
91         [EXTCON_CHG_USB_SLOW] = {
92                 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
93                 .id = EXTCON_CHG_USB_SLOW,
94                 .name = "SLOW-CHARGER",
95         },
96
97         /* Jack external connector */
98         [EXTCON_JACK_MICROPHONE] = {
99                 .type = EXTCON_TYPE_JACK,
100                 .id = EXTCON_JACK_MICROPHONE,
101                 .name = "MICROPHONE",
102         },
103         [EXTCON_JACK_HEADPHONE] = {
104                 .type = EXTCON_TYPE_JACK,
105                 .id = EXTCON_JACK_HEADPHONE,
106                 .name = "HEADPHONE",
107         },
108         [EXTCON_JACK_LINE_IN] = {
109                 .type = EXTCON_TYPE_JACK,
110                 .id = EXTCON_JACK_LINE_IN,
111                 .name = "LINE-IN",
112         },
113         [EXTCON_JACK_LINE_OUT] = {
114                 .type = EXTCON_TYPE_JACK,
115                 .id = EXTCON_JACK_LINE_OUT,
116                 .name = "LINE-OUT",
117         },
118         [EXTCON_JACK_VIDEO_IN] = {
119                 .type = EXTCON_TYPE_JACK,
120                 .id = EXTCON_JACK_VIDEO_IN,
121                 .name = "VIDEO-IN",
122         },
123         [EXTCON_JACK_VIDEO_OUT] = {
124                 .type = EXTCON_TYPE_JACK,
125                 .id = EXTCON_JACK_VIDEO_OUT,
126                 .name = "VIDEO-OUT",
127         },
128         [EXTCON_JACK_SPDIF_IN] = {
129                 .type = EXTCON_TYPE_JACK,
130                 .id = EXTCON_JACK_SPDIF_IN,
131                 .name = "SPDIF-IN",
132         },
133         [EXTCON_JACK_SPDIF_OUT] = {
134                 .type = EXTCON_TYPE_JACK,
135                 .id = EXTCON_JACK_SPDIF_OUT,
136                 .name = "SPDIF-OUT",
137         },
138
139         /* Display external connector */
140         [EXTCON_DISP_HDMI] = {
141                 .type = EXTCON_TYPE_DISP,
142                 .id = EXTCON_DISP_HDMI,
143                 .name = "HDMI",
144         },
145         [EXTCON_DISP_MHL] = {
146                 .type = EXTCON_TYPE_DISP,
147                 .id = EXTCON_DISP_MHL,
148                 .name = "MHL",
149         },
150         [EXTCON_DISP_DVI] = {
151                 .type = EXTCON_TYPE_DISP,
152                 .id = EXTCON_DISP_DVI,
153                 .name = "DVI",
154         },
155         [EXTCON_DISP_VGA] = {
156                 .type = EXTCON_TYPE_DISP,
157                 .id = EXTCON_DISP_VGA,
158                 .name = "VGA",
159         },
160
161         /* Miscellaneous external connector */
162         [EXTCON_DOCK] = {
163                 .type = EXTCON_TYPE_MISC,
164                 .id = EXTCON_DOCK,
165                 .name = "DOCK",
166         },
167         [EXTCON_JIG] = {
168                 .type = EXTCON_TYPE_MISC,
169                 .id = EXTCON_JIG,
170                 .name = "JIG",
171         },
172         [EXTCON_MECHANICAL] = {
173                 .type = EXTCON_TYPE_MISC,
174                 .id = EXTCON_MECHANICAL,
175                 .name = "MECHANICAL",
176         },
177
178         { /* sentinel */ }
179 };
180
181 /**
182  * struct extcon_cable - An internal data for each cable of extcon device.
183  * @edev:               The extcon device
184  * @cable_index:        Index of this cable in the edev
185  * @attr_g:             Attribute group for the cable
186  * @attr_name:          "name" sysfs entry
187  * @attr_state:         "state" sysfs entry
188  * @attrs:              Array pointing to attr_name and attr_state for attr_g
189  */
190 struct extcon_cable {
191         struct extcon_dev *edev;
192         int cable_index;
193
194         struct attribute_group attr_g;
195         struct device_attribute attr_name;
196         struct device_attribute attr_state;
197
198         struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
199
200         union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
201         union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
202         union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
203         union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
204 };
205
206 static struct class *extcon_class;
207 #if defined(CONFIG_ANDROID)
208 static struct class_compat *switch_class;
209 #endif /* CONFIG_ANDROID */
210
211 static LIST_HEAD(extcon_dev_list);
212 static DEFINE_MUTEX(extcon_dev_list_lock);
213
214 /**
215  * check_mutually_exclusive - Check if new_state violates mutually_exclusive
216  *                            condition.
217  * @edev:       the extcon device
218  * @new_state:  new cable attach status for @edev
219  *
220  * Returns 0 if nothing violates. Returns the index + 1 for the first
221  * violated condition.
222  */
223 static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
224 {
225         int i = 0;
226
227         if (!edev->mutually_exclusive)
228                 return 0;
229
230         for (i = 0; edev->mutually_exclusive[i]; i++) {
231                 int weight;
232                 u32 correspondants = new_state & edev->mutually_exclusive[i];
233
234                 /* calculate the total number of bits set */
235                 weight = hweight32(correspondants);
236                 if (weight > 1)
237                         return i + 1;
238         }
239
240         return 0;
241 }
242
243 static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
244 {
245         int i;
246
247         /* Find the the index of extcon cable in edev->supported_cable */
248         for (i = 0; i < edev->max_supported; i++) {
249                 if (edev->supported_cable[i] == id)
250                         return i;
251         }
252
253         return -EINVAL;
254 }
255
256 static int get_extcon_type(unsigned int prop)
257 {
258         switch (prop) {
259         case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
260                 return EXTCON_TYPE_USB;
261         case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
262                 return EXTCON_TYPE_CHG;
263         case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
264                 return EXTCON_TYPE_JACK;
265         case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
266                 return EXTCON_TYPE_DISP;
267         default:
268                 return -EINVAL;
269         }
270 }
271
272 static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
273 {
274         return !!(edev->state & BIT(index));
275 }
276
277 static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
278 {
279         if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
280                 *attached = ((new >> idx) & 0x1) ? true : false;
281                 return true;
282         }
283
284         return false;
285 }
286
287 static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
288 {
289         int type;
290
291         /* Check whether the property is supported or not. */
292         type = get_extcon_type(prop);
293         if (type < 0)
294                 return false;
295
296         /* Check whether a specific extcon id supports the property or not. */
297         return !!(extcon_info[id].type & type);
298 }
299
300 static void init_property(struct extcon_dev *edev, unsigned int id, int index)
301 {
302         unsigned int type = extcon_info[id].type;
303         struct extcon_cable *cable = &edev->cables[index];
304
305         if (EXTCON_TYPE_USB & type)
306                 memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
307         if (EXTCON_TYPE_CHG & type)
308                 memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
309         if (EXTCON_TYPE_JACK & type)
310                 memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
311         if (EXTCON_TYPE_DISP & type)
312                 memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
313 }
314
315 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
316                           char *buf)
317 {
318         int i, count = 0;
319         struct extcon_dev *edev = dev_get_drvdata(dev);
320
321         if (edev->max_supported == 0)
322                 return sprintf(buf, "%u\n", edev->state);
323
324         for (i = 0; i < edev->max_supported; i++) {
325                 count += sprintf(buf + count, "%s=%d\n",
326                                 extcon_info[edev->supported_cable[i]].name,
327                                  !!(edev->state & (1 << i)));
328         }
329
330         return count;
331 }
332 static DEVICE_ATTR_RO(state);
333
334 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
335                 char *buf)
336 {
337         struct extcon_dev *edev = dev_get_drvdata(dev);
338
339         return sprintf(buf, "%s\n", edev->name);
340 }
341 static DEVICE_ATTR_RO(name);
342
343 static ssize_t cable_name_show(struct device *dev,
344                                struct device_attribute *attr, char *buf)
345 {
346         struct extcon_cable *cable = container_of(attr, struct extcon_cable,
347                                                   attr_name);
348         int i = cable->cable_index;
349
350         return sprintf(buf, "%s\n",
351                         extcon_info[cable->edev->supported_cable[i]].name);
352 }
353
354 static ssize_t cable_state_show(struct device *dev,
355                                 struct device_attribute *attr, char *buf)
356 {
357         struct extcon_cable *cable = container_of(attr, struct extcon_cable,
358                                                   attr_state);
359
360         int i = cable->cable_index;
361
362         return sprintf(buf, "%d\n",
363                        extcon_get_cable_state_(cable->edev,
364                                                cable->edev->supported_cable[i]));
365 }
366
367 /**
368  * extcon_update_state() - Update the cable attach states of the extcon device
369  *                         only for the masked bits.
370  * @edev:       the extcon device
371  * @mask:       the bit mask to designate updated bits.
372  * @state:      new cable attach status for @edev
373  *
374  * Changing the state sends uevent with environment variable containing
375  * the name of extcon device (envp[0]) and the state output (envp[1]).
376  * Tizen uses this format for extcon device to get events from ports.
377  * Android uses this format as well.
378  *
379  * Note that the notifier provides which bits are changed in the state
380  * variable with the val parameter (second) to the callback.
381  */
382 static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
383 {
384         char name_buf[120];
385         char state_buf[120];
386         char *prop_buf;
387         char *envp[3];
388         int env_offset = 0;
389         int length;
390         int index;
391         unsigned long flags;
392         bool attached;
393
394         if (!edev)
395                 return -EINVAL;
396
397         spin_lock_irqsave(&edev->lock, flags);
398
399         if (edev->state != ((edev->state & ~mask) | (state & mask))) {
400                 u32 old_state;
401
402                 if (check_mutually_exclusive(edev, (edev->state & ~mask) |
403                                                    (state & mask))) {
404                         spin_unlock_irqrestore(&edev->lock, flags);
405                         return -EPERM;
406                 }
407
408                 old_state = edev->state;
409                 edev->state &= ~mask;
410                 edev->state |= state & mask;
411
412                 for (index = 0; index < edev->max_supported; index++) {
413                         if (is_extcon_changed(old_state, edev->state, index,
414                                               &attached))
415                                 raw_notifier_call_chain(&edev->nh[index],
416                                                         attached, edev);
417                 }
418
419                 /* This could be in interrupt handler */
420                 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
421                 if (prop_buf) {
422                         length = name_show(&edev->dev, NULL, prop_buf);
423                         if (length > 0) {
424                                 if (prop_buf[length - 1] == '\n')
425                                         prop_buf[length - 1] = 0;
426                                 snprintf(name_buf, sizeof(name_buf),
427                                         "NAME=%s", prop_buf);
428                                 envp[env_offset++] = name_buf;
429                         }
430                         length = state_show(&edev->dev, NULL, prop_buf);
431                         if (length > 0) {
432                                 if (prop_buf[length - 1] == '\n')
433                                         prop_buf[length - 1] = 0;
434                                 snprintf(state_buf, sizeof(state_buf),
435                                         "STATE=%s", prop_buf);
436                                 envp[env_offset++] = state_buf;
437                         }
438                         envp[env_offset] = NULL;
439                         /* Unlock early before uevent */
440                         spin_unlock_irqrestore(&edev->lock, flags);
441
442                         kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
443                         free_page((unsigned long)prop_buf);
444                 } else {
445                         /* Unlock early before uevent */
446                         spin_unlock_irqrestore(&edev->lock, flags);
447
448                         dev_err(&edev->dev, "out of memory in extcon_set_state\n");
449                         kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
450                 }
451         } else {
452                 /* No changes */
453                 spin_unlock_irqrestore(&edev->lock, flags);
454         }
455
456         return 0;
457 }
458
459 /**
460  * extcon_get_cable_state_() - Get the status of a specific cable.
461  * @edev:       the extcon device that has the cable.
462  * @id:         the unique id of each external connector in extcon enumeration.
463  */
464 int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
465 {
466         int index;
467
468         if (!edev)
469                 return -EINVAL;
470
471         index = find_cable_index_by_id(edev, id);
472         if (index < 0)
473                 return index;
474
475         if (edev->max_supported && edev->max_supported <= index)
476                 return -EINVAL;
477
478         return is_extcon_attached(edev, index);
479 }
480 EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
481
482 /**
483  * extcon_set_cable_state_() - Set the status of a specific cable.
484  * @edev:               the extcon device that has the cable.
485  * @id:                 the unique id of each external connector
486  *                      in extcon enumeration.
487  * @state:              the new cable status. The default semantics is
488  *                      true: attached / false: detached.
489  */
490 int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
491                                 bool cable_state)
492 {
493         u32 state;
494         int index;
495
496         if (!edev)
497                 return -EINVAL;
498
499         index = find_cable_index_by_id(edev, id);
500         if (index < 0)
501                 return index;
502
503         if (edev->max_supported && edev->max_supported <= index)
504                 return -EINVAL;
505
506         /*
507          * Initialize the value of extcon property before setting
508          * the detached state for an external connector.
509          */
510         if (!cable_state)
511                 init_property(edev, id, index);
512
513         state = cable_state ? (1 << index) : 0;
514         return extcon_update_state(edev, 1 << index, state);
515 }
516 EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
517
518 /**
519  * extcon_get_property() - Get the property value of a specific cable.
520  * @edev:               the extcon device that has the cable.
521  * @id:                 the unique id of each external connector
522  *                      in extcon enumeration.
523  * @prop:               the property id among enum extcon_property.
524  * @prop_val:           the pointer which store the value of property.
525  *
526  * When getting the property value of external connector, the external connector
527  * should be attached. If detached state, function just return 0 without
528  * property value. Also, the each property should be included in the list of
529  * supported properties according to the type of external connectors.
530  *
531  * Returns 0 if success or error number if fail
532  */
533 int extcon_get_property(struct extcon_dev *edev, unsigned int id,
534                                 unsigned int prop,
535                                 union extcon_property_value *prop_val)
536 {
537         struct extcon_cable *cable;
538         unsigned long flags;
539         int index, ret = 0;
540
541         *prop_val = (union extcon_property_value)(0);
542
543         if (!edev)
544                 return -EINVAL;
545
546         /* Check whether the property is supported or not */
547         if (!is_extcon_property_supported(id, prop))
548                 return -EINVAL;
549
550         /* Find the cable index of external connector by using id */
551         index = find_cable_index_by_id(edev, id);
552         if (index < 0)
553                 return index;
554
555         spin_lock_irqsave(&edev->lock, flags);
556
557         /*
558          * Check whether the external connector is attached.
559          * If external connector is detached, the user can not
560          * get the property value.
561          */
562         if (!is_extcon_attached(edev, index)) {
563                 spin_unlock_irqrestore(&edev->lock, flags);
564                 return 0;
565         }
566
567         cable = &edev->cables[index];
568
569         /* Get the property value according to extcon type */
570         switch (prop) {
571         case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
572                 *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
573                 break;
574         case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
575                 *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
576                 break;
577         case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
578                 *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
579                 break;
580         case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
581                 *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
582                 break;
583         default:
584                 ret = -EINVAL;
585                 break;
586         }
587
588         spin_unlock_irqrestore(&edev->lock, flags);
589
590         return ret;
591 }
592 EXPORT_SYMBOL_GPL(extcon_get_property);
593
594 /**
595  * extcon_set_property() - Set the property value of a specific cable.
596  * @edev:               the extcon device that has the cable.
597  * @id:                 the unique id of each external connector
598  *                      in extcon enumeration.
599  * @prop:               the property id among enum extcon_property.
600  * @prop_val:           the pointer including the new value of property.
601  *
602  * The each property should be included in the list of supported properties
603  * according to the type of external connectors.
604  *
605  * Returns 0 if success or error number if fail
606  */
607 int extcon_set_property(struct extcon_dev *edev, unsigned int id,
608                                 unsigned int prop,
609                                 union extcon_property_value prop_val)
610 {
611         struct extcon_cable *cable;
612         unsigned long flags;
613         int index, ret = 0;
614
615         if (!edev)
616                 return -EINVAL;
617
618         /* Check whether the property is supported or not */
619         if (!is_extcon_property_supported(id, prop))
620                 return -EINVAL;
621
622         /* Find the cable index of external connector by using id */
623         index = find_cable_index_by_id(edev, id);
624         if (index < 0)
625                 return index;
626
627         spin_lock_irqsave(&edev->lock, flags);
628
629         cable = &edev->cables[index];
630
631         /* Set the property value according to extcon type */
632         switch (prop) {
633         case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
634                 cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
635                 break;
636         case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
637                 cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
638                 break;
639         case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
640                 cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
641                 break;
642         case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
643                 cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
644                 break;
645         default:
646                 ret = -EINVAL;
647                 break;
648         }
649
650         spin_unlock_irqrestore(&edev->lock, flags);
651
652         return ret;
653 }
654 EXPORT_SYMBOL_GPL(extcon_set_property);
655
656 /**
657  * extcon_get_extcon_dev() - Get the extcon device instance from the name
658  * @extcon_name:        The extcon name provided with extcon_dev_register()
659  */
660 struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
661 {
662         struct extcon_dev *sd;
663
664         if (!extcon_name)
665                 return ERR_PTR(-EINVAL);
666
667         mutex_lock(&extcon_dev_list_lock);
668         list_for_each_entry(sd, &extcon_dev_list, entry) {
669                 if (!strcmp(sd->name, extcon_name))
670                         goto out;
671         }
672         sd = NULL;
673 out:
674         mutex_unlock(&extcon_dev_list_lock);
675         return sd;
676 }
677 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
678
679 /**
680  * extcon_register_notifier() - Register a notifiee to get notified by
681  *                              any attach status changes from the extcon.
682  * @edev:       the extcon device that has the external connecotr.
683  * @id:         the unique id of each external connector in extcon enumeration.
684  * @nb:         a notifier block to be registered.
685  *
686  * Note that the second parameter given to the callback of nb (val) is
687  * "old_state", not the current state. The current state can be retrieved
688  * by looking at the third pameter (edev pointer)'s state value.
689  */
690 int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
691                              struct notifier_block *nb)
692 {
693         unsigned long flags;
694         int ret, idx = -EINVAL;
695
696         if (!nb)
697                 return -EINVAL;
698
699         if (edev) {
700                 idx = find_cable_index_by_id(edev, id);
701                 if (idx < 0)
702                         return idx;
703
704                 spin_lock_irqsave(&edev->lock, flags);
705                 ret = raw_notifier_chain_register(&edev->nh[idx], nb);
706                 spin_unlock_irqrestore(&edev->lock, flags);
707         } else {
708                 struct extcon_dev *extd;
709
710                 mutex_lock(&extcon_dev_list_lock);
711                 list_for_each_entry(extd, &extcon_dev_list, entry) {
712                         idx = find_cable_index_by_id(extd, id);
713                         if (idx >= 0)
714                                 break;
715                 }
716                 mutex_unlock(&extcon_dev_list_lock);
717
718                 if (idx >= 0) {
719                         edev = extd;
720                         return extcon_register_notifier(extd, id, nb);
721                 } else {
722                         ret = -ENODEV;
723                 }
724         }
725
726         return ret;
727 }
728 EXPORT_SYMBOL_GPL(extcon_register_notifier);
729
730 /**
731  * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
732  * @edev:       the extcon device that has the external connecotr.
733  * @id:         the unique id of each external connector in extcon enumeration.
734  * @nb:         a notifier block to be registered.
735  */
736 int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
737                                 struct notifier_block *nb)
738 {
739         unsigned long flags;
740         int ret, idx;
741
742         if (!edev || !nb)
743                 return -EINVAL;
744
745         idx = find_cable_index_by_id(edev, id);
746         if (idx < 0)
747                 return idx;
748
749         spin_lock_irqsave(&edev->lock, flags);
750         ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
751         spin_unlock_irqrestore(&edev->lock, flags);
752
753         return ret;
754 }
755 EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
756
757 static struct attribute *extcon_attrs[] = {
758         &dev_attr_state.attr,
759         &dev_attr_name.attr,
760         NULL,
761 };
762 ATTRIBUTE_GROUPS(extcon);
763
764 static int create_extcon_class(void)
765 {
766         if (!extcon_class) {
767                 extcon_class = class_create(THIS_MODULE, "extcon");
768                 if (IS_ERR(extcon_class))
769                         return PTR_ERR(extcon_class);
770                 extcon_class->dev_groups = extcon_groups;
771
772 #if defined(CONFIG_ANDROID)
773                 switch_class = class_compat_register("switch");
774                 if (WARN(!switch_class, "cannot allocate"))
775                         return -ENOMEM;
776 #endif /* CONFIG_ANDROID */
777         }
778
779         return 0;
780 }
781
782 static void extcon_dev_release(struct device *dev)
783 {
784 }
785
786 static const char *muex_name = "mutually_exclusive";
787 static void dummy_sysfs_dev_release(struct device *dev)
788 {
789 }
790
791 /*
792  * extcon_dev_allocate() - Allocate the memory of extcon device.
793  * @supported_cable:    Array of supported extcon ending with EXTCON_NONE.
794  *                      If supported_cable is NULL, cable name related APIs
795  *                      are disabled.
796  *
797  * This function allocates the memory for extcon device without allocating
798  * memory in each extcon provider driver and initialize default setting for
799  * extcon device.
800  *
801  * Return the pointer of extcon device if success or ERR_PTR(err) if fail
802  */
803 struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
804 {
805         struct extcon_dev *edev;
806
807         if (!supported_cable)
808                 return ERR_PTR(-EINVAL);
809
810         edev = kzalloc(sizeof(*edev), GFP_KERNEL);
811         if (!edev)
812                 return ERR_PTR(-ENOMEM);
813
814         edev->max_supported = 0;
815         edev->supported_cable = supported_cable;
816
817         return edev;
818 }
819
820 /*
821  * extcon_dev_free() - Free the memory of extcon device.
822  * @edev:       the extcon device to free
823  */
824 void extcon_dev_free(struct extcon_dev *edev)
825 {
826         kfree(edev);
827 }
828 EXPORT_SYMBOL_GPL(extcon_dev_free);
829
830 /**
831  * extcon_dev_register() - Register a new extcon device
832  * @edev        : the new extcon device (should be allocated before calling)
833  *
834  * Among the members of edev struct, please set the "user initializing data"
835  * in any case and set the "optional callbacks" if required. However, please
836  * do not set the values of "internal data", which are initialized by
837  * this function.
838  */
839 int extcon_dev_register(struct extcon_dev *edev)
840 {
841         int ret, index = 0;
842         static atomic_t edev_no = ATOMIC_INIT(-1);
843
844         if (!extcon_class) {
845                 ret = create_extcon_class();
846                 if (ret < 0)
847                         return ret;
848         }
849
850         if (!edev || !edev->supported_cable)
851                 return -EINVAL;
852
853         for (; edev->supported_cable[index] != EXTCON_NONE; index++);
854
855         edev->max_supported = index;
856         if (index > SUPPORTED_CABLE_MAX) {
857                 dev_err(&edev->dev,
858                         "exceed the maximum number of supported cables\n");
859                 return -EINVAL;
860         }
861
862         edev->dev.class = extcon_class;
863         edev->dev.release = extcon_dev_release;
864
865         edev->name = dev_name(edev->dev.parent);
866         if (IS_ERR_OR_NULL(edev->name)) {
867                 dev_err(&edev->dev,
868                         "extcon device name is null\n");
869                 return -EINVAL;
870         }
871         dev_set_name(&edev->dev, "extcon%lu",
872                         (unsigned long)atomic_inc_return(&edev_no));
873
874         if (edev->max_supported) {
875                 char buf[10];
876                 char *str;
877                 struct extcon_cable *cable;
878
879                 edev->cables = kzalloc(sizeof(struct extcon_cable) *
880                                        edev->max_supported, GFP_KERNEL);
881                 if (!edev->cables) {
882                         ret = -ENOMEM;
883                         goto err_sysfs_alloc;
884                 }
885                 for (index = 0; index < edev->max_supported; index++) {
886                         cable = &edev->cables[index];
887
888                         snprintf(buf, 10, "cable.%d", index);
889                         str = kzalloc(sizeof(char) * (strlen(buf) + 1),
890                                       GFP_KERNEL);
891                         if (!str) {
892                                 for (index--; index >= 0; index--) {
893                                         cable = &edev->cables[index];
894                                         kfree(cable->attr_g.name);
895                                 }
896                                 ret = -ENOMEM;
897
898                                 goto err_alloc_cables;
899                         }
900                         strcpy(str, buf);
901
902                         cable->edev = edev;
903                         cable->cable_index = index;
904                         cable->attrs[0] = &cable->attr_name.attr;
905                         cable->attrs[1] = &cable->attr_state.attr;
906                         cable->attrs[2] = NULL;
907                         cable->attr_g.name = str;
908                         cable->attr_g.attrs = cable->attrs;
909
910                         sysfs_attr_init(&cable->attr_name.attr);
911                         cable->attr_name.attr.name = "name";
912                         cable->attr_name.attr.mode = 0444;
913                         cable->attr_name.show = cable_name_show;
914
915                         sysfs_attr_init(&cable->attr_state.attr);
916                         cable->attr_state.attr.name = "state";
917                         cable->attr_state.attr.mode = 0444;
918                         cable->attr_state.show = cable_state_show;
919                 }
920         }
921
922         if (edev->max_supported && edev->mutually_exclusive) {
923                 char buf[80];
924                 char *name;
925
926                 /* Count the size of mutually_exclusive array */
927                 for (index = 0; edev->mutually_exclusive[index]; index++)
928                         ;
929
930                 edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
931                                            (index + 1), GFP_KERNEL);
932                 if (!edev->attrs_muex) {
933                         ret = -ENOMEM;
934                         goto err_muex;
935                 }
936
937                 edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
938                                              index, GFP_KERNEL);
939                 if (!edev->d_attrs_muex) {
940                         ret = -ENOMEM;
941                         kfree(edev->attrs_muex);
942                         goto err_muex;
943                 }
944
945                 for (index = 0; edev->mutually_exclusive[index]; index++) {
946                         sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
947                         name = kzalloc(sizeof(char) * (strlen(buf) + 1),
948                                        GFP_KERNEL);
949                         if (!name) {
950                                 for (index--; index >= 0; index--) {
951                                         kfree(edev->d_attrs_muex[index].attr.
952                                               name);
953                                 }
954                                 kfree(edev->d_attrs_muex);
955                                 kfree(edev->attrs_muex);
956                                 ret = -ENOMEM;
957                                 goto err_muex;
958                         }
959                         strcpy(name, buf);
960                         sysfs_attr_init(&edev->d_attrs_muex[index].attr);
961                         edev->d_attrs_muex[index].attr.name = name;
962                         edev->d_attrs_muex[index].attr.mode = 0000;
963                         edev->attrs_muex[index] = &edev->d_attrs_muex[index]
964                                                         .attr;
965                 }
966                 edev->attr_g_muex.name = muex_name;
967                 edev->attr_g_muex.attrs = edev->attrs_muex;
968
969         }
970
971         if (edev->max_supported) {
972                 edev->extcon_dev_type.groups =
973                         kzalloc(sizeof(struct attribute_group *) *
974                                 (edev->max_supported + 2), GFP_KERNEL);
975                 if (!edev->extcon_dev_type.groups) {
976                         ret = -ENOMEM;
977                         goto err_alloc_groups;
978                 }
979
980                 edev->extcon_dev_type.name = dev_name(&edev->dev);
981                 edev->extcon_dev_type.release = dummy_sysfs_dev_release;
982
983                 for (index = 0; index < edev->max_supported; index++)
984                         edev->extcon_dev_type.groups[index] =
985                                 &edev->cables[index].attr_g;
986                 if (edev->mutually_exclusive)
987                         edev->extcon_dev_type.groups[index] =
988                                 &edev->attr_g_muex;
989
990                 edev->dev.type = &edev->extcon_dev_type;
991         }
992
993         ret = device_register(&edev->dev);
994         if (ret) {
995                 put_device(&edev->dev);
996                 goto err_dev;
997         }
998 #if defined(CONFIG_ANDROID)
999         if (switch_class)
1000                 ret = class_compat_create_link(switch_class, &edev->dev, NULL);
1001 #endif /* CONFIG_ANDROID */
1002
1003         spin_lock_init(&edev->lock);
1004
1005         edev->nh = devm_kzalloc(&edev->dev,
1006                         sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
1007         if (!edev->nh) {
1008                 ret = -ENOMEM;
1009                 goto err_dev;
1010         }
1011
1012         for (index = 0; index < edev->max_supported; index++)
1013                 RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
1014
1015         dev_set_drvdata(&edev->dev, edev);
1016         edev->state = 0;
1017
1018         mutex_lock(&extcon_dev_list_lock);
1019         list_add(&edev->entry, &extcon_dev_list);
1020         mutex_unlock(&extcon_dev_list_lock);
1021
1022         return 0;
1023
1024 err_dev:
1025         if (edev->max_supported)
1026                 kfree(edev->extcon_dev_type.groups);
1027 err_alloc_groups:
1028         if (edev->max_supported && edev->mutually_exclusive) {
1029                 for (index = 0; edev->mutually_exclusive[index]; index++)
1030                         kfree(edev->d_attrs_muex[index].attr.name);
1031                 kfree(edev->d_attrs_muex);
1032                 kfree(edev->attrs_muex);
1033         }
1034 err_muex:
1035         for (index = 0; index < edev->max_supported; index++)
1036                 kfree(edev->cables[index].attr_g.name);
1037 err_alloc_cables:
1038         if (edev->max_supported)
1039                 kfree(edev->cables);
1040 err_sysfs_alloc:
1041         return ret;
1042 }
1043 EXPORT_SYMBOL_GPL(extcon_dev_register);
1044
1045 /**
1046  * extcon_dev_unregister() - Unregister the extcon device.
1047  * @edev:       the extcon device instance to be unregistered.
1048  *
1049  * Note that this does not call kfree(edev) because edev was not allocated
1050  * by this class.
1051  */
1052 void extcon_dev_unregister(struct extcon_dev *edev)
1053 {
1054         int index;
1055
1056         if (!edev)
1057                 return;
1058
1059         mutex_lock(&extcon_dev_list_lock);
1060         list_del(&edev->entry);
1061         mutex_unlock(&extcon_dev_list_lock);
1062
1063         if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
1064                 dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
1065                                 dev_name(&edev->dev));
1066                 return;
1067         }
1068
1069         device_unregister(&edev->dev);
1070
1071         if (edev->mutually_exclusive && edev->max_supported) {
1072                 for (index = 0; edev->mutually_exclusive[index];
1073                                 index++)
1074                         kfree(edev->d_attrs_muex[index].attr.name);
1075                 kfree(edev->d_attrs_muex);
1076                 kfree(edev->attrs_muex);
1077         }
1078
1079         for (index = 0; index < edev->max_supported; index++)
1080                 kfree(edev->cables[index].attr_g.name);
1081
1082         if (edev->max_supported) {
1083                 kfree(edev->extcon_dev_type.groups);
1084                 kfree(edev->cables);
1085         }
1086
1087 #if defined(CONFIG_ANDROID)
1088         if (switch_class)
1089                 class_compat_remove_link(switch_class, &edev->dev, NULL);
1090 #endif
1091         put_device(&edev->dev);
1092 }
1093 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
1094
1095 #ifdef CONFIG_OF
1096 /*
1097  * extcon_get_edev_by_phandle - Get the extcon device from devicetree
1098  * @dev - instance to the given device
1099  * @index - index into list of extcon_dev
1100  *
1101  * return the instance of extcon device
1102  */
1103 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1104 {
1105         struct device_node *node;
1106         struct extcon_dev *edev;
1107
1108         if (!dev)
1109                 return ERR_PTR(-EINVAL);
1110
1111         if (!dev->of_node) {
1112                 dev_dbg(dev, "device does not have a device node entry\n");
1113                 return ERR_PTR(-EINVAL);
1114         }
1115
1116         node = of_parse_phandle(dev->of_node, "extcon", index);
1117         if (!node) {
1118                 dev_dbg(dev, "failed to get phandle in %s node\n",
1119                         dev->of_node->full_name);
1120                 return ERR_PTR(-ENODEV);
1121         }
1122
1123         mutex_lock(&extcon_dev_list_lock);
1124         list_for_each_entry(edev, &extcon_dev_list, entry) {
1125                 if (edev->dev.parent && edev->dev.parent->of_node == node) {
1126                         mutex_unlock(&extcon_dev_list_lock);
1127                         of_node_put(node);
1128                         return edev;
1129                 }
1130         }
1131         mutex_unlock(&extcon_dev_list_lock);
1132         of_node_put(node);
1133
1134         return ERR_PTR(-EPROBE_DEFER);
1135 }
1136 #else
1137 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1138 {
1139         return ERR_PTR(-ENOSYS);
1140 }
1141 #endif /* CONFIG_OF */
1142 EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
1143
1144 /**
1145  * extcon_get_edev_name() - Get the name of the extcon device.
1146  * @edev:       the extcon device
1147  */
1148 const char *extcon_get_edev_name(struct extcon_dev *edev)
1149 {
1150         return !edev ? NULL : edev->name;
1151 }
1152
1153 static int __init extcon_class_init(void)
1154 {
1155         return create_extcon_class();
1156 }
1157 module_init(extcon_class_init);
1158
1159 static void __exit extcon_class_exit(void)
1160 {
1161 #if defined(CONFIG_ANDROID)
1162         class_compat_unregister(switch_class);
1163 #endif
1164         class_destroy(extcon_class);
1165 }
1166 module_exit(extcon_class_exit);
1167
1168 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
1169 MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
1170 MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
1171 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1172 MODULE_DESCRIPTION("External connector (extcon) class driver");
1173 MODULE_LICENSE("GPL");