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