Merge remote-tracking branch 'extcon/extcon-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 02:51:38 +0000 (12:51 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 02:51:38 +0000 (12:51 +1000)
20 files changed:
Documentation/devicetree/bindings/extcon/qcom,pm8941-misc.txt [new file with mode: 0644]
drivers/extcon/Kconfig
drivers/extcon/Makefile
drivers/extcon/extcon-adc-jack.c
drivers/extcon/extcon-arizona.c
drivers/extcon/extcon-axp288.c
drivers/extcon/extcon-gpio.c
drivers/extcon/extcon-max14577.c
drivers/extcon/extcon-max3355.c
drivers/extcon/extcon-max77693.c
drivers/extcon/extcon-max77843.c
drivers/extcon/extcon-max8997.c
drivers/extcon/extcon-palmas.c
drivers/extcon/extcon-qcom-spmi-misc.c [new file with mode: 0644]
drivers/extcon/extcon-rt8973a.c
drivers/extcon/extcon-sm5502.c
drivers/extcon/extcon-usb-gpio.c
drivers/extcon/extcon.c
include/linux/extcon.h
include/linux/extcon/extcon-adc-jack.h

diff --git a/Documentation/devicetree/bindings/extcon/qcom,pm8941-misc.txt b/Documentation/devicetree/bindings/extcon/qcom,pm8941-misc.txt
new file mode 100644 (file)
index 0000000..35383ad
--- /dev/null
@@ -0,0 +1,41 @@
+Qualcomm's PM8941 USB ID Extcon device
+
+Some Qualcomm PMICs have a "misc" module that can be used to detect when
+the USB ID pin has been pulled low or high.
+
+PROPERTIES
+
+- compatible:
+    Usage: required
+    Value type: <string>
+    Definition: Should contain "qcom,pm8941-misc";
+
+- reg:
+    Usage: required
+    Value type: <u32>
+    Definition: Should contain the offset to the misc address space
+
+- interrupts:
+    Usage: required
+    Value type: <prop-encoded-array>
+    Definition: Should contain the usb id interrupt
+
+- interrupt-names:
+    Usage: required
+    Value type: <stringlist>
+    Definition: Should contain the string "usb_id" for the usb id interrupt
+
+Example:
+
+       pmic {
+               usb_id: misc@900 {
+                       compatible = "qcom,pm8941-misc";
+                       reg = <0x900>;
+                       interrupts = <0x0 0x9 0 IRQ_TYPE_EDGE_BOTH>;
+                       interrupt-names = "usb_id";
+               };
+       }
+
+       usb-controller {
+               extcon = <&usb_id>;
+       };
index 3d89e60a3e715d4a42adc0be917873cd76253885..04788d92ea522a5346e76f6dd6f5d6bf08431c4e 100644 (file)
@@ -96,6 +96,12 @@ config EXTCON_PALMAS
          Say Y here to enable support for USB peripheral and USB host
          detection by palmas usb.
 
+config EXTCON_QCOM_SPMI_MISC
+       tristate "Qualcomm USB extcon support"
+       help
+         Say Y here to enable SPMI PMIC based USB cable detection
+         support on Qualcomm PMICs such as PM8941.
+
 config EXTCON_RT8973A
        tristate "Richtek RT8973A EXTCON support"
        depends on I2C
index 972c813c375b086e2b5950d2be1780dccd78dbfc..31a0a999c4fb0c9b6c3da9680f55caba57e02782 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
 obj-$(CONFIG_EXTCON_MAX77843)  += extcon-max77843.o
 obj-$(CONFIG_EXTCON_MAX8997)   += extcon-max8997.o
 obj-$(CONFIG_EXTCON_PALMAS)    += extcon-palmas.o
+obj-$(CONFIG_EXTCON_QCOM_SPMI_MISC) += extcon-qcom-spmi-misc.o
 obj-$(CONFIG_EXTCON_RT8973A)   += extcon-rt8973a.o
 obj-$(CONFIG_EXTCON_SM5502)    += extcon-sm5502.o
 obj-$(CONFIG_EXTCON_USB_GPIO)  += extcon-usb-gpio.o
index 44e48aa78a84bbdaf7f925bae5c80f8ffe634680..bc538708c75370a61ac83b9331e0bf1dd6aea810 100644 (file)
@@ -3,6 +3,9 @@
  *
  * Analog Jack extcon driver with ADC-based detection capability.
  *
+ * Copyright (C) 2016 Samsung Electronics
+ * Chanwoo Choi <cw00.choi@samsung.com>
+ *
  * Copyright (C) 2012 Samsung Electronics
  * MyungJoo Ham <myungjoo.ham@samsung.com>
  *
@@ -58,7 +61,7 @@ static void adc_jack_handler(struct work_struct *work)
        struct adc_jack_data *data = container_of(to_delayed_work(work),
                        struct adc_jack_data,
                        handler);
-       u32 state = 0;
+       struct adc_jack_cond *def;
        int ret, adc_val;
        int i;
 
@@ -70,17 +73,18 @@ static void adc_jack_handler(struct work_struct *work)
 
        /* Get state from adc value with adc_conditions */
        for (i = 0; i < data->num_conditions; i++) {
-               struct adc_jack_cond *def = &data->adc_conditions[i];
-               if (!def->state)
-                       break;
+               def = &data->adc_conditions[i];
                if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
-                       state = def->state;
-                       break;
+                       extcon_set_state_sync(data->edev, def->id, true);
+                       return;
                }
        }
-       /* if no def has met, it means state = 0 (no cables attached) */
 
-       extcon_set_state(data->edev, state);
+       /* Set the detached state if adc value is not included in the range */
+       for (i = 0; i < data->num_conditions; i++) {
+               def = &data->adc_conditions[i];
+               extcon_set_state_sync(data->edev, def->id, false);
+       }
 }
 
 static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
@@ -114,16 +118,14 @@ static int adc_jack_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       if (!pdata->adc_conditions ||
-                       !pdata->adc_conditions[0].state) {
+       if (!pdata->adc_conditions) {
                dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
                return -EINVAL;
        }
        data->adc_conditions = pdata->adc_conditions;
 
        /* Check the length of array and set num_conditions */
-       for (i = 0; data->adc_conditions[i].state; i++)
-               ;
+       for (i = 0; data->adc_conditions[i].id != EXTCON_NONE; i++);
        data->num_conditions = i;
 
        data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
@@ -158,6 +160,7 @@ static int adc_jack_probe(struct platform_device *pdev)
        if (data->wakeup_source)
                device_init_wakeup(&pdev->dev, 1);
 
+       adc_jack_handler(&data->handler.work);
        return 0;
 }
 
index 1d8e0a57bd51bbe84a8ecd29d53717f2465922de..56e6c4c7c60d265912a2c2990170a48b616f9bef 100644 (file)
@@ -183,7 +183,7 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
                if (clamp)
                        val = ARIZONA_RMV_SHRT_HP1L;
                break;
-       };
+       }
 
        snd_soc_dapm_mutex_lock(arizona->dapm);
 
@@ -614,7 +614,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        }
 
        /* If the cable was removed while measuring ignore the result */
