xref: /openbmc/linux/drivers/extcon/extcon.c (revision 11eecf91)
1f68a8342SChanwoo Choi /*
2b9ec23c0SChanwoo Choi  *  drivers/extcon/extcon.c - External Connector (extcon) framework.
3f68a8342SChanwoo Choi  *
4f68a8342SChanwoo Choi  *  External connector (extcon) class driver
5f68a8342SChanwoo Choi  *
62a9de9c0SChanwoo Choi  * Copyright (C) 2015 Samsung Electronics
72a9de9c0SChanwoo Choi  * Author: Chanwoo Choi <cw00.choi@samsung.com>
82a9de9c0SChanwoo Choi  *
9f68a8342SChanwoo Choi  * Copyright (C) 2012 Samsung Electronics
10f68a8342SChanwoo Choi  * Author: Donggeun Kim <dg77.kim@samsung.com>
11f68a8342SChanwoo Choi  * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
12f68a8342SChanwoo Choi  *
13f68a8342SChanwoo Choi  * based on android/drivers/switch/switch_class.c
14f68a8342SChanwoo Choi  * Copyright (C) 2008 Google, Inc.
15f68a8342SChanwoo Choi  * Author: Mike Lockwood <lockwood@android.com>
16f68a8342SChanwoo Choi  *
17f68a8342SChanwoo Choi  * This software is licensed under the terms of the GNU General Public
18f68a8342SChanwoo Choi  * License version 2, as published by the Free Software Foundation, and
19f68a8342SChanwoo Choi  * may be copied, distributed, and modified under those terms.
20f68a8342SChanwoo Choi  *
21f68a8342SChanwoo Choi  * This program is distributed in the hope that it will be useful,
22f68a8342SChanwoo Choi  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23f68a8342SChanwoo Choi  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24f68a8342SChanwoo Choi  * GNU General Public License for more details.
25f68a8342SChanwoo Choi  */
26f68a8342SChanwoo Choi 
27f68a8342SChanwoo Choi #include <linux/module.h>
28f68a8342SChanwoo Choi #include <linux/types.h>
29f68a8342SChanwoo Choi #include <linux/init.h>
30f68a8342SChanwoo Choi #include <linux/device.h>
31f68a8342SChanwoo Choi #include <linux/fs.h>
32f68a8342SChanwoo Choi #include <linux/err.h>
33f68a8342SChanwoo Choi #include <linux/extcon.h>
34f68a8342SChanwoo Choi #include <linux/of.h>
35f68a8342SChanwoo Choi #include <linux/slab.h>
36f68a8342SChanwoo Choi #include <linux/sysfs.h>
37f68a8342SChanwoo Choi 
382a9de9c0SChanwoo Choi #define SUPPORTED_CABLE_MAX	32
392a9de9c0SChanwoo Choi #define CABLE_NAME_MAX		30
402a9de9c0SChanwoo Choi 
412a9de9c0SChanwoo Choi static const char *extcon_name[] =  {
4273b6ecdbSChanwoo Choi 	[EXTCON_NONE]			= "NONE",
4373b6ecdbSChanwoo Choi 
448e9bc36dSChanwoo Choi 	/* USB external connector */
45f68a8342SChanwoo Choi 	[EXTCON_USB]			= "USB",
468e9bc36dSChanwoo Choi 	[EXTCON_USB_HOST]		= "USB-HOST",
478e9bc36dSChanwoo Choi 
4811eecf91SChanwoo Choi 	/* Charging external connector */
4911eecf91SChanwoo Choi 	[EXTCON_CHG_USB_SDP]		= "SDP",
5011eecf91SChanwoo Choi 	[EXTCON_CHG_USB_DCP]		= "DCP",
5111eecf91SChanwoo Choi 	[EXTCON_CHG_USB_CDP]		= "CDP",
5211eecf91SChanwoo Choi 	[EXTCON_CHG_USB_ACA]		= "ACA",
5311eecf91SChanwoo Choi 	[EXTCON_CHG_USB_FAST]		= "FAST-CHARGER",
5411eecf91SChanwoo Choi 	[EXTCON_CHG_USB_SLOW]		= "SLOW-CHARGER",
558e9bc36dSChanwoo Choi 
5611eecf91SChanwoo Choi 	/* Jack external connector */
5711eecf91SChanwoo Choi 	[EXTCON_JACK_MICROPHONE]	= "MICROPHONE",
5811eecf91SChanwoo Choi 	[EXTCON_JACK_HEADPHONE]		= "HEADPHONE",
5911eecf91SChanwoo Choi 	[EXTCON_JACK_LINE_IN]		= "LINE-IN",
6011eecf91SChanwoo Choi 	[EXTCON_JACK_LINE_OUT]		= "LINE-OUT",
6111eecf91SChanwoo Choi 	[EXTCON_JACK_VIDEO_IN]		= "VIDEO-IN",
6211eecf91SChanwoo Choi 	[EXTCON_JACK_VIDEO_OUT]		= "VIDEO-OUT",
6311eecf91SChanwoo Choi 	[EXTCON_JACK_SPDIF_IN]		= "SPDIF-IN",
6411eecf91SChanwoo Choi 	[EXTCON_JACK_SPDIF_OUT]		= "SPDIF-OUT",
658e9bc36dSChanwoo Choi 
6611eecf91SChanwoo Choi 	/* Display external connector */
6711eecf91SChanwoo Choi 	[EXTCON_DISP_HDMI]		= "HDMI",
6811eecf91SChanwoo Choi 	[EXTCON_DISP_MHL]		= "MHL",
6911eecf91SChanwoo Choi 	[EXTCON_DISP_DVI]		= "DVI",
7011eecf91SChanwoo Choi 	[EXTCON_DISP_VGA]		= "VGA",
718e9bc36dSChanwoo Choi 
7211eecf91SChanwoo Choi 	/* Miscellaneous external connector */
738e9bc36dSChanwoo Choi 	[EXTCON_DOCK]			= "DOCK",
742a9de9c0SChanwoo Choi 	[EXTCON_JIG]			= "JIG",
758e9bc36dSChanwoo Choi 	[EXTCON_MECHANICAL]		= "MECHANICAL",
768e9bc36dSChanwoo Choi 
772a9de9c0SChanwoo Choi 	NULL,
78f68a8342SChanwoo Choi };
79f68a8342SChanwoo Choi 
80f68a8342SChanwoo Choi static struct class *extcon_class;
81f68a8342SChanwoo Choi #if defined(CONFIG_ANDROID)
82f68a8342SChanwoo Choi static struct class_compat *switch_class;
83f68a8342SChanwoo Choi #endif /* CONFIG_ANDROID */
84f68a8342SChanwoo Choi 
85f68a8342SChanwoo Choi static LIST_HEAD(extcon_dev_list);
86f68a8342SChanwoo Choi static DEFINE_MUTEX(extcon_dev_list_lock);
87f68a8342SChanwoo Choi 
88f68a8342SChanwoo Choi /**
89f68a8342SChanwoo Choi  * check_mutually_exclusive - Check if new_state violates mutually_exclusive
90f68a8342SChanwoo Choi  *			      condition.
91f68a8342SChanwoo Choi  * @edev:	the extcon device
92f68a8342SChanwoo Choi  * @new_state:	new cable attach status for @edev
93f68a8342SChanwoo Choi  *
94f68a8342SChanwoo Choi  * Returns 0 if nothing violates. Returns the index + 1 for the first
95f68a8342SChanwoo Choi  * violated condition.
96f68a8342SChanwoo Choi  */
97f68a8342SChanwoo Choi static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
98f68a8342SChanwoo Choi {
99f68a8342SChanwoo Choi 	int i = 0;
100f68a8342SChanwoo Choi 
101f68a8342SChanwoo Choi 	if (!edev->mutually_exclusive)
102f68a8342SChanwoo Choi 		return 0;
103f68a8342SChanwoo Choi 
104f68a8342SChanwoo Choi 	for (i = 0; edev->mutually_exclusive[i]; i++) {
105f68a8342SChanwoo Choi 		int weight;
106f68a8342SChanwoo Choi 		u32 correspondants = new_state & edev->mutually_exclusive[i];
107f68a8342SChanwoo Choi 
108f68a8342SChanwoo Choi 		/* calculate the total number of bits set */
109f68a8342SChanwoo Choi 		weight = hweight32(correspondants);
110f68a8342SChanwoo Choi 		if (weight > 1)
111f68a8342SChanwoo Choi 			return i + 1;
112f68a8342SChanwoo Choi 	}
113f68a8342SChanwoo Choi 
114f68a8342SChanwoo Choi 	return 0;
115f68a8342SChanwoo Choi }
116f68a8342SChanwoo Choi 
11773b6ecdbSChanwoo Choi static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
1182a9de9c0SChanwoo Choi {
1192a9de9c0SChanwoo Choi 	int i;
1202a9de9c0SChanwoo Choi 
1212a9de9c0SChanwoo Choi 	/* Find the the index of extcon cable in edev->supported_cable */
1222a9de9c0SChanwoo Choi 	for (i = 0; i < edev->max_supported; i++) {
1232a9de9c0SChanwoo Choi 		if (edev->supported_cable[i] == id)
1242a9de9c0SChanwoo Choi 			return i;
1252a9de9c0SChanwoo Choi 	}
1262a9de9c0SChanwoo Choi 
1272a9de9c0SChanwoo Choi 	return -EINVAL;
1282a9de9c0SChanwoo Choi }
1292a9de9c0SChanwoo Choi 
130be052cc8SRoger Quadros static int find_cable_id_by_name(struct extcon_dev *edev, const char *name)
1312a9de9c0SChanwoo Choi {
132a598af7fSDan Carpenter 	int id = -EINVAL;
13373b6ecdbSChanwoo Choi 	int i = 0;
1342a9de9c0SChanwoo Choi 
135be052cc8SRoger Quadros 	/* Find the id of extcon cable */
13673b6ecdbSChanwoo Choi 	while (extcon_name[i]) {
1372a9de9c0SChanwoo Choi 		if (!strncmp(extcon_name[i], name, CABLE_NAME_MAX)) {
1382a9de9c0SChanwoo Choi 			id = i;
1392a9de9c0SChanwoo Choi 			break;
1402a9de9c0SChanwoo Choi 		}
141be052cc8SRoger Quadros 		i++;
1422a9de9c0SChanwoo Choi 	}
1432a9de9c0SChanwoo Choi 
144be052cc8SRoger Quadros 	return id;
145be052cc8SRoger Quadros }
146be052cc8SRoger Quadros 
147be052cc8SRoger Quadros static int find_cable_index_by_name(struct extcon_dev *edev, const char *name)
148be052cc8SRoger Quadros {
149a598af7fSDan Carpenter 	int id;
150be052cc8SRoger Quadros 
151be052cc8SRoger Quadros 	if (edev->max_supported == 0)
1522a9de9c0SChanwoo Choi 		return -EINVAL;
1532a9de9c0SChanwoo Choi 
154be052cc8SRoger Quadros 	/* Find the the number of extcon cable */
155be052cc8SRoger Quadros 	id = find_cable_id_by_name(edev, name);
156be052cc8SRoger Quadros 	if (id < 0)
157be052cc8SRoger Quadros 		return id;
158be052cc8SRoger Quadros 
1592a9de9c0SChanwoo Choi 	return find_cable_index_by_id(edev, id);
1602a9de9c0SChanwoo Choi }
1612a9de9c0SChanwoo Choi 
162046050f6SChanwoo Choi static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
163046050f6SChanwoo Choi {
164046050f6SChanwoo Choi 	if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
165f4513b06SHans de Goede 		*attached = ((new >> idx) & 0x1) ? true : false;
166046050f6SChanwoo Choi 		return true;
167046050f6SChanwoo Choi 	}
168046050f6SChanwoo Choi 
169046050f6SChanwoo Choi 	return false;
170046050f6SChanwoo Choi }
171046050f6SChanwoo Choi 
172f68a8342SChanwoo Choi static ssize_t state_show(struct device *dev, struct device_attribute *attr,
173f68a8342SChanwoo Choi 			  char *buf)
174f68a8342SChanwoo Choi {
175f68a8342SChanwoo Choi 	int i, count = 0;
176f68a8342SChanwoo Choi 	struct extcon_dev *edev = dev_get_drvdata(dev);
177f68a8342SChanwoo Choi 
178f68a8342SChanwoo Choi 	if (edev->max_supported == 0)
179f68a8342SChanwoo Choi 		return sprintf(buf, "%u\n", edev->state);
180f68a8342SChanwoo Choi 
1812a9de9c0SChanwoo Choi 	for (i = 0; i < edev->max_supported; i++) {
182f68a8342SChanwoo Choi 		count += sprintf(buf + count, "%s=%d\n",
1832a9de9c0SChanwoo Choi 				extcon_name[edev->supported_cable[i]],
184f68a8342SChanwoo Choi 				 !!(edev->state & (1 << i)));
185f68a8342SChanwoo Choi 	}
186f68a8342SChanwoo Choi 
187f68a8342SChanwoo Choi 	return count;
188f68a8342SChanwoo Choi }
189f68a8342SChanwoo Choi 
190f68a8342SChanwoo Choi static ssize_t state_store(struct device *dev, struct device_attribute *attr,
191f68a8342SChanwoo Choi 			   const char *buf, size_t count)
192f68a8342SChanwoo Choi {
193f68a8342SChanwoo Choi 	u32 state;
194f68a8342SChanwoo Choi 	ssize_t ret = 0;
195f68a8342SChanwoo Choi 	struct extcon_dev *edev = dev_get_drvdata(dev);
196f68a8342SChanwoo Choi 
197f68a8342SChanwoo Choi 	ret = sscanf(buf, "0x%x", &state);
198f68a8342SChanwoo Choi 	if (ret == 0)
199f68a8342SChanwoo Choi 		ret = -EINVAL;
200f68a8342SChanwoo Choi 	else
201f68a8342SChanwoo Choi 		ret = extcon_set_state(edev, state);
202f68a8342SChanwoo Choi 
203f68a8342SChanwoo Choi 	if (ret < 0)
204f68a8342SChanwoo Choi 		return ret;
205f68a8342SChanwoo Choi 
206f68a8342SChanwoo Choi 	return count;
207f68a8342SChanwoo Choi }
208f68a8342SChanwoo Choi static DEVICE_ATTR_RW(state);
209f68a8342SChanwoo Choi 
210f68a8342SChanwoo Choi static ssize_t name_show(struct device *dev, struct device_attribute *attr,
211f68a8342SChanwoo Choi 		char *buf)
212f68a8342SChanwoo Choi {
213f68a8342SChanwoo Choi 	struct extcon_dev *edev = dev_get_drvdata(dev);
214f68a8342SChanwoo Choi 
21571c3ffa5SChanwoo Choi 	return sprintf(buf, "%s\n", edev->name);
216f68a8342SChanwoo Choi }
217f68a8342SChanwoo Choi static DEVICE_ATTR_RO(name);
218f68a8342SChanwoo Choi 
219f68a8342SChanwoo Choi static ssize_t cable_name_show(struct device *dev,
220f68a8342SChanwoo Choi 			       struct device_attribute *attr, char *buf)
221f68a8342SChanwoo Choi {
222f68a8342SChanwoo Choi 	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
223f68a8342SChanwoo Choi 						  attr_name);
2242a9de9c0SChanwoo Choi 	int i = cable->cable_index;
225f68a8342SChanwoo Choi 
226f68a8342SChanwoo Choi 	return sprintf(buf, "%s\n",
2272a9de9c0SChanwoo Choi 			extcon_name[cable->edev->supported_cable[i]]);
228f68a8342SChanwoo Choi }
229f68a8342SChanwoo Choi 
230f68a8342SChanwoo Choi static ssize_t cable_state_show(struct device *dev,
231f68a8342SChanwoo Choi 				struct device_attribute *attr, char *buf)
232f68a8342SChanwoo Choi {
233f68a8342SChanwoo Choi 	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
234f68a8342SChanwoo Choi 						  attr_state);
235f68a8342SChanwoo Choi 
236be052cc8SRoger Quadros 	int i = cable->cable_index;
237be052cc8SRoger Quadros 
238f68a8342SChanwoo Choi 	return sprintf(buf, "%d\n",
239f68a8342SChanwoo Choi 		       extcon_get_cable_state_(cable->edev,
240be052cc8SRoger Quadros 					       cable->edev->supported_cable[i]));
241f68a8342SChanwoo Choi }
242f68a8342SChanwoo Choi 
243f68a8342SChanwoo Choi /**
244f68a8342SChanwoo Choi  * extcon_update_state() - Update the cable attach states of the extcon device
245f68a8342SChanwoo Choi  *			   only for the masked bits.
246f68a8342SChanwoo Choi  * @edev:	the extcon device
247f68a8342SChanwoo Choi  * @mask:	the bit mask to designate updated bits.
248f68a8342SChanwoo Choi  * @state:	new cable attach status for @edev
249f68a8342SChanwoo Choi  *
250f68a8342SChanwoo Choi  * Changing the state sends uevent with environment variable containing
251f68a8342SChanwoo Choi  * the name of extcon device (envp[0]) and the state output (envp[1]).
252f68a8342SChanwoo Choi  * Tizen uses this format for extcon device to get events from ports.
253f68a8342SChanwoo Choi  * Android uses this format as well.
254f68a8342SChanwoo Choi  *
255f68a8342SChanwoo Choi  * Note that the notifier provides which bits are changed in the state
256f68a8342SChanwoo Choi  * variable with the val parameter (second) to the callback.
257f68a8342SChanwoo Choi  */
258f68a8342SChanwoo Choi int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
259f68a8342SChanwoo Choi {
260f68a8342SChanwoo Choi 	char name_buf[120];
261f68a8342SChanwoo Choi 	char state_buf[120];
262f68a8342SChanwoo Choi 	char *prop_buf;
263f68a8342SChanwoo Choi 	char *envp[3];
264f68a8342SChanwoo Choi 	int env_offset = 0;
265f68a8342SChanwoo Choi 	int length;
266046050f6SChanwoo Choi 	int index;
267f68a8342SChanwoo Choi 	unsigned long flags;
268046050f6SChanwoo Choi 	bool attached;
269f68a8342SChanwoo Choi 
2707eae43aeSChanwoo Choi 	if (!edev)
2717eae43aeSChanwoo Choi 		return -EINVAL;
2727eae43aeSChanwoo Choi 
273f68a8342SChanwoo Choi 	spin_lock_irqsave(&edev->lock, flags);
274f68a8342SChanwoo Choi 
275f68a8342SChanwoo Choi 	if (edev->state != ((edev->state & ~mask) | (state & mask))) {
276f7a89811SRoger Quadros 		u32 old_state;
277f7a89811SRoger Quadros 
278f68a8342SChanwoo Choi 		if (check_mutually_exclusive(edev, (edev->state & ~mask) |
279f68a8342SChanwoo Choi 						   (state & mask))) {
280f68a8342SChanwoo Choi 			spin_unlock_irqrestore(&edev->lock, flags);
281f68a8342SChanwoo Choi 			return -EPERM;
282f68a8342SChanwoo Choi 		}
283f68a8342SChanwoo Choi 
284f7a89811SRoger Quadros 		old_state = edev->state;
285f68a8342SChanwoo Choi 		edev->state &= ~mask;
286f68a8342SChanwoo Choi 		edev->state |= state & mask;
287f68a8342SChanwoo Choi 
288f7a89811SRoger Quadros 		for (index = 0; index < edev->max_supported; index++) {
289f7a89811SRoger Quadros 			if (is_extcon_changed(old_state, edev->state, index,
290f7a89811SRoger Quadros 					      &attached))
291f7a89811SRoger Quadros 				raw_notifier_call_chain(&edev->nh[index],
292f7a89811SRoger Quadros 							attached, edev);
293f7a89811SRoger Quadros 		}
294f7a89811SRoger Quadros 
295f68a8342SChanwoo Choi 		/* This could be in interrupt handler */
296f68a8342SChanwoo Choi 		prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
297f68a8342SChanwoo Choi 		if (prop_buf) {
298f68a8342SChanwoo Choi 			length = name_show(&edev->dev, NULL, prop_buf);
299f68a8342SChanwoo Choi 			if (length > 0) {
300f68a8342SChanwoo Choi 				if (prop_buf[length - 1] == '\n')
301f68a8342SChanwoo Choi 					prop_buf[length - 1] = 0;
302f68a8342SChanwoo Choi 				snprintf(name_buf, sizeof(name_buf),
303f68a8342SChanwoo Choi 					"NAME=%s", prop_buf);
304f68a8342SChanwoo Choi 				envp[env_offset++] = name_buf;
305f68a8342SChanwoo Choi 			}
306f68a8342SChanwoo Choi 			length = state_show(&edev->dev, NULL, prop_buf);
307f68a8342SChanwoo Choi 			if (length > 0) {
308f68a8342SChanwoo Choi 				if (prop_buf[length - 1] == '\n')
309f68a8342SChanwoo Choi 					prop_buf[length - 1] = 0;
310f68a8342SChanwoo Choi 				snprintf(state_buf, sizeof(state_buf),
311f68a8342SChanwoo Choi 					"STATE=%s", prop_buf);
312f68a8342SChanwoo Choi 				envp[env_offset++] = state_buf;
313f68a8342SChanwoo Choi 			}
314f68a8342SChanwoo Choi 			envp[env_offset] = NULL;
315f68a8342SChanwoo Choi 			/* Unlock early before uevent */
316f68a8342SChanwoo Choi 			spin_unlock_irqrestore(&edev->lock, flags);
317f68a8342SChanwoo Choi 
318f68a8342SChanwoo Choi 			kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
319f68a8342SChanwoo Choi 			free_page((unsigned long)prop_buf);
320f68a8342SChanwoo Choi 		} else {
321f68a8342SChanwoo Choi 			/* Unlock early before uevent */
322f68a8342SChanwoo Choi 			spin_unlock_irqrestore(&edev->lock, flags);
323f68a8342SChanwoo Choi 
324f68a8342SChanwoo Choi 			dev_err(&edev->dev, "out of memory in extcon_set_state\n");
325f68a8342SChanwoo Choi 			kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
326f68a8342SChanwoo Choi 		}
327f68a8342SChanwoo Choi 	} else {
328f68a8342SChanwoo Choi 		/* No changes */
329f68a8342SChanwoo Choi 		spin_unlock_irqrestore(&edev->lock, flags);
330f68a8342SChanwoo Choi 	}
331f68a8342SChanwoo Choi 
332f68a8342SChanwoo Choi 	return 0;
333f68a8342SChanwoo Choi }
334f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_update_state);
335f68a8342SChanwoo Choi 
336f68a8342SChanwoo Choi /**
337f68a8342SChanwoo Choi  * extcon_set_state() - Set the cable attach states of the extcon device.
338f68a8342SChanwoo Choi  * @edev:	the extcon device
339f68a8342SChanwoo Choi  * @state:	new cable attach status for @edev
340f68a8342SChanwoo Choi  *
341f68a8342SChanwoo Choi  * Note that notifier provides which bits are changed in the state
342f68a8342SChanwoo Choi  * variable with the val parameter (second) to the callback.
343f68a8342SChanwoo Choi  */
344f68a8342SChanwoo Choi int extcon_set_state(struct extcon_dev *edev, u32 state)
345f68a8342SChanwoo Choi {
3467eae43aeSChanwoo Choi 	if (!edev)
3477eae43aeSChanwoo Choi 		return -EINVAL;
3487eae43aeSChanwoo Choi 
349f68a8342SChanwoo Choi 	return extcon_update_state(edev, 0xffffffff, state);
350f68a8342SChanwoo Choi }
351f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_set_state);
352f68a8342SChanwoo Choi 
353f68a8342SChanwoo Choi /**
354f68a8342SChanwoo Choi  * extcon_get_cable_state_() - Get the status of a specific cable.
355f68a8342SChanwoo Choi  * @edev:	the extcon device that has the cable.
3562a9de9c0SChanwoo Choi  * @id:		the unique id of each external connector in extcon enumeration.
357f68a8342SChanwoo Choi  */
35873b6ecdbSChanwoo Choi int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
359f68a8342SChanwoo Choi {
3602a9de9c0SChanwoo Choi 	int index;
3612a9de9c0SChanwoo Choi 
3627eae43aeSChanwoo Choi 	if (!edev)
3637eae43aeSChanwoo Choi 		return -EINVAL;
3647eae43aeSChanwoo Choi 
3652a9de9c0SChanwoo Choi 	index = find_cable_index_by_id(edev, id);
3662a9de9c0SChanwoo Choi 	if (index < 0)
3672a9de9c0SChanwoo Choi 		return index;
3682a9de9c0SChanwoo Choi 
3692a9de9c0SChanwoo Choi 	if (edev->max_supported && edev->max_supported <= index)
370f68a8342SChanwoo Choi 		return -EINVAL;
371f68a8342SChanwoo Choi 
372f68a8342SChanwoo Choi 	return !!(edev->state & (1 << index));
373f68a8342SChanwoo Choi }
374f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
375f68a8342SChanwoo Choi 
376f68a8342SChanwoo Choi /**
377f68a8342SChanwoo Choi  * extcon_get_cable_state() - Get the status of a specific cable.
378f68a8342SChanwoo Choi  * @edev:	the extcon device that has the cable.
379f68a8342SChanwoo Choi  * @cable_name:	cable name.
380f68a8342SChanwoo Choi  *
381f68a8342SChanwoo Choi  * Note that this is slower than extcon_get_cable_state_.
382f68a8342SChanwoo Choi  */
383f68a8342SChanwoo Choi int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
384f68a8342SChanwoo Choi {
385a598af7fSDan Carpenter 	int id;
386be052cc8SRoger Quadros 
387be052cc8SRoger Quadros 	id = find_cable_id_by_name(edev, cable_name);
388be052cc8SRoger Quadros 	if (id < 0)
389be052cc8SRoger Quadros 		return id;
390be052cc8SRoger Quadros 
391be052cc8SRoger Quadros 	return extcon_get_cable_state_(edev, id);
392f68a8342SChanwoo Choi }
393f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_get_cable_state);
394f68a8342SChanwoo Choi 
395f68a8342SChanwoo Choi /**
396f68a8342SChanwoo Choi  * extcon_set_cable_state_() - Set the status of a specific cable.
397f68a8342SChanwoo Choi  * @edev:		the extcon device that has the cable.
3982a9de9c0SChanwoo Choi  * @id:			the unique id of each external connector
3992a9de9c0SChanwoo Choi  *			in extcon enumeration.
4002a9de9c0SChanwoo Choi  * @state:		the new cable status. The default semantics is
401f68a8342SChanwoo Choi  *			true: attached / false: detached.
402f68a8342SChanwoo Choi  */
40373b6ecdbSChanwoo Choi int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
4042a9de9c0SChanwoo Choi 				bool cable_state)
405f68a8342SChanwoo Choi {
406f68a8342SChanwoo Choi 	u32 state;
4072a9de9c0SChanwoo Choi 	int index;
408f68a8342SChanwoo Choi 
4097eae43aeSChanwoo Choi 	if (!edev)
4107eae43aeSChanwoo Choi 		return -EINVAL;
4117eae43aeSChanwoo Choi 
4122a9de9c0SChanwoo Choi 	index = find_cable_index_by_id(edev, id);
4132a9de9c0SChanwoo Choi 	if (index < 0)
4142a9de9c0SChanwoo Choi 		return index;
4152a9de9c0SChanwoo Choi 
4162a9de9c0SChanwoo Choi 	if (edev->max_supported && edev->max_supported <= index)
417f68a8342SChanwoo Choi 		return -EINVAL;
418f68a8342SChanwoo Choi 
419f68a8342SChanwoo Choi 	state = cable_state ? (1 << index) : 0;
420f68a8342SChanwoo Choi 	return extcon_update_state(edev, 1 << index, state);
421f68a8342SChanwoo Choi }
422f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
423f68a8342SChanwoo Choi 
424f68a8342SChanwoo Choi /**
425f68a8342SChanwoo Choi  * extcon_set_cable_state() - Set the status of a specific cable.
426f68a8342SChanwoo Choi  * @edev:		the extcon device that has the cable.
427f68a8342SChanwoo Choi  * @cable_name:		cable name.
428f68a8342SChanwoo Choi  * @cable_state:	the new cable status. The default semantics is
429f68a8342SChanwoo Choi  *			true: attached / false: detached.
430f68a8342SChanwoo Choi  *
431f68a8342SChanwoo Choi  * Note that this is slower than extcon_set_cable_state_.
432f68a8342SChanwoo Choi  */
433f68a8342SChanwoo Choi int extcon_set_cable_state(struct extcon_dev *edev,
434f68a8342SChanwoo Choi 			const char *cable_name, bool cable_state)
435f68a8342SChanwoo Choi {
436a598af7fSDan Carpenter 	int id;
437be052cc8SRoger Quadros 
438be052cc8SRoger Quadros 	id = find_cable_id_by_name(edev, cable_name);
439be052cc8SRoger Quadros 	if (id < 0)
440be052cc8SRoger Quadros 		return id;
441be052cc8SRoger Quadros 
442be052cc8SRoger Quadros 	return extcon_set_cable_state_(edev, id, cable_state);
443f68a8342SChanwoo Choi }
444f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_set_cable_state);
445f68a8342SChanwoo Choi 
446f68a8342SChanwoo Choi /**
447f68a8342SChanwoo Choi  * extcon_get_extcon_dev() - Get the extcon device instance from the name
448f68a8342SChanwoo Choi  * @extcon_name:	The extcon name provided with extcon_dev_register()
449f68a8342SChanwoo Choi  */
450f68a8342SChanwoo Choi struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
451f68a8342SChanwoo Choi {
452f68a8342SChanwoo Choi 	struct extcon_dev *sd;
453f68a8342SChanwoo Choi 
4547eae43aeSChanwoo Choi 	if (!extcon_name)
4557eae43aeSChanwoo Choi 		return ERR_PTR(-EINVAL);
4567eae43aeSChanwoo Choi 
457f68a8342SChanwoo Choi 	mutex_lock(&extcon_dev_list_lock);
458f68a8342SChanwoo Choi 	list_for_each_entry(sd, &extcon_dev_list, entry) {
459f68a8342SChanwoo Choi 		if (!strcmp(sd->name, extcon_name))
460f68a8342SChanwoo Choi 			goto out;
461f68a8342SChanwoo Choi 	}
462f68a8342SChanwoo Choi 	sd = NULL;
463f68a8342SChanwoo Choi out:
464f68a8342SChanwoo Choi 	mutex_unlock(&extcon_dev_list_lock);
465f68a8342SChanwoo Choi 	return sd;
466f68a8342SChanwoo Choi }
467f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
468f68a8342SChanwoo Choi 
469f68a8342SChanwoo Choi /**
470f68a8342SChanwoo Choi  * extcon_register_interest() - Register a notifier for a state change of a
471f68a8342SChanwoo Choi  *				specific cable, not an entier set of cables of a
472f68a8342SChanwoo Choi  *				extcon device.
473f68a8342SChanwoo Choi  * @obj:		an empty extcon_specific_cable_nb object to be returned.
474f68a8342SChanwoo Choi  * @extcon_name:	the name of extcon device.
475f68a8342SChanwoo Choi  *			if NULL, extcon_register_interest will register
476f68a8342SChanwoo Choi  *			every cable with the target cable_name given.
477f68a8342SChanwoo Choi  * @cable_name:		the target cable name.
478f68a8342SChanwoo Choi  * @nb:			the notifier block to get notified.
479f68a8342SChanwoo Choi  *
480f68a8342SChanwoo Choi  * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
481f68a8342SChanwoo Choi  * the struct for you.
482f68a8342SChanwoo Choi  *
483f68a8342SChanwoo Choi  * extcon_register_interest is a helper function for those who want to get
484f68a8342SChanwoo Choi  * notification for a single specific cable's status change. If a user wants
485f68a8342SChanwoo Choi  * to get notification for any changes of all cables of a extcon device,
486f68a8342SChanwoo Choi  * he/she should use the general extcon_register_notifier().
487f68a8342SChanwoo Choi  *
488f68a8342SChanwoo Choi  * Note that the second parameter given to the callback of nb (val) is
489f68a8342SChanwoo Choi  * "old_state", not the current state. The current state can be retrieved
490f68a8342SChanwoo Choi  * by looking at the third pameter (edev pointer)'s state value.
491f68a8342SChanwoo Choi  */
492f68a8342SChanwoo Choi int extcon_register_interest(struct extcon_specific_cable_nb *obj,
493f68a8342SChanwoo Choi 			     const char *extcon_name, const char *cable_name,
494f68a8342SChanwoo Choi 			     struct notifier_block *nb)
495f68a8342SChanwoo Choi {
49666bee35fSHans de Goede 	unsigned long flags;
49766bee35fSHans de Goede 	int ret;
49866bee35fSHans de Goede 
499f68a8342SChanwoo Choi 	if (!obj || !cable_name || !nb)
500f68a8342SChanwoo Choi 		return -EINVAL;
501f68a8342SChanwoo Choi 
502f68a8342SChanwoo Choi 	if (extcon_name) {
503f68a8342SChanwoo Choi 		obj->edev = extcon_get_extcon_dev(extcon_name);
504f68a8342SChanwoo Choi 		if (!obj->edev)
505f68a8342SChanwoo Choi 			return -ENODEV;
506f68a8342SChanwoo Choi 
5072a9de9c0SChanwoo Choi 		obj->cable_index = find_cable_index_by_name(obj->edev,
508f68a8342SChanwoo Choi 							cable_name);
509f68a8342SChanwoo Choi 		if (obj->cable_index < 0)
510f68a8342SChanwoo Choi 			return obj->cable_index;
511f68a8342SChanwoo Choi 
512f68a8342SChanwoo Choi 		obj->user_nb = nb;
513f68a8342SChanwoo Choi 
51466bee35fSHans de Goede 		spin_lock_irqsave(&obj->edev->lock, flags);
515046050f6SChanwoo Choi 		ret = raw_notifier_chain_register(
516046050f6SChanwoo Choi 					&obj->edev->nh[obj->cable_index],
517046050f6SChanwoo Choi 					obj->user_nb);
51866bee35fSHans de Goede 		spin_unlock_irqrestore(&obj->edev->lock, flags);
519f68a8342SChanwoo Choi 	} else {
520f68a8342SChanwoo Choi 		struct class_dev_iter iter;
521f68a8342SChanwoo Choi 		struct extcon_dev *extd;
522f68a8342SChanwoo Choi 		struct device *dev;
523f68a8342SChanwoo Choi 
524f68a8342SChanwoo Choi 		if (!extcon_class)
525f68a8342SChanwoo Choi 			return -ENODEV;
526f68a8342SChanwoo Choi 		class_dev_iter_init(&iter, extcon_class, NULL, NULL);
527f68a8342SChanwoo Choi 		while ((dev = class_dev_iter_next(&iter))) {
528f68a8342SChanwoo Choi 			extd = dev_get_drvdata(dev);
529f68a8342SChanwoo Choi 
5302a9de9c0SChanwoo Choi 			if (find_cable_index_by_name(extd, cable_name) < 0)
531f68a8342SChanwoo Choi 				continue;
532f68a8342SChanwoo Choi 
533f68a8342SChanwoo Choi 			class_dev_iter_exit(&iter);
534f68a8342SChanwoo Choi 			return extcon_register_interest(obj, extd->name,
535f68a8342SChanwoo Choi 						cable_name, nb);
536f68a8342SChanwoo Choi 		}
537f68a8342SChanwoo Choi 
538b9ec23c0SChanwoo Choi 		ret = -ENODEV;
539f68a8342SChanwoo Choi 	}
540b9ec23c0SChanwoo Choi 
541b9ec23c0SChanwoo Choi 	return ret;
542f68a8342SChanwoo Choi }
543f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_register_interest);
544f68a8342SChanwoo Choi 
545f68a8342SChanwoo Choi /**
546f68a8342SChanwoo Choi  * extcon_unregister_interest() - Unregister the notifier registered by
547f68a8342SChanwoo Choi  *				  extcon_register_interest().
548f68a8342SChanwoo Choi  * @obj:	the extcon_specific_cable_nb object returned by
549f68a8342SChanwoo Choi  *		extcon_register_interest().
550f68a8342SChanwoo Choi  */
551f68a8342SChanwoo Choi int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
552f68a8342SChanwoo Choi {
55366bee35fSHans de Goede 	unsigned long flags;
55466bee35fSHans de Goede 	int ret;
55566bee35fSHans de Goede 
556f68a8342SChanwoo Choi 	if (!obj)
557f68a8342SChanwoo Choi 		return -EINVAL;
558f68a8342SChanwoo Choi 
55966bee35fSHans de Goede 	spin_lock_irqsave(&obj->edev->lock, flags);
560046050f6SChanwoo Choi 	ret = raw_notifier_chain_unregister(
561046050f6SChanwoo Choi 			&obj->edev->nh[obj->cable_index], obj->user_nb);
56266bee35fSHans de Goede 	spin_unlock_irqrestore(&obj->edev->lock, flags);
56366bee35fSHans de Goede 
56466bee35fSHans de Goede 	return ret;
565f68a8342SChanwoo Choi }
566f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_unregister_interest);
567f68a8342SChanwoo Choi 
568f68a8342SChanwoo Choi /**
569f68a8342SChanwoo Choi  * extcon_register_notifier() - Register a notifiee to get notified by
570f68a8342SChanwoo Choi  *				any attach status changes from the extcon.
571046050f6SChanwoo Choi  * @edev:	the extcon device that has the external connecotr.
572046050f6SChanwoo Choi  * @id:		the unique id of each external connector in extcon enumeration.
573f68a8342SChanwoo Choi  * @nb:		a notifier block to be registered.
574f68a8342SChanwoo Choi  *
575f68a8342SChanwoo Choi  * Note that the second parameter given to the callback of nb (val) is
576f68a8342SChanwoo Choi  * "old_state", not the current state. The current state can be retrieved
577f68a8342SChanwoo Choi  * by looking at the third pameter (edev pointer)'s state value.
578f68a8342SChanwoo Choi  */
57973b6ecdbSChanwoo Choi int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
580f68a8342SChanwoo Choi 			     struct notifier_block *nb)
581f68a8342SChanwoo Choi {
58266bee35fSHans de Goede 	unsigned long flags;
583046050f6SChanwoo Choi 	int ret, idx;
584046050f6SChanwoo Choi 
5857eae43aeSChanwoo Choi 	if (!edev || !nb)
5867eae43aeSChanwoo Choi 		return -EINVAL;
5877eae43aeSChanwoo Choi 
588046050f6SChanwoo Choi 	idx = find_cable_index_by_id(edev, id);
58966bee35fSHans de Goede 
59066bee35fSHans de Goede 	spin_lock_irqsave(&edev->lock, flags);
591046050f6SChanwoo Choi 	ret = raw_notifier_chain_register(&edev->nh[idx], nb);
59266bee35fSHans de Goede 	spin_unlock_irqrestore(&edev->lock, flags);
59366bee35fSHans de Goede 
59466bee35fSHans de Goede 	return ret;
595f68a8342SChanwoo Choi }
596f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_register_notifier);
597f68a8342SChanwoo Choi 
598f68a8342SChanwoo Choi /**
599f68a8342SChanwoo Choi  * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
600046050f6SChanwoo Choi  * @edev:	the extcon device that has the external connecotr.
601046050f6SChanwoo Choi  * @id:		the unique id of each external connector in extcon enumeration.
602046050f6SChanwoo Choi  * @nb:		a notifier block to be registered.
603f68a8342SChanwoo Choi  */
60473b6ecdbSChanwoo Choi int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
605f68a8342SChanwoo Choi 				struct notifier_block *nb)
606f68a8342SChanwoo Choi {
60766bee35fSHans de Goede 	unsigned long flags;
608046050f6SChanwoo Choi 	int ret, idx;
609046050f6SChanwoo Choi 
6107eae43aeSChanwoo Choi 	if (!edev || !nb)
6117eae43aeSChanwoo Choi 		return -EINVAL;
6127eae43aeSChanwoo Choi 
613046050f6SChanwoo Choi 	idx = find_cable_index_by_id(edev, id);
61466bee35fSHans de Goede 
61566bee35fSHans de Goede 	spin_lock_irqsave(&edev->lock, flags);
616046050f6SChanwoo Choi 	ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
61766bee35fSHans de Goede 	spin_unlock_irqrestore(&edev->lock, flags);
61866bee35fSHans de Goede 
61966bee35fSHans de Goede 	return ret;
620f68a8342SChanwoo Choi }
621f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
622f68a8342SChanwoo Choi 
623f68a8342SChanwoo Choi static struct attribute *extcon_attrs[] = {
624f68a8342SChanwoo Choi 	&dev_attr_state.attr,
625f68a8342SChanwoo Choi 	&dev_attr_name.attr,
626f68a8342SChanwoo Choi 	NULL,
627f68a8342SChanwoo Choi };
628f68a8342SChanwoo Choi ATTRIBUTE_GROUPS(extcon);
629f68a8342SChanwoo Choi 
630f68a8342SChanwoo Choi static int create_extcon_class(void)
631f68a8342SChanwoo Choi {
632f68a8342SChanwoo Choi 	if (!extcon_class) {
633f68a8342SChanwoo Choi 		extcon_class = class_create(THIS_MODULE, "extcon");
634f68a8342SChanwoo Choi 		if (IS_ERR(extcon_class))
635f68a8342SChanwoo Choi 			return PTR_ERR(extcon_class);
636f68a8342SChanwoo Choi 		extcon_class->dev_groups = extcon_groups;
637f68a8342SChanwoo Choi 
638f68a8342SChanwoo Choi #if defined(CONFIG_ANDROID)
639f68a8342SChanwoo Choi 		switch_class = class_compat_register("switch");
640f68a8342SChanwoo Choi 		if (WARN(!switch_class, "cannot allocate"))
641f68a8342SChanwoo Choi 			return -ENOMEM;
642f68a8342SChanwoo Choi #endif /* CONFIG_ANDROID */
643f68a8342SChanwoo Choi 	}
644f68a8342SChanwoo Choi 
645f68a8342SChanwoo Choi 	return 0;
646f68a8342SChanwoo Choi }
647f68a8342SChanwoo Choi 
648f68a8342SChanwoo Choi static void extcon_dev_release(struct device *dev)
649f68a8342SChanwoo Choi {
650f68a8342SChanwoo Choi }
651f68a8342SChanwoo Choi 
652f68a8342SChanwoo Choi static const char *muex_name = "mutually_exclusive";
653f68a8342SChanwoo Choi static void dummy_sysfs_dev_release(struct device *dev)
654f68a8342SChanwoo Choi {
655f68a8342SChanwoo Choi }
656f68a8342SChanwoo Choi 
657f68a8342SChanwoo Choi /*
658f68a8342SChanwoo Choi  * extcon_dev_allocate() - Allocate the memory of extcon device.
6592a9de9c0SChanwoo Choi  * @supported_cable:	Array of supported extcon ending with EXTCON_NONE.
660f68a8342SChanwoo Choi  *			If supported_cable is NULL, cable name related APIs
661f68a8342SChanwoo Choi  *			are disabled.
662f68a8342SChanwoo Choi  *
663f68a8342SChanwoo Choi  * This function allocates the memory for extcon device without allocating
664f68a8342SChanwoo Choi  * memory in each extcon provider driver and initialize default setting for
665f68a8342SChanwoo Choi  * extcon device.
666f68a8342SChanwoo Choi  *
667f68a8342SChanwoo Choi  * Return the pointer of extcon device if success or ERR_PTR(err) if fail
668f68a8342SChanwoo Choi  */
66973b6ecdbSChanwoo Choi struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
670f68a8342SChanwoo Choi {
671f68a8342SChanwoo Choi 	struct extcon_dev *edev;
672f68a8342SChanwoo Choi 
6737eae43aeSChanwoo Choi 	if (!supported_cable)
6747eae43aeSChanwoo Choi 		return ERR_PTR(-EINVAL);
6757eae43aeSChanwoo Choi 
676f68a8342SChanwoo Choi 	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
677f68a8342SChanwoo Choi 	if (!edev)
678f68a8342SChanwoo Choi 		return ERR_PTR(-ENOMEM);
679f68a8342SChanwoo Choi 
680f68a8342SChanwoo Choi 	edev->max_supported = 0;
681f68a8342SChanwoo Choi 	edev->supported_cable = supported_cable;
682f68a8342SChanwoo Choi 
683f68a8342SChanwoo Choi 	return edev;
684f68a8342SChanwoo Choi }
685f68a8342SChanwoo Choi 
686f68a8342SChanwoo Choi /*
687f68a8342SChanwoo Choi  * extcon_dev_free() - Free the memory of extcon device.
688f68a8342SChanwoo Choi  * @edev:	the extcon device to free
689f68a8342SChanwoo Choi  */
690f68a8342SChanwoo Choi void extcon_dev_free(struct extcon_dev *edev)
691f68a8342SChanwoo Choi {
692f68a8342SChanwoo Choi 	kfree(edev);
693f68a8342SChanwoo Choi }
694f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_dev_free);
695f68a8342SChanwoo Choi 
696f68a8342SChanwoo Choi static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
697f68a8342SChanwoo Choi {
698f68a8342SChanwoo Choi 	struct extcon_dev **r = res;
699f68a8342SChanwoo Choi 
700f68a8342SChanwoo Choi 	if (WARN_ON(!r || !*r))
701f68a8342SChanwoo Choi 		return 0;
702f68a8342SChanwoo Choi 
703f68a8342SChanwoo Choi 	return *r == data;
704f68a8342SChanwoo Choi }
705f68a8342SChanwoo Choi 
706f68a8342SChanwoo Choi static void devm_extcon_dev_release(struct device *dev, void *res)
707f68a8342SChanwoo Choi {
708f68a8342SChanwoo Choi 	extcon_dev_free(*(struct extcon_dev **)res);
709f68a8342SChanwoo Choi }
710f68a8342SChanwoo Choi 
711f68a8342SChanwoo Choi /**
712f68a8342SChanwoo Choi  * devm_extcon_dev_allocate - Allocate managed extcon device
713f68a8342SChanwoo Choi  * @dev:		device owning the extcon device being created
7142a9de9c0SChanwoo Choi  * @supported_cable:	Array of supported extcon ending with EXTCON_NONE.
715f68a8342SChanwoo Choi  *			If supported_cable is NULL, cable name related APIs
716f68a8342SChanwoo Choi  *			are disabled.
717f68a8342SChanwoo Choi  *
718f68a8342SChanwoo Choi  * This function manages automatically the memory of extcon device using device
719f68a8342SChanwoo Choi  * resource management and simplify the control of freeing the memory of extcon
720f68a8342SChanwoo Choi  * device.
721f68a8342SChanwoo Choi  *
722f68a8342SChanwoo Choi  * Returns the pointer memory of allocated extcon_dev if success
723f68a8342SChanwoo Choi  * or ERR_PTR(err) if fail
724f68a8342SChanwoo Choi  */
725f68a8342SChanwoo Choi struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
72673b6ecdbSChanwoo Choi 					const unsigned int *supported_cable)
727f68a8342SChanwoo Choi {
728f68a8342SChanwoo Choi 	struct extcon_dev **ptr, *edev;
729f68a8342SChanwoo Choi 
730f68a8342SChanwoo Choi 	ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
731f68a8342SChanwoo Choi 	if (!ptr)
732f68a8342SChanwoo Choi 		return ERR_PTR(-ENOMEM);
733f68a8342SChanwoo Choi 
734f68a8342SChanwoo Choi 	edev = extcon_dev_allocate(supported_cable);
735f68a8342SChanwoo Choi 	if (IS_ERR(edev)) {
736f68a8342SChanwoo Choi 		devres_free(ptr);
737f68a8342SChanwoo Choi 		return edev;
738f68a8342SChanwoo Choi 	}
739f68a8342SChanwoo Choi 
740f68a8342SChanwoo Choi 	edev->dev.parent = dev;
741f68a8342SChanwoo Choi 
742f68a8342SChanwoo Choi 	*ptr = edev;
743f68a8342SChanwoo Choi 	devres_add(dev, ptr);
744f68a8342SChanwoo Choi 
745f68a8342SChanwoo Choi 	return edev;
746f68a8342SChanwoo Choi }
747f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
748f68a8342SChanwoo Choi 
749f68a8342SChanwoo Choi void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
750f68a8342SChanwoo Choi {
751f68a8342SChanwoo Choi 	WARN_ON(devres_release(dev, devm_extcon_dev_release,
752f68a8342SChanwoo Choi 			       devm_extcon_dev_match, edev));
753f68a8342SChanwoo Choi }
754f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
755f68a8342SChanwoo Choi 
756f68a8342SChanwoo Choi /**
757f68a8342SChanwoo Choi  * extcon_dev_register() - Register a new extcon device
758f68a8342SChanwoo Choi  * @edev	: the new extcon device (should be allocated before calling)
759f68a8342SChanwoo Choi  *
760f68a8342SChanwoo Choi  * Among the members of edev struct, please set the "user initializing data"
761f68a8342SChanwoo Choi  * in any case and set the "optional callbacks" if required. However, please
762f68a8342SChanwoo Choi  * do not set the values of "internal data", which are initialized by
763f68a8342SChanwoo Choi  * this function.
764f68a8342SChanwoo Choi  */
765f68a8342SChanwoo Choi int extcon_dev_register(struct extcon_dev *edev)
766f68a8342SChanwoo Choi {
767f68a8342SChanwoo Choi 	int ret, index = 0;
76871c3ffa5SChanwoo Choi 	static atomic_t edev_no = ATOMIC_INIT(-1);
769f68a8342SChanwoo Choi 
770f68a8342SChanwoo Choi 	if (!extcon_class) {
771f68a8342SChanwoo Choi 		ret = create_extcon_class();
772f68a8342SChanwoo Choi 		if (ret < 0)
773f68a8342SChanwoo Choi 			return ret;
774f68a8342SChanwoo Choi 	}
775f68a8342SChanwoo Choi 
7767eae43aeSChanwoo Choi 	if (!edev || !edev->supported_cable)
7772a9de9c0SChanwoo Choi 		return -EINVAL;
778f68a8342SChanwoo Choi 
7792a9de9c0SChanwoo Choi 	for (; edev->supported_cable[index] != EXTCON_NONE; index++);
7802a9de9c0SChanwoo Choi 
7812a9de9c0SChanwoo Choi 	edev->max_supported = index;
782f68a8342SChanwoo Choi 	if (index > SUPPORTED_CABLE_MAX) {
7832a9de9c0SChanwoo Choi 		dev_err(&edev->dev,
7842a9de9c0SChanwoo Choi 			"exceed the maximum number of supported cables\n");
785f68a8342SChanwoo Choi 		return -EINVAL;
786f68a8342SChanwoo Choi 	}
787f68a8342SChanwoo Choi 
788f68a8342SChanwoo Choi 	edev->dev.class = extcon_class;
789f68a8342SChanwoo Choi 	edev->dev.release = extcon_dev_release;
790f68a8342SChanwoo Choi 
79171c3ffa5SChanwoo Choi 	edev->name = dev_name(edev->dev.parent);
792f68a8342SChanwoo Choi 	if (IS_ERR_OR_NULL(edev->name)) {
793f68a8342SChanwoo Choi 		dev_err(&edev->dev,
794f68a8342SChanwoo Choi 			"extcon device name is null\n");
795f68a8342SChanwoo Choi 		return -EINVAL;
796f68a8342SChanwoo Choi 	}
79771c3ffa5SChanwoo Choi 	dev_set_name(&edev->dev, "extcon%lu",
79871c3ffa5SChanwoo Choi 			(unsigned long)atomic_inc_return(&edev_no));
799f68a8342SChanwoo Choi 
800f68a8342SChanwoo Choi 	if (edev->max_supported) {
801f68a8342SChanwoo Choi 		char buf[10];
802f68a8342SChanwoo Choi 		char *str;
803f68a8342SChanwoo Choi 		struct extcon_cable *cable;
804f68a8342SChanwoo Choi 
805f68a8342SChanwoo Choi 		edev->cables = kzalloc(sizeof(struct extcon_cable) *
806f68a8342SChanwoo Choi 				       edev->max_supported, GFP_KERNEL);
807f68a8342SChanwoo Choi 		if (!edev->cables) {
808f68a8342SChanwoo Choi 			ret = -ENOMEM;
809f68a8342SChanwoo Choi 			goto err_sysfs_alloc;
810f68a8342SChanwoo Choi 		}
811f68a8342SChanwoo Choi 		for (index = 0; index < edev->max_supported; index++) {
812f68a8342SChanwoo Choi 			cable = &edev->cables[index];
813f68a8342SChanwoo Choi 
814f68a8342SChanwoo Choi 			snprintf(buf, 10, "cable.%d", index);
815f68a8342SChanwoo Choi 			str = kzalloc(sizeof(char) * (strlen(buf) + 1),
816f68a8342SChanwoo Choi 				      GFP_KERNEL);
817f68a8342SChanwoo Choi 			if (!str) {
818f68a8342SChanwoo Choi 				for (index--; index >= 0; index--) {
819f68a8342SChanwoo Choi 					cable = &edev->cables[index];
820f68a8342SChanwoo Choi 					kfree(cable->attr_g.name);
821f68a8342SChanwoo Choi 				}
822f68a8342SChanwoo Choi 				ret = -ENOMEM;
823f68a8342SChanwoo Choi 
824f68a8342SChanwoo Choi 				goto err_alloc_cables;
825f68a8342SChanwoo Choi 			}
826f68a8342SChanwoo Choi 			strcpy(str, buf);
827f68a8342SChanwoo Choi 
828f68a8342SChanwoo Choi 			cable->edev = edev;
829f68a8342SChanwoo Choi 			cable->cable_index = index;
830f68a8342SChanwoo Choi 			cable->attrs[0] = &cable->attr_name.attr;
831f68a8342SChanwoo Choi 			cable->attrs[1] = &cable->attr_state.attr;
832f68a8342SChanwoo Choi 			cable->attrs[2] = NULL;
833f68a8342SChanwoo Choi 			cable->attr_g.name = str;
834f68a8342SChanwoo Choi 			cable->attr_g.attrs = cable->attrs;
835f68a8342SChanwoo Choi 
836f68a8342SChanwoo Choi 			sysfs_attr_init(&cable->attr_name.attr);
837f68a8342SChanwoo Choi 			cable->attr_name.attr.name = "name";
838f68a8342SChanwoo Choi 			cable->attr_name.attr.mode = 0444;
839f68a8342SChanwoo Choi 			cable->attr_name.show = cable_name_show;
840f68a8342SChanwoo Choi 
841f68a8342SChanwoo Choi 			sysfs_attr_init(&cable->attr_state.attr);
842f68a8342SChanwoo Choi 			cable->attr_state.attr.name = "state";
843f68a8342SChanwoo Choi 			cable->attr_state.attr.mode = 0444;
844f68a8342SChanwoo Choi 			cable->attr_state.show = cable_state_show;
845f68a8342SChanwoo Choi 		}
846f68a8342SChanwoo Choi 	}
847f68a8342SChanwoo Choi 
848f68a8342SChanwoo Choi 	if (edev->max_supported && edev->mutually_exclusive) {
849f68a8342SChanwoo Choi 		char buf[80];
850f68a8342SChanwoo Choi 		char *name;
851f68a8342SChanwoo Choi 
852f68a8342SChanwoo Choi 		/* Count the size of mutually_exclusive array */
853f68a8342SChanwoo Choi 		for (index = 0; edev->mutually_exclusive[index]; index++)
854f68a8342SChanwoo Choi 			;
855f68a8342SChanwoo Choi 
856f68a8342SChanwoo Choi 		edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
857f68a8342SChanwoo Choi 					   (index + 1), GFP_KERNEL);
858f68a8342SChanwoo Choi 		if (!edev->attrs_muex) {
859f68a8342SChanwoo Choi 			ret = -ENOMEM;
860f68a8342SChanwoo Choi 			goto err_muex;
861f68a8342SChanwoo Choi 		}
862f68a8342SChanwoo Choi 
863f68a8342SChanwoo Choi 		edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
864f68a8342SChanwoo Choi 					     index, GFP_KERNEL);
865f68a8342SChanwoo Choi 		if (!edev->d_attrs_muex) {
866f68a8342SChanwoo Choi 			ret = -ENOMEM;
867f68a8342SChanwoo Choi 			kfree(edev->attrs_muex);
868f68a8342SChanwoo Choi 			goto err_muex;
869f68a8342SChanwoo Choi 		}
870f68a8342SChanwoo Choi 
871f68a8342SChanwoo Choi 		for (index = 0; edev->mutually_exclusive[index]; index++) {
872f68a8342SChanwoo Choi 			sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
873f68a8342SChanwoo Choi 			name = kzalloc(sizeof(char) * (strlen(buf) + 1),
874f68a8342SChanwoo Choi 				       GFP_KERNEL);
875f68a8342SChanwoo Choi 			if (!name) {
876f68a8342SChanwoo Choi 				for (index--; index >= 0; index--) {
877f68a8342SChanwoo Choi 					kfree(edev->d_attrs_muex[index].attr.
878f68a8342SChanwoo Choi 					      name);
879f68a8342SChanwoo Choi 				}
880f68a8342SChanwoo Choi 				kfree(edev->d_attrs_muex);
881f68a8342SChanwoo Choi 				kfree(edev->attrs_muex);
882f68a8342SChanwoo Choi 				ret = -ENOMEM;
883f68a8342SChanwoo Choi 				goto err_muex;
884f68a8342SChanwoo Choi 			}
885f68a8342SChanwoo Choi 			strcpy(name, buf);
886f68a8342SChanwoo Choi 			sysfs_attr_init(&edev->d_attrs_muex[index].attr);
887f68a8342SChanwoo Choi 			edev->d_attrs_muex[index].attr.name = name;
888f68a8342SChanwoo Choi 			edev->d_attrs_muex[index].attr.mode = 0000;
889f68a8342SChanwoo Choi 			edev->attrs_muex[index] = &edev->d_attrs_muex[index]
890f68a8342SChanwoo Choi 							.attr;
891f68a8342SChanwoo Choi 		}
892f68a8342SChanwoo Choi 		edev->attr_g_muex.name = muex_name;
893f68a8342SChanwoo Choi 		edev->attr_g_muex.attrs = edev->attrs_muex;
894f68a8342SChanwoo Choi 
895f68a8342SChanwoo Choi 	}
896f68a8342SChanwoo Choi 
897f68a8342SChanwoo Choi 	if (edev->max_supported) {
898f68a8342SChanwoo Choi 		edev->extcon_dev_type.groups =
899f68a8342SChanwoo Choi 			kzalloc(sizeof(struct attribute_group *) *
900f68a8342SChanwoo Choi 				(edev->max_supported + 2), GFP_KERNEL);
901f68a8342SChanwoo Choi 		if (!edev->extcon_dev_type.groups) {
902f68a8342SChanwoo Choi 			ret = -ENOMEM;
903f68a8342SChanwoo Choi 			goto err_alloc_groups;
904f68a8342SChanwoo Choi 		}
905f68a8342SChanwoo Choi 
906f68a8342SChanwoo Choi 		edev->extcon_dev_type.name = dev_name(&edev->dev);
907f68a8342SChanwoo Choi 		edev->extcon_dev_type.release = dummy_sysfs_dev_release;
908f68a8342SChanwoo Choi 
909f68a8342SChanwoo Choi 		for (index = 0; index < edev->max_supported; index++)
910f68a8342SChanwoo Choi 			edev->extcon_dev_type.groups[index] =
911f68a8342SChanwoo Choi 				&edev->cables[index].attr_g;
912f68a8342SChanwoo Choi 		if (edev->mutually_exclusive)
913f68a8342SChanwoo Choi 			edev->extcon_dev_type.groups[index] =
914f68a8342SChanwoo Choi 				&edev->attr_g_muex;
915f68a8342SChanwoo Choi 
916f68a8342SChanwoo Choi 		edev->dev.type = &edev->extcon_dev_type;
917f68a8342SChanwoo Choi 	}
918f68a8342SChanwoo Choi 
919f68a8342SChanwoo Choi 	ret = device_register(&edev->dev);
920f68a8342SChanwoo Choi 	if (ret) {
921f68a8342SChanwoo Choi 		put_device(&edev->dev);
922f68a8342SChanwoo Choi 		goto err_dev;
923f68a8342SChanwoo Choi 	}
924f68a8342SChanwoo Choi #if defined(CONFIG_ANDROID)
925f68a8342SChanwoo Choi 	if (switch_class)
926f68a8342SChanwoo Choi 		ret = class_compat_create_link(switch_class, &edev->dev, NULL);
927f68a8342SChanwoo Choi #endif /* CONFIG_ANDROID */
928f68a8342SChanwoo Choi 
929f68a8342SChanwoo Choi 	spin_lock_init(&edev->lock);
930f68a8342SChanwoo Choi 
931046050f6SChanwoo Choi 	edev->nh = devm_kzalloc(&edev->dev,
932046050f6SChanwoo Choi 			sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
933046050f6SChanwoo Choi 	if (!edev->nh) {
934046050f6SChanwoo Choi 		ret = -ENOMEM;
935046050f6SChanwoo Choi 		goto err_dev;
936046050f6SChanwoo Choi 	}
937046050f6SChanwoo Choi 
938046050f6SChanwoo Choi 	for (index = 0; index < edev->max_supported; index++)
939046050f6SChanwoo Choi 		RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
940f68a8342SChanwoo Choi 
941f68a8342SChanwoo Choi 	dev_set_drvdata(&edev->dev, edev);
942f68a8342SChanwoo Choi 	edev->state = 0;
943f68a8342SChanwoo Choi 
944f68a8342SChanwoo Choi 	mutex_lock(&extcon_dev_list_lock);
945f68a8342SChanwoo Choi 	list_add(&edev->entry, &extcon_dev_list);
946f68a8342SChanwoo Choi 	mutex_unlock(&extcon_dev_list_lock);
947f68a8342SChanwoo Choi 
948f68a8342SChanwoo Choi 	return 0;
949f68a8342SChanwoo Choi 
950f68a8342SChanwoo Choi err_dev:
951f68a8342SChanwoo Choi 	if (edev->max_supported)
952f68a8342SChanwoo Choi 		kfree(edev->extcon_dev_type.groups);
953f68a8342SChanwoo Choi err_alloc_groups:
954f68a8342SChanwoo Choi 	if (edev->max_supported && edev->mutually_exclusive) {
955f68a8342SChanwoo Choi 		for (index = 0; edev->mutually_exclusive[index]; index++)
956f68a8342SChanwoo Choi 			kfree(edev->d_attrs_muex[index].attr.name);
957f68a8342SChanwoo Choi 		kfree(edev->d_attrs_muex);
958f68a8342SChanwoo Choi 		kfree(edev->attrs_muex);
959f68a8342SChanwoo Choi 	}
960f68a8342SChanwoo Choi err_muex:
961f68a8342SChanwoo Choi 	for (index = 0; index < edev->max_supported; index++)
962f68a8342SChanwoo Choi 		kfree(edev->cables[index].attr_g.name);
963f68a8342SChanwoo Choi err_alloc_cables:
964f68a8342SChanwoo Choi 	if (edev->max_supported)
965f68a8342SChanwoo Choi 		kfree(edev->cables);
966f68a8342SChanwoo Choi err_sysfs_alloc:
967f68a8342SChanwoo Choi 	return ret;
968f68a8342SChanwoo Choi }
969f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_dev_register);
970f68a8342SChanwoo Choi 
971f68a8342SChanwoo Choi /**
972f68a8342SChanwoo Choi  * extcon_dev_unregister() - Unregister the extcon device.
973f68a8342SChanwoo Choi  * @edev:	the extcon device instance to be unregistered.
974f68a8342SChanwoo Choi  *
975f68a8342SChanwoo Choi  * Note that this does not call kfree(edev) because edev was not allocated
976f68a8342SChanwoo Choi  * by this class.
977f68a8342SChanwoo Choi  */
978f68a8342SChanwoo Choi void extcon_dev_unregister(struct extcon_dev *edev)
979f68a8342SChanwoo Choi {
980f68a8342SChanwoo Choi 	int index;
981f68a8342SChanwoo Choi 
9827eae43aeSChanwoo Choi 	if (!edev)
9837eae43aeSChanwoo Choi 		return;
9847eae43aeSChanwoo Choi 
985f68a8342SChanwoo Choi 	mutex_lock(&extcon_dev_list_lock);
986f68a8342SChanwoo Choi 	list_del(&edev->entry);
987f68a8342SChanwoo Choi 	mutex_unlock(&extcon_dev_list_lock);
988f68a8342SChanwoo Choi 
989f68a8342SChanwoo Choi 	if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
990f68a8342SChanwoo Choi 		dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
991f68a8342SChanwoo Choi 				dev_name(&edev->dev));
992f68a8342SChanwoo Choi 		return;
993f68a8342SChanwoo Choi 	}
994f68a8342SChanwoo Choi 
995f68a8342SChanwoo Choi 	device_unregister(&edev->dev);
996f68a8342SChanwoo Choi 
997f68a8342SChanwoo Choi 	if (edev->mutually_exclusive && edev->max_supported) {
998f68a8342SChanwoo Choi 		for (index = 0; edev->mutually_exclusive[index];
999f68a8342SChanwoo Choi 				index++)
1000f68a8342SChanwoo Choi 			kfree(edev->d_attrs_muex[index].attr.name);
1001f68a8342SChanwoo Choi 		kfree(edev->d_attrs_muex);
1002f68a8342SChanwoo Choi 		kfree(edev->attrs_muex);
1003f68a8342SChanwoo Choi 	}
1004f68a8342SChanwoo Choi 
1005f68a8342SChanwoo Choi 	for (index = 0; index < edev->max_supported; index++)
1006f68a8342SChanwoo Choi 		kfree(edev->cables[index].attr_g.name);
1007f68a8342SChanwoo Choi 
1008f68a8342SChanwoo Choi 	if (edev->max_supported) {
1009f68a8342SChanwoo Choi 		kfree(edev->extcon_dev_type.groups);
1010f68a8342SChanwoo Choi 		kfree(edev->cables);
1011f68a8342SChanwoo Choi 	}
1012f68a8342SChanwoo Choi 
1013f68a8342SChanwoo Choi #if defined(CONFIG_ANDROID)
1014f68a8342SChanwoo Choi 	if (switch_class)
1015f68a8342SChanwoo Choi 		class_compat_remove_link(switch_class, &edev->dev, NULL);
1016f68a8342SChanwoo Choi #endif
1017f68a8342SChanwoo Choi 	put_device(&edev->dev);
1018f68a8342SChanwoo Choi }
1019f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_dev_unregister);
1020f68a8342SChanwoo Choi 
1021f68a8342SChanwoo Choi static void devm_extcon_dev_unreg(struct device *dev, void *res)
1022f68a8342SChanwoo Choi {
1023f68a8342SChanwoo Choi 	extcon_dev_unregister(*(struct extcon_dev **)res);
1024f68a8342SChanwoo Choi }
1025f68a8342SChanwoo Choi 
1026f68a8342SChanwoo Choi /**
1027f68a8342SChanwoo Choi  * devm_extcon_dev_register() - Resource-managed extcon_dev_register()
1028f68a8342SChanwoo Choi  * @dev:	device to allocate extcon device
1029f68a8342SChanwoo Choi  * @edev:	the new extcon device to register
1030f68a8342SChanwoo Choi  *
1031f68a8342SChanwoo Choi  * Managed extcon_dev_register() function. If extcon device is attached with
1032f68a8342SChanwoo Choi  * this function, that extcon device is automatically unregistered on driver
1033f68a8342SChanwoo Choi  * detach. Internally this function calls extcon_dev_register() function.
1034f68a8342SChanwoo Choi  * To get more information, refer that function.
1035f68a8342SChanwoo Choi  *
1036f68a8342SChanwoo Choi  * If extcon device is registered with this function and the device needs to be
1037f68a8342SChanwoo Choi  * unregistered separately, devm_extcon_dev_unregister() should be used.
1038f68a8342SChanwoo Choi  *
1039f68a8342SChanwoo Choi  * Returns 0 if success or negaive error number if failure.
1040f68a8342SChanwoo Choi  */
1041f68a8342SChanwoo Choi int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
1042f68a8342SChanwoo Choi {
1043f68a8342SChanwoo Choi 	struct extcon_dev **ptr;
1044f68a8342SChanwoo Choi 	int ret;
1045f68a8342SChanwoo Choi 
1046f68a8342SChanwoo Choi 	ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
1047f68a8342SChanwoo Choi 	if (!ptr)
1048f68a8342SChanwoo Choi 		return -ENOMEM;
1049f68a8342SChanwoo Choi 
1050f68a8342SChanwoo Choi 	ret = extcon_dev_register(edev);
1051f68a8342SChanwoo Choi 	if (ret) {
1052f68a8342SChanwoo Choi 		devres_free(ptr);
1053f68a8342SChanwoo Choi 		return ret;
1054f68a8342SChanwoo Choi 	}
1055f68a8342SChanwoo Choi 
1056f68a8342SChanwoo Choi 	*ptr = edev;
1057f68a8342SChanwoo Choi 	devres_add(dev, ptr);
1058f68a8342SChanwoo Choi 
1059f68a8342SChanwoo Choi 	return 0;
1060f68a8342SChanwoo Choi }
1061f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
1062f68a8342SChanwoo Choi 
1063f68a8342SChanwoo Choi /**
1064f68a8342SChanwoo Choi  * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
1065f68a8342SChanwoo Choi  * @dev:	device the extcon belongs to
1066f68a8342SChanwoo Choi  * @edev:	the extcon device to unregister
1067f68a8342SChanwoo Choi  *
1068f68a8342SChanwoo Choi  * Unregister extcon device that is registered with devm_extcon_dev_register()
1069f68a8342SChanwoo Choi  * function.
1070f68a8342SChanwoo Choi  */
1071f68a8342SChanwoo Choi void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
1072f68a8342SChanwoo Choi {
1073f68a8342SChanwoo Choi 	WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
1074f68a8342SChanwoo Choi 			       devm_extcon_dev_match, edev));
1075f68a8342SChanwoo Choi }
1076f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
1077f68a8342SChanwoo Choi 
1078f68a8342SChanwoo Choi #ifdef CONFIG_OF
1079f68a8342SChanwoo Choi /*
1080f68a8342SChanwoo Choi  * extcon_get_edev_by_phandle - Get the extcon device from devicetree
1081f68a8342SChanwoo Choi  * @dev - instance to the given device
1082f68a8342SChanwoo Choi  * @index - index into list of extcon_dev
1083f68a8342SChanwoo Choi  *
1084f68a8342SChanwoo Choi  * return the instance of extcon device
1085f68a8342SChanwoo Choi  */
1086f68a8342SChanwoo Choi struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1087f68a8342SChanwoo Choi {
1088f68a8342SChanwoo Choi 	struct device_node *node;
1089f68a8342SChanwoo Choi 	struct extcon_dev *edev;
1090f68a8342SChanwoo Choi 
10917eae43aeSChanwoo Choi 	if (!dev)
10927eae43aeSChanwoo Choi 		return ERR_PTR(-EINVAL);
10937eae43aeSChanwoo Choi 
1094f68a8342SChanwoo Choi 	if (!dev->of_node) {
1095f68a8342SChanwoo Choi 		dev_err(dev, "device does not have a device node entry\n");
1096f68a8342SChanwoo Choi 		return ERR_PTR(-EINVAL);
1097f68a8342SChanwoo Choi 	}
1098f68a8342SChanwoo Choi 
1099f68a8342SChanwoo Choi 	node = of_parse_phandle(dev->of_node, "extcon", index);
1100f68a8342SChanwoo Choi 	if (!node) {
1101f68a8342SChanwoo Choi 		dev_err(dev, "failed to get phandle in %s node\n",
1102f68a8342SChanwoo Choi 			dev->of_node->full_name);
1103f68a8342SChanwoo Choi 		return ERR_PTR(-ENODEV);
1104f68a8342SChanwoo Choi 	}
1105f68a8342SChanwoo Choi 
1106f68a8342SChanwoo Choi 	mutex_lock(&extcon_dev_list_lock);
1107f68a8342SChanwoo Choi 	list_for_each_entry(edev, &extcon_dev_list, entry) {
1108f68a8342SChanwoo Choi 		if (edev->dev.parent && edev->dev.parent->of_node == node) {
1109f68a8342SChanwoo Choi 			mutex_unlock(&extcon_dev_list_lock);
1110f68a8342SChanwoo Choi 			return edev;
1111f68a8342SChanwoo Choi 		}
1112f68a8342SChanwoo Choi 	}
1113f68a8342SChanwoo Choi 	mutex_unlock(&extcon_dev_list_lock);
1114f68a8342SChanwoo Choi 
1115f68a8342SChanwoo Choi 	return ERR_PTR(-EPROBE_DEFER);
1116f68a8342SChanwoo Choi }
1117f68a8342SChanwoo Choi #else
1118f68a8342SChanwoo Choi struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1119f68a8342SChanwoo Choi {
1120f68a8342SChanwoo Choi 	return ERR_PTR(-ENOSYS);
1121f68a8342SChanwoo Choi }
1122f68a8342SChanwoo Choi #endif /* CONFIG_OF */
1123f68a8342SChanwoo Choi EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
1124f68a8342SChanwoo Choi 
1125707d7550SChanwoo Choi /**
1126707d7550SChanwoo Choi  * extcon_get_edev_name() - Get the name of the extcon device.
1127707d7550SChanwoo Choi  * @edev:	the extcon device
1128707d7550SChanwoo Choi  */
1129707d7550SChanwoo Choi const char *extcon_get_edev_name(struct extcon_dev *edev)
1130707d7550SChanwoo Choi {
1131707d7550SChanwoo Choi 	return !edev ? NULL : edev->name;
1132707d7550SChanwoo Choi }
1133707d7550SChanwoo Choi 
1134f68a8342SChanwoo Choi static int __init extcon_class_init(void)
1135f68a8342SChanwoo Choi {
1136f68a8342SChanwoo Choi 	return create_extcon_class();
1137f68a8342SChanwoo Choi }
1138f68a8342SChanwoo Choi module_init(extcon_class_init);
1139f68a8342SChanwoo Choi 
1140f68a8342SChanwoo Choi static void __exit extcon_class_exit(void)
1141f68a8342SChanwoo Choi {
1142f68a8342SChanwoo Choi #if defined(CONFIG_ANDROID)
1143f68a8342SChanwoo Choi 	class_compat_unregister(switch_class);
1144f68a8342SChanwoo Choi #endif
1145f68a8342SChanwoo Choi 	class_destroy(extcon_class);
1146f68a8342SChanwoo Choi }
1147f68a8342SChanwoo Choi module_exit(extcon_class_exit);
1148f68a8342SChanwoo Choi 
11492a9de9c0SChanwoo Choi MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
1150f68a8342SChanwoo Choi MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
1151f68a8342SChanwoo Choi MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
1152f68a8342SChanwoo Choi MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1153f68a8342SChanwoo Choi MODULE_DESCRIPTION("External connector (extcon) class driver");
1154f68a8342SChanwoo Choi MODULE_LICENSE("GPL");
1155