From 366380cd62f4fce82d970e9a510d0510b4dea5ee Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 21 Mar 2017 20:37:47 +0200 Subject: [PATCH 01/11] Revert "extcon: usb-gpio: add support for ACPI gpio interface" The commit 942c7924a51e introduced a check for ACPI handle for the device that never appears on any ACPI-enabled platform so far. It seems a confusion with extcon-intel-int3496 which does support ACPI-enabled platforms. Revert commit 942c7924a51e to avoid any confusion in the future. Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usb-gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index a5e1882b4ca66..a0d7de8675e05 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #define USB_GPIO_DEBOUNCE_MS 20 /* ms */ @@ -111,7 +110,7 @@ static int usb_extcon_probe(struct platform_device *pdev) struct usb_extcon_info *info; int ret; - if (!np && !ACPI_HANDLE(dev)) + if (!np) return -EINVAL; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); From 815429b39d94c64f6d05eed9e7c1a9bdfdd5bd70 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 29 Mar 2017 19:30:17 +0900 Subject: [PATCH 02/11] extcon: Add new extcon_register_notifier_all() to monitor all external connectors The extcon core already provides the extcon_register_notifier() function in order to register the notifier block which is used to monitor the state change for the specific external connector such as EXTCON_USB, EXTCON_USB_HOST and so on. The extcon consumer uses the this function. The extcon consumer might need to monitor the all supported external connectors from the extcon device. In this case, The extcon consumer should have each notifier_block structure for each external connector. This patch adds the new extcon_register_notifier_all() function that extcon consumer is able to monitor the state change of all supported external connectors by using only one notifier_block structure. - List of new added functions: int extcon_register_notifier_all(struct extcon_dev *edev, struct notifier_block *nb); int extcon_unregister_notifier_all(struct extcon_dev *edev, struct notifier_block *nb); int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev, struct notifier_block *nb); void devm_extcon_unregister_notifier_all(struct device *dev, struct extcon_dev *edev, struct notifier_block *nb); Suggested-by: Hans de Goede Signed-off-by: Chanwoo Choi Tested-by: Hans de Goede Acked-by: Hans de Goede --- drivers/extcon/devres.c | 61 +++++++++++++++++++++++++++++++++++++ drivers/extcon/extcon.c | 66 +++++++++++++++++++++++++++++++++++++++++ drivers/extcon/extcon.h | 3 ++ include/linux/extcon.h | 21 +++++++++---- 4 files changed, 146 insertions(+), 5 deletions(-) diff --git a/drivers/extcon/devres.c b/drivers/extcon/devres.c index b40eb18059273..186fd735eb286 100644 --- a/drivers/extcon/devres.c +++ b/drivers/extcon/devres.c @@ -50,6 +50,13 @@ static void devm_extcon_dev_notifier_unreg(struct device *dev, void *res) extcon_unregister_notifier(this->edev, this->id, this->nb); } +static void devm_extcon_dev_notifier_all_unreg(struct device *dev, void *res) +{ + struct extcon_dev_notifier_devres *this = res; + + extcon_unregister_notifier_all(this->edev, this->nb); +} + /** * devm_extcon_dev_allocate - Allocate managed extcon device * @dev: device owning the extcon device being created @@ -214,3 +221,57 @@ void devm_extcon_unregister_notifier(struct device *dev, devm_extcon_dev_match, edev)); } EXPORT_SYMBOL(devm_extcon_unregister_notifier); + +/** + * devm_extcon_register_notifier_all() + * - Resource-managed extcon_register_notifier_all() + * @dev: device to allocate extcon device + * @edev: the extcon device that has the external connecotr. + * @nb: a notifier block to be registered. + * + * This function manages automatically the notifier of extcon device using + * device resource management and simplify the control of unregistering + * the notifier of extcon device. To get more information, refer that function. + * + * Returns 0 if success or negaive error number if failure. + */ +int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev, + struct notifier_block *nb) +{ + struct extcon_dev_notifier_devres *ptr; + int ret; + + ptr = devres_alloc(devm_extcon_dev_notifier_all_unreg, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = extcon_register_notifier_all(edev, nb); + if (ret) { + devres_free(ptr); + return ret; + } + + ptr->edev = edev; + ptr->nb = nb; + devres_add(dev, ptr); + + return 0; +} +EXPORT_SYMBOL(devm_extcon_register_notifier_all); + +/** + * devm_extcon_unregister_notifier_all() + * - Resource-managed extcon_unregister_notifier_all() + * @dev: device to allocate extcon device + * @edev: the extcon device that has the external connecotr. + * @nb: a notifier block to be registered. + */ +void devm_extcon_unregister_notifier_all(struct device *dev, + struct extcon_dev *edev, + struct notifier_block *nb) +{ + WARN_ON(devres_release(dev, devm_extcon_dev_notifier_all_unreg, + devm_extcon_dev_match, edev)); +} +EXPORT_SYMBOL(devm_extcon_unregister_notifier_all); diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 09ac5e70c2f38..e7750545469f8 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -448,8 +448,19 @@ int extcon_sync(struct extcon_dev *edev, unsigned int id) spin_lock_irqsave(&edev->lock, flags); state = !!(edev->state & BIT(index)); + + /* + * Call functions in a raw notifier chain for the specific one + * external connector. + */ raw_notifier_call_chain(&edev->nh[index], state, edev); + /* + * Call functions in a raw notifier chain for the all supported + * external connectors. + */ + raw_notifier_call_chain(&edev->nh_all, state, edev); + /* This could be in interrupt handler */ prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); if (!prop_buf) { @@ -954,6 +965,59 @@ int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, } EXPORT_SYMBOL_GPL(extcon_unregister_notifier); +/** + * extcon_register_notifier_all() - Register a notifier block for all connectors + * @edev: the extcon device that has the external connecotr. + * @nb: a notifier block to be registered. + * + * This fucntion registers a notifier block in order to receive the state + * change of all supported external connectors from extcon device. + * And The second parameter given to the callback of nb (val) is + * the current state and third parameter is the edev pointer. + * + * Returns 0 if success or error number if fail + */ +int extcon_register_notifier_all(struct extcon_dev *edev, + struct notifier_block *nb) +{ + unsigned long flags; + int ret; + + if (!edev || !nb) + return -EINVAL; + + spin_lock_irqsave(&edev->lock, flags); + ret = raw_notifier_chain_register(&edev->nh_all, nb); + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(extcon_register_notifier_all); + +/** + * extcon_unregister_notifier_all() - Unregister a notifier block from extcon. + * @edev: the extcon device that has the external connecotr. + * @nb: a notifier block to be registered. + * + * Returns 0 if success or error number if fail + */ +int extcon_unregister_notifier_all(struct extcon_dev *edev, + struct notifier_block *nb) +{ + unsigned long flags; + int ret; + + if (!edev || !nb) + return -EINVAL; + + spin_lock_irqsave(&edev->lock, flags); + ret = raw_notifier_chain_unregister(&edev->nh_all, nb); + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all); + static struct attribute *extcon_attrs[] = { &dev_attr_state.attr, &dev_attr_name.attr, @@ -1212,6 +1276,8 @@ int extcon_dev_register(struct extcon_dev *edev) for (index = 0; index < edev->max_supported; index++) RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]); + RAW_INIT_NOTIFIER_HEAD(&edev->nh_all); + dev_set_drvdata(&edev->dev, edev); edev->state = 0; diff --git a/drivers/extcon/extcon.h b/drivers/extcon/extcon.h index 993ddccafe113..dddddcfa05873 100644 --- a/drivers/extcon/extcon.h +++ b/drivers/extcon/extcon.h @@ -21,6 +21,8 @@ * @dev: Device of this extcon. * @state: Attach/detach state of this extcon. Do not provide at * register-time. + * @nh_all: Notifier for the state change events for all supported + * external connectors from this extcon. * @nh: Notifier for the state change events from this extcon * @entry: To support list of extcon devices so that users can * search for extcon devices based on the extcon name. @@ -43,6 +45,7 @@ struct extcon_dev { /* Internal data. Please do not set. */ struct device dev; + struct raw_notifier_head nh_all; struct raw_notifier_head *nh; struct list_head entry; int max_supported; diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 7010fb01a81a3..7e206a9f88db8 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -236,11 +236,11 @@ 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. - * Registrar gets notified for every external port of a connection device. - * Probably this could be used to debug an action of notifier; however, - * we do not recommend to use this for normal 'notifiee' device drivers who - * want to be notified by a specific external port of the notifier. + * Following APIs are to monitor the status change of the external connectors. + * extcon_register_notifier(*edev, id, *nb) : Register a notifier block + * for specific external connector of the extcon. + * extcon_register_notifier_all(*edev, *nb) : Register a notifier block + * for all supported external connectors of the extcon. */ extern int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); @@ -253,6 +253,17 @@ extern void devm_extcon_unregister_notifier(struct device *dev, struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); +extern int extcon_register_notifier_all(struct extcon_dev *edev, + struct notifier_block *nb); +extern int extcon_unregister_notifier_all(struct extcon_dev *edev, + struct notifier_block *nb); +extern int devm_extcon_register_notifier_all(struct device *dev, + struct extcon_dev *edev, + struct notifier_block *nb); +extern void devm_extcon_unregister_notifier_all(struct device *dev, + struct extcon_dev *edev, + struct notifier_block *nb); + /* * Following API get the extcon device from devicetree. * This function use phandle of devicetree to get extcon device directly. From e57bb43c7e346943d73b44d05485aac68d8aff9d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 25 Jan 2017 09:34:06 +0000 Subject: [PATCH 03/11] extcon: arizona: Wait for any running HPDETs to complete on jack removal As the HPDET can't be aborted mid way through we should not allow any new insertion to be processed until the previous HPDET has finished. It is very unlikely but with low enough debounce settings you could start a new HPDET before the old one has completed, which results in an erroneous reading. Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-arizona.c | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index ed78b7c26627e..e2d78cd7030d3 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -51,6 +51,9 @@ #define HPDET_DEBOUNCE 500 #define DEFAULT_MICD_TIMEOUT 2000 +#define ARIZONA_HPDET_WAIT_COUNT 15 +#define ARIZONA_HPDET_WAIT_DELAY_MS 20 + #define QUICK_HEADPHONE_MAX_OHM 3 #define MICROPHONE_MIN_OHM 1257 #define MICROPHONE_MAX_OHM 30000 @@ -1049,6 +1052,40 @@ static void arizona_hpdet_work(struct work_struct *work) mutex_unlock(&info->lock); } +static int arizona_hpdet_wait(struct arizona_extcon_info *info) +{ + struct arizona *arizona = info->arizona; + unsigned int val; + int i, ret; + + for (i = 0; i < ARIZONA_HPDET_WAIT_COUNT; i++) { + ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, + &val); + if (ret) { + dev_err(arizona->dev, + "Failed to read HPDET state: %d\n", ret); + return ret; + } + + switch (info->hpdet_ip_version) { + case 0: + if (val & ARIZONA_HP_DONE) + return 0; + break; + default: + if (val & ARIZONA_HP_DONE_B) + return 0; + break; + } + + msleep(ARIZONA_HPDET_WAIT_DELAY_MS); + } + + dev_warn(arizona->dev, "HPDET did not appear to complete\n"); + + return -ETIMEDOUT; +} + static irqreturn_t arizona_jackdet(int irq, void *data) { struct arizona_extcon_info *info = data; @@ -1155,6 +1192,15 @@ static irqreturn_t arizona_jackdet(int irq, void *data) "Removal report failed: %d\n", ret); } + /* + * If the jack was removed during a headphone detection we + * need to wait for the headphone detection to finish, as + * it can not be aborted. We don't want to be able to start + * a new headphone detection from a fresh insert until this + * one is finished. + */ + arizona_hpdet_wait(info); + regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE, ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, From 98fd079297dd274c15c926a337253675573c5832 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 13 Feb 2017 17:21:52 +0800 Subject: [PATCH 04/11] extcon: usb-gpio: Do not enable USB as wakeup source by default Whether the USB port as a wakeup source should be determined by user, but not enabled by default. Signed-off-by: Peter Chen Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usb-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index a0d7de8675e05..0ac32dfdda505 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -194,7 +194,7 @@ static int usb_extcon_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, info); - device_init_wakeup(dev, true); + device_set_wakeup_capable(&pdev->dev, true); /* Perform initial detection */ usb_extcon_detect_cable(&info->wq_detcable.work); From 8680b4d1933fbe3349d51a4e1fd4513b12abffed Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 15 Feb 2017 14:31:28 +0200 Subject: [PATCH 05/11] extcon: usb-gpio: Don't miss event during suspend/resume We must check for ID/VBUS changes during resume irrespective of whether our device wakeup is enabled or not. Without this we seem to be missing ID/VBUS events after system suspend/resume. Signed-off-by: Roger Quadros Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usb-gpio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 0ac32dfdda505..9c925b05b7aaa 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -281,9 +281,8 @@ static int usb_extcon_resume(struct device *dev) if (info->vbus_gpiod) enable_irq(info->vbus_irq); - if (!device_may_wakeup(dev)) - queue_delayed_work(system_power_efficient_wq, - &info->wq_detcable, 0); + queue_delayed_work(system_power_efficient_wq, + &info->wq_detcable, 0); return ret; } From 01944321de3021020886564328684b7603ee216c Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 15 Feb 2017 14:31:29 +0200 Subject: [PATCH 06/11] extcon: palmas: Don't miss GPIO events during suspend/resume The USB cable state can change during suspend/resume so be sure to check and update the extcon state. Signed-off-by: Roger Quadros Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-palmas.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index ca904e8b32351..2af4369a2d733 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -413,6 +413,12 @@ static int palmas_usb_resume(struct device *dev) if (palmas_usb->enable_gpio_id_detection) disable_irq_wake(palmas_usb->gpio_id_irq); } + + /* check if GPIO states changed while suspend/resume */ + if (palmas_usb->enable_gpio_vbus_detection) + palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb); + palmas_gpio_id_detect(&palmas_usb->wq_detectid.work); + return 0; }; #endif From db0f3baaa38bb587d831b1127082643b5e813074 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 23 Mar 2017 17:01:42 +0100 Subject: [PATCH 07/11] extcon: intel-cht-wc: Add Intel Cherry Trail Whiskey Cove PMIC extcon driver Add a driver for charger detection / control on the Intel Cherrytrail Whiskey Cove PMIC. Signed-off-by: Hans de Goede Signed-off-by: Chanwoo Choi --- drivers/extcon/Kconfig | 7 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-intel-cht-wc.c | 353 +++++++++++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 drivers/extcon/extcon-intel-cht-wc.c diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index fc09c76248b41..32f2dc8e4702c 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -52,6 +52,13 @@ config EXTCON_INTEL_INT3496 This ACPI device is typically found on Intel Baytrail or Cherrytrail based tablets, or other Baytrail / Cherrytrail devices. +config EXTCON_INTEL_CHT_WC + tristate "Intel Cherrytrail Whiskey Cove PMIC extcon driver" + depends on INTEL_SOC_PMIC_CHTWC + help + Say Y here to enable extcon support for charger detection / control + on the Intel Cherrytrail Whiskey Cove PMIC. + config EXTCON_MAX14577 tristate "Maxim MAX14577/77836 EXTCON Support" depends on MFD_MAX14577 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 237ac3f953c2b..ecfa95804427e 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o +obj-$(CONFIG_EXTCON_INTEL_CHT_WC) += extcon-intel-cht-wc.o obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c new file mode 100644 index 0000000000000..f1c43af7682b5 --- /dev/null +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -0,0 +1,353 @@ +/* + * Extcon charger detection driver for Intel Cherrytrail Whiskey Cove PMIC + * Copyright (C) 2017 Hans de Goede + * + * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: + * Copyright (C) 2013-2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include +#include +#include + +#define CHT_WC_PHYCTRL 0x5e07 + +#define CHT_WC_CHGRCTRL0 0x5e16 +#define CHT_WC_CHGRCTRL0_CHGRRESET BIT(0) +#define CHT_WC_CHGRCTRL0_EMRGCHREN BIT(1) +#define CHT_WC_CHGRCTRL0_EXTCHRDIS BIT(2) +#define CHT_WC_CHGRCTRL0_SWCONTROL BIT(3) +#define CHT_WC_CHGRCTRL0_TTLCK_MASK BIT(4) +#define CHT_WC_CHGRCTRL0_CCSM_OFF_MASK BIT(5) +#define CHT_WC_CHGRCTRL0_DBPOFF_MASK BIT(6) +#define CHT_WC_CHGRCTRL0_WDT_NOKICK BIT(7) + +#define CHT_WC_CHGRCTRL1 0x5e17 + +#define CHT_WC_USBSRC 0x5e29 +#define CHT_WC_USBSRC_STS_MASK GENMASK(1, 0) +#define CHT_WC_USBSRC_STS_SUCCESS 2 +#define CHT_WC_USBSRC_STS_FAIL 3 +#define CHT_WC_USBSRC_TYPE_SHIFT 2 +#define CHT_WC_USBSRC_TYPE_MASK GENMASK(5, 2) +#define CHT_WC_USBSRC_TYPE_NONE 0 +#define CHT_WC_USBSRC_TYPE_SDP 1 +#define CHT_WC_USBSRC_TYPE_DCP 2 +#define CHT_WC_USBSRC_TYPE_CDP 3 +#define CHT_WC_USBSRC_TYPE_ACA 4 +#define CHT_WC_USBSRC_TYPE_SE1 5 +#define CHT_WC_USBSRC_TYPE_MHL 6 +#define CHT_WC_USBSRC_TYPE_FLOAT_DP_DN 7 +#define CHT_WC_USBSRC_TYPE_OTHER 8 +#define CHT_WC_USBSRC_TYPE_DCP_EXTPHY 9 + +#define CHT_WC_PWRSRC_IRQ 0x6e03 +#define CHT_WC_PWRSRC_IRQ_MASK 0x6e0f +#define CHT_WC_PWRSRC_STS 0x6e1e +#define CHT_WC_PWRSRC_VBUS BIT(0) +#define CHT_WC_PWRSRC_DC BIT(1) +#define CHT_WC_PWRSRC_BAT BIT(2) +#define CHT_WC_PWRSRC_ID_GND BIT(3) +#define CHT_WC_PWRSRC_ID_FLOAT BIT(4) + +enum cht_wc_usb_id { + USB_ID_OTG, + USB_ID_GND, + USB_ID_FLOAT, + USB_RID_A, + USB_RID_B, + USB_RID_C, +}; + +enum cht_wc_mux_select { + MUX_SEL_PMIC = 0, + MUX_SEL_SOC, +}; + +static const unsigned int cht_wc_extcon_cables[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_CHG_USB_SDP, + EXTCON_CHG_USB_CDP, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_ACA, + EXTCON_NONE, +}; + +struct cht_wc_extcon_data { + struct device *dev; + struct regmap *regmap; + struct extcon_dev *edev; + unsigned int previous_cable; +}; + +static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts) +{ + if (pwrsrc_sts & CHT_WC_PWRSRC_ID_GND) + return USB_ID_GND; + if (pwrsrc_sts & CHT_WC_PWRSRC_ID_FLOAT) + return USB_ID_FLOAT; + + /* + * Once we have iio support for the gpadc we should read the USBID + * gpadc channel here and determine ACA role based on that. + */ + return USB_ID_FLOAT; +} + +static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext) +{ + int ret, usbsrc, status; + unsigned long timeout; + + /* Charger detection can take upto 600ms, wait 800ms max. */ + timeout = jiffies + msecs_to_jiffies(800); + do { + ret = regmap_read(ext->regmap, CHT_WC_USBSRC, &usbsrc); + if (ret) { + dev_err(ext->dev, "Error reading usbsrc: %d\n", ret); + return ret; + } + + status = usbsrc & CHT_WC_USBSRC_STS_MASK; + if (status == CHT_WC_USBSRC_STS_SUCCESS || + status == CHT_WC_USBSRC_STS_FAIL) + break; + + msleep(50); /* Wait a bit before retrying */ + } while (time_before(jiffies, timeout)); + + if (status != CHT_WC_USBSRC_STS_SUCCESS) { + if (status == CHT_WC_USBSRC_STS_FAIL) + dev_warn(ext->dev, "Could not detect charger type\n"); + else + dev_warn(ext->dev, "Timeout detecting charger type\n"); + return EXTCON_CHG_USB_SDP; /* Save fallback */ + } + + usbsrc = (usbsrc & CHT_WC_USBSRC_TYPE_MASK) >> CHT_WC_USBSRC_TYPE_SHIFT; + switch (usbsrc) { + default: + dev_warn(ext->dev, + "Unhandled charger type %d, defaulting to SDP\n", + ret); + /* Fall through, treat as SDP */ + case CHT_WC_USBSRC_TYPE_SDP: + case CHT_WC_USBSRC_TYPE_FLOAT_DP_DN: + case CHT_WC_USBSRC_TYPE_OTHER: + return EXTCON_CHG_USB_SDP; + case CHT_WC_USBSRC_TYPE_CDP: + return EXTCON_CHG_USB_CDP; + case CHT_WC_USBSRC_TYPE_DCP: + case CHT_WC_USBSRC_TYPE_DCP_EXTPHY: + case CHT_WC_USBSRC_TYPE_MHL: /* MHL2+ delivers upto 2A, treat as DCP */ + return EXTCON_CHG_USB_DCP; + case CHT_WC_USBSRC_TYPE_ACA: + return EXTCON_CHG_USB_ACA; + } +} + +static void cht_wc_extcon_set_phymux(struct cht_wc_extcon_data *ext, u8 state) +{ + int ret; + + ret = regmap_write(ext->regmap, CHT_WC_PHYCTRL, state); + if (ret) + dev_err(ext->dev, "Error writing phyctrl: %d\n", ret); +} + +/* Small helper to sync EXTCON_CHG_USB_SDP and EXTCON_USB state */ +static void cht_wc_extcon_set_state(struct cht_wc_extcon_data *ext, + unsigned int cable, bool state) +{ + extcon_set_state_sync(ext->edev, cable, state); + if (cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(ext->edev, EXTCON_USB, state); +} + +static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext) +{ + int ret, pwrsrc_sts, id; + unsigned int cable = EXTCON_NONE; + + ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts); + if (ret) { + dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret); + return; + } + + id = cht_wc_extcon_get_id(ext, pwrsrc_sts); + if (id == USB_ID_GND) { + /* The 5v boost causes a false VBUS / SDP detect, skip */ + goto charger_det_done; + } + + /* Plugged into a host/charger or not connected? */ + if (!(pwrsrc_sts & CHT_WC_PWRSRC_VBUS)) { + /* Route D+ and D- to PMIC for future charger detection */ + cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC); + goto set_state; + } + + ret = cht_wc_extcon_get_charger(ext); + if (ret >= 0) + cable = ret; + +charger_det_done: + /* Route D+ and D- to SoC for the host or gadget controller */ + cht_wc_extcon_set_phymux(ext, MUX_SEL_SOC); + +set_state: + if (cable != ext->previous_cable) { + cht_wc_extcon_set_state(ext, cable, true); + cht_wc_extcon_set_state(ext, ext->previous_cable, false); + ext->previous_cable = cable; + } + + extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, + id == USB_ID_GND || id == USB_RID_A); +} + +static irqreturn_t cht_wc_extcon_isr(int irq, void *data) +{ + struct cht_wc_extcon_data *ext = data; + int ret, irqs; + + ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_IRQ, &irqs); + if (ret) { + dev_err(ext->dev, "Error reading irqs: %d\n", ret); + return IRQ_NONE; + } + + cht_wc_extcon_pwrsrc_event(ext); + + ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ, irqs); + if (ret) { + dev_err(ext->dev, "Error writing irqs: %d\n", ret); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable) +{ + int ret, mask, val; + + mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF_MASK; + val = enable ? mask : 0; + ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL0, mask, val); + if (ret) + dev_err(ext->dev, "Error setting sw control: %d\n", ret); + + return ret; +} + +static int cht_wc_extcon_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct cht_wc_extcon_data *ext; + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ext = devm_kzalloc(&pdev->dev, sizeof(*ext), GFP_KERNEL); + if (!ext) + return -ENOMEM; + + ext->dev = &pdev->dev; + ext->regmap = pmic->regmap; + ext->previous_cable = EXTCON_NONE; + + /* Initialize extcon device */ + ext->edev = devm_extcon_dev_allocate(ext->dev, cht_wc_extcon_cables); + if (IS_ERR(ext->edev)) + return PTR_ERR(ext->edev); + + /* Enable sw control */ + ret = cht_wc_extcon_sw_control(ext, true); + if (ret) + return ret; + + /* Register extcon device */ + ret = devm_extcon_dev_register(ext->dev, ext->edev); + if (ret) { + dev_err(ext->dev, "Error registering extcon device: %d\n", ret); + goto disable_sw_control; + } + + /* Route D+ and D- to PMIC for initial charger detection */ + cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC); + + /* Get initial state */ + cht_wc_extcon_pwrsrc_event(ext); + + ret = devm_request_threaded_irq(ext->dev, irq, NULL, cht_wc_extcon_isr, + IRQF_ONESHOT, pdev->name, ext); + if (ret) { + dev_err(ext->dev, "Error requesting interrupt: %d\n", ret); + goto disable_sw_control; + } + + /* Unmask irqs */ + ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, + (int)~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_ID_GND | + CHT_WC_PWRSRC_ID_FLOAT)); + if (ret) { + dev_err(ext->dev, "Error writing irq-mask: %d\n", ret); + goto disable_sw_control; + } + + platform_set_drvdata(pdev, ext); + + return 0; + +disable_sw_control: + cht_wc_extcon_sw_control(ext, false); + return ret; +} + +static int cht_wc_extcon_remove(struct platform_device *pdev) +{ + struct cht_wc_extcon_data *ext = platform_get_drvdata(pdev); + + cht_wc_extcon_sw_control(ext, false); + + return 0; +} + +static const struct platform_device_id cht_wc_extcon_table[] = { + { .name = "cht_wcove_pwrsrc" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, cht_wc_extcon_table); + +static struct platform_driver cht_wc_extcon_driver = { + .probe = cht_wc_extcon_probe, + .remove = cht_wc_extcon_remove, + .id_table = cht_wc_extcon_table, + .driver = { + .name = "cht_wcove_pwrsrc", + }, +}; +module_platform_driver(cht_wc_extcon_driver); + +MODULE_DESCRIPTION("Intel Cherrytrail Whiskey Cove PMIC extcon driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL v2"); From c9d0f1d121cf038afc787ceb03d60798a1db389b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sun, 1 Jan 2017 21:14:26 +0900 Subject: [PATCH 08/11] extcon: Remove porting compatibility of swich class This patch removes the porting compatibility for switch class because there is no any usage and requirement of swich class over a couple of years. Signed-off-by: Chanwoo Choi --- .../extcon/porting-android-switch-class | 123 ------------------ drivers/extcon/extcon.c | 20 --- 2 files changed, 143 deletions(-) delete mode 100644 Documentation/extcon/porting-android-switch-class diff --git a/Documentation/extcon/porting-android-switch-class b/Documentation/extcon/porting-android-switch-class deleted file mode 100644 index 49c81caef84dc..0000000000000 --- a/Documentation/extcon/porting-android-switch-class +++ /dev/null @@ -1,123 +0,0 @@ - - Staging/Android Switch Class Porting Guide - (linux/drivers/staging/android/switch) - (c) Copyright 2012 Samsung Electronics - -AUTHORS -MyungJoo Ham - -/***************************************************************** - * CHAPTER 1. * - * PORTING SWITCH CLASS DEVICE DRIVERS * - *****************************************************************/ - -****** STEP 1. Basic Functionality - No extcon extended feature, but switch features only. - -- struct switch_dev (fed to switch_dev_register/unregister) - @name: no change - @dev: no change - @index: drop (not used in switch device driver side anyway) - @state: no change - If you have used @state with magic numbers, keep it - at this step. - @print_name: no change but type change (switch_dev->extcon_dev) - @print_state: no change but type change (switch_dev->extcon_dev) - -- switch_dev_register(sdev, dev) - => extcon_dev_register(edev) - : type change (sdev->edev) - : remove second param('dev'). if edev has parent device, should store - 'dev' to 'edev.dev.parent' before registering extcon device -- switch_dev_unregister(sdev) - => extcon_dev_unregister(edev) - : no change but type change (sdev->edev) -- switch_get_state(sdev) - => extcon_get_state(edev) - : no change but type change (sdev->edev) and (return: int->u32) -- switch_set_state(sdev, state) - => extcon_set_state(edev, state) - : no change but type change (sdev->edev) and (state: int->u32) - -With this changes, the ex-switch extcon class device works as it once -worked as switch class device. However, it will now have additional -interfaces (both ABI and in-kernel API) and different ABI locations. -However, if CONFIG_ANDROID is enabled without CONFIG_ANDROID_SWITCH, -/sys/class/switch/* will be symbolically linked to /sys/class/extcon/ -so that they are still compatible with legacy userspace processes. - -****** STEP 2. Multistate (no more magic numbers in state value) - Extcon's extended features for switch device drivers with - complex features usually required magic numbers in state - value of switch_dev. With extcon, such magic numbers that - support multiple cables are no more required or supported. - - 1. Define cable names at edev->supported_cable. - 2. (Recommended) remove print_state callback. - 3. Use extcon_get_cable_state_(edev, index) or - extcon_get_cable_state(edev, cable_name) instead of - extcon_get_state(edev) if you intend to get a state of a specific - cable. Same for set_state. This way, you can remove the usage of - magic numbers in state value. - 4. Use extcon_update_state() if you are updating specific bits of - the state value. - -Example: a switch device driver w/ magic numbers for two cables. - "0x00": no cables connected. - "0x01": cable 1 connected - "0x02": cable 2 connected - "0x03": cable 1 and 2 connected - 1. edev->supported_cable = {"1", "2", NULL}; - 2. edev->print_state = NULL; - 3. extcon_get_cable_state_(edev, 0) shows cable 1's state. - extcon_get_cable_state(edev, "1") shows cable 1's state. - extcon_set_cable_state_(edev, 1) sets cable 2's state. - extcon_set_cable_state(edev, "2") sets cable 2's state - 4. extcon_update_state(edev, 0x01, 0) sets the least bit's 0. - -****** STEP 3. Notify other device drivers - - You can notify others of the cable attach/detach events with -notifier chains. - - At the side of other device drivers (the extcon device itself -does not need to get notified of its own events), there are two -methods to register notifier_block for cable events: -(a) for a specific cable or (b) for every cable. - - (a) extcon_register_interest(obj, extcon_name, cable_name, nb) - Example: want to get news of "MAX8997_MUIC"'s "USB" cable - - obj = kzalloc(sizeof(struct extcon_specific_cable_nb), - GFP_KERNEL); - nb->notifier_call = the_callback_to_handle_usb; - - extcon_register_intereset(obj, "MAX8997_MUIC", "USB", nb); - - (b) extcon_register_notifier(edev, nb) - Call nb for any changes in edev. - - Please note that in order to properly behave with method (a), -the extcon device driver should support multistate feature (STEP 2). - -****** STEP 4. Inter-cable relation (mutually exclusive) - - You can provide inter-cable mutually exclusiveness information -for an extcon device. When cables A and B are declared to be mutually -exclusive, the two cables cannot be in ATTACHED state simulteneously. - - -/***************************************************************** - * CHAPTER 2. * - * PORTING USERSPACE w/ SWITCH CLASS DEVICE SUPPORT * - *****************************************************************/ - -****** ABI Location - - If "CONFIG_ANDROID" is enabled, /sys/class/switch/* are created -as symbolic links to /sys/class/extcon/*. - - The two files of switch class, name and state, are provided with -extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is -not enabled or print_state callback is supplied, the output of -state ABI is same with switch class. diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index e7750545469f8..6817e047cd40d 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -230,9 +230,6 @@ struct extcon_cable { }; static struct class *extcon_class; -#if defined(CONFIG_ANDROID) -static struct class_compat *switch_class; -#endif /* CONFIG_ANDROID */ static LIST_HEAD(extcon_dev_list); static DEFINE_MUTEX(extcon_dev_list_lock); @@ -1032,12 +1029,6 @@ static int create_extcon_class(void) if (IS_ERR(extcon_class)) return PTR_ERR(extcon_class); extcon_class->dev_groups = extcon_groups; - -#if defined(CONFIG_ANDROID) - switch_class = class_compat_register("switch"); - if (WARN(!switch_class, "cannot allocate")) - return -ENOMEM; -#endif /* CONFIG_ANDROID */ } return 0; @@ -1259,10 +1250,6 @@ int extcon_dev_register(struct extcon_dev *edev) put_device(&edev->dev); goto err_dev; } -#if defined(CONFIG_ANDROID) - if (switch_class) - ret = class_compat_create_link(switch_class, &edev->dev, NULL); -#endif /* CONFIG_ANDROID */ spin_lock_init(&edev->lock); @@ -1350,10 +1337,6 @@ void extcon_dev_unregister(struct extcon_dev *edev) kfree(edev->cables); } -#if defined(CONFIG_ANDROID) - if (switch_class) - class_compat_remove_link(switch_class, &edev->dev, NULL); -#endif put_device(&edev->dev); } EXPORT_SYMBOL_GPL(extcon_dev_unregister); @@ -1424,9 +1407,6 @@ module_init(extcon_class_init); static void __exit extcon_class_exit(void) { -#if defined(CONFIG_ANDROID) - class_compat_unregister(switch_class); -#endif class_destroy(extcon_class); } module_exit(extcon_class_exit); From 585cb239f4de6c11349e900dd8b4d8cf0825e802 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 5 Apr 2017 00:04:51 +0200 Subject: [PATCH 09/11] extcon: intel-cht-wc: Disable external 5v boost converter on probe Disable the 5v boost converter on probe in case it was left on by the BIOS, this fixes 2 problems: 1) This gets seen by the external battery charger as a valid Vbus supply and it then tries to feed Vsys from this creating a feedback loop which causes aprox. 300 mA extra battery drain (and unless we drive the external-charger-disable pin high it also tries to charge the battery causing even more feedback). 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply Since the external battery charger has its own 5v boost converter which does not have these issues, we simply turn the separate external 5v boost converter off and leave it off entirely. Signed-off-by: Hans de Goede Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-cht-wc.c | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index f1c43af7682b5..e22df5f2bea57 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -64,6 +64,9 @@ #define CHT_WC_PWRSRC_ID_GND BIT(3) #define CHT_WC_PWRSRC_ID_FLOAT BIT(4) +#define CHT_WC_VBUS_GPIO_CTLO 0x6e2d +#define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0) + enum cht_wc_usb_id { USB_ID_OTG, USB_ID_GND, @@ -170,6 +173,23 @@ static void cht_wc_extcon_set_phymux(struct cht_wc_extcon_data *ext, u8 state) dev_err(ext->dev, "Error writing phyctrl: %d\n", ret); } +static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext, + bool enable) +{ + int ret, val; + + val = enable ? CHT_WC_VBUS_GPIO_CTLO_OUTPUT : 0; + + /* + * The 5V boost converter is enabled through a gpio on the PMIC, since + * there currently is no gpio driver we access the gpio reg directly. + */ + ret = regmap_update_bits(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, + CHT_WC_VBUS_GPIO_CTLO_OUTPUT, val); + if (ret) + dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret); +} + /* Small helper to sync EXTCON_CHG_USB_SDP and EXTCON_USB state */ static void cht_wc_extcon_set_state(struct cht_wc_extcon_data *ext, unsigned int cable, bool state) @@ -280,6 +300,21 @@ static int cht_wc_extcon_probe(struct platform_device *pdev) if (IS_ERR(ext->edev)) return PTR_ERR(ext->edev); + /* + * When a host-cable is detected the BIOS enables an external 5v boost + * converter to power connected devices there are 2 problems with this: + * 1) This gets seen by the external battery charger as a valid Vbus + * supply and it then tries to feed Vsys from this creating a + * feedback loop which causes aprox. 300 mA extra battery drain + * (and unless we drive the external-charger-disable pin high it + * also tries to charge the battery causing even more feedback). + * 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply + * Since the external battery charger has its own 5v boost converter + * which does not have these issues, we simply turn the separate + * external 5v boost converter off and leave it off entirely. + */ + cht_wc_extcon_set_5v_boost(ext, false); + /* Enable sw control */ ret = cht_wc_extcon_sw_control(ext, true); if (ret) From c42a880c3139cde603c5c83950ca7613aef7eedc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 5 Apr 2017 00:04:52 +0200 Subject: [PATCH 10/11] extcon: intel-cht-wc: Ignore failure to detect charger-type on host mode exit When we leave host-mode because the id-pin is no longer connected to ground, the 5v boost converter is normally still on, so we will see Vbus, but it is not from a charger (normally) so the charger-type detection will fail. This commit silences the cht_wc_extcon_get_charger() false-positive errors when we're leaving host mode. Signed-off-by: Hans de Goede Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-cht-wc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index e22df5f2bea57..91a0023074af3 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -96,6 +96,7 @@ struct cht_wc_extcon_data { struct regmap *regmap; struct extcon_dev *edev; unsigned int previous_cable; + bool usb_host; }; static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts) @@ -112,7 +113,8 @@ static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts) return USB_ID_FLOAT; } -static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext) +static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext, + bool ignore_errors) { int ret, usbsrc, status; unsigned long timeout; @@ -135,6 +137,9 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext) } while (time_before(jiffies, timeout)); if (status != CHT_WC_USBSRC_STS_SUCCESS) { + if (ignore_errors) + return EXTCON_CHG_USB_SDP; /* Save fallback */ + if (status == CHT_WC_USBSRC_STS_FAIL) dev_warn(ext->dev, "Could not detect charger type\n"); else @@ -203,6 +208,8 @@ static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext) { int ret, pwrsrc_sts, id; unsigned int cable = EXTCON_NONE; + /* Ignore errors in host mode, as the 5v boost converter is on then */ + bool ignore_get_charger_errors = ext->usb_host; ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts); if (ret) { @@ -223,7 +230,7 @@ static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext) goto set_state; } - ret = cht_wc_extcon_get_charger(ext); + ret = cht_wc_extcon_get_charger(ext, ignore_get_charger_errors); if (ret >= 0) cable = ret; @@ -238,8 +245,8 @@ static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext) ext->previous_cable = cable; } - extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, - id == USB_ID_GND || id == USB_RID_A); + ext->usb_host = ((id == USB_ID_GND) || (id == USB_RID_A)); + extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, ext->usb_host); } static irqreturn_t cht_wc_extcon_isr(int irq, void *data) From 70641a0a84e1ae3c6eee3d47ed5a482f85b11e3e Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 29 Mar 2017 19:18:36 +0900 Subject: [PATCH 11/11] extcon: Use BIT() macro for the left-shift operation This patch just uses the BIT() macro to make the code simple. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 6817e047cd40d..f422a78ba3424 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -377,7 +377,7 @@ 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_info[edev->supported_cable[i]].name, - !!(edev->state & (1 << i))); + !!(edev->state & BIT(i))); } return count;