-       ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
+       ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
        if (ret < 0) {
                dev_err(arizona->dev, "Failed to check cable state: %d\n",
                        ret);
@@ -649,7 +649,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        else
                report = EXTCON_JACK_HEADPHONE;
 
-       ret = extcon_set_cable_state_(info->edev, report, true);
+       ret = extcon_set_state_sync(info->edev, report, true);
        if (ret != 0)
                dev_err(arizona->dev, "Failed to report HP/line: %d\n",
                        ret);
@@ -732,7 +732,7 @@ err:
                           ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
 
        /* Just report headphone */
-       ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
+       ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
        if (ret != 0)
                dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
 
@@ -789,7 +789,7 @@ err:
                           ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
 
        /* Just report headphone */
-       ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
+       ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
        if (ret != 0)
                dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
 
@@ -829,7 +829,7 @@ static void arizona_micd_detect(struct work_struct *work)
        mutex_lock(&info->lock);
 
        /* If the cable was removed while measuring ignore the result */
-       ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
+       ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
        if (ret < 0) {
                dev_err(arizona->dev, "Failed to check cable state: %d\n",
                                ret);
@@ -914,7 +914,7 @@ static void arizona_micd_detect(struct work_struct *work)
 
                arizona_identify_headphone(info);
 
-               ret = extcon_set_cable_state_(info->edev,
+               ret = extcon_set_state_sync(info->edev,
                                              EXTCON_JACK_MICROPHONE, true);
                if (ret != 0)
                        dev_err(arizona->dev, "Headset report failed: %d\n",
@@ -1108,7 +1108,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
 
        if (info->last_jackdet == present) {
                dev_dbg(arizona->dev, "Detected jack\n");
-               ret = extcon_set_cable_state_(info->edev,
+               ret = extcon_set_state_sync(info->edev,
                                              EXTCON_MECHANICAL, true);
 
                if (ret != 0)
@@ -1149,10 +1149,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                                         info->micd_ranges[i].key, 0);
                input_sync(info->input);
 
-               ret = extcon_update_state(info->edev, 0xffffffff, 0);
-               if (ret != 0)
-                       dev_err(arizona->dev, "Removal report failed: %d\n",
-                               ret);
+               for (i = 0; i < ARRAY_SIZE(arizona_cable) - 1; i++) {
+                       ret = extcon_set_state_sync(info->edev,
+                                       arizona_cable[i], false);
+                       if (ret != 0)
+                               dev_err(arizona->dev,
+                                       "Removal report failed: %d\n", ret);
+               }
 
                regmap_update_bits(arizona->regmap,
                                   ARIZONA_JACK_DETECT_DEBOUNCE,
index fd55c2f2080ab4873ae7df5eb3af8dcc0c246bc4..42f41e8082924e229d99ceac264549494449b9d3 100644 (file)
@@ -189,19 +189,19 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
 
        switch (chrg_type) {
        case DET_STAT_SDP:
-               dev_dbg(info->dev, "sdp cable is connecetd\n");
+               dev_dbg(info->dev, "sdp cable is connected\n");
                notify_otg = true;
                notify_charger = true;
                cable = EXTCON_CHG_USB_SDP;
                break;
        case DET_STAT_CDP:
-               dev_dbg(info->dev, "cdp cable is connecetd\n");
+               dev_dbg(info->dev, "cdp cable is connected\n");
                notify_otg = true;
                notify_charger = true;
                cable = EXTCON_CHG_USB_CDP;
                break;
        case DET_STAT_DCP:
-               dev_dbg(info->dev, "dcp cable is connecetd\n");
+               dev_dbg(info->dev, "dcp cable is connected\n");
                notify_charger = true;
                cable = EXTCON_CHG_USB_DCP;
                break;
@@ -226,7 +226,7 @@ notify_otg:
        }
 
        if (notify_charger)
-               extcon_set_cable_state_(info->edev, cable, vbus_attach);
+               extcon_set_state_sync(info->edev, cable, vbus_attach);
 
        /* Clear the flags on disconnect event */
        if (!vbus_attach)
index d023789f0fdac94a2fd8b6bfc90618793a2bda37..e7aebbc945f65bc24f846efbc465efb1f969db98 100644 (file)
@@ -49,7 +49,7 @@ static void gpio_extcon_work(struct work_struct *work)
        state = gpiod_get_value_cansleep(data->id_gpiod);
        if (data->pdata->gpio_active_low)
                state = !state;
-       extcon_set_state(data->edev, state);
+       extcon_set_state_sync(data->edev, data->pdata->extcon_id, state);
 }
 
 static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
index 852a7112f451990243c20c8a746f7d2e7d167cad..12e26c4e77638cf16041037b040a93371c714bd4 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2013,2014 Samsung Electronics
  * Chanwoo Choi <cw00.choi@samsung.com>
- * Krzysztof Kozlowski <k.kozlowski@samsung.com>
+ * Krzysztof Kozlowski <krzk@kernel.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -357,7 +357,7 @@ static int max14577_muic_jig_handler(struct max14577_muic_info *info,
        if (ret < 0)
                return ret;
 
-       extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+       extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
 
        return 0;
 }
@@ -454,24 +454,24 @@ static int max14577_muic_chg_handler(struct max14577_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+               extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                        attached);
                break;
        case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
                                        attached);
                break;
        case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
                                        attached);
                break;
        case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
                                        attached);
                break;
        case MAX14577_CHARGER_TYPE_SPECIAL_1A:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
                                        attached);
                break;
        case MAX14577_CHARGER_TYPE_NONE:
@@ -791,6 +791,6 @@ static struct platform_driver max14577_muic_driver = {
 module_platform_driver(max14577_muic_driver);
 
 MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
-MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
+MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <krzk@kernel.org>");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:extcon-max14577");
index c24abec5d06cf0e264af44eb29116afa0b5ad744..533e16a952b848b5db0d0842740ad46be85edd4b 100644 (file)
@@ -39,16 +39,16 @@ static irqreturn_t max3355_id_irq(int irq, void *dev_id)
                 * As we don't have event for USB peripheral cable attached,
                 * we simulate USB peripheral attach here.
                 */
-               extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, false);
-               extcon_set_cable_state_(data->edev, EXTCON_USB, true);
+               extcon_set_state_sync(data->edev, EXTCON_USB_HOST, false);
+               extcon_set_state_sync(data->edev, EXTCON_USB, true);
        } else {
                /*
                 * ID = 0 means USB HOST cable attached.
                 * As we don't have event for USB peripheral cable detached,
                 * we simulate USB peripheral detach here.
                 */
-               extcon_set_cable_state_(data->edev, EXTCON_USB, false);
-               extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, true);
+               extcon_set_state_sync(data->edev, EXTCON_USB, false);
+               extcon_set_state_sync(data->edev, EXTCON_USB_HOST, true);
        }
 
        return IRQ_HANDLED;
index f17cb76b567cc949e4827932e1eeff9953ca7ea8..68dbcb814b2ff78a64bc7a456a380e89227e32f9 100644 (file)
@@ -505,8 +505,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached);
-               extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+               extcon_set_state_sync(info->edev, EXTCON_DOCK, attached);
+               extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
                goto out;
        case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE:       /* Dock-Desk */
                dock_id = EXTCON_DOCK;
@@ -514,8 +514,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
        case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:         /* Dock-Audio */
                dock_id = EXTCON_DOCK;
                if (!attached) {
-                       extcon_set_cable_state_(info->edev, EXTCON_USB, false);
-                       extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+                       extcon_set_state_sync(info->edev, EXTCON_USB, false);
+                       extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                                false);
                }
                break;
@@ -530,7 +530,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
                                        attached);
        if (ret < 0)
                return ret;
-       extcon_set_cable_state_(info->edev, dock_id, attached);
+       extcon_set_state_sync(info->edev, dock_id, attached);
 
 out:
        return 0;
@@ -596,7 +596,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
                                                attached);
                if (ret < 0)
                        return ret;
-               extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
+               extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached);
                break;
        case MAX77693_MUIC_GND_AV_CABLE_LOAD:
                /* Audio Video Cable with load, PATH:AUDIO */
@@ -604,14 +604,14 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
                                                attached);
                if (ret < 0)
                        return ret;
-               extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+               extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                        attached);
                break;
        case MAX77693_MUIC_GND_MHL:
        case MAX77693_MUIC_GND_MHL_VB:
                /* MHL or MHL with USB/TA cable */
