cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

extcon.c (36479B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * drivers/extcon/extcon.c - External Connector (extcon) framework.
      4 *
      5 * Copyright (C) 2015 Samsung Electronics
      6 * Author: Chanwoo Choi <cw00.choi@samsung.com>
      7 *
      8 * Copyright (C) 2012 Samsung Electronics
      9 * Author: Donggeun Kim <dg77.kim@samsung.com>
     10 * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
     11 *
     12 * based on android/drivers/switch/switch_class.c
     13 * Copyright (C) 2008 Google, Inc.
     14 * Author: Mike Lockwood <lockwood@android.com>
     15 */
     16
     17#include <linux/module.h>
     18#include <linux/types.h>
     19#include <linux/init.h>
     20#include <linux/device.h>
     21#include <linux/fs.h>
     22#include <linux/err.h>
     23#include <linux/of.h>
     24#include <linux/slab.h>
     25#include <linux/sysfs.h>
     26
     27#include "extcon.h"
     28
     29#define SUPPORTED_CABLE_MAX	32
     30
     31static const struct __extcon_info {
     32	unsigned int type;
     33	unsigned int id;
     34	const char *name;
     35
     36} extcon_info[] = {
     37	[EXTCON_NONE] = {
     38		.type = EXTCON_TYPE_MISC,
     39		.id = EXTCON_NONE,
     40		.name = "NONE",
     41	},
     42
     43	/* USB external connector */
     44	[EXTCON_USB] = {
     45		.type = EXTCON_TYPE_USB,
     46		.id = EXTCON_USB,
     47		.name = "USB",
     48	},
     49	[EXTCON_USB_HOST] = {
     50		.type = EXTCON_TYPE_USB,
     51		.id = EXTCON_USB_HOST,
     52		.name = "USB-HOST",
     53	},
     54
     55	/* Charging external connector */
     56	[EXTCON_CHG_USB_SDP] = {
     57		.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
     58		.id = EXTCON_CHG_USB_SDP,
     59		.name = "SDP",
     60	},
     61	[EXTCON_CHG_USB_DCP] = {
     62		.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
     63		.id = EXTCON_CHG_USB_DCP,
     64		.name = "DCP",
     65	},
     66	[EXTCON_CHG_USB_CDP] = {
     67		.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
     68		.id = EXTCON_CHG_USB_CDP,
     69		.name = "CDP",
     70	},
     71	[EXTCON_CHG_USB_ACA] = {
     72		.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
     73		.id = EXTCON_CHG_USB_ACA,
     74		.name = "ACA",
     75	},
     76	[EXTCON_CHG_USB_FAST] = {
     77		.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
     78		.id = EXTCON_CHG_USB_FAST,
     79		.name = "FAST-CHARGER",
     80	},
     81	[EXTCON_CHG_USB_SLOW] = {
     82		.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
     83		.id = EXTCON_CHG_USB_SLOW,
     84		.name = "SLOW-CHARGER",
     85	},
     86	[EXTCON_CHG_WPT] = {
     87		.type = EXTCON_TYPE_CHG,
     88		.id = EXTCON_CHG_WPT,
     89		.name = "WPT",
     90	},
     91	[EXTCON_CHG_USB_PD] = {
     92		.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
     93		.id = EXTCON_CHG_USB_PD,
     94		.name = "PD",
     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	[EXTCON_DISP_DP] = {
    161		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
    162		.id = EXTCON_DISP_DP,
    163		.name = "DP",
    164	},
    165	[EXTCON_DISP_HMD] = {
    166		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
    167		.id = EXTCON_DISP_HMD,
    168		.name = "HMD",
    169	},
    170
    171	/* Miscellaneous external connector */
    172	[EXTCON_DOCK] = {
    173		.type = EXTCON_TYPE_MISC,
    174		.id = EXTCON_DOCK,
    175		.name = "DOCK",
    176	},
    177	[EXTCON_JIG] = {
    178		.type = EXTCON_TYPE_MISC,
    179		.id = EXTCON_JIG,
    180		.name = "JIG",
    181	},
    182	[EXTCON_MECHANICAL] = {
    183		.type = EXTCON_TYPE_MISC,
    184		.id = EXTCON_MECHANICAL,
    185		.name = "MECHANICAL",
    186	},
    187
    188	{ /* sentinel */ }
    189};
    190
    191/**
    192 * struct extcon_cable - An internal data for an external connector.
    193 * @edev:		the extcon device
    194 * @cable_index:	the index of this cable in the edev
    195 * @attr_g:		the attribute group for the cable
    196 * @attr_name:		"name" sysfs entry
    197 * @attr_state:		"state" sysfs entry
    198 * @attrs:		the array pointing to attr_name and attr_state for attr_g
    199 */
    200struct extcon_cable {
    201	struct extcon_dev *edev;
    202	int cable_index;
    203
    204	struct attribute_group attr_g;
    205	struct device_attribute attr_name;
    206	struct device_attribute attr_state;
    207
    208	struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
    209
    210	union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
    211	union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
    212	union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
    213	union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
    214
    215	unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
    216	unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
    217	unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
    218	unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
    219};
    220
    221static struct class *extcon_class;
    222
    223static LIST_HEAD(extcon_dev_list);
    224static DEFINE_MUTEX(extcon_dev_list_lock);
    225
    226static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
    227{
    228	int i = 0;
    229
    230	if (!edev->mutually_exclusive)
    231		return 0;
    232
    233	for (i = 0; edev->mutually_exclusive[i]; i++) {
    234		int weight;
    235		u32 correspondants = new_state & edev->mutually_exclusive[i];
    236
    237		/* calculate the total number of bits set */
    238		weight = hweight32(correspondants);
    239		if (weight > 1)
    240			return i + 1;
    241	}
    242
    243	return 0;
    244}
    245
    246static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
    247{
    248	int i;
    249
    250	/* Find the the index of extcon cable in edev->supported_cable */
    251	for (i = 0; i < edev->max_supported; i++) {
    252		if (edev->supported_cable[i] == id)
    253			return i;
    254	}
    255
    256	return -EINVAL;
    257}
    258
    259static int get_extcon_type(unsigned int prop)
    260{
    261	switch (prop) {
    262	case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
    263		return EXTCON_TYPE_USB;
    264	case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
    265		return EXTCON_TYPE_CHG;
    266	case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
    267		return EXTCON_TYPE_JACK;
    268	case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
    269		return EXTCON_TYPE_DISP;
    270	default:
    271		return -EINVAL;
    272	}
    273}
    274
    275static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
    276{
    277	return !!(edev->state & BIT(index));
    278}
    279
    280static bool is_extcon_changed(struct extcon_dev *edev, int index,
    281				bool new_state)
    282{
    283	int state = !!(edev->state & BIT(index));
    284	return (state != new_state);
    285}
    286
    287static 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
    300static int is_extcon_property_capability(struct extcon_dev *edev,
    301				unsigned int id, int index,unsigned int prop)
    302{
    303	struct extcon_cable *cable;
    304	int type, ret;
    305
    306	/* Check whether the property is supported or not. */
    307	type = get_extcon_type(prop);
    308	if (type < 0)
    309		return type;
    310
    311	cable = &edev->cables[index];
    312
    313	switch (type) {
    314	case EXTCON_TYPE_USB:
    315		ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
    316		break;
    317	case EXTCON_TYPE_CHG:
    318		ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
    319		break;
    320	case EXTCON_TYPE_JACK:
    321		ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
    322		break;
    323	case EXTCON_TYPE_DISP:
    324		ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
    325		break;
    326	default:
    327		ret = -EINVAL;
    328	}
    329
    330	return ret;
    331}
    332
    333static void init_property(struct extcon_dev *edev, unsigned int id, int index)
    334{
    335	unsigned int type = extcon_info[id].type;
    336	struct extcon_cable *cable = &edev->cables[index];
    337
    338	if (EXTCON_TYPE_USB & type)
    339		memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
    340	if (EXTCON_TYPE_CHG & type)
    341		memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
    342	if (EXTCON_TYPE_JACK & type)
    343		memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
    344	if (EXTCON_TYPE_DISP & type)
    345		memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
    346}
    347
    348static ssize_t state_show(struct device *dev, struct device_attribute *attr,
    349			  char *buf)
    350{
    351	int i, count = 0;
    352	struct extcon_dev *edev = dev_get_drvdata(dev);
    353
    354	if (edev->max_supported == 0)
    355		return sprintf(buf, "%u\n", edev->state);
    356
    357	for (i = 0; i < edev->max_supported; i++) {
    358		count += sprintf(buf + count, "%s=%d\n",
    359				extcon_info[edev->supported_cable[i]].name,
    360				 !!(edev->state & BIT(i)));
    361	}
    362
    363	return count;
    364}
    365static DEVICE_ATTR_RO(state);
    366
    367static ssize_t name_show(struct device *dev, struct device_attribute *attr,
    368		char *buf)
    369{
    370	struct extcon_dev *edev = dev_get_drvdata(dev);
    371
    372	return sprintf(buf, "%s\n", edev->name);
    373}
    374static DEVICE_ATTR_RO(name);
    375
    376static ssize_t cable_name_show(struct device *dev,
    377			       struct device_attribute *attr, char *buf)
    378{
    379	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
    380						  attr_name);
    381	int i = cable->cable_index;
    382
    383	return sprintf(buf, "%s\n",
    384			extcon_info[cable->edev->supported_cable[i]].name);
    385}
    386
    387static ssize_t cable_state_show(struct device *dev,
    388				struct device_attribute *attr, char *buf)
    389{
    390	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
    391						  attr_state);
    392
    393	int i = cable->cable_index;
    394
    395	return sprintf(buf, "%d\n",
    396		extcon_get_state(cable->edev, cable->edev->supported_cable[i]));
    397}
    398
    399/**
    400 * extcon_sync() - Synchronize the state for an external connector.
    401 * @edev:	the extcon device
    402 * @id:		the unique id indicating an external connector
    403 *
    404 * Note that this function send a notification in order to synchronize
    405 * the state and property of an external connector.
    406 *
    407 * Returns 0 if success or error number if fail.
    408 */
    409int extcon_sync(struct extcon_dev *edev, unsigned int id)
    410{
    411	char name_buf[120];
    412	char state_buf[120];
    413	char *prop_buf;
    414	char *envp[3];
    415	int env_offset = 0;
    416	int length;
    417	int index;
    418	int state;
    419	unsigned long flags;
    420
    421	if (!edev)
    422		return -EINVAL;
    423
    424	index = find_cable_index_by_id(edev, id);
    425	if (index < 0)
    426		return index;
    427
    428	spin_lock_irqsave(&edev->lock, flags);
    429	state = !!(edev->state & BIT(index));
    430	spin_unlock_irqrestore(&edev->lock, flags);
    431
    432	/*
    433	 * Call functions in a raw notifier chain for the specific one
    434	 * external connector.
    435	 */
    436	raw_notifier_call_chain(&edev->nh[index], state, edev);
    437
    438	/*
    439	 * Call functions in a raw notifier chain for the all supported
    440	 * external connectors.
    441	 */
    442	raw_notifier_call_chain(&edev->nh_all, state, edev);
    443
    444	spin_lock_irqsave(&edev->lock, flags);
    445	/* This could be in interrupt handler */
    446	prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
    447	if (!prop_buf) {
    448		/* Unlock early before uevent */
    449		spin_unlock_irqrestore(&edev->lock, flags);
    450
    451		dev_err(&edev->dev, "out of memory in extcon_set_state\n");
    452		kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
    453
    454		return -ENOMEM;
    455	}
    456
    457	length = name_show(&edev->dev, NULL, prop_buf);
    458	if (length > 0) {
    459		if (prop_buf[length - 1] == '\n')
    460			prop_buf[length - 1] = 0;
    461		snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf);
    462		envp[env_offset++] = name_buf;
    463	}
    464
    465	length = state_show(&edev->dev, NULL, prop_buf);
    466	if (length > 0) {
    467		if (prop_buf[length - 1] == '\n')
    468			prop_buf[length - 1] = 0;
    469		snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf);
    470		envp[env_offset++] = state_buf;
    471	}
    472	envp[env_offset] = NULL;
    473
    474	/* Unlock early before uevent */
    475	spin_unlock_irqrestore(&edev->lock, flags);
    476	kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
    477	free_page((unsigned long)prop_buf);
    478
    479	return 0;
    480}
    481EXPORT_SYMBOL_GPL(extcon_sync);
    482
    483/**
    484 * extcon_get_state() - Get the state of an external connector.
    485 * @edev:	the extcon device
    486 * @id:		the unique id indicating an external connector
    487 *
    488 * Returns 0 if success or error number if fail.
    489 */
    490int extcon_get_state(struct extcon_dev *edev, const unsigned int id)
    491{
    492	int index, state;
    493	unsigned long flags;
    494
    495	if (!edev)
    496		return -EINVAL;
    497
    498	index = find_cable_index_by_id(edev, id);
    499	if (index < 0)
    500		return index;
    501
    502	spin_lock_irqsave(&edev->lock, flags);
    503	state = is_extcon_attached(edev, index);
    504	spin_unlock_irqrestore(&edev->lock, flags);
    505
    506	return state;
    507}
    508EXPORT_SYMBOL_GPL(extcon_get_state);
    509
    510/**
    511 * extcon_set_state() - Set the state of an external connector.
    512 * @edev:	the extcon device
    513 * @id:		the unique id indicating an external connector
    514 * @state:	the new state of an external connector.
    515 *		the default semantics is true: attached / false: detached.
    516 *
    517 * Note that this function set the state of an external connector without
    518 * a notification. To synchronize the state of an external connector,
    519 * have to use extcon_set_state_sync() and extcon_sync().
    520 *
    521 * Returns 0 if success or error number if fail.
    522 */
    523int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state)
    524{
    525	unsigned long flags;
    526	int index, ret = 0;
    527
    528	if (!edev)
    529		return -EINVAL;
    530
    531	index = find_cable_index_by_id(edev, id);
    532	if (index < 0)
    533		return index;
    534
    535	spin_lock_irqsave(&edev->lock, flags);
    536
    537	/* Check whether the external connector's state is changed. */
    538	if (!is_extcon_changed(edev, index, state))
    539		goto out;
    540
    541	if (check_mutually_exclusive(edev,
    542		(edev->state & ~BIT(index)) | (state & BIT(index)))) {
    543		ret = -EPERM;
    544		goto out;
    545	}
    546
    547	/*
    548	 * Initialize the value of extcon property before setting
    549	 * the detached state for an external connector.
    550	 */
    551	if (!state)
    552		init_property(edev, id, index);
    553
    554	/* Update the state for an external connector. */
    555	if (state)
    556		edev->state |= BIT(index);
    557	else
    558		edev->state &= ~(BIT(index));
    559out:
    560	spin_unlock_irqrestore(&edev->lock, flags);
    561
    562	return ret;
    563}
    564EXPORT_SYMBOL_GPL(extcon_set_state);
    565
    566/**
    567 * extcon_set_state_sync() - Set the state of an external connector with sync.
    568 * @edev:	the extcon device
    569 * @id:		the unique id indicating an external connector
    570 * @state:	the new state of external connector.
    571 *		the default semantics is true: attached / false: detached.
    572 *
    573 * Note that this function set the state of external connector
    574 * and synchronize the state by sending a notification.
    575 *
    576 * Returns 0 if success or error number if fail.
    577 */
    578int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state)
    579{
    580	int ret;
    581
    582	ret = extcon_set_state(edev, id, state);
    583	if (ret < 0)
    584		return ret;
    585
    586	return extcon_sync(edev, id);
    587}
    588EXPORT_SYMBOL_GPL(extcon_set_state_sync);
    589
    590/**
    591 * extcon_get_property() - Get the property value of an external connector.
    592 * @edev:	the extcon device
    593 * @id:		the unique id indicating an external connector
    594 * @prop:	the property id indicating an extcon property
    595 * @prop_val:	the pointer which store the value of extcon property
    596 *
    597 * Note that when getting the property value of external connector,
    598 * the external connector should be attached. If detached state, function
    599 * return 0 without property value. Also, the each property should be
    600 * included in the list of supported properties according to extcon type.
    601 *
    602 * Returns 0 if success or error number if fail.
    603 */
    604int extcon_get_property(struct extcon_dev *edev, unsigned int id,
    605				unsigned int prop,
    606				union extcon_property_value *prop_val)
    607{
    608	struct extcon_cable *cable;
    609	unsigned long flags;
    610	int index, ret = 0;
    611
    612	*prop_val = (union extcon_property_value){0};
    613
    614	if (!edev)
    615		return -EINVAL;
    616
    617	/* Check whether the property is supported or not */
    618	if (!is_extcon_property_supported(id, prop))
    619		return -EINVAL;
    620
    621	/* Find the cable index of external connector by using id */
    622	index = find_cable_index_by_id(edev, id);
    623	if (index < 0)
    624		return index;
    625
    626	spin_lock_irqsave(&edev->lock, flags);
    627
    628	/* Check whether the property is available or not. */
    629	if (!is_extcon_property_capability(edev, id, index, prop)) {
    630		spin_unlock_irqrestore(&edev->lock, flags);
    631		return -EPERM;
    632	}
    633
    634	/*
    635	 * Check whether the external connector is attached.
    636	 * If external connector is detached, the user can not
    637	 * get the property value.
    638	 */
    639	if (!is_extcon_attached(edev, index)) {
    640		spin_unlock_irqrestore(&edev->lock, flags);
    641		return 0;
    642	}
    643
    644	cable = &edev->cables[index];
    645
    646	/* Get the property value according to extcon type */
    647	switch (prop) {
    648	case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
    649		*prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
    650		break;
    651	case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
    652		*prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
    653		break;
    654	case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
    655		*prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
    656		break;
    657	case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
    658		*prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
    659		break;
    660	default:
    661		ret = -EINVAL;
    662		break;
    663	}
    664
    665	spin_unlock_irqrestore(&edev->lock, flags);
    666
    667	return ret;
    668}
    669EXPORT_SYMBOL_GPL(extcon_get_property);
    670
    671/**
    672 * extcon_set_property() - Set the property value of an external connector.
    673 * @edev:	the extcon device
    674 * @id:		the unique id indicating an external connector
    675 * @prop:	the property id indicating an extcon property
    676 * @prop_val:	the pointer including the new value of extcon property
    677 *
    678 * Note that each property should be included in the list of supported
    679 * properties according to the extcon type.
    680 *
    681 * Returns 0 if success or error number if fail.
    682 */
    683int extcon_set_property(struct extcon_dev *edev, unsigned int id,
    684				unsigned int prop,
    685				union extcon_property_value prop_val)
    686{
    687	struct extcon_cable *cable;
    688	unsigned long flags;
    689	int index, ret = 0;
    690
    691	if (!edev)
    692		return -EINVAL;
    693
    694	/* Check whether the property is supported or not */
    695	if (!is_extcon_property_supported(id, prop))
    696		return -EINVAL;
    697
    698	/* Find the cable index of external connector by using id */
    699	index = find_cable_index_by_id(edev, id);
    700	if (index < 0)
    701		return index;
    702
    703	spin_lock_irqsave(&edev->lock, flags);
    704
    705	/* Check whether the property is available or not. */
    706	if (!is_extcon_property_capability(edev, id, index, prop)) {
    707		spin_unlock_irqrestore(&edev->lock, flags);
    708		return -EPERM;
    709	}
    710
    711	cable = &edev->cables[index];
    712
    713	/* Set the property value according to extcon type */
    714	switch (prop) {
    715	case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
    716		cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
    717		break;
    718	case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
    719		cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
    720		break;
    721	case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
    722		cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
    723		break;
    724	case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
    725		cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
    726		break;
    727	default:
    728		ret = -EINVAL;
    729		break;
    730	}
    731
    732	spin_unlock_irqrestore(&edev->lock, flags);
    733
    734	return ret;
    735}
    736EXPORT_SYMBOL_GPL(extcon_set_property);
    737
    738/**
    739 * extcon_set_property_sync() - Set property of an external connector with sync.
    740 * @edev:	the extcon device
    741 * @id:		the unique id indicating an external connector
    742 * @prop:	the property id indicating an extcon property
    743 * @prop_val:	the pointer including the new value of extcon property
    744 *
    745 * Note that when setting the property value of external connector,
    746 * the external connector should be attached. The each property should
    747 * be included in the list of supported properties according to extcon type.
    748 *
    749 * Returns 0 if success or error number if fail.
    750 */
    751int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
    752				unsigned int prop,
    753				union extcon_property_value prop_val)
    754{
    755	int ret;
    756
    757	ret = extcon_set_property(edev, id, prop, prop_val);
    758	if (ret < 0)
    759		return ret;
    760
    761	return extcon_sync(edev, id);
    762}
    763EXPORT_SYMBOL_GPL(extcon_set_property_sync);
    764
    765/**
    766 * extcon_get_property_capability() - Get the capability of the property
    767 *					for an external connector.
    768 * @edev:	the extcon device
    769 * @id:		the unique id indicating an external connector
    770 * @prop:	the property id indicating an extcon property
    771 *
    772 * Returns 1 if the property is available or 0 if not available.
    773 */
    774int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
    775					unsigned int prop)
    776{
    777	int index;
    778
    779	if (!edev)
    780		return -EINVAL;
    781
    782	/* Check whether the property is supported or not */
    783	if (!is_extcon_property_supported(id, prop))
    784		return -EINVAL;
    785
    786	/* Find the cable index of external connector by using id */
    787	index = find_cable_index_by_id(edev, id);
    788	if (index < 0)
    789		return index;
    790
    791	return is_extcon_property_capability(edev, id, index, prop);
    792}
    793EXPORT_SYMBOL_GPL(extcon_get_property_capability);
    794
    795/**
    796 * extcon_set_property_capability() - Set the capability of the property
    797 *					for an external connector.
    798 * @edev:	the extcon device
    799 * @id:		the unique id indicating an external connector
    800 * @prop:	the property id indicating an extcon property
    801 *
    802 * Note that this function set the capability of the property
    803 * for an external connector in order to mark the bit in capability
    804 * bitmap which mean the available state of the property.
    805 *
    806 * Returns 0 if success or error number if fail.
    807 */
    808int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
    809					unsigned int prop)
    810{
    811	struct extcon_cable *cable;
    812	int index, type, ret = 0;
    813
    814	if (!edev)
    815		return -EINVAL;
    816
    817	/* Check whether the property is supported or not. */
    818	if (!is_extcon_property_supported(id, prop))
    819		return -EINVAL;
    820
    821	/* Find the cable index of external connector by using id. */
    822	index = find_cable_index_by_id(edev, id);
    823	if (index < 0)
    824		return index;
    825
    826	type = get_extcon_type(prop);
    827	if (type < 0)
    828		return type;
    829
    830	cable = &edev->cables[index];
    831
    832	switch (type) {
    833	case EXTCON_TYPE_USB:
    834		__set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
    835		break;
    836	case EXTCON_TYPE_CHG:
    837		__set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
    838		break;
    839	case EXTCON_TYPE_JACK:
    840		__set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
    841		break;
    842	case EXTCON_TYPE_DISP:
    843		__set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
    844		break;
    845	default:
    846		ret = -EINVAL;
    847	}
    848
    849	return ret;
    850}
    851EXPORT_SYMBOL_GPL(extcon_set_property_capability);
    852
    853/**
    854 * extcon_get_extcon_dev() - Get the extcon device instance from the name.
    855 * @extcon_name:	the extcon name provided with extcon_dev_register()
    856 *
    857 * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
    858 * NOTE: This function returns -EPROBE_DEFER so it may only be called from
    859 * probe() functions.
    860 */
    861struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
    862{
    863	struct extcon_dev *sd;
    864
    865	if (!extcon_name)
    866		return ERR_PTR(-EINVAL);
    867
    868	mutex_lock(&extcon_dev_list_lock);
    869	list_for_each_entry(sd, &extcon_dev_list, entry) {
    870		if (!strcmp(sd->name, extcon_name))
    871			goto out;
    872	}
    873	sd = ERR_PTR(-EPROBE_DEFER);
    874out:
    875	mutex_unlock(&extcon_dev_list_lock);
    876	return sd;
    877}
    878EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
    879
    880/**
    881 * extcon_register_notifier() - Register a notifier block to get notified by
    882 *				any state changes from the extcon.
    883 * @edev:	the extcon device
    884 * @id:		the unique id indicating an external connector
    885 * @nb:		a notifier block to be registered
    886 *
    887 * Note that the second parameter given to the callback of nb (val) is
    888 * the current state of an external connector and the third pameter
    889 * is the pointer of extcon device.
    890 *
    891 * Returns 0 if success or error number if fail.
    892 */
    893int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
    894			     struct notifier_block *nb)
    895{
    896	unsigned long flags;
    897	int ret, idx;
    898
    899	if (!edev || !nb)
    900		return -EINVAL;
    901
    902	idx = find_cable_index_by_id(edev, id);
    903	if (idx < 0)
    904		return idx;
    905
    906	spin_lock_irqsave(&edev->lock, flags);
    907	ret = raw_notifier_chain_register(&edev->nh[idx], nb);
    908	spin_unlock_irqrestore(&edev->lock, flags);
    909
    910	return ret;
    911}
    912EXPORT_SYMBOL_GPL(extcon_register_notifier);
    913
    914/**
    915 * extcon_unregister_notifier() - Unregister a notifier block from the extcon.
    916 * @edev:	the extcon device
    917 * @id:		the unique id indicating an external connector
    918 * @nb:		a notifier block to be registered
    919 *
    920 * Returns 0 if success or error number if fail.
    921 */
    922int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
    923				struct notifier_block *nb)
    924{
    925	unsigned long flags;
    926	int ret, idx;
    927
    928	if (!edev || !nb)
    929		return -EINVAL;
    930
    931	idx = find_cable_index_by_id(edev, id);
    932	if (idx < 0)
    933		return idx;
    934
    935	spin_lock_irqsave(&edev->lock, flags);
    936	ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
    937	spin_unlock_irqrestore(&edev->lock, flags);
    938
    939	return ret;
    940}
    941EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
    942
    943/**
    944 * extcon_register_notifier_all() - Register a notifier block for all connectors.
    945 * @edev:	the extcon device
    946 * @nb:		a notifier block to be registered
    947 *
    948 * Note that this function registers a notifier block in order to receive
    949 * the state change of all supported external connectors from extcon device.
    950 * And the second parameter given to the callback of nb (val) is
    951 * the current state and the third pameter is the pointer of extcon device.
    952 *
    953 * Returns 0 if success or error number if fail.
    954 */
    955int extcon_register_notifier_all(struct extcon_dev *edev,
    956				struct notifier_block *nb)
    957{
    958	unsigned long flags;
    959	int ret;
    960
    961	if (!edev || !nb)
    962		return -EINVAL;
    963
    964	spin_lock_irqsave(&edev->lock, flags);
    965	ret = raw_notifier_chain_register(&edev->nh_all, nb);
    966	spin_unlock_irqrestore(&edev->lock, flags);
    967
    968	return ret;
    969}
    970EXPORT_SYMBOL_GPL(extcon_register_notifier_all);
    971
    972/**
    973 * extcon_unregister_notifier_all() - Unregister a notifier block from extcon.
    974 * @edev:	the extcon device
    975 * @nb:		a notifier block to be registered
    976 *
    977 * Returns 0 if success or error number if fail.
    978 */
    979int extcon_unregister_notifier_all(struct extcon_dev *edev,
    980				struct notifier_block *nb)
    981{
    982	unsigned long flags;
    983	int ret;
    984
    985	if (!edev || !nb)
    986		return -EINVAL;
    987
    988	spin_lock_irqsave(&edev->lock, flags);
    989	ret = raw_notifier_chain_unregister(&edev->nh_all, nb);
    990	spin_unlock_irqrestore(&edev->lock, flags);
    991
    992	return ret;
    993}
    994EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all);
    995
    996static struct attribute *extcon_attrs[] = {
    997	&dev_attr_state.attr,
    998	&dev_attr_name.attr,
    999	NULL,
   1000};
   1001ATTRIBUTE_GROUPS(extcon);
   1002
   1003static int create_extcon_class(void)
   1004{
   1005	if (!extcon_class) {
   1006		extcon_class = class_create(THIS_MODULE, "extcon");
   1007		if (IS_ERR(extcon_class))
   1008			return PTR_ERR(extcon_class);
   1009		extcon_class->dev_groups = extcon_groups;
   1010	}
   1011
   1012	return 0;
   1013}
   1014
   1015static void extcon_dev_release(struct device *dev)
   1016{
   1017}
   1018
   1019static const char *muex_name = "mutually_exclusive";
   1020static void dummy_sysfs_dev_release(struct device *dev)
   1021{
   1022}
   1023
   1024/*
   1025 * extcon_dev_allocate() - Allocate the memory of extcon device.
   1026 * @supported_cable:	the array of the supported external connectors
   1027 *			ending with EXTCON_NONE.
   1028 *
   1029 * Note that this function allocates the memory for extcon device 
   1030 * and initialize default setting for the extcon device.
   1031 *
   1032 * Returns the pointer memory of allocated extcon_dev if success
   1033 * or ERR_PTR(err) if fail.
   1034 */
   1035struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
   1036{
   1037	struct extcon_dev *edev;
   1038
   1039	if (!supported_cable)
   1040		return ERR_PTR(-EINVAL);
   1041
   1042	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
   1043	if (!edev)
   1044		return ERR_PTR(-ENOMEM);
   1045
   1046	edev->max_supported = 0;
   1047	edev->supported_cable = supported_cable;
   1048
   1049	return edev;
   1050}
   1051
   1052/*
   1053 * extcon_dev_free() - Free the memory of extcon device.
   1054 * @edev:	the extcon device
   1055 */
   1056void extcon_dev_free(struct extcon_dev *edev)
   1057{
   1058	kfree(edev);
   1059}
   1060EXPORT_SYMBOL_GPL(extcon_dev_free);
   1061
   1062/**
   1063 * extcon_dev_register() - Register an new extcon device
   1064 * @edev:	the extcon device to be registered
   1065 *
   1066 * Among the members of edev struct, please set the "user initializing data"
   1067 * do not set the values of "internal data", which are initialized by
   1068 * this function.
   1069 *
   1070 * Note that before calling this funciton, have to allocate the memory
   1071 * of an extcon device by using the extcon_dev_allocate(). And the extcon
   1072 * dev should include the supported_cable information.
   1073 *
   1074 * Returns 0 if success or error number if fail.
   1075 */
   1076int extcon_dev_register(struct extcon_dev *edev)
   1077{
   1078	int ret, index = 0;
   1079	static atomic_t edev_no = ATOMIC_INIT(-1);
   1080
   1081	if (!extcon_class) {
   1082		ret = create_extcon_class();
   1083		if (ret < 0)
   1084			return ret;
   1085	}
   1086
   1087	if (!edev || !edev->supported_cable)
   1088		return -EINVAL;
   1089
   1090	for (; edev->supported_cable[index] != EXTCON_NONE; index++);
   1091
   1092	edev->max_supported = index;
   1093	if (index > SUPPORTED_CABLE_MAX) {
   1094		dev_err(&edev->dev,
   1095			"exceed the maximum number of supported cables\n");
   1096		return -EINVAL;
   1097	}
   1098
   1099	edev->dev.class = extcon_class;
   1100	edev->dev.release = extcon_dev_release;
   1101
   1102	edev->name = dev_name(edev->dev.parent);
   1103	if (IS_ERR_OR_NULL(edev->name)) {
   1104		dev_err(&edev->dev,
   1105			"extcon device name is null\n");
   1106		return -EINVAL;
   1107	}
   1108	dev_set_name(&edev->dev, "extcon%lu",
   1109			(unsigned long)atomic_inc_return(&edev_no));
   1110
   1111	if (edev->max_supported) {
   1112		char *str;
   1113		struct extcon_cable *cable;
   1114
   1115		edev->cables = kcalloc(edev->max_supported,
   1116				       sizeof(struct extcon_cable),
   1117				       GFP_KERNEL);
   1118		if (!edev->cables) {
   1119			ret = -ENOMEM;
   1120			goto err_sysfs_alloc;
   1121		}
   1122		for (index = 0; index < edev->max_supported; index++) {
   1123			cable = &edev->cables[index];
   1124
   1125			str = kasprintf(GFP_KERNEL, "cable.%d", index);
   1126			if (!str) {
   1127				for (index--; index >= 0; index--) {
   1128					cable = &edev->cables[index];
   1129					kfree(cable->attr_g.name);
   1130				}
   1131				ret = -ENOMEM;
   1132
   1133				goto err_alloc_cables;
   1134			}
   1135
   1136			cable->edev = edev;
   1137			cable->cable_index = index;
   1138			cable->attrs[0] = &cable->attr_name.attr;
   1139			cable->attrs[1] = &cable->attr_state.attr;
   1140			cable->attrs[2] = NULL;
   1141			cable->attr_g.name = str;
   1142			cable->attr_g.attrs = cable->attrs;
   1143
   1144			sysfs_attr_init(&cable->attr_name.attr);
   1145			cable->attr_name.attr.name = "name";
   1146			cable->attr_name.attr.mode = 0444;
   1147			cable->attr_name.show = cable_name_show;
   1148
   1149			sysfs_attr_init(&cable->attr_state.attr);
   1150			cable->attr_state.attr.name = "state";
   1151			cable->attr_state.attr.mode = 0444;
   1152			cable->attr_state.show = cable_state_show;
   1153		}
   1154	}
   1155
   1156	if (edev->max_supported && edev->mutually_exclusive) {
   1157		char *name;
   1158
   1159		/* Count the size of mutually_exclusive array */
   1160		for (index = 0; edev->mutually_exclusive[index]; index++)
   1161			;
   1162
   1163		edev->attrs_muex = kcalloc(index + 1,
   1164					   sizeof(struct attribute *),
   1165					   GFP_KERNEL);
   1166		if (!edev->attrs_muex) {
   1167			ret = -ENOMEM;
   1168			goto err_muex;
   1169		}
   1170
   1171		edev->d_attrs_muex = kcalloc(index,
   1172					     sizeof(struct device_attribute),
   1173					     GFP_KERNEL);
   1174		if (!edev->d_attrs_muex) {
   1175			ret = -ENOMEM;
   1176			kfree(edev->attrs_muex);
   1177			goto err_muex;
   1178		}
   1179
   1180		for (index = 0; edev->mutually_exclusive[index]; index++) {
   1181			name = kasprintf(GFP_KERNEL, "0x%x",
   1182					 edev->mutually_exclusive[index]);
   1183			if (!name) {
   1184				for (index--; index >= 0; index--) {
   1185					kfree(edev->d_attrs_muex[index].attr.
   1186					      name);
   1187				}
   1188				kfree(edev->d_attrs_muex);
   1189				kfree(edev->attrs_muex);
   1190				ret = -ENOMEM;
   1191				goto err_muex;
   1192			}
   1193			sysfs_attr_init(&edev->d_attrs_muex[index].attr);
   1194			edev->d_attrs_muex[index].attr.name = name;
   1195			edev->d_attrs_muex[index].attr.mode = 0000;
   1196			edev->attrs_muex[index] = &edev->d_attrs_muex[index]
   1197							.attr;
   1198		}
   1199		edev->attr_g_muex.name = muex_name;
   1200		edev->attr_g_muex.attrs = edev->attrs_muex;
   1201
   1202	}
   1203
   1204	if (edev->max_supported) {
   1205		edev->extcon_dev_type.groups =
   1206			kcalloc(edev->max_supported + 2,
   1207				sizeof(struct attribute_group *),
   1208				GFP_KERNEL);
   1209		if (!edev->extcon_dev_type.groups) {
   1210			ret = -ENOMEM;
   1211			goto err_alloc_groups;
   1212		}
   1213
   1214		edev->extcon_dev_type.name = dev_name(&edev->dev);
   1215		edev->extcon_dev_type.release = dummy_sysfs_dev_release;
   1216
   1217		for (index = 0; index < edev->max_supported; index++)
   1218			edev->extcon_dev_type.groups[index] =
   1219				&edev->cables[index].attr_g;
   1220		if (edev->mutually_exclusive)
   1221			edev->extcon_dev_type.groups[index] =
   1222				&edev->attr_g_muex;
   1223
   1224		edev->dev.type = &edev->extcon_dev_type;
   1225	}
   1226
   1227	spin_lock_init(&edev->lock);
   1228	if (edev->max_supported) {
   1229		edev->nh = kcalloc(edev->max_supported, sizeof(*edev->nh),
   1230				GFP_KERNEL);
   1231		if (!edev->nh) {
   1232			ret = -ENOMEM;
   1233			goto err_alloc_nh;
   1234		}
   1235	}
   1236
   1237	for (index = 0; index < edev->max_supported; index++)
   1238		RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
   1239
   1240	RAW_INIT_NOTIFIER_HEAD(&edev->nh_all);
   1241
   1242	dev_set_drvdata(&edev->dev, edev);
   1243	edev->state = 0;
   1244
   1245	ret = device_register(&edev->dev);
   1246	if (ret) {
   1247		put_device(&edev->dev);
   1248		goto err_dev;
   1249	}
   1250
   1251	mutex_lock(&extcon_dev_list_lock);
   1252	list_add(&edev->entry, &extcon_dev_list);
   1253	mutex_unlock(&extcon_dev_list_lock);
   1254
   1255	return 0;
   1256
   1257err_dev:
   1258	if (edev->max_supported)
   1259		kfree(edev->nh);
   1260err_alloc_nh:
   1261	if (edev->max_supported)
   1262		kfree(edev->extcon_dev_type.groups);
   1263err_alloc_groups:
   1264	if (edev->max_supported && edev->mutually_exclusive) {
   1265		for (index = 0; edev->mutually_exclusive[index]; index++)
   1266			kfree(edev->d_attrs_muex[index].attr.name);
   1267		kfree(edev->d_attrs_muex);
   1268		kfree(edev->attrs_muex);
   1269	}
   1270err_muex:
   1271	for (index = 0; index < edev->max_supported; index++)
   1272		kfree(edev->cables[index].attr_g.name);
   1273err_alloc_cables:
   1274	if (edev->max_supported)
   1275		kfree(edev->cables);
   1276err_sysfs_alloc:
   1277	return ret;
   1278}
   1279EXPORT_SYMBOL_GPL(extcon_dev_register);
   1280
   1281/**
   1282 * extcon_dev_unregister() - Unregister the extcon device.
   1283 * @edev:	the extcon device to be unregistered.
   1284 *
   1285 * Note that this does not call kfree(edev) because edev was not allocated
   1286 * by this class.
   1287 */
   1288void extcon_dev_unregister(struct extcon_dev *edev)
   1289{
   1290	int index;
   1291
   1292	if (!edev)
   1293		return;
   1294
   1295	mutex_lock(&extcon_dev_list_lock);
   1296	list_del(&edev->entry);
   1297	mutex_unlock(&extcon_dev_list_lock);
   1298
   1299	if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
   1300		dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
   1301				dev_name(&edev->dev));
   1302		return;
   1303	}
   1304
   1305	device_unregister(&edev->dev);
   1306
   1307	if (edev->mutually_exclusive && edev->max_supported) {
   1308		for (index = 0; edev->mutually_exclusive[index];
   1309				index++)
   1310			kfree(edev->d_attrs_muex[index].attr.name);
   1311		kfree(edev->d_attrs_muex);
   1312		kfree(edev->attrs_muex);
   1313	}
   1314
   1315	for (index = 0; index < edev->max_supported; index++)
   1316		kfree(edev->cables[index].attr_g.name);
   1317
   1318	if (edev->max_supported) {
   1319		kfree(edev->extcon_dev_type.groups);
   1320		kfree(edev->cables);
   1321		kfree(edev->nh);
   1322	}
   1323
   1324	put_device(&edev->dev);
   1325}
   1326EXPORT_SYMBOL_GPL(extcon_dev_unregister);
   1327
   1328#ifdef CONFIG_OF
   1329
   1330/*
   1331 * extcon_find_edev_by_node - Find the extcon device from devicetree.
   1332 * @node	: OF node identifying edev
   1333 *
   1334 * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
   1335 */
   1336struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
   1337{
   1338	struct extcon_dev *edev;
   1339
   1340	mutex_lock(&extcon_dev_list_lock);
   1341	list_for_each_entry(edev, &extcon_dev_list, entry)
   1342		if (edev->dev.parent && edev->dev.parent->of_node == node)
   1343			goto out;
   1344	edev = ERR_PTR(-EPROBE_DEFER);
   1345out:
   1346	mutex_unlock(&extcon_dev_list_lock);
   1347
   1348	return edev;
   1349}
   1350
   1351/*
   1352 * extcon_get_edev_by_phandle - Get the extcon device from devicetree.
   1353 * @dev		: the instance to the given device
   1354 * @index	: the index into list of extcon_dev
   1355 *
   1356 * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
   1357 */
   1358struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
   1359{
   1360	struct device_node *node;
   1361	struct extcon_dev *edev;
   1362
   1363	if (!dev)
   1364		return ERR_PTR(-EINVAL);
   1365
   1366	if (!dev->of_node) {
   1367		dev_dbg(dev, "device does not have a device node entry\n");
   1368		return ERR_PTR(-EINVAL);
   1369	}
   1370
   1371	node = of_parse_phandle(dev->of_node, "extcon", index);
   1372	if (!node) {
   1373		dev_dbg(dev, "failed to get phandle in %pOF node\n",
   1374			dev->of_node);
   1375		return ERR_PTR(-ENODEV);
   1376	}
   1377
   1378	edev = extcon_find_edev_by_node(node);
   1379	of_node_put(node);
   1380
   1381	return edev;
   1382}
   1383
   1384#else
   1385
   1386struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
   1387{
   1388	return ERR_PTR(-ENOSYS);
   1389}
   1390
   1391struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
   1392{
   1393	return ERR_PTR(-ENOSYS);
   1394}
   1395
   1396#endif /* CONFIG_OF */
   1397
   1398EXPORT_SYMBOL_GPL(extcon_find_edev_by_node);
   1399EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
   1400
   1401/**
   1402 * extcon_get_edev_name() - Get the name of the extcon device.
   1403 * @edev:	the extcon device
   1404 */
   1405const char *extcon_get_edev_name(struct extcon_dev *edev)
   1406{
   1407	return !edev ? NULL : edev->name;
   1408}
   1409EXPORT_SYMBOL_GPL(extcon_get_edev_name);
   1410
   1411static int __init extcon_class_init(void)
   1412{
   1413	return create_extcon_class();
   1414}
   1415module_init(extcon_class_init);
   1416
   1417static void __exit extcon_class_exit(void)
   1418{
   1419	class_destroy(extcon_class);
   1420}
   1421module_exit(extcon_class_exit);
   1422
   1423MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
   1424MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
   1425MODULE_DESCRIPTION("External Connector (extcon) framework");
   1426MODULE_LICENSE("GPL v2");