blob: 5099c11d88339cdfe58c13f62abc8b0ed1c13b42 [file] [log] [blame]
MyungJoo Hamde55d872012-04-20 14:16:22 +09001/*
Chanwoo Choib9ec23c2015-04-24 14:48:52 +09002 * drivers/extcon/extcon.c - External Connector (extcon) framework.
MyungJoo Hamde55d872012-04-20 14:16:22 +09003 *
4 * External connector (extcon) class driver
5 *
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +09006 * Copyright (C) 2015 Samsung Electronics
7 * Author: Chanwoo Choi <cw00.choi@samsung.com>
8 *
MyungJoo Hamde55d872012-04-20 14:16:22 +09009 * Copyright (C) 2012 Samsung Electronics
10 * Author: Donggeun Kim <dg77.kim@samsung.com>
11 * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
12 *
13 * based on android/drivers/switch/switch_class.c
14 * Copyright (C) 2008 Google, Inc.
15 * Author: Mike Lockwood <lockwood@android.com>
16 *
17 * This software is licensed under the terms of the GNU General Public
18 * License version 2, as published by the Free Software Foundation, and
19 * may be copied, distributed, and modified under those terms.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
Chanwoo Choib9ec23c2015-04-24 14:48:52 +090025 */
MyungJoo Hamde55d872012-04-20 14:16:22 +090026
27#include <linux/module.h>
28#include <linux/types.h>
29#include <linux/init.h>
30#include <linux/device.h>
31#include <linux/fs.h>
32#include <linux/err.h>
33#include <linux/extcon.h>
Tomasz Figaf841afb12014-10-16 15:11:44 +020034#include <linux/of.h>
MyungJoo Hamde55d872012-04-20 14:16:22 +090035#include <linux/slab.h>
Mark Brown9baf3222012-08-16 20:03:21 +010036#include <linux/sysfs.h>
MyungJoo Hamde55d872012-04-20 14:16:22 +090037
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +090038#define SUPPORTED_CABLE_MAX 32
39#define CABLE_NAME_MAX 30
40
41static const char *extcon_name[] = {
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090042 /* USB external connector */
MyungJoo Ham806d9dd2012-04-20 14:16:25 +090043 [EXTCON_USB] = "USB",
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090044 [EXTCON_USB_HOST] = "USB-HOST",
45
46 /* Charger external connector */
MyungJoo Ham806d9dd2012-04-20 14:16:25 +090047 [EXTCON_TA] = "TA",
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090048 [EXTCON_FAST_CHARGER] = "FAST-CHARGER",
49 [EXTCON_SLOW_CHARGER] = "SLOW-CHARGER",
50 [EXTCON_CHARGE_DOWNSTREAM] = "CHARGE-DOWNSTREAM",
51
52 /* Audio/Video external connector */
53 [EXTCON_LINE_IN] = "LINE-IN",
54 [EXTCON_LINE_OUT] = "LINE-OUT",
55 [EXTCON_MICROPHONE] = "MICROPHONE",
56 [EXTCON_HEADPHONE] = "HEADPHONE",
57
MyungJoo Ham806d9dd2012-04-20 14:16:25 +090058 [EXTCON_HDMI] = "HDMI",
59 [EXTCON_MHL] = "MHL",
60 [EXTCON_DVI] = "DVI",
61 [EXTCON_VGA] = "VGA",
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090062 [EXTCON_SPDIF_IN] = "SPDIF-IN",
63 [EXTCON_SPDIF_OUT] = "SPDIF-OUT",
64 [EXTCON_VIDEO_IN] = "VIDEO-IN",
65 [EXTCON_VIDEO_OUT] = "VIDEO-OUT",
66
67 /* Etc external connector */
68 [EXTCON_DOCK] = "DOCK",
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +090069 [EXTCON_JIG] = "JIG",
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090070 [EXTCON_MECHANICAL] = "MECHANICAL",
71
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +090072 NULL,
MyungJoo Ham806d9dd2012-04-20 14:16:25 +090073};
74
Mark Brownbe3a07f2012-06-05 16:14:38 +010075static struct class *extcon_class;
MyungJoo Ham449a2bf2012-04-23 20:19:57 +090076#if defined(CONFIG_ANDROID)
MyungJoo Hamde55d872012-04-20 14:16:22 +090077static struct class_compat *switch_class;
MyungJoo Ham449a2bf2012-04-23 20:19:57 +090078#endif /* CONFIG_ANDROID */
MyungJoo Hamde55d872012-04-20 14:16:22 +090079
Donggeun Kim74c5d092012-04-20 14:16:24 +090080static LIST_HEAD(extcon_dev_list);
81static DEFINE_MUTEX(extcon_dev_list_lock);
82
MyungJoo Hambde68e62012-04-20 14:16:26 +090083/**
84 * check_mutually_exclusive - Check if new_state violates mutually_exclusive
Chanwoo Choia75e1c72013-08-31 13:16:49 +090085 * condition.
MyungJoo Hambde68e62012-04-20 14:16:26 +090086 * @edev: the extcon device
87 * @new_state: new cable attach status for @edev
88 *
89 * Returns 0 if nothing violates. Returns the index + 1 for the first
90 * violated condition.
91 */
92static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
93{
94 int i = 0;
95
96 if (!edev->mutually_exclusive)
97 return 0;
98
99 for (i = 0; edev->mutually_exclusive[i]; i++) {
anish kumar28c0ada2012-08-30 00:35:10 +0530100 int weight;
MyungJoo Hambde68e62012-04-20 14:16:26 +0900101 u32 correspondants = new_state & edev->mutually_exclusive[i];
MyungJoo Hambde68e62012-04-20 14:16:26 +0900102
anish kumar28c0ada2012-08-30 00:35:10 +0530103 /* calculate the total number of bits set */
104 weight = hweight32(correspondants);
105 if (weight > 1)
106 return i + 1;
MyungJoo Hambde68e62012-04-20 14:16:26 +0900107 }
108
109 return 0;
110}
111
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900112static int find_cable_index_by_id(struct extcon_dev *edev, const enum extcon id)
113{
114 int i;
115
116 /* Find the the index of extcon cable in edev->supported_cable */
117 for (i = 0; i < edev->max_supported; i++) {
118 if (edev->supported_cable[i] == id)
119 return i;
120 }
121
122 return -EINVAL;
123}
124
125static int find_cable_index_by_name(struct extcon_dev *edev, const char *name)
126{
127 enum extcon id = EXTCON_NONE;
128 int i;
129
130 if (edev->max_supported == 0)
131 return -EINVAL;
132
133 /* Find the the number of extcon cable */
134 for (i = 0; i < EXTCON_END; i++) {
135 if (!extcon_name[i])
136 continue;
137 if (!strncmp(extcon_name[i], name, CABLE_NAME_MAX)) {
138 id = i;
139 break;
140 }
141 }
142
143 if (id == EXTCON_NONE)
144 return -EINVAL;
145
146 return find_cable_index_by_id(edev, id);
147}
148
Chanwoo Choi046050f2015-05-19 20:01:12 +0900149static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
150{
151 if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
152 *attached = new ? true : false;
153 return true;
154 }
155
156 return false;
157}
158
MyungJoo Hamde55d872012-04-20 14:16:22 +0900159static ssize_t state_show(struct device *dev, struct device_attribute *attr,
160 char *buf)
161{
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900162 int i, count = 0;
Jingoo Hancb8bb3a2013-09-09 14:33:32 +0900163 struct extcon_dev *edev = dev_get_drvdata(dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900164
165 if (edev->print_state) {
166 int ret = edev->print_state(edev, buf);
167
168 if (ret >= 0)
169 return ret;
170 /* Use default if failed */
171 }
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900172
173 if (edev->max_supported == 0)
174 return sprintf(buf, "%u\n", edev->state);
175
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900176 for (i = 0; i < edev->max_supported; i++) {
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900177 count += sprintf(buf + count, "%s=%d\n",
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900178 extcon_name[edev->supported_cable[i]],
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900179 !!(edev->state & (1 << i)));
180 }
181
182 return count;
183}
184
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900185static ssize_t state_store(struct device *dev, struct device_attribute *attr,
186 const char *buf, size_t count)
187{
188 u32 state;
189 ssize_t ret = 0;
Jingoo Hancb8bb3a2013-09-09 14:33:32 +0900190 struct extcon_dev *edev = dev_get_drvdata(dev);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900191
192 ret = sscanf(buf, "0x%x", &state);
193 if (ret == 0)
194 ret = -EINVAL;
195 else
MyungJoo Hambde68e62012-04-20 14:16:26 +0900196 ret = extcon_set_state(edev, state);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900197
198 if (ret < 0)
199 return ret;
200
201 return count;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900202}
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700203static DEVICE_ATTR_RW(state);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900204
205static ssize_t name_show(struct device *dev, struct device_attribute *attr,
206 char *buf)
207{
Jingoo Hancb8bb3a2013-09-09 14:33:32 +0900208 struct extcon_dev *edev = dev_get_drvdata(dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900209
210 /* Optional callback given by the user */
211 if (edev->print_name) {
212 int ret = edev->print_name(edev, buf);
Chanwoo Choi34825e52015-03-07 01:41:36 +0900213
MyungJoo Hamde55d872012-04-20 14:16:22 +0900214 if (ret >= 0)
215 return ret;
216 }
217
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +0900218 return sprintf(buf, "%s\n", edev->name);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900219}
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700220static DEVICE_ATTR_RO(name);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900221
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900222static ssize_t cable_name_show(struct device *dev,
223 struct device_attribute *attr, char *buf)
224{
225 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
226 attr_name);
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900227 int i = cable->cable_index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900228
229 return sprintf(buf, "%s\n",
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900230 extcon_name[cable->edev->supported_cable[i]]);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900231}
232
233static ssize_t cable_state_show(struct device *dev,
234 struct device_attribute *attr, char *buf)
235{
236 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
237 attr_state);
238
239 return sprintf(buf, "%d\n",
240 extcon_get_cable_state_(cable->edev,
241 cable->cable_index));
242}
243
MyungJoo Hamde55d872012-04-20 14:16:22 +0900244/**
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900245 * extcon_update_state() - Update the cable attach states of the extcon device
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900246 * only for the masked bits.
MyungJoo Hamde55d872012-04-20 14:16:22 +0900247 * @edev: the extcon device
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900248 * @mask: the bit mask to designate updated bits.
MyungJoo Hamde55d872012-04-20 14:16:22 +0900249 * @state: new cable attach status for @edev
250 *
251 * Changing the state sends uevent with environment variable containing
252 * the name of extcon device (envp[0]) and the state output (envp[1]).
253 * Tizen uses this format for extcon device to get events from ports.
254 * Android uses this format as well.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900255 *
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900256 * Note that the notifier provides which bits are changed in the state
257 * variable with the val parameter (second) to the callback.
MyungJoo Hamde55d872012-04-20 14:16:22 +0900258 */
MyungJoo Hambde68e62012-04-20 14:16:26 +0900259int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900260{
261 char name_buf[120];
262 char state_buf[120];
263 char *prop_buf;
264 char *envp[3];
265 int env_offset = 0;
266 int length;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900267 int index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900268 unsigned long flags;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900269 bool attached;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900270
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900271 spin_lock_irqsave(&edev->lock, flags);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900272
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900273 if (edev->state != ((edev->state & ~mask) | (state & mask))) {
MyungJoo Hambde68e62012-04-20 14:16:26 +0900274 if (check_mutually_exclusive(edev, (edev->state & ~mask) |
275 (state & mask))) {
276 spin_unlock_irqrestore(&edev->lock, flags);
277 return -EPERM;
278 }
279
Chanwoo Choi046050f2015-05-19 20:01:12 +0900280 for (index = 0; index < edev->max_supported; index++) {
281 if (is_extcon_changed(edev->state, state, index, &attached))
282 raw_notifier_call_chain(&edev->nh[index], attached, edev);
283 }
284
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900285 edev->state &= ~mask;
286 edev->state |= state & mask;
287
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900288 /* This could be in interrupt handler */
289 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900290 if (prop_buf) {
Chanwoo Choidae61652013-09-27 09:19:40 +0900291 length = name_show(&edev->dev, NULL, prop_buf);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900292 if (length > 0) {
293 if (prop_buf[length - 1] == '\n')
294 prop_buf[length - 1] = 0;
295 snprintf(name_buf, sizeof(name_buf),
296 "NAME=%s", prop_buf);
297 envp[env_offset++] = name_buf;
298 }
Chanwoo Choidae61652013-09-27 09:19:40 +0900299 length = state_show(&edev->dev, NULL, prop_buf);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900300 if (length > 0) {
301 if (prop_buf[length - 1] == '\n')
302 prop_buf[length - 1] = 0;
303 snprintf(state_buf, sizeof(state_buf),
304 "STATE=%s", prop_buf);
305 envp[env_offset++] = state_buf;
306 }
307 envp[env_offset] = NULL;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900308 /* Unlock early before uevent */
309 spin_unlock_irqrestore(&edev->lock, flags);
310
Chanwoo Choidae61652013-09-27 09:19:40 +0900311 kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900312 free_page((unsigned long)prop_buf);
313 } else {
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900314 /* Unlock early before uevent */
315 spin_unlock_irqrestore(&edev->lock, flags);
316
Chanwoo Choidae61652013-09-27 09:19:40 +0900317 dev_err(&edev->dev, "out of memory in extcon_set_state\n");
318 kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900319 }
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900320 } else {
321 /* No changes */
322 spin_unlock_irqrestore(&edev->lock, flags);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900323 }
MyungJoo Hambde68e62012-04-20 14:16:26 +0900324
325 return 0;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900326}
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900327EXPORT_SYMBOL_GPL(extcon_update_state);
328
329/**
330 * extcon_set_state() - Set the cable attach states of the extcon device.
331 * @edev: the extcon device
332 * @state: new cable attach status for @edev
333 *
334 * Note that notifier provides which bits are changed in the state
335 * variable with the val parameter (second) to the callback.
336 */
MyungJoo Hambde68e62012-04-20 14:16:26 +0900337int extcon_set_state(struct extcon_dev *edev, u32 state)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900338{
MyungJoo Hambde68e62012-04-20 14:16:26 +0900339 return extcon_update_state(edev, 0xffffffff, state);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900340}
MyungJoo Hamde55d872012-04-20 14:16:22 +0900341EXPORT_SYMBOL_GPL(extcon_set_state);
342
Donggeun Kim74c5d092012-04-20 14:16:24 +0900343/**
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900344 * extcon_get_cable_state_() - Get the status of a specific cable.
345 * @edev: the extcon device that has the cable.
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900346 * @id: the unique id of each external connector in extcon enumeration.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900347 */
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900348int extcon_get_cable_state_(struct extcon_dev *edev, const enum extcon id)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900349{
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900350 int index;
351
352 index = find_cable_index_by_id(edev, id);
353 if (index < 0)
354 return index;
355
356 if (edev->max_supported && edev->max_supported <= index)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900357 return -EINVAL;
358
359 return !!(edev->state & (1 << index));
360}
361EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
362
363/**
364 * extcon_get_cable_state() - Get the status of a specific cable.
365 * @edev: the extcon device that has the cable.
366 * @cable_name: cable name.
367 *
368 * Note that this is slower than extcon_get_cable_state_.
369 */
370int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
371{
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900372 return extcon_get_cable_state_(edev, find_cable_index_by_name
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900373 (edev, cable_name));
374}
375EXPORT_SYMBOL_GPL(extcon_get_cable_state);
376
377/**
Axel Lin909f9ec2012-10-04 09:53:45 +0900378 * extcon_set_cable_state_() - Set the status of a specific cable.
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900379 * @edev: the extcon device that has the cable.
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900380 * @id: the unique id of each external connector
381 * in extcon enumeration.
382 * @state: the new cable status. The default semantics is
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900383 * true: attached / false: detached.
384 */
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900385int extcon_set_cable_state_(struct extcon_dev *edev, enum extcon id,
386 bool cable_state)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900387{
388 u32 state;
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900389 int index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900390
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900391 index = find_cable_index_by_id(edev, id);
392 if (index < 0)
393 return index;
394
395 if (edev->max_supported && edev->max_supported <= index)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900396 return -EINVAL;
397
398 state = cable_state ? (1 << index) : 0;
MyungJoo Hambde68e62012-04-20 14:16:26 +0900399 return extcon_update_state(edev, 1 << index, state);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900400}
401EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
402
403/**
Axel Lin909f9ec2012-10-04 09:53:45 +0900404 * extcon_set_cable_state() - Set the status of a specific cable.
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900405 * @edev: the extcon device that has the cable.
406 * @cable_name: cable name.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900407 * @cable_state: the new cable status. The default semantics is
408 * true: attached / false: detached.
409 *
410 * Note that this is slower than extcon_set_cable_state_.
411 */
412int extcon_set_cable_state(struct extcon_dev *edev,
413 const char *cable_name, bool cable_state)
414{
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900415 return extcon_set_cable_state_(edev, find_cable_index_by_name
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900416 (edev, cable_name), cable_state);
417}
418EXPORT_SYMBOL_GPL(extcon_set_cable_state);
419
420/**
Donggeun Kim74c5d092012-04-20 14:16:24 +0900421 * extcon_get_extcon_dev() - Get the extcon device instance from the name
422 * @extcon_name: The extcon name provided with extcon_dev_register()
423 */
424struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
425{
426 struct extcon_dev *sd;
427
428 mutex_lock(&extcon_dev_list_lock);
429 list_for_each_entry(sd, &extcon_dev_list, entry) {
430 if (!strcmp(sd->name, extcon_name))
431 goto out;
432 }
433 sd = NULL;
434out:
435 mutex_unlock(&extcon_dev_list_lock);
436 return sd;
437}
438EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
439
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900440/**
441 * extcon_register_interest() - Register a notifier for a state change of a
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900442 * specific cable, not an entier set of cables of a
443 * extcon device.
444 * @obj: an empty extcon_specific_cable_nb object to be returned.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900445 * @extcon_name: the name of extcon device.
Jenny TC4f2de3b2012-10-18 21:00:32 +0900446 * if NULL, extcon_register_interest will register
447 * every cable with the target cable_name given.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900448 * @cable_name: the target cable name.
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900449 * @nb: the notifier block to get notified.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900450 *
451 * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
452 * the struct for you.
453 *
454 * extcon_register_interest is a helper function for those who want to get
455 * notification for a single specific cable's status change. If a user wants
456 * to get notification for any changes of all cables of a extcon device,
457 * he/she should use the general extcon_register_notifier().
458 *
459 * Note that the second parameter given to the callback of nb (val) is
460 * "old_state", not the current state. The current state can be retrieved
461 * by looking at the third pameter (edev pointer)'s state value.
462 */
463int extcon_register_interest(struct extcon_specific_cable_nb *obj,
464 const char *extcon_name, const char *cable_name,
465 struct notifier_block *nb)
466{
Hans de Goede66bee352015-03-21 17:26:24 +0100467 unsigned long flags;
468 int ret;
469
Jenny TC4f2de3b2012-10-18 21:00:32 +0900470 if (!obj || !cable_name || !nb)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900471 return -EINVAL;
472
Jenny TC4f2de3b2012-10-18 21:00:32 +0900473 if (extcon_name) {
474 obj->edev = extcon_get_extcon_dev(extcon_name);
475 if (!obj->edev)
476 return -ENODEV;
477
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900478 obj->cable_index = find_cable_index_by_name(obj->edev,
479 cable_name);
Jenny TC4f2de3b2012-10-18 21:00:32 +0900480 if (obj->cable_index < 0)
Sachin Kamatc0c078c2012-11-20 16:30:31 +0900481 return obj->cable_index;
Jenny TC4f2de3b2012-10-18 21:00:32 +0900482
483 obj->user_nb = nb;
484
Hans de Goede66bee352015-03-21 17:26:24 +0100485 spin_lock_irqsave(&obj->edev->lock, flags);
Chanwoo Choi046050f2015-05-19 20:01:12 +0900486 ret = raw_notifier_chain_register(
487 &obj->edev->nh[obj->cable_index],
488 obj->user_nb);
Hans de Goede66bee352015-03-21 17:26:24 +0100489 spin_unlock_irqrestore(&obj->edev->lock, flags);
Jenny TC4f2de3b2012-10-18 21:00:32 +0900490 } else {
491 struct class_dev_iter iter;
492 struct extcon_dev *extd;
493 struct device *dev;
494
495 if (!extcon_class)
496 return -ENODEV;
497 class_dev_iter_init(&iter, extcon_class, NULL, NULL);
498 while ((dev = class_dev_iter_next(&iter))) {
Jingoo Hancb8bb3a2013-09-09 14:33:32 +0900499 extd = dev_get_drvdata(dev);
Jenny TC4f2de3b2012-10-18 21:00:32 +0900500
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900501 if (find_cable_index_by_name(extd, cable_name) < 0)
Jenny TC4f2de3b2012-10-18 21:00:32 +0900502 continue;
503
504 class_dev_iter_exit(&iter);
505 return extcon_register_interest(obj, extd->name,
506 cable_name, nb);
507 }
508
Chanwoo Choib9ec23c2015-04-24 14:48:52 +0900509 ret = -ENODEV;
Jenny TC4f2de3b2012-10-18 21:00:32 +0900510 }
Chanwoo Choib9ec23c2015-04-24 14:48:52 +0900511
512 return ret;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900513}
Kishon Vijay Abraham I9c8a0132013-06-04 01:13:38 +0900514EXPORT_SYMBOL_GPL(extcon_register_interest);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900515
516/**
517 * extcon_unregister_interest() - Unregister the notifier registered by
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900518 * extcon_register_interest().
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900519 * @obj: the extcon_specific_cable_nb object returned by
520 * extcon_register_interest().
521 */
522int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
523{
Hans de Goede66bee352015-03-21 17:26:24 +0100524 unsigned long flags;
525 int ret;
526
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900527 if (!obj)
528 return -EINVAL;
529
Hans de Goede66bee352015-03-21 17:26:24 +0100530 spin_lock_irqsave(&obj->edev->lock, flags);
Chanwoo Choi046050f2015-05-19 20:01:12 +0900531 ret = raw_notifier_chain_unregister(
532 &obj->edev->nh[obj->cable_index], obj->user_nb);
Hans de Goede66bee352015-03-21 17:26:24 +0100533 spin_unlock_irqrestore(&obj->edev->lock, flags);
534
535 return ret;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900536}
Kishon Vijay Abraham I9c8a0132013-06-04 01:13:38 +0900537EXPORT_SYMBOL_GPL(extcon_unregister_interest);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900538
Donggeun Kim74c5d092012-04-20 14:16:24 +0900539/**
Peter Meerwaldc338bb02012-08-23 09:11:54 +0900540 * extcon_register_notifier() - Register a notifiee to get notified by
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900541 * any attach status changes from the extcon.
Chanwoo Choi046050f2015-05-19 20:01:12 +0900542 * @edev: the extcon device that has the external connecotr.
543 * @id: the unique id of each external connector in extcon enumeration.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900544 * @nb: a notifier block to be registered.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900545 *
546 * Note that the second parameter given to the callback of nb (val) is
547 * "old_state", not the current state. The current state can be retrieved
548 * by looking at the third pameter (edev pointer)'s state value.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900549 */
Chanwoo Choi046050f2015-05-19 20:01:12 +0900550int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
551 struct notifier_block *nb)
Donggeun Kim74c5d092012-04-20 14:16:24 +0900552{
Hans de Goede66bee352015-03-21 17:26:24 +0100553 unsigned long flags;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900554 int ret, idx;
555
556 idx = find_cable_index_by_id(edev, id);
Hans de Goede66bee352015-03-21 17:26:24 +0100557
558 spin_lock_irqsave(&edev->lock, flags);
Chanwoo Choi046050f2015-05-19 20:01:12 +0900559 ret = raw_notifier_chain_register(&edev->nh[idx], nb);
Hans de Goede66bee352015-03-21 17:26:24 +0100560 spin_unlock_irqrestore(&edev->lock, flags);
561
562 return ret;
Donggeun Kim74c5d092012-04-20 14:16:24 +0900563}
564EXPORT_SYMBOL_GPL(extcon_register_notifier);
565
566/**
Peter Meerwaldc338bb02012-08-23 09:11:54 +0900567 * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
Chanwoo Choi046050f2015-05-19 20:01:12 +0900568 * @edev: the extcon device that has the external connecotr.
569 * @id: the unique id of each external connector in extcon enumeration.
570 * @nb: a notifier block to be registered.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900571 */
Chanwoo Choi046050f2015-05-19 20:01:12 +0900572int extcon_unregister_notifier(struct extcon_dev *edev, enum extcon id,
573 struct notifier_block *nb)
Donggeun Kim74c5d092012-04-20 14:16:24 +0900574{
Hans de Goede66bee352015-03-21 17:26:24 +0100575 unsigned long flags;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900576 int ret, idx;
577
578 idx = find_cable_index_by_id(edev, id);
Hans de Goede66bee352015-03-21 17:26:24 +0100579
580 spin_lock_irqsave(&edev->lock, flags);
Chanwoo Choi046050f2015-05-19 20:01:12 +0900581 ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
Hans de Goede66bee352015-03-21 17:26:24 +0100582 spin_unlock_irqrestore(&edev->lock, flags);
583
584 return ret;
Donggeun Kim74c5d092012-04-20 14:16:24 +0900585}
586EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
587
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700588static struct attribute *extcon_attrs[] = {
589 &dev_attr_state.attr,
590 &dev_attr_name.attr,
591 NULL,
MyungJoo Hamde55d872012-04-20 14:16:22 +0900592};
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700593ATTRIBUTE_GROUPS(extcon);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900594
595static int create_extcon_class(void)
596{
597 if (!extcon_class) {
598 extcon_class = class_create(THIS_MODULE, "extcon");
599 if (IS_ERR(extcon_class))
600 return PTR_ERR(extcon_class);
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700601 extcon_class->dev_groups = extcon_groups;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900602
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900603#if defined(CONFIG_ANDROID)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900604 switch_class = class_compat_register("switch");
605 if (WARN(!switch_class, "cannot allocate"))
606 return -ENOMEM;
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900607#endif /* CONFIG_ANDROID */
MyungJoo Hamde55d872012-04-20 14:16:22 +0900608 }
609
610 return 0;
611}
612
MyungJoo Hamde55d872012-04-20 14:16:22 +0900613static void extcon_dev_release(struct device *dev)
614{
MyungJoo Hamde55d872012-04-20 14:16:22 +0900615}
616
MyungJoo Hambde68e62012-04-20 14:16:26 +0900617static const char *muex_name = "mutually_exclusive";
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900618static void dummy_sysfs_dev_release(struct device *dev)
619{
620}
621
Chanwoo Choia9af6522014-04-24 19:46:49 +0900622/*
623 * extcon_dev_allocate() - Allocate the memory of extcon device.
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900624 * @supported_cable: Array of supported extcon ending with EXTCON_NONE.
Chanwoo Choia9af6522014-04-24 19:46:49 +0900625 * If supported_cable is NULL, cable name related APIs
626 * are disabled.
627 *
628 * This function allocates the memory for extcon device without allocating
629 * memory in each extcon provider driver and initialize default setting for
630 * extcon device.
631 *
632 * Return the pointer of extcon device if success or ERR_PTR(err) if fail
633 */
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900634struct extcon_dev *extcon_dev_allocate(const enum extcon *supported_cable)
Chanwoo Choia9af6522014-04-24 19:46:49 +0900635{
636 struct extcon_dev *edev;
637
638 edev = kzalloc(sizeof(*edev), GFP_KERNEL);
639 if (!edev)
640 return ERR_PTR(-ENOMEM);
641
642 edev->max_supported = 0;
643 edev->supported_cable = supported_cable;
644
645 return edev;
646}
647
648/*
649 * extcon_dev_free() - Free the memory of extcon device.
650 * @edev: the extcon device to free
651 */
652void extcon_dev_free(struct extcon_dev *edev)
653{
654 kfree(edev);
655}
656EXPORT_SYMBOL_GPL(extcon_dev_free);
657
Chanwoo Choi739ba1b2014-04-24 20:12:15 +0900658static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
659{
660 struct extcon_dev **r = res;
661
662 if (WARN_ON(!r || !*r))
663 return 0;
664
665 return *r == data;
666}
667
668static void devm_extcon_dev_release(struct device *dev, void *res)
669{
670 extcon_dev_free(*(struct extcon_dev **)res);
671}
672
673/**
674 * devm_extcon_dev_allocate - Allocate managed extcon device
675 * @dev: device owning the extcon device being created
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900676 * @supported_cable: Array of supported extcon ending with EXTCON_NONE.
Chanwoo Choi739ba1b2014-04-24 20:12:15 +0900677 * If supported_cable is NULL, cable name related APIs
678 * are disabled.
679 *
680 * This function manages automatically the memory of extcon device using device
681 * resource management and simplify the control of freeing the memory of extcon
682 * device.
683 *
684 * Returns the pointer memory of allocated extcon_dev if success
685 * or ERR_PTR(err) if fail
686 */
687struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900688 const enum extcon *supported_cable)
Chanwoo Choi739ba1b2014-04-24 20:12:15 +0900689{
690 struct extcon_dev **ptr, *edev;
691
692 ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
693 if (!ptr)
694 return ERR_PTR(-ENOMEM);
695
696 edev = extcon_dev_allocate(supported_cable);
697 if (IS_ERR(edev)) {
698 devres_free(ptr);
699 return edev;
700 }
701
Chanwoo Choiac65a622014-05-30 10:13:15 +0900702 edev->dev.parent = dev;
703
Chanwoo Choi739ba1b2014-04-24 20:12:15 +0900704 *ptr = edev;
705 devres_add(dev, ptr);
706
707 return edev;
708}
709EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
710
711void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
712{
713 WARN_ON(devres_release(dev, devm_extcon_dev_release,
714 devm_extcon_dev_match, edev));
715}
716EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
717
MyungJoo Hamde55d872012-04-20 14:16:22 +0900718/**
719 * extcon_dev_register() - Register a new extcon device
720 * @edev : the new extcon device (should be allocated before calling)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900721 *
722 * Among the members of edev struct, please set the "user initializing data"
723 * in any case and set the "optional callbacks" if required. However, please
724 * do not set the values of "internal data", which are initialized by
725 * this function.
726 */
Chanwoo Choi42d7d752013-09-27 09:20:26 +0900727int extcon_dev_register(struct extcon_dev *edev)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900728{
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900729 int ret, index = 0;
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +0900730 static atomic_t edev_no = ATOMIC_INIT(-1);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900731
732 if (!extcon_class) {
733 ret = create_extcon_class();
734 if (ret < 0)
735 return ret;
736 }
737
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900738 if (!edev->supported_cable)
739 return -EINVAL;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900740
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900741 for (; edev->supported_cable[index] != EXTCON_NONE; index++);
742
743 edev->max_supported = index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900744 if (index > SUPPORTED_CABLE_MAX) {
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900745 dev_err(&edev->dev,
746 "exceed the maximum number of supported cables\n");
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900747 return -EINVAL;
748 }
749
Chanwoo Choidae61652013-09-27 09:19:40 +0900750 edev->dev.class = extcon_class;
751 edev->dev.release = extcon_dev_release;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900752
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +0900753 edev->name = dev_name(edev->dev.parent);
Chanwoo Choi42d7d752013-09-27 09:20:26 +0900754 if (IS_ERR_OR_NULL(edev->name)) {
755 dev_err(&edev->dev,
756 "extcon device name is null\n");
757 return -EINVAL;
758 }
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +0900759 dev_set_name(&edev->dev, "extcon%lu",
760 (unsigned long)atomic_inc_return(&edev_no));
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900761
762 if (edev->max_supported) {
763 char buf[10];
764 char *str;
765 struct extcon_cable *cable;
766
767 edev->cables = kzalloc(sizeof(struct extcon_cable) *
768 edev->max_supported, GFP_KERNEL);
769 if (!edev->cables) {
770 ret = -ENOMEM;
771 goto err_sysfs_alloc;
772 }
773 for (index = 0; index < edev->max_supported; index++) {
774 cable = &edev->cables[index];
775
776 snprintf(buf, 10, "cable.%d", index);
777 str = kzalloc(sizeof(char) * (strlen(buf) + 1),
778 GFP_KERNEL);
779 if (!str) {
780 for (index--; index >= 0; index--) {
781 cable = &edev->cables[index];
782 kfree(cable->attr_g.name);
783 }
784 ret = -ENOMEM;
785
786 goto err_alloc_cables;
787 }
788 strcpy(str, buf);
789
790 cable->edev = edev;
791 cable->cable_index = index;
792 cable->attrs[0] = &cable->attr_name.attr;
793 cable->attrs[1] = &cable->attr_state.attr;
794 cable->attrs[2] = NULL;
795 cable->attr_g.name = str;
796 cable->attr_g.attrs = cable->attrs;
797
Mark Brown9baf3222012-08-16 20:03:21 +0100798 sysfs_attr_init(&cable->attr_name.attr);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900799 cable->attr_name.attr.name = "name";
800 cable->attr_name.attr.mode = 0444;
801 cable->attr_name.show = cable_name_show;
802
Mark Brown9baf3222012-08-16 20:03:21 +0100803 sysfs_attr_init(&cable->attr_state.attr);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900804 cable->attr_state.attr.name = "state";
Chanwoo Choiea9dd9d2013-05-22 19:31:59 +0900805 cable->attr_state.attr.mode = 0444;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900806 cable->attr_state.show = cable_state_show;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900807 }
808 }
809
MyungJoo Hambde68e62012-04-20 14:16:26 +0900810 if (edev->max_supported && edev->mutually_exclusive) {
811 char buf[80];
812 char *name;
813
814 /* Count the size of mutually_exclusive array */
815 for (index = 0; edev->mutually_exclusive[index]; index++)
816 ;
817
818 edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
819 (index + 1), GFP_KERNEL);
820 if (!edev->attrs_muex) {
821 ret = -ENOMEM;
822 goto err_muex;
823 }
824
825 edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
826 index, GFP_KERNEL);
827 if (!edev->d_attrs_muex) {
828 ret = -ENOMEM;
829 kfree(edev->attrs_muex);
830 goto err_muex;
831 }
832
833 for (index = 0; edev->mutually_exclusive[index]; index++) {
834 sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
835 name = kzalloc(sizeof(char) * (strlen(buf) + 1),
836 GFP_KERNEL);
837 if (!name) {
838 for (index--; index >= 0; index--) {
839 kfree(edev->d_attrs_muex[index].attr.
840 name);
841 }
842 kfree(edev->d_attrs_muex);
843 kfree(edev->attrs_muex);
844 ret = -ENOMEM;
845 goto err_muex;
846 }
847 strcpy(name, buf);
Mark Brown9baf3222012-08-16 20:03:21 +0100848 sysfs_attr_init(&edev->d_attrs_muex[index].attr);
MyungJoo Hambde68e62012-04-20 14:16:26 +0900849 edev->d_attrs_muex[index].attr.name = name;
850 edev->d_attrs_muex[index].attr.mode = 0000;
851 edev->attrs_muex[index] = &edev->d_attrs_muex[index]
852 .attr;
853 }
854 edev->attr_g_muex.name = muex_name;
855 edev->attr_g_muex.attrs = edev->attrs_muex;
856
857 }
858
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900859 if (edev->max_supported) {
860 edev->extcon_dev_type.groups =
861 kzalloc(sizeof(struct attribute_group *) *
MyungJoo Hambde68e62012-04-20 14:16:26 +0900862 (edev->max_supported + 2), GFP_KERNEL);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900863 if (!edev->extcon_dev_type.groups) {
864 ret = -ENOMEM;
865 goto err_alloc_groups;
866 }
867
Chanwoo Choidae61652013-09-27 09:19:40 +0900868 edev->extcon_dev_type.name = dev_name(&edev->dev);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900869 edev->extcon_dev_type.release = dummy_sysfs_dev_release;
870
871 for (index = 0; index < edev->max_supported; index++)
872 edev->extcon_dev_type.groups[index] =
873 &edev->cables[index].attr_g;
MyungJoo Hambde68e62012-04-20 14:16:26 +0900874 if (edev->mutually_exclusive)
875 edev->extcon_dev_type.groups[index] =
876 &edev->attr_g_muex;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900877
Chanwoo Choidae61652013-09-27 09:19:40 +0900878 edev->dev.type = &edev->extcon_dev_type;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900879 }
880
Chanwoo Choidae61652013-09-27 09:19:40 +0900881 ret = device_register(&edev->dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900882 if (ret) {
Chanwoo Choidae61652013-09-27 09:19:40 +0900883 put_device(&edev->dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900884 goto err_dev;
885 }
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900886#if defined(CONFIG_ANDROID)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900887 if (switch_class)
Chanwoo Choidae61652013-09-27 09:19:40 +0900888 ret = class_compat_create_link(switch_class, &edev->dev, NULL);
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900889#endif /* CONFIG_ANDROID */
MyungJoo Hamde55d872012-04-20 14:16:22 +0900890
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900891 spin_lock_init(&edev->lock);
892
Chanwoo Choi046050f2015-05-19 20:01:12 +0900893 edev->nh = devm_kzalloc(&edev->dev,
894 sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
895 if (!edev->nh) {
896 ret = -ENOMEM;
897 goto err_dev;
898 }
899
900 for (index = 0; index < edev->max_supported; index++)
901 RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
Donggeun Kim74c5d092012-04-20 14:16:24 +0900902
Chanwoo Choidae61652013-09-27 09:19:40 +0900903 dev_set_drvdata(&edev->dev, edev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900904 edev->state = 0;
Donggeun Kim74c5d092012-04-20 14:16:24 +0900905
906 mutex_lock(&extcon_dev_list_lock);
907 list_add(&edev->entry, &extcon_dev_list);
908 mutex_unlock(&extcon_dev_list_lock);
909
MyungJoo Hamde55d872012-04-20 14:16:22 +0900910 return 0;
911
912err_dev:
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900913 if (edev->max_supported)
914 kfree(edev->extcon_dev_type.groups);
915err_alloc_groups:
MyungJoo Hambde68e62012-04-20 14:16:26 +0900916 if (edev->max_supported && edev->mutually_exclusive) {
917 for (index = 0; edev->mutually_exclusive[index]; index++)
918 kfree(edev->d_attrs_muex[index].attr.name);
919 kfree(edev->d_attrs_muex);
920 kfree(edev->attrs_muex);
921 }
922err_muex:
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900923 for (index = 0; index < edev->max_supported; index++)
924 kfree(edev->cables[index].attr_g.name);
925err_alloc_cables:
926 if (edev->max_supported)
927 kfree(edev->cables);
928err_sysfs_alloc:
MyungJoo Hamde55d872012-04-20 14:16:22 +0900929 return ret;
930}
931EXPORT_SYMBOL_GPL(extcon_dev_register);
932
933/**
934 * extcon_dev_unregister() - Unregister the extcon device.
Peter Meerwaldc338bb02012-08-23 09:11:54 +0900935 * @edev: the extcon device instance to be unregistered.
MyungJoo Hamde55d872012-04-20 14:16:22 +0900936 *
937 * Note that this does not call kfree(edev) because edev was not allocated
938 * by this class.
939 */
940void extcon_dev_unregister(struct extcon_dev *edev)
941{
anish kumar57e7cd32012-10-22 09:43:33 +0900942 int index;
943
944 mutex_lock(&extcon_dev_list_lock);
945 list_del(&edev->entry);
946 mutex_unlock(&extcon_dev_list_lock);
947
Chanwoo Choidae61652013-09-27 09:19:40 +0900948 if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
949 dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
950 dev_name(&edev->dev));
anish kumar57e7cd32012-10-22 09:43:33 +0900951 return;
952 }
953
Wang, Xiaoming7585ca02013-11-01 18:48:14 -0400954 device_unregister(&edev->dev);
955
anish kumar57e7cd32012-10-22 09:43:33 +0900956 if (edev->mutually_exclusive && edev->max_supported) {
957 for (index = 0; edev->mutually_exclusive[index];
958 index++)
959 kfree(edev->d_attrs_muex[index].attr.name);
960 kfree(edev->d_attrs_muex);
961 kfree(edev->attrs_muex);
962 }
963
964 for (index = 0; index < edev->max_supported; index++)
965 kfree(edev->cables[index].attr_g.name);
966
967 if (edev->max_supported) {
968 kfree(edev->extcon_dev_type.groups);
969 kfree(edev->cables);
970 }
971
972#if defined(CONFIG_ANDROID)
973 if (switch_class)
Chanwoo Choidae61652013-09-27 09:19:40 +0900974 class_compat_remove_link(switch_class, &edev->dev, NULL);
anish kumar57e7cd32012-10-22 09:43:33 +0900975#endif
Chanwoo Choidae61652013-09-27 09:19:40 +0900976 put_device(&edev->dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900977}
978EXPORT_SYMBOL_GPL(extcon_dev_unregister);
979
Sangjung Woo11112442014-04-21 19:10:08 +0900980static void devm_extcon_dev_unreg(struct device *dev, void *res)
981{
982 extcon_dev_unregister(*(struct extcon_dev **)res);
983}
984
Sangjung Woo11112442014-04-21 19:10:08 +0900985/**
986 * devm_extcon_dev_register() - Resource-managed extcon_dev_register()
987 * @dev: device to allocate extcon device
988 * @edev: the new extcon device to register
989 *
990 * Managed extcon_dev_register() function. If extcon device is attached with
991 * this function, that extcon device is automatically unregistered on driver
992 * detach. Internally this function calls extcon_dev_register() function.
993 * To get more information, refer that function.
994 *
995 * If extcon device is registered with this function and the device needs to be
996 * unregistered separately, devm_extcon_dev_unregister() should be used.
997 *
998 * Returns 0 if success or negaive error number if failure.
999 */
1000int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
1001{
1002 struct extcon_dev **ptr;
1003 int ret;
1004
1005 ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
1006 if (!ptr)
1007 return -ENOMEM;
1008
1009 ret = extcon_dev_register(edev);
1010 if (ret) {
1011 devres_free(ptr);
1012 return ret;
1013 }
1014
1015 *ptr = edev;
1016 devres_add(dev, ptr);
1017
1018 return 0;
1019}
1020EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
1021
1022/**
1023 * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
1024 * @dev: device the extcon belongs to
1025 * @edev: the extcon device to unregister
1026 *
1027 * Unregister extcon device that is registered with devm_extcon_dev_register()
1028 * function.
1029 */
1030void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
1031{
1032 WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
1033 devm_extcon_dev_match, edev));
1034}
1035EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
1036
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001037#ifdef CONFIG_OF
1038/*
1039 * extcon_get_edev_by_phandle - Get the extcon device from devicetree
1040 * @dev - instance to the given device
1041 * @index - index into list of extcon_dev
1042 *
1043 * return the instance of extcon device
1044 */
1045struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1046{
1047 struct device_node *node;
1048 struct extcon_dev *edev;
1049
1050 if (!dev->of_node) {
1051 dev_err(dev, "device does not have a device node entry\n");
1052 return ERR_PTR(-EINVAL);
1053 }
1054
1055 node = of_parse_phandle(dev->of_node, "extcon", index);
1056 if (!node) {
1057 dev_err(dev, "failed to get phandle in %s node\n",
1058 dev->of_node->full_name);
1059 return ERR_PTR(-ENODEV);
1060 }
1061
Tomasz Figaf841afb12014-10-16 15:11:44 +02001062 mutex_lock(&extcon_dev_list_lock);
1063 list_for_each_entry(edev, &extcon_dev_list, entry) {
1064 if (edev->dev.parent && edev->dev.parent->of_node == node) {
1065 mutex_unlock(&extcon_dev_list_lock);
1066 return edev;
1067 }
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001068 }
Tomasz Figaf841afb12014-10-16 15:11:44 +02001069 mutex_unlock(&extcon_dev_list_lock);
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001070
Tomasz Figaf841afb12014-10-16 15:11:44 +02001071 return ERR_PTR(-EPROBE_DEFER);
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001072}
1073#else
1074struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1075{
1076 return ERR_PTR(-ENOSYS);
1077}
1078#endif /* CONFIG_OF */
1079EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
1080
Chanwoo Choi707d7552015-04-15 13:57:51 +09001081/**
1082 * extcon_get_edev_name() - Get the name of the extcon device.
1083 * @edev: the extcon device
1084 */
1085const char *extcon_get_edev_name(struct extcon_dev *edev)
1086{
1087 return !edev ? NULL : edev->name;
1088}
1089
MyungJoo Hamde55d872012-04-20 14:16:22 +09001090static int __init extcon_class_init(void)
1091{
1092 return create_extcon_class();
1093}
1094module_init(extcon_class_init);
1095
1096static void __exit extcon_class_exit(void)
1097{
Peter Huewe0dc77b62012-09-24 15:32:31 +09001098#if defined(CONFIG_ANDROID)
1099 class_compat_unregister(switch_class);
1100#endif
MyungJoo Hamde55d872012-04-20 14:16:22 +09001101 class_destroy(extcon_class);
1102}
1103module_exit(extcon_class_exit);
1104
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +09001105MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MyungJoo Hamde55d872012-04-20 14:16:22 +09001106MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
1107MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
1108MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1109MODULE_DESCRIPTION("External connector (extcon) class driver");
1110MODULE_LICENSE("GPL");