-               extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+               extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
                break;
        default:
                dev_err(info->dev, "failed to detect %s cable of gnd type\n",
@@ -653,7 +653,7 @@ static int max77693_muic_jig_handler(struct max77693_muic_info *info,
        if (ret < 0)
                return ret;
 
-       extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+       extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
 
        return 0;
 }
@@ -807,10 +807,10 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                         * - Support charging through micro-usb port without
                         *   data connection
                         */
-                       extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+                       extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
                                                attached);
                        if (!cable_attached)
-                               extcon_set_cable_state_(info->edev,
+                               extcon_set_state_sync(info->edev,
                                        EXTCON_DISP_MHL, cable_attached);
                        break;
                }
@@ -834,13 +834,13 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                         * - Support charging through micro-usb port without
                         *   data connection.
                         */
-                       extcon_set_cable_state_(info->edev, EXTCON_USB,
+                       extcon_set_state_sync(info->edev, EXTCON_USB,
                                                attached);
-                       extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+                       extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                                attached);
 
                        if (!cable_attached)
-                               extcon_set_cable_state_(info->edev, EXTCON_DOCK,
+                               extcon_set_state_sync(info->edev, EXTCON_DOCK,
                                                        cable_attached);
                        break;
                case MAX77693_MUIC_ADC_RESERVED_ACC_3:          /* Dock-Smart */
@@ -869,9 +869,9 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                        if (ret < 0)
                                return ret;
 
-                       extcon_set_cable_state_(info->edev, EXTCON_DOCK,
+                       extcon_set_state_sync(info->edev, EXTCON_DOCK,
                                                attached);
-                       extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL,
+                       extcon_set_state_sync(info->edev, EXTCON_DISP_MHL,
                                                attached);
                        break;
                }
@@ -905,28 +905,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                        if (ret < 0)
                                return ret;
 
-                       extcon_set_cable_state_(info->edev, EXTCON_USB,
+                       extcon_set_state_sync(info->edev, EXTCON_USB,
                                                attached);
-                       extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+                       extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                                attached);
                        break;
                case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
                        /* Only TA cable */
-                       extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+                       extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
                                                attached);
                        break;
                }
                break;
        case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
                                        attached);
                break;
        case MAX77693_CHARGER_TYPE_APPLE_500MA:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
                                        attached);
                break;
        case MAX77693_CHARGER_TYPE_APPLE_1A_2A:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
                                        attached);
                break;
        case MAX77693_CHARGER_TYPE_DEAD_BATTERY:
index b188bd650efa5261c13d1fc9d4fb3fea894f225a..5d11fdf36e9424b9e03eb484098e4752f147be4c 100644 (file)
@@ -346,7 +346,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
+               extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached);
                break;
        case MAX77843_MUIC_GND_MHL_VB:
        case MAX77843_MUIC_GND_MHL:
@@ -356,7 +356,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+               extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
                break;
        default:
                dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n",
@@ -392,7 +392,7 @@ static int max77843_muic_jig_handler(struct max77843_muic_info *info,
        if (ret < 0)
                return ret;
 
-       extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+       extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
 
        return 0;
 }
@@ -486,8 +486,8 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+               extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                        attached);
                break;
        case MAX77843_MUIC_CHG_DOWNSTREAM:
@@ -497,7 +497,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
                                        attached);
                break;
        case MAX77843_MUIC_CHG_DEDICATED:
@@ -507,7 +507,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
                                        attached);
                break;
        case MAX77843_MUIC_CHG_SPECIAL_500MA:
@@ -517,7 +517,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
                                        attached);
                break;
        case MAX77843_MUIC_CHG_SPECIAL_1A:
@@ -527,7 +527,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
                if (ret < 0)
                        return ret;
 
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
                                        attached);
                break;
        case MAX77843_MUIC_CHG_GND:
@@ -536,10 +536,10 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
 
                /* Charger cable on MHL accessory is attach or detach */
                if (gnd_type == MAX77843_MUIC_GND_MHL_VB)
-                       extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+                       extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
                                                true);
                else if (gnd_type == MAX77843_MUIC_GND_MHL)
-                       extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+                       extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
                                                false);
                break;
        case MAX77843_MUIC_CHG_NONE:
index 9a89320d09a8b6e08660cadca6abd6b79e23c24d..4a0612fb9c07029b830c128b13f4efbf3832369a 100644 (file)
@@ -331,11 +331,11 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info,
 
        switch (usb_type) {
        case MAX8997_USB_HOST:
-               extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
+               extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached);
                break;
        case MAX8997_USB_DEVICE:
-               extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+               extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                        attached);
                break;
        default:
@@ -361,7 +361,7 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info,
        switch (cable_type) {
        case MAX8997_MUIC_ADC_AV_CABLE_NOLOAD:
        case MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON:
-               extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached);
+               extcon_set_state_sync(info->edev, EXTCON_DOCK, attached);
                break;
        default:
                dev_err(info->dev, "failed to detect %s dock device\n",
@@ -384,7 +384,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
                return ret;
        }
 
-       extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+       extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
 
        return 0;
 }
@@ -406,7 +406,7 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info)
                        return ret;
                break;
        case MAX8997_MUIC_ADC_MHL:
-               extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+               extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
                break;
        case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF:
        case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON:
@@ -489,19 +489,19 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info)
                }
                break;
        case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
                                        attached);
                break;
        case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
                                        attached);
                break;
        case MAX8997_CHARGER_TYPE_500MA:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
                                        attached);
                break;
        case MAX8997_CHARGER_TYPE_1A:
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
                                        attached);
                break;
        default:
index caff46c0e214bd3e18fda8ccbd7949d5c09ea00b..634ba70782de9c92f256e9e5cdd535f71dd642f2 100644 (file)
@@ -61,7 +61,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
        if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
                if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
                        palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
-                       extcon_set_cable_state_(edev, EXTCON_USB, true);
+                       extcon_set_state_sync(edev, EXTCON_USB, true);
                        dev_info(palmas_usb->dev, "USB cable is attached\n");
                } else {
                        dev_dbg(palmas_usb->dev,
@@ -70,7 +70,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
        } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
                if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
                        palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
-                       extcon_set_cable_state_(edev, EXTCON_USB, false);
+                       extcon_set_state_sync(edev, EXTCON_USB, false);
                        dev_info(palmas_usb->dev, "USB cable is detached\n");
                } else {
                        dev_dbg(palmas_usb->dev,
@@ -98,7 +98,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
                        PALMAS_USB_ID_INT_LATCH_CLR,
                        PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
                palmas_usb->linkstat = PALMAS_USB_STATE_ID;
-               extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
+               extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
                dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
        } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
                                (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
@@ -106,17 +106,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
                        PALMAS_USB_ID_INT_LATCH_CLR,
                        PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
                palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
-               extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
+               extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
                dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
        } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
                                (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
                palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
-               extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
+               extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
                dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
        } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
                                (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
                palmas_usb->linkstat = PALMAS_USB_STATE_ID;
-               extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
+               extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
                dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
        }
 
@@ -137,10 +137,10 @@ static void palmas_gpio_id_detect(struct work_struct *work)
        id = gpiod_get_value_cansleep(palmas_usb->id_gpiod);
 
        if (id) {
-               extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
+               extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
                dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
        } else {
-               extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
+               extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
                dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
        }
 }
diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c
new file mode 100644 (file)
index 0000000..ca957a5
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID
+ *                             detection based on extcon-usb-gpio.c.
+ *
+ * Copyright (C) 2016 Linaro, Ltd.
+ * Stephen Boyd <stephen.boyd@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/extcon.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define USB_ID_DEBOUNCE_MS     5       /* ms */
+
+struct qcom_usb_extcon_info {
+       struct extcon_dev *edev;
+       int irq;
+       struct delayed_work wq_detcable;
+       unsigned long debounce_jiffies;
+};
+
+static const unsigned int qcom_usb_extcon_cable[] = {
+       EXTCON_USB_HOST,
+       EXTCON_NONE,
+};
+
+static void qcom_usb_extcon_detect_cable(struct work_struct *work)
+{
+       bool id;
+       int ret;
+       struct qcom_usb_extcon_info *info = container_of(to_delayed_work(work),
+                                                   struct qcom_usb_extcon_info,
+                                                   wq_detcable);
+
+       /* check ID and update cable state */
+       ret = irq_get_irqchip_state(info->irq, IRQCHIP_STATE_LINE_LEVEL, &id);
+       if (ret)
+               return;
+
+       extcon_set_state(info->edev, EXTCON_USB_HOST, !id);
+}
+
+static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id)
+{
+       struct qcom_usb_extcon_info *info = dev_id;
+
+       queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
+                          info->debounce_jiffies);
+
+       return IRQ_HANDLED;
+}
+
+static int qcom_usb_extcon_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct qcom_usb_extcon_info *info;
+       int ret;
+
+       info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->edev = devm_extcon_dev_allocate(dev, qcom_usb_extcon_cable);
+       if (IS_ERR(info->edev)) {
+               dev_err(dev, "failed to allocate extcon device\n");
+               return -ENOMEM;
+       }
+
+       ret = devm_extcon_dev_register(dev, info->edev);
+       if (ret < 0) {
+               dev_err(dev, "failed to register extcon device\n");
+               return ret;
+       }
+
+       info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
+       INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable);
+
+       info->irq = platform_get_irq_byname(pdev, "usb_id");
+       if (info->irq < 0)
+               return info->irq;
+
+       ret = devm_request_threaded_irq(dev, info->irq, NULL,
+                                       qcom_usb_irq_handler,
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       pdev->name, info);
+       if (ret < 0) {
+               dev_err(dev, "failed to request handler for ID IRQ\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, info);
+       device_init_wakeup(dev, 1);
+
+       /* Perform initial detection */
+       qcom_usb_extcon_detect_cable(&info->wq_detcable.work);
+
+       return 0;
+}
+
+static int qcom_usb_extcon_remove(struct platform_device *pdev)
+{
+       struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&info->wq_detcable);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qcom_usb_extcon_suspend(struct device *dev)
+{
+       struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (device_may_wakeup(dev))
+               ret = enable_irq_wake(info->irq);
+
+       return ret;
+}
+
+static int qcom_usb_extcon_resume(struct device *dev)
+{
+       struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (device_may_wakeup(dev))
+               ret = disable_irq_wake(info->irq);
+
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qcom_usb_extcon_pm_ops,
+                        qcom_usb_extcon_suspend, qcom_usb_extcon_resume);
+
+static const struct of_device_id qcom_usb_extcon_dt_match[] = {
+       { .compatible = "qcom,pm8941-misc", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match);
+
+static struct platform_driver qcom_usb_extcon_driver = {
+       .probe          = qcom_usb_extcon_probe,
+       .remove         = qcom_usb_extcon_remove,
+       .driver         = {
+               .name   = "extcon-pm8941-misc",
+               .pm     = &qcom_usb_extcon_pm_ops,
+               .of_match_table = qcom_usb_extcon_dt_match,
+       },
+};
+module_platform_driver(qcom_usb_extcon_driver);
+
+MODULE_DESCRIPTION("QCOM USB ID extcon driver");
+MODULE_AUTHOR("Stephen Boyd <stephen.boyd@linaro.org>");
+MODULE_LICENSE("GPL v2");
index 97e074d70eca21264d0743ae7ebf3e90b4ce228c..174c388739ea8da35105a892b61c12741be4638b 100644 (file)
@@ -398,9 +398,9 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info,
                return ret;
 
        /* Change the state of external accessory */
-       extcon_set_cable_state_(info->edev, id, attached);
+       extcon_set_state_sync(info->edev, id, attached);
        if (id == EXTCON_USB)
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                        attached);
 
        return 0;
index df769a17e736fcadae1fbf05543fd6837ebd0f13..b223256885033f01a2af1be8da6fbdb8d5a1ae2c 100644 (file)
@@ -411,9 +411,9 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
                return ret;
 
        /* Change the state of external accessory */
-       extcon_set_cable_state_(info->edev, id, attached);
+       extcon_set_state_sync(info->edev, id, attached);
        if (id == EXTCON_USB)
-               extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+               extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
                                        attached);
 
        return 0;
index 2512660dc4b9508c5dd26a2a63177a7681f52d56..a27d350f69e30cb7cf6ca5f7cc039aa90ea54c65 100644 (file)
@@ -63,16 +63,16 @@ static void usb_extcon_detect_cable(struct work_struct *work)
                 * As we don't have event for USB peripheral cable attached,
                 * we simulate USB peripheral attach here.
                 */
-               extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, false);
-               extcon_set_cable_state_(info->edev, EXTCON_USB, true);
+               extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
+               extcon_set_state_sync(info->edev, EXTCON_USB, true);
        } else {
                /*
                 * ID = 0 means USB HOST cable attached.
                 * As we don't have event for USB peripheral cable detached,
                 * we simulate USB peripheral detach here.
                 */
-               extcon_set_cable_state_(info->edev, EXTCON_USB, false);
-               extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, true);
+               extcon_set_state_sync(info->edev, EXTCON_USB, false);
+               extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
        }
 }
 
index 8682efc0f57b7557761feef72433ecfad08d237b..78298460d1686a79864c57d228ac6af5e114acf9 100644 (file)
 #define SUPPORTED_CABLE_MAX    32
 #define CABLE_NAME_MAX         30
 
-static const char *extcon_name[] =  {
-       [EXTCON_NONE]                   = "NONE",
+struct __extcon_info {
+       unsigned int type;
+       unsigned int id;
+       const char *name;
+
+} extcon_info[] = {
+       [EXTCON_NONE] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_NONE,
+               .name = "NONE",
+       },
 
        /* USB external connector */
-       [EXTCON_USB]                    = "USB",
-       [EXTCON_USB_HOST]               = "USB-HOST",
+       [EXTCON_USB] = {
+               .type = EXTCON_TYPE_USB,
+               .id = EXTCON_USB,
+               .name = "USB",
+       },
+       [EXTCON_USB_HOST] = {
+               .type = EXTCON_TYPE_USB,
+               .id = EXTCON_USB_HOST,
+               .name = "USB_HOST",
+       },
 
        /* Charging external connector */
-       [EXTCON_CHG_USB_SDP]            = "SDP",
-       [EXTCON_CHG_USB_DCP]            = "DCP",
-       [EXTCON_CHG_USB_CDP]            = "CDP",
-       [EXTCON_CHG_USB_ACA]            = "ACA",
-       [EXTCON_CHG_USB_FAST]           = "FAST-CHARGER",
-       [EXTCON_CHG_USB_SLOW]           = "SLOW-CHARGER",
+       [EXTCON_CHG_USB_SDP] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_SDP,
+               .name = "SDP",
+       },
+       [EXTCON_CHG_USB_DCP] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_DCP,
+               .name = "DCP",
+       },
+       [EXTCON_CHG_USB_CDP] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_CDP,
+               .name = "CDP",
+       },
+       [EXTCON_CHG_USB_ACA] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_ACA,
+               .name = "ACA",
+       },
+       [EXTCON_CHG_USB_FAST] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_FAST,
+               .name = "FAST-CHARGER",
+       },
+       [EXTCON_CHG_USB_SLOW] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_SLOW,
+               .name = "SLOW-CHARGER",
+       },
+       [EXTCON_CHG_WPT] = {
+               .type = EXTCON_TYPE_CHG,
+               .id = EXTCON_CHG_WPT,
+               .name = "WPT",
+       },
 
        /* Jack external connector */
