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