Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * drivers/extcon/extcon.c - External Connector (extcon) framework.
0004  *
0005  * Copyright (C) 2015 Samsung Electronics
0006  * Author: Chanwoo Choi <cw00.choi@samsung.com>
0007  *
0008  * Copyright (C) 2012 Samsung Electronics
0009  * Author: Donggeun Kim <dg77.kim@samsung.com>
0010  * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
0011  *
0012  * based on android/drivers/switch/switch_class.c
0013  * Copyright (C) 2008 Google, Inc.
0014  * Author: Mike Lockwood <lockwood@android.com>
0015  */
0016 
0017 #include <linux/module.h>
0018 #include <linux/types.h>
0019 #include <linux/init.h>
0020 #include <linux/device.h>
0021 #include <linux/fs.h>
0022 #include <linux/err.h>
0023 #include <linux/of.h>
0024 #include <linux/slab.h>
0025 #include <linux/sysfs.h>
0026 
0027 #include "extcon.h"
0028 
0029 #define SUPPORTED_CABLE_MAX 32
0030 
0031 static const struct __extcon_info {
0032     unsigned int type;
0033     unsigned int id;
0034     const char *name;
0035 
0036 } extcon_info[] = {
0037     [EXTCON_NONE] = {
0038         .type = EXTCON_TYPE_MISC,
0039         .id = EXTCON_NONE,
0040         .name = "NONE",
0041     },
0042 
0043     /* USB external connector */
0044     [EXTCON_USB] = {
0045         .type = EXTCON_TYPE_USB,
0046         .id = EXTCON_USB,
0047         .name = "USB",
0048     },
0049     [EXTCON_USB_HOST] = {
0050         .type = EXTCON_TYPE_USB,
0051         .id = EXTCON_USB_HOST,
0052         .name = "USB-HOST",
0053     },
0054 
0055     /* Charging external connector */
0056     [EXTCON_CHG_USB_SDP] = {
0057         .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0058         .id = EXTCON_CHG_USB_SDP,
0059         .name = "SDP",
0060     },
0061     [EXTCON_CHG_USB_DCP] = {
0062         .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0063         .id = EXTCON_CHG_USB_DCP,
0064         .name = "DCP",
0065     },
0066     [EXTCON_CHG_USB_CDP] = {
0067         .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0068         .id = EXTCON_CHG_USB_CDP,
0069         .name = "CDP",
0070     },
0071     [EXTCON_CHG_USB_ACA] = {
0072         .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0073         .id = EXTCON_CHG_USB_ACA,
0074         .name = "ACA",
0075     },
0076     [EXTCON_CHG_USB_FAST] = {
0077         .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0078         .id = EXTCON_CHG_USB_FAST,
0079         .name = "FAST-CHARGER",
0080     },
0081     [EXTCON_CHG_USB_SLOW] = {
0082         .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0083         .id = EXTCON_CHG_USB_SLOW,
0084         .name = "SLOW-CHARGER",
0085     },
0086     [EXTCON_CHG_WPT] = {
0087         .type = EXTCON_TYPE_CHG,
0088         .id = EXTCON_CHG_WPT,
0089         .name = "WPT",
0090     },
0091     [EXTCON_CHG_USB_PD] = {
0092         .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0093         .id = EXTCON_CHG_USB_PD,
0094         .name = "PD",
0095     },
0096 
0097     /* Jack external connector */
0098     [EXTCON_JACK_MICROPHONE] = {
0099         .type = EXTCON_TYPE_JACK,
0100         .id = EXTCON_JACK_MICROPHONE,
0101         .name = "MICROPHONE",
0102     },
0103     [EXTCON_JACK_HEADPHONE] = {
0104         .type = EXTCON_TYPE_JACK,
0105         .id = EXTCON_JACK_HEADPHONE,
0106         .name = "HEADPHONE",
0107     },
0108     [EXTCON_JACK_LINE_IN] = {
0109         .type = EXTCON_TYPE_JACK,
0110         .id = EXTCON_JACK_LINE_IN,
0111         .name = "LINE-IN",
0112     },
0113     [EXTCON_JACK_LINE_OUT] = {
0114         .type = EXTCON_TYPE_JACK,
0115         .id = EXTCON_JACK_LINE_OUT,
0116         .name = "LINE-OUT",
0117     },
0118     [EXTCON_JACK_VIDEO_IN] = {
0119         .type = EXTCON_TYPE_JACK,
0120         .id = EXTCON_JACK_VIDEO_IN,
0121         .name = "VIDEO-IN",
0122     },
0123     [EXTCON_JACK_VIDEO_OUT] = {
0124         .type = EXTCON_TYPE_JACK,
0125         .id = EXTCON_JACK_VIDEO_OUT,
0126         .name = "VIDEO-OUT",
0127     },
0128     [EXTCON_JACK_SPDIF_IN] = {
0129         .type = EXTCON_TYPE_JACK,
0130         .id = EXTCON_JACK_SPDIF_IN,
0131         .name = "SPDIF-IN",
0132     },
0133     [EXTCON_JACK_SPDIF_OUT] = {
0134         .type = EXTCON_TYPE_JACK,
0135         .id = EXTCON_JACK_SPDIF_OUT,
0136         .name = "SPDIF-OUT",
0137     },
0138 
0139     /* Display external connector */
0140     [EXTCON_DISP_HDMI] = {
0141         .type = EXTCON_TYPE_DISP,
0142         .id = EXTCON_DISP_HDMI,
0143         .name = "HDMI",
0144     },
0145     [EXTCON_DISP_MHL] = {
0146         .type = EXTCON_TYPE_DISP,
0147         .id = EXTCON_DISP_MHL,
0148         .name = "MHL",
0149     },
0150     [EXTCON_DISP_DVI] = {
0151         .type = EXTCON_TYPE_DISP,
0152         .id = EXTCON_DISP_DVI,
0153         .name = "DVI",
0154     },
0155     [EXTCON_DISP_VGA] = {
0156         .type = EXTCON_TYPE_DISP,
0157         .id = EXTCON_DISP_VGA,
0158         .name = "VGA",
0159     },
0160     [EXTCON_DISP_DP] = {
0161         .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
0162         .id = EXTCON_DISP_DP,
0163         .name = "DP",
0164     },
0165     [EXTCON_DISP_HMD] = {
0166         .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
0167         .id = EXTCON_DISP_HMD,
0168         .name = "HMD",
0169     },
0170     [EXTCON_DISP_CVBS] = {
0171         .type = EXTCON_TYPE_DISP,
0172         .id = EXTCON_DISP_CVBS,
0173         .name = "CVBS",
0174     },
0175     [EXTCON_DISP_EDP] = {
0176         .type = EXTCON_TYPE_DISP,
0177         .id = EXTCON_DISP_EDP,
0178         .name = "EDP",
0179     },
0180 
0181     /* Miscellaneous external connector */
0182     [EXTCON_DOCK] = {
0183         .type = EXTCON_TYPE_MISC,
0184         .id = EXTCON_DOCK,
0185         .name = "DOCK",
0186     },
0187     [EXTCON_JIG] = {
0188         .type = EXTCON_TYPE_MISC,
0189         .id = EXTCON_JIG,
0190         .name = "JIG",
0191     },
0192     [EXTCON_MECHANICAL] = {
0193         .type = EXTCON_TYPE_MISC,
0194         .id = EXTCON_MECHANICAL,
0195         .name = "MECHANICAL",
0196     },
0197 
0198     { /* sentinel */ }
0199 };
0200 
0201 /**
0202  * struct extcon_cable - An internal data for an external connector.
0203  * @edev:       the extcon device
0204  * @cable_index:    the index of this cable in the edev
0205  * @attr_g:     the attribute group for the cable
0206  * @attr_name:      "name" sysfs entry
0207  * @attr_state:     "state" sysfs entry
0208  * @attrs:      the array pointing to attr_name and attr_state for attr_g
0209  */
0210 struct extcon_cable {
0211     struct extcon_dev *edev;
0212     int cable_index;
0213 
0214     struct attribute_group attr_g;
0215     struct device_attribute attr_name;
0216     struct device_attribute attr_state;
0217 
0218     struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
0219 
0220     union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
0221     union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
0222     union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
0223     union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
0224 
0225     unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
0226     unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
0227     unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
0228     unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
0229 };
0230 
0231 static struct class *extcon_class;
0232 
0233 static LIST_HEAD(extcon_dev_list);
0234 static DEFINE_MUTEX(extcon_dev_list_lock);
0235 
0236 static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
0237 {
0238     int i = 0;
0239 
0240     if (!edev->mutually_exclusive)
0241         return 0;
0242 
0243     for (i = 0; edev->mutually_exclusive[i]; i++) {
0244         int weight;
0245         u32 correspondants = new_state & edev->mutually_exclusive[i];
0246 
0247         /* calculate the total number of bits set */
0248         weight = hweight32(correspondants);
0249         if (weight > 1)
0250             return i + 1;
0251     }
0252 
0253     return 0;
0254 }
0255 
0256 static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
0257 {
0258     int i;
0259 
0260     /* Find the index of extcon cable in edev->supported_cable */
0261     for (i = 0; i < edev->max_supported; i++) {
0262         if (edev->supported_cable[i] == id)
0263             return i;
0264     }
0265 
0266     return -EINVAL;
0267 }
0268 
0269 static int get_extcon_type(unsigned int prop)
0270 {
0271     switch (prop) {
0272     case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
0273         return EXTCON_TYPE_USB;
0274     case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
0275         return EXTCON_TYPE_CHG;
0276     case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
0277         return EXTCON_TYPE_JACK;
0278     case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
0279         return EXTCON_TYPE_DISP;
0280     default:
0281         return -EINVAL;
0282     }
0283 }
0284 
0285 static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
0286 {
0287     return !!(edev->state & BIT(index));
0288 }
0289 
0290 static bool is_extcon_changed(struct extcon_dev *edev, int index,
0291                 bool new_state)
0292 {
0293     int state = !!(edev->state & BIT(index));
0294     return (state != new_state);
0295 }
0296 
0297 static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
0298 {
0299     int type;
0300 
0301     /* Check whether the property is supported or not. */
0302     type = get_extcon_type(prop);
0303     if (type < 0)
0304         return false;
0305 
0306     /* Check whether a specific extcon id supports the property or not. */
0307     return !!(extcon_info[id].type & type);
0308 }
0309 
0310 static int is_extcon_property_capability(struct extcon_dev *edev,
0311                 unsigned int id, int index,unsigned int prop)
0312 {
0313     struct extcon_cable *cable;
0314     int type, ret;
0315 
0316     /* Check whether the property is supported or not. */
0317     type = get_extcon_type(prop);
0318     if (type < 0)
0319         return type;
0320 
0321     cable = &edev->cables[index];
0322 
0323     switch (type) {
0324     case EXTCON_TYPE_USB:
0325         ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
0326         break;
0327     case EXTCON_TYPE_CHG:
0328         ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
0329         break;
0330     case EXTCON_TYPE_JACK:
0331         ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
0332         break;
0333     case EXTCON_TYPE_DISP:
0334         ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
0335         break;
0336     default:
0337         ret = -EINVAL;
0338     }
0339 
0340     return ret;
0341 }
0342 
0343 static void init_property(struct extcon_dev *edev, unsigned int id, int index)
0344 {
0345     unsigned int type = extcon_info[id].type;
0346     struct extcon_cable *cable = &edev->cables[index];
0347 
0348     if (EXTCON_TYPE_USB & type)
0349         memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
0350     if (EXTCON_TYPE_CHG & type)
0351         memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
0352     if (EXTCON_TYPE_JACK & type)
0353         memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
0354     if (EXTCON_TYPE_DISP & type)
0355         memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
0356 }
0357 
0358 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
0359               char *buf)
0360 {
0361     int i, count = 0;
0362     struct extcon_dev *edev = dev_get_drvdata(dev);
0363 
0364     if (edev->max_supported == 0)
0365         return sprintf(buf, "%u\n", edev->state);
0366 
0367     for (i = 0; i < edev->max_supported; i++) {
0368         count += sprintf(buf + count, "%s=%d\n",
0369                 extcon_info[edev->supported_cable[i]].name,
0370                  !!(edev->state & BIT(i)));
0371     }
0372 
0373     return count;
0374 }
0375 static DEVICE_ATTR_RO(state);
0376 
0377 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
0378         char *buf)
0379 {
0380     struct extcon_dev *edev = dev_get_drvdata(dev);
0381 
0382     return sprintf(buf, "%s\n", edev->name);
0383 }
0384 static DEVICE_ATTR_RO(name);
0385 
0386 static ssize_t cable_name_show(struct device *dev,
0387                    struct device_attribute *attr, char *buf)
0388 {
0389     struct extcon_cable *cable = container_of(attr, struct extcon_cable,
0390                           attr_name);
0391     int i = cable->cable_index;
0392 
0393     return sprintf(buf, "%s\n",
0394             extcon_info[cable->edev->supported_cable[i]].name);
0395 }
0396 
0397 static ssize_t cable_state_show(struct device *dev,
0398                 struct device_attribute *attr, char *buf)
0399 {
0400     struct extcon_cable *cable = container_of(attr, struct extcon_cable,
0401                           attr_state);
0402 
0403     int i = cable->cable_index;
0404 
0405     return sprintf(buf, "%d\n",
0406         extcon_get_state(cable->edev, cable->edev->supported_cable[i]));
0407 }
0408 
0409 /**
0410  * extcon_sync() - Synchronize the state for an external connector.
0411  * @edev:   the extcon device
0412  * @id:     the unique id indicating an external connector
0413  *
0414  * Note that this function send a notification in order to synchronize
0415  * the state and property of an external connector.
0416  *
0417  * Returns 0 if success or error number if fail.
0418  */
0419 int extcon_sync(struct extcon_dev *edev, unsigned int id)
0420 {
0421     char name_buf[120];
0422     char state_buf[120];
0423     char *prop_buf;
0424     char *envp[3];
0425     int env_offset = 0;
0426     int length;
0427     int index;
0428     int state;
0429     unsigned long flags;
0430 
0431     if (!edev)
0432         return -EINVAL;
0433 
0434     index = find_cable_index_by_id(edev, id);
0435     if (index < 0)
0436         return index;
0437 
0438     spin_lock_irqsave(&edev->lock, flags);
0439     state = !!(edev->state & BIT(index));
0440     spin_unlock_irqrestore(&edev->lock, flags);
0441 
0442     /*
0443      * Call functions in a raw notifier chain for the specific one
0444      * external connector.
0445      */
0446     raw_notifier_call_chain(&edev->nh[index], state, edev);
0447 
0448     /*
0449      * Call functions in a raw notifier chain for the all supported
0450      * external connectors.
0451      */
0452     raw_notifier_call_chain(&edev->nh_all, state, edev);
0453 
0454     spin_lock_irqsave(&edev->lock, flags);
0455     /* This could be in interrupt handler */
0456     prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
0457     if (!prop_buf) {
0458         /* Unlock early before uevent */
0459         spin_unlock_irqrestore(&edev->lock, flags);
0460 
0461         dev_err(&edev->dev, "out of memory in extcon_set_state\n");
0462         kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
0463 
0464         return -ENOMEM;
0465     }
0466 
0467     length = name_show(&edev->dev, NULL, prop_buf);
0468     if (length > 0) {
0469         if (prop_buf[length - 1] == '\n')
0470             prop_buf[length - 1] = 0;
0471         snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf);
0472         envp[env_offset++] = name_buf;
0473     }
0474 
0475     length = state_show(&edev->dev, NULL, prop_buf);
0476     if (length > 0) {
0477         if (prop_buf[length - 1] == '\n')
0478             prop_buf[length - 1] = 0;
0479         snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf);
0480         envp[env_offset++] = state_buf;
0481     }
0482     envp[env_offset] = NULL;
0483 
0484     /* Unlock early before uevent */
0485     spin_unlock_irqrestore(&edev->lock, flags);
0486     kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
0487     free_page((unsigned long)prop_buf);
0488 
0489     return 0;
0490 }
0491 EXPORT_SYMBOL_GPL(extcon_sync);
0492 
0493 /**
0494  * extcon_get_state() - Get the state of an external connector.
0495  * @edev:   the extcon device
0496  * @id:     the unique id indicating an external connector
0497  *
0498  * Returns 0 if success or error number if fail.
0499  */
0500 int extcon_get_state(struct extcon_dev *edev, const unsigned int id)
0501 {
0502     int index, state;
0503     unsigned long flags;
0504 
0505     if (!edev)
0506         return -EINVAL;
0507 
0508     index = find_cable_index_by_id(edev, id);
0509     if (index < 0)
0510         return index;
0511 
0512     spin_lock_irqsave(&edev->lock, flags);
0513     state = is_extcon_attached(edev, index);
0514     spin_unlock_irqrestore(&edev->lock, flags);
0515 
0516     return state;
0517 }
0518 EXPORT_SYMBOL_GPL(extcon_get_state);
0519 
0520 /**
0521  * extcon_set_state() - Set the state of an external connector.
0522  * @edev:   the extcon device
0523  * @id:     the unique id indicating an external connector
0524  * @state:  the new state of an external connector.
0525  *      the default semantics is true: attached / false: detached.
0526  *
0527  * Note that this function set the state of an external connector without
0528  * a notification. To synchronize the state of an external connector,
0529  * have to use extcon_set_state_sync() and extcon_sync().
0530  *
0531  * Returns 0 if success or error number if fail.
0532  */
0533 int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state)
0534 {
0535     unsigned long flags;
0536     int index, ret = 0;
0537 
0538     if (!edev)
0539         return -EINVAL;
0540 
0541     index = find_cable_index_by_id(edev, id);
0542     if (index < 0)
0543         return index;
0544 
0545     spin_lock_irqsave(&edev->lock, flags);
0546 
0547     /* Check whether the external connector's state is changed. */
0548     if (!is_extcon_changed(edev, index, state))
0549         goto out;
0550 
0551     if (check_mutually_exclusive(edev,
0552         (edev->state & ~BIT(index)) | (state & BIT(index)))) {
0553         ret = -EPERM;
0554         goto out;
0555     }
0556 
0557     /*
0558      * Initialize the value of extcon property before setting
0559      * the detached state for an external connector.
0560      */
0561     if (!state)
0562         init_property(edev, id, index);
0563 
0564     /* Update the state for an external connector. */
0565     if (state)
0566         edev->state |= BIT(index);
0567     else
0568         edev->state &= ~(BIT(index));
0569 out:
0570     spin_unlock_irqrestore(&edev->lock, flags);
0571 
0572     return ret;
0573 }
0574 EXPORT_SYMBOL_GPL(extcon_set_state);
0575 
0576 /**
0577  * extcon_set_state_sync() - Set the state of an external connector with sync.
0578  * @edev:   the extcon device
0579  * @id:     the unique id indicating an external connector
0580  * @state:  the new state of external connector.
0581  *      the default semantics is true: attached / false: detached.
0582  *
0583  * Note that this function set the state of external connector
0584  * and synchronize the state by sending a notification.
0585  *
0586  * Returns 0 if success or error number if fail.
0587  */
0588 int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state)
0589 {
0590     int ret;
0591 
0592     ret = extcon_set_state(edev, id, state);
0593     if (ret < 0)
0594         return ret;
0595 
0596     return extcon_sync(edev, id);
0597 }
0598 EXPORT_SYMBOL_GPL(extcon_set_state_sync);
0599 
0600 /**
0601  * extcon_get_property() - Get the property value of an external connector.
0602  * @edev:   the extcon device
0603  * @id:     the unique id indicating an external connector
0604  * @prop:   the property id indicating an extcon property
0605  * @prop_val:   the pointer which store the value of extcon property
0606  *
0607  * Note that when getting the property value of external connector,
0608  * the external connector should be attached. If detached state, function
0609  * return 0 without property value. Also, the each property should be
0610  * included in the list of supported properties according to extcon type.
0611  *
0612  * Returns 0 if success or error number if fail.
0613  */
0614 int extcon_get_property(struct extcon_dev *edev, unsigned int id,
0615                 unsigned int prop,
0616                 union extcon_property_value *prop_val)
0617 {
0618     struct extcon_cable *cable;
0619     unsigned long flags;
0620     int index, ret = 0;
0621 
0622     *prop_val = (union extcon_property_value){0};
0623 
0624     if (!edev)
0625         return -EINVAL;
0626 
0627     /* Check whether the property is supported or not */
0628     if (!is_extcon_property_supported(id, prop))
0629         return -EINVAL;
0630 
0631     /* Find the cable index of external connector by using id */
0632     index = find_cable_index_by_id(edev, id);
0633     if (index < 0)
0634         return index;
0635 
0636     spin_lock_irqsave(&edev->lock, flags);
0637 
0638     /* Check whether the property is available or not. */
0639     if (!is_extcon_property_capability(edev, id, index, prop)) {
0640         spin_unlock_irqrestore(&edev->lock, flags);
0641         return -EPERM;
0642     }
0643 
0644     /*
0645      * Check whether the external connector is attached.
0646      * If external connector is detached, the user can not
0647      * get the property value.
0648      */
0649     if (!is_extcon_attached(edev, index)) {
0650         spin_unlock_irqrestore(&edev->lock, flags);
0651         return 0;
0652     }
0653 
0654     cable = &edev->cables[index];
0655 
0656     /* Get the property value according to extcon type */
0657     switch (prop) {
0658     case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
0659         *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
0660         break;
0661     case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
0662         *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
0663         break;
0664     case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
0665         *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
0666         break;
0667     case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
0668         *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
0669         break;
0670     default:
0671         ret = -EINVAL;
0672         break;
0673     }
0674 
0675     spin_unlock_irqrestore(&edev->lock, flags);
0676 
0677     return ret;
0678 }
0679 EXPORT_SYMBOL_GPL(extcon_get_property);
0680 
0681 /**
0682  * extcon_set_property() - Set the property value of an external connector.
0683  * @edev:   the extcon device
0684  * @id:     the unique id indicating an external connector
0685  * @prop:   the property id indicating an extcon property
0686  * @prop_val:   the pointer including the new value of extcon property
0687  *
0688  * Note that each property should be included in the list of supported
0689  * properties according to the extcon type.
0690  *
0691  * Returns 0 if success or error number if fail.
0692  */
0693 int extcon_set_property(struct extcon_dev *edev, unsigned int id,
0694                 unsigned int prop,
0695                 union extcon_property_value prop_val)
0696 {
0697     struct extcon_cable *cable;
0698     unsigned long flags;
0699     int index, ret = 0;
0700 
0701     if (!edev)
0702         return -EINVAL;
0703 
0704     /* Check whether the property is supported or not */
0705     if (!is_extcon_property_supported(id, prop))
0706         return -EINVAL;
0707 
0708     /* Find the cable index of external connector by using id */
0709     index = find_cable_index_by_id(edev, id);
0710     if (index < 0)
0711         return index;
0712 
0713     spin_lock_irqsave(&edev->lock, flags);
0714 
0715     /* Check whether the property is available or not. */
0716     if (!is_extcon_property_capability(edev, id, index, prop)) {
0717         spin_unlock_irqrestore(&edev->lock, flags);
0718         return -EPERM;
0719     }
0720 
0721     cable = &edev->cables[index];
0722 
0723     /* Set the property value according to extcon type */
0724     switch (prop) {
0725     case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
0726         cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
0727         break;
0728     case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
0729         cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
0730         break;
0731     case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
0732         cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
0733         break;
0734     case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
0735         cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
0736         break;
0737     default:
0738         ret = -EINVAL;
0739         break;
0740     }
0741 
0742     spin_unlock_irqrestore(&edev->lock, flags);
0743 
0744     return ret;
0745 }
0746 EXPORT_SYMBOL_GPL(extcon_set_property);
0747 
0748 /**
0749  * extcon_set_property_sync() - Set property of an external connector with sync.
0750  * @edev:   the extcon device
0751  * @id:     the unique id indicating an external connector
0752  * @prop:   the property id indicating an extcon property
0753  * @prop_val:   the pointer including the new value of extcon property
0754  *
0755  * Note that when setting the property value of external connector,
0756  * the external connector should be attached. The each property should
0757  * be included in the list of supported properties according to extcon type.
0758  *
0759  * Returns 0 if success or error number if fail.
0760  */
0761 int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
0762                 unsigned int prop,
0763                 union extcon_property_value prop_val)
0764 {
0765     int ret;
0766 
0767     ret = extcon_set_property(edev, id, prop, prop_val);
0768     if (ret < 0)
0769         return ret;
0770 
0771     return extcon_sync(edev, id);
0772 }
0773 EXPORT_SYMBOL_GPL(extcon_set_property_sync);
0774 
0775 /**
0776  * extcon_get_property_capability() - Get the capability of the property
0777  *                  for an external connector.
0778  * @edev:   the extcon device
0779  * @id:     the unique id indicating an external connector
0780  * @prop:   the property id indicating an extcon property
0781  *
0782  * Returns 1 if the property is available or 0 if not available.
0783  */
0784 int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
0785                     unsigned int prop)
0786 {
0787     int index;
0788 
0789     if (!edev)
0790         return -EINVAL;
0791 
0792     /* Check whether the property is supported or not */
0793     if (!is_extcon_property_supported(id, prop))
0794         return -EINVAL;
0795 
0796     /* Find the cable index of external connector by using id */
0797     index = find_cable_index_by_id(edev, id);
0798     if (index < 0)
0799         return index;
0800 
0801     return is_extcon_property_capability(edev, id, index, prop);
0802 }
0803 EXPORT_SYMBOL_GPL(extcon_get_property_capability);
0804 
0805 /**
0806  * extcon_set_property_capability() - Set the capability of the property
0807  *                  for an external connector.
0808  * @edev:   the extcon device
0809  * @id:     the unique id indicating an external connector
0810  * @prop:   the property id indicating an extcon property
0811  *
0812  * Note that this function set the capability of the property
0813  * for an external connector in order to mark the bit in capability
0814  * bitmap which mean the available state of the property.
0815  *
0816  * Returns 0 if success or error number if fail.
0817  */
0818 int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
0819                     unsigned int prop)
0820 {
0821     struct extcon_cable *cable;
0822     int index, type, ret = 0;
0823 
0824     if (!edev)
0825         return -EINVAL;
0826 
0827     /* Check whether the property is supported or not. */
0828     if (!is_extcon_property_supported(id, prop))
0829         return -EINVAL;
0830 
0831     /* Find the cable index of external connector by using id. */
0832     index = find_cable_index_by_id(edev, id);
0833     if (index < 0)
0834         return index;
0835 
0836     type = get_extcon_type(prop);
0837     if (type < 0)
0838         return type;
0839 
0840     cable = &edev->cables[index];
0841 
0842     switch (type) {
0843     case EXTCON_TYPE_USB:
0844         __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
0845         break;
0846     case EXTCON_TYPE_CHG:
0847         __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
0848         break;
0849     case EXTCON_TYPE_JACK:
0850         __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
0851         break;
0852     case EXTCON_TYPE_DISP:
0853         __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
0854         break;
0855     default:
0856         ret = -EINVAL;
0857     }
0858 
0859     return ret;
0860 }
0861 EXPORT_SYMBOL_GPL(extcon_set_property_capability);
0862 
0863 /**
0864  * extcon_get_extcon_dev() - Get the extcon device instance from the name.
0865  * @extcon_name:    the extcon name provided with extcon_dev_register()
0866  *
0867  * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
0868  * NOTE: This function returns -EPROBE_DEFER so it may only be called from
0869  * probe() functions.
0870  */
0871 struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
0872 {
0873     struct extcon_dev *sd;
0874 
0875     if (!extcon_name)
0876         return ERR_PTR(-EINVAL);
0877 
0878     mutex_lock(&extcon_dev_list_lock);
0879     list_for_each_entry(sd, &extcon_dev_list, entry) {
0880         if (!strcmp(sd->name, extcon_name))
0881             goto out;
0882     }
0883     sd = ERR_PTR(-EPROBE_DEFER);
0884 out:
0885     mutex_unlock(&extcon_dev_list_lock);
0886     return sd;
0887 }
0888 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
0889 
0890 /**
0891  * extcon_register_notifier() - Register a notifier block to get notified by
0892  *              any state changes from the extcon.
0893  * @edev:   the extcon device
0894  * @id:     the unique id indicating an external connector
0895  * @nb:     a notifier block to be registered
0896  *
0897  * Note that the second parameter given to the callback of nb (val) is
0898  * the current state of an external connector and the third pameter
0899  * is the pointer of extcon device.
0900  *
0901  * Returns 0 if success or error number if fail.
0902  */
0903 int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
0904                  struct notifier_block *nb)
0905 {
0906     unsigned long flags;
0907     int ret, idx;
0908 
0909     if (!edev || !nb)
0910         return -EINVAL;
0911 
0912     idx = find_cable_index_by_id(edev, id);
0913     if (idx < 0)
0914         return idx;
0915 
0916     spin_lock_irqsave(&edev->lock, flags);
0917     ret = raw_notifier_chain_register(&edev->nh[idx], nb);
0918     spin_unlock_irqrestore(&edev->lock, flags);
0919 
0920     return ret;
0921 }
0922 EXPORT_SYMBOL_GPL(extcon_register_notifier);
0923 
0924 /**
0925  * extcon_unregister_notifier() - Unregister a notifier block from the extcon.
0926  * @edev:   the extcon device
0927  * @id:     the unique id indicating an external connector
0928  * @nb:     a notifier block to be registered
0929  *
0930  * Returns 0 if success or error number if fail.
0931  */
0932 int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
0933                 struct notifier_block *nb)
0934 {
0935     unsigned long flags;
0936     int ret, idx;
0937 
0938     if (!edev || !nb)
0939         return -EINVAL;
0940 
0941     idx = find_cable_index_by_id(edev, id);
0942     if (idx < 0)
0943         return idx;
0944 
0945     spin_lock_irqsave(&edev->lock, flags);
0946     ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
0947     spin_unlock_irqrestore(&edev->lock, flags);
0948 
0949     return ret;
0950 }
0951 EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
0952 
0953 /**
0954  * extcon_register_notifier_all() - Register a notifier block for all connectors.
0955  * @edev:   the extcon device
0956  * @nb:     a notifier block to be registered
0957  *
0958  * Note that this function registers a notifier block in order to receive
0959  * the state change of all supported external connectors from extcon device.
0960  * And the second parameter given to the callback of nb (val) is
0961  * the current state and the third pameter is the pointer of extcon device.
0962  *
0963  * Returns 0 if success or error number if fail.
0964  */
0965 int extcon_register_notifier_all(struct extcon_dev *edev,
0966                 struct notifier_block *nb)
0967 {
0968     unsigned long flags;
0969     int ret;
0970 
0971     if (!edev || !nb)
0972         return -EINVAL;
0973 
0974     spin_lock_irqsave(&edev->lock, flags);
0975     ret = raw_notifier_chain_register(&edev->nh_all, nb);
0976     spin_unlock_irqrestore(&edev->lock, flags);
0977 
0978     return ret;
0979 }
0980 EXPORT_SYMBOL_GPL(extcon_register_notifier_all);
0981 
0982 /**
0983  * extcon_unregister_notifier_all() - Unregister a notifier block from extcon.
0984  * @edev:   the extcon device
0985  * @nb:     a notifier block to be registered
0986  *
0987  * Returns 0 if success or error number if fail.
0988  */
0989 int extcon_unregister_notifier_all(struct extcon_dev *edev,
0990                 struct notifier_block *nb)
0991 {
0992     unsigned long flags;
0993     int ret;
0994 
0995     if (!edev || !nb)
0996         return -EINVAL;
0997 
0998     spin_lock_irqsave(&edev->lock, flags);
0999     ret = raw_notifier_chain_unregister(&edev->nh_all, nb);
1000     spin_unlock_irqrestore(&edev->lock, flags);
1001 
1002     return ret;
1003 }
1004 EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all);
1005 
1006 static struct attribute *extcon_attrs[] = {
1007     &dev_attr_state.attr,
1008     &dev_attr_name.attr,
1009     NULL,
1010 };
1011 ATTRIBUTE_GROUPS(extcon);
1012 
1013 static int create_extcon_class(void)
1014 {
1015     if (!extcon_class) {
1016         extcon_class = class_create(THIS_MODULE, "extcon");
1017         if (IS_ERR(extcon_class))
1018             return PTR_ERR(extcon_class);
1019         extcon_class->dev_groups = extcon_groups;
1020     }
1021 
1022     return 0;
1023 }
1024 
1025 static void extcon_dev_release(struct device *dev)
1026 {
1027 }
1028 
1029 static const char *muex_name = "mutually_exclusive";
1030 static void dummy_sysfs_dev_release(struct device *dev)
1031 {
1032 }
1033 
1034 /*
1035  * extcon_dev_allocate() - Allocate the memory of extcon device.
1036  * @supported_cable:    the array of the supported external connectors
1037  *          ending with EXTCON_NONE.
1038  *
1039  * Note that this function allocates the memory for extcon device 
1040  * and initialize default setting for the extcon device.
1041  *
1042  * Returns the pointer memory of allocated extcon_dev if success
1043  * or ERR_PTR(err) if fail.
1044  */
1045 struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
1046 {
1047     struct extcon_dev *edev;
1048 
1049     if (!supported_cable)
1050         return ERR_PTR(-EINVAL);
1051 
1052     edev = kzalloc(sizeof(*edev), GFP_KERNEL);
1053     if (!edev)
1054         return ERR_PTR(-ENOMEM);
1055 
1056     edev->max_supported = 0;
1057     edev->supported_cable = supported_cable;
1058 
1059     return edev;
1060 }
1061 
1062 /*
1063  * extcon_dev_free() - Free the memory of extcon device.
1064  * @edev:   the extcon device
1065  */
1066 void extcon_dev_free(struct extcon_dev *edev)
1067 {
1068     kfree(edev);
1069 }
1070 EXPORT_SYMBOL_GPL(extcon_dev_free);
1071 
1072 /**
1073  * extcon_dev_register() - Register an new extcon device
1074  * @edev:   the extcon device to be registered
1075  *
1076  * Among the members of edev struct, please set the "user initializing data"
1077  * do not set the values of "internal data", which are initialized by
1078  * this function.
1079  *
1080  * Note that before calling this funciton, have to allocate the memory
1081  * of an extcon device by using the extcon_dev_allocate(). And the extcon
1082  * dev should include the supported_cable information.
1083  *
1084  * Returns 0 if success or error number if fail.
1085  */
1086 int extcon_dev_register(struct extcon_dev *edev)
1087 {
1088     int ret, index = 0;
1089     static atomic_t edev_no = ATOMIC_INIT(-1);
1090 
1091     if (!extcon_class) {
1092         ret = create_extcon_class();
1093         if (ret < 0)
1094             return ret;
1095     }
1096 
1097     if (!edev || !edev->supported_cable)
1098         return -EINVAL;
1099 
1100     for (; edev->supported_cable[index] != EXTCON_NONE; index++);
1101 
1102     edev->max_supported = index;
1103     if (index > SUPPORTED_CABLE_MAX) {
1104         dev_err(&edev->dev,
1105             "exceed the maximum number of supported cables\n");
1106         return -EINVAL;
1107     }
1108 
1109     edev->dev.class = extcon_class;
1110     edev->dev.release = extcon_dev_release;
1111 
1112     edev->name = dev_name(edev->dev.parent);
1113     if (IS_ERR_OR_NULL(edev->name)) {
1114         dev_err(&edev->dev,
1115             "extcon device name is null\n");
1116         return -EINVAL;
1117     }
1118     dev_set_name(&edev->dev, "extcon%lu",
1119             (unsigned long)atomic_inc_return(&edev_no));
1120 
1121     if (edev->max_supported) {
1122         char *str;
1123         struct extcon_cable *cable;
1124 
1125         edev->cables = kcalloc(edev->max_supported,
1126                        sizeof(struct extcon_cable),
1127                        GFP_KERNEL);
1128         if (!edev->cables) {
1129             ret = -ENOMEM;
1130             goto err_sysfs_alloc;
1131         }
1132         for (index = 0; index < edev->max_supported; index++) {
1133             cable = &edev->cables[index];
1134 
1135             str = kasprintf(GFP_KERNEL, "cable.%d", index);
1136             if (!str) {
1137                 for (index--; index >= 0; index--) {
1138                     cable = &edev->cables[index];
1139                     kfree(cable->attr_g.name);
1140                 }
1141                 ret = -ENOMEM;
1142 
1143                 goto err_alloc_cables;
1144             }
1145 
1146             cable->edev = edev;
1147             cable->cable_index = index;
1148             cable->attrs[0] = &cable->attr_name.attr;
1149             cable->attrs[1] = &cable->attr_state.attr;
1150             cable->attrs[2] = NULL;
1151             cable->attr_g.name = str;
1152             cable->attr_g.attrs = cable->attrs;
1153 
1154             sysfs_attr_init(&cable->attr_name.attr);
1155             cable->attr_name.attr.name = "name";
1156             cable->attr_name.attr.mode = 0444;
1157             cable->attr_name.show = cable_name_show;
1158 
1159             sysfs_attr_init(&cable->attr_state.attr);
1160             cable->attr_state.attr.name = "state";
1161             cable->attr_state.attr.mode = 0444;
1162             cable->attr_state.show = cable_state_show;
1163         }
1164     }
1165 
1166     if (edev->max_supported && edev->mutually_exclusive) {
1167         char *name;
1168 
1169         /* Count the size of mutually_exclusive array */
1170         for (index = 0; edev->mutually_exclusive[index]; index++)
1171             ;
1172 
1173         edev->attrs_muex = kcalloc(index + 1,
1174                        sizeof(struct attribute *),
1175                        GFP_KERNEL);
1176         if (!edev->attrs_muex) {
1177             ret = -ENOMEM;
1178             goto err_muex;
1179         }
1180 
1181         edev->d_attrs_muex = kcalloc(index,
1182                          sizeof(struct device_attribute),
1183                          GFP_KERNEL);
1184         if (!edev->d_attrs_muex) {
1185             ret = -ENOMEM;
1186             kfree(edev->attrs_muex);
1187             goto err_muex;
1188         }
1189 
1190         for (index = 0; edev->mutually_exclusive[index]; index++) {
1191             name = kasprintf(GFP_KERNEL, "0x%x",
1192                      edev->mutually_exclusive[index]);
1193             if (!name) {
1194                 for (index--; index >= 0; index--) {
1195                     kfree(edev->d_attrs_muex[index].attr.
1196                           name);
1197                 }
1198                 kfree(edev->d_attrs_muex);
1199                 kfree(edev->attrs_muex);
1200                 ret = -ENOMEM;
1201                 goto err_muex;
1202             }
1203             sysfs_attr_init(&edev->d_attrs_muex[index].attr);
1204             edev->d_attrs_muex[index].attr.name = name;
1205             edev->d_attrs_muex[index].attr.mode = 0000;
1206             edev->attrs_muex[index] = &edev->d_attrs_muex[index]
1207                             .attr;
1208         }
1209         edev->attr_g_muex.name = muex_name;
1210         edev->attr_g_muex.attrs = edev->attrs_muex;
1211 
1212     }
1213 
1214     if (edev->max_supported) {
1215         edev->extcon_dev_type.groups =
1216             kcalloc(edev->max_supported + 2,
1217                 sizeof(struct attribute_group *),
1218                 GFP_KERNEL);
1219         if (!edev->extcon_dev_type.groups) {
1220             ret = -ENOMEM;
1221             goto err_alloc_groups;
1222         }
1223 
1224         edev->extcon_dev_type.name = dev_name(&edev->dev);
1225         edev->extcon_dev_type.release = dummy_sysfs_dev_release;
1226 
1227         for (index = 0; index < edev->max_supported; index++)
1228             edev->extcon_dev_type.groups[index] =
1229                 &edev->cables[index].attr_g;
1230         if (edev->mutually_exclusive)
1231             edev->extcon_dev_type.groups[index] =
1232                 &edev->attr_g_muex;
1233 
1234         edev->dev.type = &edev->extcon_dev_type;
1235     }
1236 
1237     spin_lock_init(&edev->lock);
1238     if (edev->max_supported) {
1239         edev->nh = kcalloc(edev->max_supported, sizeof(*edev->nh),
1240                 GFP_KERNEL);
1241         if (!edev->nh) {
1242             ret = -ENOMEM;
1243             goto err_alloc_nh;
1244         }
1245     }
1246 
1247     for (index = 0; index < edev->max_supported; index++)
1248         RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
1249 
1250     RAW_INIT_NOTIFIER_HEAD(&edev->nh_all);
1251 
1252     dev_set_drvdata(&edev->dev, edev);
1253     edev->state = 0;
1254 
1255     ret = device_register(&edev->dev);
1256     if (ret) {
1257         put_device(&edev->dev);
1258         goto err_dev;
1259     }
1260 
1261     mutex_lock(&extcon_dev_list_lock);
1262     list_add(&edev->entry, &extcon_dev_list);
1263     mutex_unlock(&extcon_dev_list_lock);
1264 
1265     return 0;
1266 
1267 err_dev:
1268     if (edev->max_supported)
1269         kfree(edev->nh);
1270 err_alloc_nh:
1271     if (edev->max_supported)
1272         kfree(edev->extcon_dev_type.groups);
1273 err_alloc_groups:
1274     if (edev->max_supported && edev->mutually_exclusive) {
1275         for (index = 0; edev->mutually_exclusive[index]; index++)
1276             kfree(edev->d_attrs_muex[index].attr.name);
1277         kfree(edev->d_attrs_muex);
1278         kfree(edev->attrs_muex);
1279     }
1280 err_muex:
1281     for (index = 0; index < edev->max_supported; index++)
1282         kfree(edev->cables[index].attr_g.name);
1283 err_alloc_cables:
1284     if (edev->max_supported)
1285         kfree(edev->cables);
1286 err_sysfs_alloc:
1287     return ret;
1288 }
1289 EXPORT_SYMBOL_GPL(extcon_dev_register);
1290 
1291 /**
1292  * extcon_dev_unregister() - Unregister the extcon device.
1293  * @edev:   the extcon device to be unregistered.
1294  *
1295  * Note that this does not call kfree(edev) because edev was not allocated
1296  * by this class.
1297  */
1298 void extcon_dev_unregister(struct extcon_dev *edev)
1299 {
1300     int index;
1301 
1302     if (!edev)
1303         return;
1304 
1305     mutex_lock(&extcon_dev_list_lock);
1306     list_del(&edev->entry);
1307     mutex_unlock(&extcon_dev_list_lock);
1308 
1309     if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
1310         dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
1311                 dev_name(&edev->dev));
1312         return;
1313     }
1314 
1315     device_unregister(&edev->dev);
1316 
1317     if (edev->mutually_exclusive && edev->max_supported) {
1318         for (index = 0; edev->mutually_exclusive[index];
1319                 index++)
1320             kfree(edev->d_attrs_muex[index].attr.name);
1321         kfree(edev->d_attrs_muex);
1322         kfree(edev->attrs_muex);
1323     }
1324 
1325     for (index = 0; index < edev->max_supported; index++)
1326         kfree(edev->cables[index].attr_g.name);
1327 
1328     if (edev->max_supported) {
1329         kfree(edev->extcon_dev_type.groups);
1330         kfree(edev->cables);
1331         kfree(edev->nh);
1332     }
1333 
1334     put_device(&edev->dev);
1335 }
1336 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
1337 
1338 #ifdef CONFIG_OF
1339 
1340 /*
1341  * extcon_find_edev_by_node - Find the extcon device from devicetree.
1342  * @node    : OF node identifying edev
1343  *
1344  * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
1345  */
1346 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
1347 {
1348     struct extcon_dev *edev;
1349 
1350     mutex_lock(&extcon_dev_list_lock);
1351     list_for_each_entry(edev, &extcon_dev_list, entry)
1352         if (edev->dev.parent && edev->dev.parent->of_node == node)
1353             goto out;
1354     edev = ERR_PTR(-EPROBE_DEFER);
1355 out:
1356     mutex_unlock(&extcon_dev_list_lock);
1357 
1358     return edev;
1359 }
1360 
1361 /*
1362  * extcon_get_edev_by_phandle - Get the extcon device from devicetree.
1363  * @dev     : the instance to the given device
1364  * @index   : the index into list of extcon_dev
1365  *
1366  * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
1367  */
1368 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1369 {
1370     struct device_node *node;
1371     struct extcon_dev *edev;
1372 
1373     if (!dev)
1374         return ERR_PTR(-EINVAL);
1375 
1376     if (!dev->of_node) {
1377         dev_dbg(dev, "device does not have a device node entry\n");
1378         return ERR_PTR(-EINVAL);
1379     }
1380 
1381     node = of_parse_phandle(dev->of_node, "extcon", index);
1382     if (!node) {
1383         dev_dbg(dev, "failed to get phandle in %pOF node\n",
1384             dev->of_node);
1385         return ERR_PTR(-ENODEV);
1386     }
1387 
1388     edev = extcon_find_edev_by_node(node);
1389     of_node_put(node);
1390 
1391     return edev;
1392 }
1393 
1394 #else
1395 
1396 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
1397 {
1398     return ERR_PTR(-ENOSYS);
1399 }
1400 
1401 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1402 {
1403     return ERR_PTR(-ENOSYS);
1404 }
1405 
1406 #endif /* CONFIG_OF */
1407 
1408 EXPORT_SYMBOL_GPL(extcon_find_edev_by_node);
1409 EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
1410 
1411 /**
1412  * extcon_get_edev_name() - Get the name of the extcon device.
1413  * @edev:   the extcon device
1414  */
1415 const char *extcon_get_edev_name(struct extcon_dev *edev)
1416 {
1417     return !edev ? NULL : edev->name;
1418 }
1419 EXPORT_SYMBOL_GPL(extcon_get_edev_name);
1420 
1421 static int __init extcon_class_init(void)
1422 {
1423     return create_extcon_class();
1424 }
1425 module_init(extcon_class_init);
1426 
1427 static void __exit extcon_class_exit(void)
1428 {
1429     class_destroy(extcon_class);
1430 }
1431 module_exit(extcon_class_exit);
1432 
1433 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
1434 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1435 MODULE_DESCRIPTION("External Connector (extcon) framework");
1436 MODULE_LICENSE("GPL v2");