-       [EXTCON_JACK_MICROPHONE]        = "MICROPHONE",
-       [EXTCON_JACK_HEADPHONE]         = "HEADPHONE",
-       [EXTCON_JACK_LINE_IN]           = "LINE-IN",
-       [EXTCON_JACK_LINE_OUT]          = "LINE-OUT",
-       [EXTCON_JACK_VIDEO_IN]          = "VIDEO-IN",
-       [EXTCON_JACK_VIDEO_OUT]         = "VIDEO-OUT",
-       [EXTCON_JACK_SPDIF_IN]          = "SPDIF-IN",
-       [EXTCON_JACK_SPDIF_OUT]         = "SPDIF-OUT",
+       [EXTCON_JACK_MICROPHONE] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_MICROPHONE,
+               .name = "MICROPHONE",
+       },
+       [EXTCON_JACK_HEADPHONE] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_HEADPHONE,
+               .name = "HEADPHONE",
+       },
+       [EXTCON_JACK_LINE_IN] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_LINE_IN,
+               .name = "LINE-IN",
+       },
+       [EXTCON_JACK_LINE_OUT] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_LINE_OUT,
+               .name = "LINE-OUT",
+       },
+       [EXTCON_JACK_VIDEO_IN] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_VIDEO_IN,
+               .name = "VIDEO-IN",
+       },
+       [EXTCON_JACK_VIDEO_OUT] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_VIDEO_OUT,
+               .name = "VIDEO-OUT",
+       },
+       [EXTCON_JACK_SPDIF_IN] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_SPDIF_IN,
+               .name = "SPDIF-IN",
+       },
+       [EXTCON_JACK_SPDIF_OUT] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_SPDIF_OUT,
+               .name = "SPDIF-OUT",
+       },
 
        /* Display external connector */
-       [EXTCON_DISP_HDMI]              = "HDMI",
-       [EXTCON_DISP_MHL]               = "MHL",
-       [EXTCON_DISP_DVI]               = "DVI",
-       [EXTCON_DISP_VGA]               = "VGA",
+       [EXTCON_DISP_HDMI] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_HDMI,
+               .name = "HDMI",
+       },
+       [EXTCON_DISP_MHL] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_MHL,
+               .name = "MHL",
+       },
+       [EXTCON_DISP_DVI] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_DVI,
+               .name = "DVI",
+       },
+       [EXTCON_DISP_VGA] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_VGA,
+               .name = "VGA",
+       },
+       [EXTCON_DISP_DP] = {
+               .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+               .id = EXTCON_DISP_DP,
+               .name = "DP",
+       },
+       [EXTCON_DISP_HMD] = {
+               .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+               .id = EXTCON_DISP_HMD,
+               .name = "HMD",
+       },
 
        /* Miscellaneous external connector */
-       [EXTCON_DOCK]                   = "DOCK",
-       [EXTCON_JIG]                    = "JIG",
-       [EXTCON_MECHANICAL]             = "MECHANICAL",
-
-       NULL,
+       [EXTCON_DOCK] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_DOCK,
+               .name = "DOCK",
+       },
+       [EXTCON_JIG] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_JIG,
+               .name = "JIG",
+       },
+       [EXTCON_MECHANICAL] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_MECHANICAL,
+               .name = "MECHANICAL",
+       },
+
+       { /* sentinel */ }
 };
 
 /**
@@ -95,6 +211,16 @@ struct extcon_cable {
        struct device_attribute attr_state;
 
        struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
+
+       union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
+       union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
+       union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
+       union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
+
+       unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
+       unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
+       unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
+       unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
 };
 
 static struct class *extcon_class;
@@ -147,14 +273,93 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id
        return -EINVAL;
 }
 
-static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
+static int get_extcon_type(unsigned int prop)
 {
-       if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
-               *attached = ((new >> idx) & 0x1) ? true : false;
-               return true;
+       switch (prop) {
+       case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+               return EXTCON_TYPE_USB;
+       case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+               return EXTCON_TYPE_CHG;
+       case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+               return EXTCON_TYPE_JACK;
+       case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+               return EXTCON_TYPE_DISP;
+       default:
+               return -EINVAL;
        }
+}
+
+static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
+{
+       return !!(edev->state & BIT(index));
+}
+
+static bool is_extcon_changed(struct extcon_dev *edev, int index,
+                               bool new_state)
+{
+       int state = !!(edev->state & BIT(index));
+       return (state != new_state);
+}
+
+static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
+{
+       int type;
+
+       /* Check whether the property is supported or not. */
+       type = get_extcon_type(prop);
+       if (type < 0)
+               return false;
 
