lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <99c5c7c3a67768a0715e3b89b80430b75ad25a0f.1489995966.git.baolin.wang@linaro.org>
Date:   Mon, 20 Mar 2017 15:59:42 +0800
From:   Baolin Wang <baolin.wang@...aro.org>
To:     myungjoo.ham@...sung.com, cw00.choi@...sung.com,
        robh+dt@...nel.org, mark.rutland@....com
Cc:     broonie@...nel.org, baolin.wang@...aro.org,
        linaro-kernel@...ts.linaro.org, linux-kernel@...r.kernel.org,
        devicetree@...r.kernel.org
Subject: [PATCH 2/2] extcon: usb-gpio: Add the GPIO level trigger support

GPIOs may need level trigger to detect VBUS/ID on some platforms, thus
we should add GPIO level trigger to support this situation.

Signed-off-by: Baolin Wang <baolin.wang@...aro.org>
---
 drivers/extcon/extcon-usb-gpio.c |   81 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 77 insertions(+), 4 deletions(-)

diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index a5e1882..cc89e25 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -39,6 +39,8 @@ struct usb_extcon_info {
 	struct gpio_desc *vbus_gpiod;
 	int id_irq;
 	int vbus_irq;
+	int level_trigger;
+	int trigger_irq;
 
 	unsigned long debounce_jiffies;
 	struct delayed_work wq_detcable;
@@ -70,6 +72,7 @@ struct usb_extcon_info {
 static void usb_extcon_detect_cable(struct work_struct *work)
 {
 	int id, vbus;
+	unsigned int trigger;
 	struct usb_extcon_info *info = container_of(to_delayed_work(work),
 						    struct usb_extcon_info,
 						    wq_detcable);
@@ -80,6 +83,53 @@ static void usb_extcon_detect_cable(struct work_struct *work)
 	vbus = info->vbus_gpiod ?
 		gpiod_get_value_cansleep(info->vbus_gpiod) : id;
 
+	if (info->level_trigger) {
+		if (info->trigger_irq == info->id_irq && info->id_gpiod) {
+			trigger = irqd_get_trigger_type(
+					irq_get_irq_data(info->id_irq));
+
+			/* Ignore incorrect ID trigger */
+			if (((trigger & IRQF_TRIGGER_LOW) && (id > 0)) ||
+			    ((trigger & IRQF_TRIGGER_HIGH) && (id == 0))) {
+				enable_irq(info->id_irq);
+				return;
+			}
+
+			if (id) {
+				trigger &= ~IRQF_TRIGGER_HIGH;
+				trigger |= IRQF_TRIGGER_LOW;
+			} else {
+				trigger &= ~IRQF_TRIGGER_LOW;
+				trigger |= IRQF_TRIGGER_HIGH;
+			}
+
+			irq_set_irq_type(info->id_irq, trigger);
+			enable_irq(info->id_irq);
+		} else if (info->trigger_irq == info->vbus_irq &&
+			   info->vbus_gpiod) {
+			trigger = irqd_get_trigger_type(
+					irq_get_irq_data(info->vbus_irq));
+
+			/* Ignore incorrect VBUS trigger */
+			if (((trigger & IRQF_TRIGGER_LOW) && (vbus > 0)) ||
+			    ((trigger & IRQF_TRIGGER_HIGH) && (vbus == 0))) {
+				enable_irq(info->vbus_irq);
+				return;
+			}
+
+			if (vbus) {
+				trigger &= ~IRQF_TRIGGER_HIGH;
+				trigger |= IRQF_TRIGGER_LOW;
+			} else {
+				trigger &= ~IRQF_TRIGGER_LOW;
+				trigger |= IRQF_TRIGGER_HIGH;
+			}
+
+			irq_set_irq_type(info->vbus_irq, trigger);
+			enable_irq(info->vbus_irq);
+		}
+	}
+
 	/* at first we clean states which are no longer active */
 	if (id)
 		extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
@@ -98,6 +148,11 @@ static irqreturn_t usb_irq_handler(int irq, void *dev_id)
 {
 	struct usb_extcon_info *info = dev_id;
 
+	if (info->level_trigger) {
+		info->trigger_irq = irq;
+		disable_irq_nosync(irq);
+	}
+
 	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
 			   info->debounce_jiffies);
 
@@ -109,7 +164,9 @@ static int usb_extcon_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct device_node *np = dev->of_node;
 	struct usb_extcon_info *info;
+	unsigned long id_irqflags, vbus_irqflags;
 	int ret;
+	int id_active_low, vbus_active_low;
 
 	if (!np && !ACPI_HANDLE(dev))
 		return -EINVAL;
@@ -134,6 +191,14 @@ static int usb_extcon_probe(struct platform_device *pdev)
 	if (IS_ERR(info->vbus_gpiod))
 		return PTR_ERR(info->vbus_gpiod);
 
+	info->level_trigger = of_property_read_bool(np,
+					"extcon-gpio,level-trigger");
+	info->trigger_irq = -1;
+	id_active_low = info->id_gpiod ?
+		gpiod_is_active_low(info->id_gpiod) : 0;
+	vbus_active_low = info->vbus_gpiod ?
+		gpiod_is_active_low(info->vbus_gpiod) : 0;
+
 	info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
 	if (IS_ERR(info->edev)) {
 		dev_err(dev, "failed to allocate extcon device\n");
@@ -158,6 +223,16 @@ static int usb_extcon_probe(struct platform_device *pdev)
 
 	INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable);
 
+	if (info->level_trigger) {
+		id_irqflags = id_active_low ?
+			IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+		vbus_irqflags = vbus_active_low ?
+			IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
+	} else {
+		id_irqflags = vbus_irqflags = IRQF_TRIGGER_RISING |
+			IRQF_TRIGGER_FALLING;
+	}
+
 	if (info->id_gpiod) {
 		info->id_irq = gpiod_to_irq(info->id_gpiod);
 		if (info->id_irq < 0) {
@@ -167,8 +242,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
 
 		ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
 						usb_irq_handler,
-						IRQF_TRIGGER_RISING |
-						IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+						id_irqflags | IRQF_ONESHOT,
 						pdev->name, info);
 		if (ret < 0) {
 			dev_err(dev, "failed to request handler for ID IRQ\n");
@@ -185,8 +259,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
 
 		ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
 						usb_irq_handler,
-						IRQF_TRIGGER_RISING |
-						IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+						vbus_irqflags | IRQF_ONESHOT,
 						pdev->name, info);
 		if (ret < 0) {
 			dev_err(dev, "failed to request handler for VBUS IRQ\n");
-- 
1.7.9.5

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