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