-       return false;
+       /* Check whether a specific extcon id supports the property or not. */
+       return !!(extcon_info[id].type & type);
+}
+
+static int is_extcon_property_capability(struct extcon_dev *edev,
+                               unsigned int id, int index,unsigned int prop)
+{
+       struct extcon_cable *cable;
+       int type, ret;
+
+       /* Check whether the property is supported or not. */
+       type = get_extcon_type(prop);
+       if (type < 0)
+               return type;
+
+       cable = &edev->cables[index];
+
+       switch (type) {
+       case EXTCON_TYPE_USB:
+               ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
+               break;
+       case EXTCON_TYPE_CHG:
+               ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
+               break;
+       case EXTCON_TYPE_JACK:
+               ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
+               break;
+       case EXTCON_TYPE_DISP:
+               ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static void init_property(struct extcon_dev *edev, unsigned int id, int index)
+{
+       unsigned int type = extcon_info[id].type;
+       struct extcon_cable *cable = &edev->cables[index];
+
+       if (EXTCON_TYPE_USB & type)
+               memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
+       if (EXTCON_TYPE_CHG & type)
+               memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
+       if (EXTCON_TYPE_JACK & type)
+               memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
+       if (EXTCON_TYPE_DISP & type)
+               memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
 }
 
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
@@ -168,32 +373,13 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 
        for (i = 0; i < edev->max_supported; i++) {
                count += sprintf(buf + count, "%s=%d\n",
-                               extcon_name[edev->supported_cable[i]],
+                               extcon_info[edev->supported_cable[i]].name,
                                 !!(edev->state & (1 << i)));
        }
 
        return count;
 }
-
-static ssize_t state_store(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
-{
-       u32 state;
-       ssize_t ret = 0;
-       struct extcon_dev *edev = dev_get_drvdata(dev);
-
-       ret = sscanf(buf, "0x%x", &state);
-       if (ret == 0)
-               ret = -EINVAL;
-       else
-               ret = extcon_set_state(edev, state);
-
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-static DEVICE_ATTR_RW(state);
+static DEVICE_ATTR_RO(state);
 
 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
                char *buf)
@@ -212,7 +398,7 @@ static ssize_t cable_name_show(struct device *dev,
        int i = cable->cable_index;
 
        return sprintf(buf, "%s\n",
-                       extcon_name[cable->edev->supported_cable[i]]);
+                       extcon_info[cable->edev->supported_cable[i]].name);
 }
 
 static ssize_t cable_state_show(struct device *dev,
@@ -224,26 +410,17 @@ static ssize_t cable_state_show(struct device *dev,
        int i = cable->cable_index;
 
        return sprintf(buf, "%d\n",
-                      extcon_get_cable_state_(cable->edev,
-                                              cable->edev->supported_cable[i]));
+               extcon_get_state(cable->edev, cable->edev->supported_cable[i]));
 }
 
 /**
- * extcon_update_state() - Update the cable attach states of the extcon device
- *                        only for the masked bits.
- * @edev:      the extcon device
- * @mask:      the bit mask to designate updated bits.
- * @state:     new cable attach status for @edev
- *
- * Changing the state sends uevent with environment variable containing
- * the name of extcon device (envp[0]) and the state output (envp[1]).
- * Tizen uses this format for extcon device to get events from ports.
- * Android uses this format as well.
+ * extcon_sync()       - Synchronize the states for both the attached/detached
+ * @edev:              the extcon device that has the cable.
  *
- * Note that the notifier provides which bits are changed in the state
- * variable with the val parameter (second) to the callback.
+ * This function send a notification to synchronize the all states of a
+ * specific external connector
  */
-int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
+int extcon_sync(struct extcon_dev *edev, unsigned int id)
 {
        char name_buf[120];
        char state_buf[120];
@@ -252,100 +429,102 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
        int env_offset = 0;
        int length;
        int index;
+       int state;
        unsigned long flags;
-       bool attached;
 
        if (!edev)
                return -EINVAL;
 
-       spin_lock_irqsave(&edev->lock, flags);
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
 
-       if (edev->state != ((edev->state & ~mask) | (state & mask))) {
-               u32 old_state;
+       spin_lock_irqsave(&edev->lock, flags);
 
-               if (check_mutually_exclusive(edev, (edev->state & ~mask) |
-                                                  (state & mask))) {
-                       spin_unlock_irqrestore(&edev->lock, flags);
-                       return -EPERM;
-               }
+       state = !!(edev->state & BIT(index));
+       raw_notifier_call_chain(&edev->nh[index], state, edev);
 
-               old_state = edev->state;
-               edev->state &= ~mask;
-               edev->state |= state & mask;
+       /* This could be in interrupt handler */
+       prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
+       if (!prop_buf) {
+               /* Unlock early before uevent */
+               spin_unlock_irqrestore(&edev->lock, flags);
 
-               for (index = 0; index < edev->max_supported; index++) {
-                       if (is_extcon_changed(old_state, edev->state, index,
-                                             &attached))
-                               raw_notifier_call_chain(&edev->nh[index],
-                                                       attached, edev);
-               }
+               dev_err(&edev->dev, "out of memory in extcon_set_state\n");
+               kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
 
-               /* This could be in interrupt handler */
-               prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
-               if (prop_buf) {
-                       length = name_show(&edev->dev, NULL, prop_buf);
-                       if (length > 0) {
-                               if (prop_buf[length - 1] == '\n')
-                                       prop_buf[length - 1] = 0;
-                               snprintf(name_buf, sizeof(name_buf),
-                                       "NAME=%s", prop_buf);
-                               envp[env_offset++] = name_buf;
-                       }
-                       length = state_show(&edev->dev, NULL, prop_buf);
-                       if (length > 0) {
-                               if (prop_buf[length - 1] == '\n')
-                                       prop_buf[length - 1] = 0;
-                               snprintf(state_buf, sizeof(state_buf),
-                                       "STATE=%s", prop_buf);
-                               envp[env_offset++] = state_buf;
-                       }
-                       envp[env_offset] = NULL;
-                       /* Unlock early before uevent */
-                       spin_unlock_irqrestore(&edev->lock, flags);
+               return 0;
+       }
 
-                       kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
-                       free_page((unsigned long)prop_buf);
-               } else {
-                       /* Unlock early before uevent */
-                       spin_unlock_irqrestore(&edev->lock, flags);
+       length = name_show(&edev->dev, NULL, prop_buf);
+       if (length > 0) {
+               if (prop_buf[length - 1] == '\n')
+                       prop_buf[length - 1] = 0;
+               snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf);
+               envp[env_offset++] = name_buf;
+       }
 
-                       dev_err(&edev->dev, "out of memory in extcon_set_state\n");
-                       kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
-               }
-       } else {
-               /* No changes */
-               spin_unlock_irqrestore(&edev->lock, flags);
+       length = state_show(&edev->dev, NULL, prop_buf);
+       if (length > 0) {
+               if (prop_buf[length - 1] == '\n')
+                       prop_buf[length - 1] = 0;
+               snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf);
+               envp[env_offset++] = state_buf;
        }
+       envp[env_offset] = NULL;
+
+       /* Unlock early before uevent */
+       spin_unlock_irqrestore(&edev->lock, flags);
+       kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
+       free_page((unsigned long)prop_buf);
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(extcon_update_state);
+EXPORT_SYMBOL_GPL(extcon_sync);
 
 /**
- * extcon_set_state() - Set the cable attach states of the extcon device.
- * @edev:      the extcon device
- * @state:     new cable attach status for @edev
- *
- * Note that notifier provides which bits are changed in the state
- * variable with the val parameter (second) to the callback.
+ * extcon_get_state() - Get the state of a external connector.
+ * @edev:      the extcon device that has the cable.
+ * @id:                the unique id of each external connector in extcon enumeration.
  */
-int extcon_set_state(struct extcon_dev *edev, u32 state)
+int extcon_get_state(struct extcon_dev *edev, const unsigned int id)
 {
+       int index, state;
+       unsigned long flags;
+
        if (!edev)
                return -EINVAL;
 
-       return extcon_update_state(edev, 0xffffffff, state);
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       spin_lock_irqsave(&edev->lock, flags);
+       state = is_extcon_attached(edev, index);
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return state;
 }
-EXPORT_SYMBOL_GPL(extcon_set_state);
+EXPORT_SYMBOL_GPL(extcon_get_state);
 
 /**
- * extcon_get_cable_state_() - Get the status of a specific cable.
- * @edev:      the extcon device that has the cable.
- * @id:                the unique id of each external connector in extcon enumeration.
+ * extcon_set_state() - Set the state of a external connector.
+ *                     without a notification.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @state:             the new cable status. The default semantics is
+ *                     true: attached / false: detached.
+ *
+ * This function only set the state of a external connector without
+ * a notification. To synchronize the data of a external connector,
+ * use extcon_set_state_sync() and extcon_sync().
  */
-int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
+int extcon_set_state(struct extcon_dev *edev, unsigned int id,
+                               bool cable_state)
 {
-       int index;
+       unsigned long flags;
+       int index, ret = 0;
 
        if (!edev)
                return -EINVAL;
@@ -354,41 +533,338 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
        if (index < 0)
                return index;
 
-       if (edev->max_supported && edev->max_supported <= index)
-               return -EINVAL;
+       spin_lock_irqsave(&edev->lock, flags);
+
+       /* Check whether the external connector's state is changed. */
+       if (!is_extcon_changed(edev, index, cable_state))
+               goto out;
+
+       if (check_mutually_exclusive(edev,
+               (edev->state & ~BIT(index)) | (cable_state & BIT(index)))) {
+               ret = -EPERM;
+               goto out;
+       }
+
+       /*
+        * Initialize the value of extcon property before setting
+        * the detached state for an external connector.
+        */
+       if (!cable_state)
+               init_property(edev, id, index);
+
+       /* Update the state for a external connector. */
+       if (cable_state)
+               edev->state |= BIT(index);
+       else
+               edev->state &= ~(BIT(index));
+out:
+       spin_unlock_irqrestore(&edev->lock, flags);
 
-       return !!(edev->state & (1 << index));
+       return ret;
 }
-EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
+EXPORT_SYMBOL_GPL(extcon_set_state);
 
 /**
- * extcon_set_cable_state_() - Set the status of a specific cable.
+ * extcon_set_state_sync() - Set the state of a external connector
+ *                     with a notification.
  * @edev:              the extcon device that has the cable.
  * @id:                        the unique id of each external connector
  *                     in extcon enumeration.
  * @state:             the new cable status. The default semantics is
  *                     true: attached / false: detached.
+ *
+ * This function set the state of external connector and synchronize the data
+ * by usning a notification.
  */
-int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
+int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
                                bool cable_state)
 {
-       u32 state;
+       int ret, index;
+       unsigned long flags;
+
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       /* Check whether the external connector's state is changed. */
+       spin_lock_irqsave(&edev->lock, flags);
+       ret = is_extcon_changed(edev, index, cable_state);
+       spin_unlock_irqrestore(&edev->lock, flags);
+       if (!ret)
+               return 0;
+
+       ret = extcon_set_state(edev, id, cable_state);
+       if (ret < 0)
+               return ret;
+
+       return extcon_sync(edev, id);
+}
+EXPORT_SYMBOL_GPL(extcon_set_state_sync);
+
+/**
+ * extcon_get_property() - Get the property value of a specific cable.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ * @prop_val:          the pointer which store the value of property.
+ *
+ * When getting the property value of external connector, the external connector
+ * should be attached. If detached state, function just return 0 without
+ * property value. Also, the each property should be included in the list of
+ * supported properties according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_get_property(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value *prop_val)
+{
+       struct extcon_cable *cable;
+       unsigned long flags;
+       int index, ret = 0;
+
+       *prop_val = (union extcon_property_value)(0);
+
+       if (!edev)
+               return -EINVAL;
+
+       /* Check whether the property is supported or not */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id */
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       spin_lock_irqsave(&edev->lock, flags);
+
+       /* Check whether the property is available or not. */
+       if (!is_extcon_property_capability(edev, id, index, prop)) {
+               spin_unlock_irqrestore(&edev->lock, flags);
+               return -EPERM;
+       }
+
+       /*
+        * Check whether the external connector is attached.
+        * If external connector is detached, the user can not
+        * get the property value.
+        */
+       if (!is_extcon_attached(edev, index)) {
+               spin_unlock_irqrestore(&edev->lock, flags);
+               return 0;
+       }
+
+       cable = &edev->cables[index];
+
+       /* Get the property value according to extcon type */
+       switch (prop) {
+       case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+               *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
+               break;
+       case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+               *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
+               break;
+       case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+               *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
+               break;
+       case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+               *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_get_property);
+
+/**
+ * extcon_set_property() - Set the property value of a specific cable.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ * @prop_val:          the pointer including the new value of property.
+ *
+ * The each property should be included in the list of supported properties
+ * according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value prop_val)
+{
+       struct extcon_cable *cable;
+       unsigned long flags;
+       int index, ret = 0;
+
+       if (!edev)
+               return -EINVAL;
+
+       /* Check whether the property is supported or not */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id */
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       spin_lock_irqsave(&edev->lock, flags);
+
+       /* Check whether the property is available or not. */
+       if (!is_extcon_property_capability(edev, id, index, prop)) {
+               spin_unlock_irqrestore(&edev->lock, flags);
+               return -EPERM;
+       }
+
+       cable = &edev->cables[index];
+
+       /* Set the property value according to extcon type */
+       switch (prop) {
+       case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+               cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
+               break;
+       case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+               cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
+               break;
+       case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+               cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
+               break;
+       case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+               cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_set_property);
+
+/**
+ * extcon_set_property_sync() - Set the property value of a specific cable
+                       with a notification.
+ * @prop_val:          the pointer including the new value of property.
+ *
+ * When setting the property value of external connector, the external connector
+ * should be attached. The each property should be included in the list of
+ * supported properties according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value prop_val)
+{
+       int ret;
+
+       ret = extcon_set_property(edev, id, prop, prop_val);
+       if (ret < 0)
+               return ret;
+
+       return extcon_sync(edev, id);
+}
+EXPORT_SYMBOL_GPL(extcon_set_property_sync);
+
+/**
+ * extcon_get_property_capability() - Get the capability of property
+ *                     of an external connector.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ *
+ * Returns 1 if the property is available or 0 if not available.
+ */
+int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop)
+{
        int index;
 
        if (!edev)
                return -EINVAL;
 
+       /* Check whether the property is supported or not */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id */
        index = find_cable_index_by_id(edev, id);
        if (index < 0)
                return index;
 
-       if (edev->max_supported && edev->max_supported <= index)
+       return is_extcon_property_capability(edev, id, index, prop);
+}
+EXPORT_SYMBOL_GPL(extcon_get_property_capability);
+
+/**
+ * extcon_set_property_capability() - Set the capability of a property
+ *                     of an external connector.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ *
+ * This function set the capability of a property for an external connector
+ * to mark the bit in capability bitmap which mean the available state of
+ * a property.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop)
+{
+       struct extcon_cable *cable;
+       int index, type, ret = 0;
+
+       if (!edev)
                return -EINVAL;
 
-       state = cable_state ? (1 << index) : 0;
-       return extcon_update_state(edev, 1 << index, state);
+       /* Check whether the property is supported or not. */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id. */
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       type = get_extcon_type(prop);
+       if (type < 0)
+               return type;
+
+       cable = &edev->cables[index];
+
+       switch (type) {
+       case EXTCON_TYPE_USB:
+               __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
+               break;
+       case EXTCON_TYPE_CHG:
+               __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
+               break;
+       case EXTCON_TYPE_JACK:
+               __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
+               break;
+       case EXTCON_TYPE_DISP:
+               __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
 }
-EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
+EXPORT_SYMBOL_GPL(extcon_set_property_capability);
 
 /**
  * extcon_get_extcon_dev() - Get the extcon device instance from the name
@@ -428,7 +904,7 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
                             struct notifier_block *nb)
 {
        unsigned long flags;
-       int ret, idx;
+       int ret, idx = -EINVAL;
 
        if (!nb)
                return -EINVAL;
@@ -846,13 +1322,13 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
                return ERR_PTR(-EINVAL);
 
        if (!dev->of_node) {
-               dev_err(dev, "device does not have a device node entry\n");
+               dev_dbg(dev, "device does not have a device node entry\n");
                return ERR_PTR(-EINVAL);
        }
 
        node = of_parse_phandle(dev->of_node, "extcon", index);
        if (!node) {
-               dev_err(dev, "failed to get phandle in %s node\n",
+               dev_dbg(dev, "failed to get phandle in %s node\n",
                        dev->of_node->full_name);
                return ERR_PTR(-ENODEV);
        }
index 61004413dc649594ca17b2d0453678d1b2cc621f..5c35f9d1822c96ef962c6b04e87e1ccbb94387d8 100644 (file)
 
 #include <linux/device.h>
 
+/*
+ * Define the type of supported external connectors
+ */
+#define EXTCON_TYPE_USB                BIT(0)  /* USB connector */
+#define EXTCON_TYPE_CHG                BIT(1)  /* Charger connector */
+#define EXTCON_TYPE_JACK       BIT(2)  /* Jack connector */
+#define EXTCON_TYPE_DISP       BIT(3)  /* Display connector */
+#define EXTCON_TYPE_MISC       BIT(4)  /* Miscellaneous connector */
+
 /*
  * Define the unique id of supported external connectors
  */
@@ -44,6 +53,7 @@
 #define EXTCON_CHG_USB_ACA     8       /* Accessory Charger Adapter */
 #define EXTCON_CHG_USB_FAST    9
 #define EXTCON_CHG_USB_SLOW    10
+#define EXTCON_CHG_WPT         11      /* Wireless Power Transfer */
 
 /* Jack external connector */
 #define EXTCON_JACK_MICROPHONE 20
@@ -60,6 +70,8 @@
 #define EXTCON_DISP_MHL                41      /* Mobile High-Definition Link */
 #define EXTCON_DISP_DVI                42      /* Digital Visual Interface */
 #define EXTCON_DISP_VGA                43      /* Video Graphics Array */
+#define EXTCON_DISP_DP         44      /* Display Port */
+#define EXTCON_DISP_HMD                45      /* Head-Mounted Display */
 
 /* Miscellaneous external connector */
 #define EXTCON_DOCK            60
 
 #define EXTCON_NUM             63
 
+/*
+ * Define the property of supported external connectors.
+ *
+ * When adding the new extcon property, they *must* have
+ * the type/value/default information. Also, you *have to*
+ * modify the EXTCON_PROP_[type]_START/END definitions
+ * which mean the range of the supported properties
+ * for each extcon type.
+ *
+ * The naming style of property
+ * : EXTCON_PROP_[type]_[property name]
+ *
+ * EXTCON_PROP_USB_[property name]     : USB property
+ * EXTCON_PROP_CHG_[property name]     : Charger property
+ * EXTCON_PROP_JACK_[property name]    : Jack property
+ * EXTCON_PROP_DISP_[property name]    : Display property
+ */
+
+/*
+ * Properties of EXTCON_TYPE_USB.
+ *
+ * - EXTCON_PROP_USB_VBUS
+ * @type:      integer (intval)
+ * @value:     0 (low) or 1 (high)
+ * @default:   0 (low)
+ * - EXTCON_PROP_USB_TYPEC_POLARITY
+ * @type:      integer (intval)
+ * @value:     0 (normal) or 1 (flip)
+ * @default:   0 (normal)
+ * - EXTCON_PROP_USB_SS (SuperSpeed)
+ * @type:       integer (intval)
+ * @value:      0 (USB/USB2) or 1 (USB3)
+ * @default:    0 (USB/USB2)
+ *
+ */
+#define EXTCON_PROP_USB_VBUS           0
+#define EXTCON_PROP_USB_TYPEC_POLARITY 1
+#define EXTCON_PROP_USB_SS             2
+
+#define EXTCON_PROP_USB_MIN            0
+#define EXTCON_PROP_USB_MAX            2
+#define EXTCON_PROP_USB_CNT    (EXTCON_PROP_USB_MAX - EXTCON_PROP_USB_MIN + 1)
+
+/* Properties of EXTCON_TYPE_CHG. */
+#define EXTCON_PROP_CHG_MIN            50
+#define EXTCON_PROP_CHG_MAX            50
+#define EXTCON_PROP_CHG_CNT    (EXTCON_PROP_CHG_MAX - EXTCON_PROP_CHG_MIN + 1)
+
+/* Properties of EXTCON_TYPE_JACK. */
+#define EXTCON_PROP_JACK_MIN           100
+#define EXTCON_PROP_JACK_MAX           100
+#define EXTCON_PROP_JACK_CNT (EXTCON_PROP_JACK_MAX - EXTCON_PROP_JACK_MIN + 1)
+
+/*
+ * Properties of EXTCON_TYPE_DISP.
+ *
+ * - EXTCON_PROP_DISP_HPD (Hot Plug Detect)
+ * @type:       integer (intval)
+ * @value:      0 (no hpd) or 1 (hpd)
+ * @default:    0 (no hpd)
+ *
+ */
+#define EXTCON_PROP_DISP_HPD           150
+
+/* Properties of EXTCON_TYPE_DISP. */
+#define EXTCON_PROP_DISP_MIN           150
+#define EXTCON_PROP_DISP_MAX           151
+#define EXTCON_PROP_DISP_CNT (EXTCON_PROP_DISP_MAX - EXTCON_PROP_DISP_MIN + 1)
+
+/*
+ * Define the type of property's value.
+ *
+ * Define the property's value as union type. Because each property
+ * would need the different data type to store it.
+ */
+union extcon_property_value {
+       int intval;     /* type : integer (intval) */
+};
+
 struct extcon_cable;
 
 /**
@@ -150,26 +241,43 @@ extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
 extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev);
 
 /*
- * get/set/update_state access the 32b encoded state value, which represents
- * states of all possible cables of the multistate port. For example, if one
- * calls extcon_set_state(edev, 0x7), it may mean that all the three cables
- * are attached to the port.
+ * get/set_state access each bit of the 32b encoded state value.
+ * They are used to access the status of each cable based on the cable id.
  */
-static inline u32 extcon_get_state(struct extcon_dev *edev)
-{
-       return edev->state;
-}
+extern int extcon_get_state(struct extcon_dev *edev, unsigned int id);
+extern int extcon_set_state(struct extcon_dev *edev, unsigned int id,
+                                  bool cable_state);
+extern int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
+                               bool cable_state);
 
-extern int extcon_set_state(struct extcon_dev *edev, u32 state);
-extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
+/*
+ * Synchronize the state and property data for a specific external connector.
+ */
+extern int extcon_sync(struct extcon_dev *edev, unsigned int id);
 
 /*
- * get/set_cable_state access each bit of the 32b encoded state value.
- * They are used to access the status of each cable based on the cable id.
+ * get/set_property access the property value of each external connector.
+ * They are used to access the property of each cable based on the property id.
  */
-extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
-extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
-                                  bool cable_state);
+extern int extcon_get_property(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value *prop_val);
+extern int extcon_set_property(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value prop_val);
+extern int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value prop_val);
+
+/*
+ * get/set_property_capability set the capability of the property for each
+ * external connector. They are used to set the capability of the property
+ * of each external connector based on the id and property.
+ */
+extern int extcon_get_property_capability(struct extcon_dev *edev,
+                               unsigned int id, unsigned int prop);
+extern int extcon_set_property_capability(struct extcon_dev *edev,
+                               unsigned int id, unsigned int prop);
 
 /*
  * Following APIs are to monitor every action of a notifier.
@@ -232,30 +340,57 @@ static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
 
 static inline void devm_extcon_dev_free(struct extcon_dev *edev) { }
 
-static inline u32 extcon_get_state(struct extcon_dev *edev)
+
+static inline int extcon_get_state(struct extcon_dev *edev, unsigned int id)
+{
+       return 0;
+}
+
+static inline int extcon_set_state(struct extcon_dev *edev, unsigned int id,
+                               bool cable_state)
+{
+       return 0;
+}
+
+static inline int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
+                               bool cable_state)
 {
        return 0;
 }
 
-static inline int extcon_set_state(struct extcon_dev *edev, u32 state)
+static inline int extcon_sync(struct extcon_dev *edev, unsigned int id)
 {
        return 0;
 }
 
-static inline int extcon_update_state(struct extcon_dev *edev, u32 mask,
-                                      u32 state)
+static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop,
+                                       union extcon_property_value *prop_val)
+{
+       return 0;
+}
+static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop,
+                                       union extcon_property_value prop_val)
 {
        return 0;
 }
 
-static inline int extcon_get_cable_state_(struct extcon_dev *edev,
-                                         unsigned int id)
+static inline int extcon_set_property_sync(struct extcon_dev *edev,
+                                       unsigned int id, unsigned int prop,
+                                       union extcon_property_value prop_val)
 {
        return 0;
 }
 
-static inline int extcon_set_cable_state_(struct extcon_dev *edev,
-                                         unsigned int id, bool cable_state)
+static inline int extcon_get_property_capability(struct extcon_dev *edev,
+                                       unsigned int id, unsigned int prop)
+{
+       return 0;
+}
+
+static inline int extcon_set_property_capability(struct extcon_dev *edev,
+                                       unsigned int id, unsigned int prop)
 {
        return 0;
 }
@@ -320,4 +455,15 @@ static inline int extcon_unregister_interest(struct extcon_specific_cable_nb
 {
        return -EINVAL;
 }
+
+static inline int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id)
+{
+       return extcon_get_state(edev, id);
+}
+
+static inline int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
+                                  bool cable_state)
+{
+       return extcon_set_state_sync(edev, id, cable_state);
+}
 #endif /* __LINUX_EXTCON_H__ */
index ac85f2061351996e871da73a26fd0dcf81293bad..a0e03b13b449dbaa721abae03977470e571afc39 100644 (file)
@@ -20,8 +20,8 @@
 
 /**
  * struct adc_jack_cond - condition to use an extcon state
- * @state:             the corresponding extcon state (if 0, this struct
  *                     denotes the last adc_jack_cond element among the array)
+ * @id:                        the unique id of each external connector
  * @min_adc:           min adc value for this condition
  * @max_adc:           max adc value for this condition
  *
@@ -33,7 +33,7 @@
  * because when no adc_jack_cond is met, state = 0 is automatically chosen.
  */
 struct adc_jack_cond {
-       u32 state;      /* extcon state value. 0 if invalid */
+       unsigned int id;
        u32 min_adc;
        u32 max_adc;
 };
This page took 0.078491 seconds and 5 git commands to generate.