Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab3b6fb1 authored by Sriharsha Allenki's avatar Sriharsha Allenki Committed by Gerrit - the friendly Code Review server
Browse files

gpio-usbdetect: Add support for ID detection



The existing driver does not support for role
switch based on ID. Add this support to switch to
host mode depending upon the value of the GPIO
qcom,id-det-gpio.
Also the final state table if this GPIO is present
	USB STATE	ID	VBUS
	PERIPHERAL	HIGH	HIGH
	NONE		HIGH	LOW
	HOST		LOW	LOW
	HOST		LOW	HIGH
Also add support for DPDM routing to different ports
based on a switch driven by a GPIO. In host mode
it is routed to one and in peripheral to the other.

Change-Id: I3a6aa36e06ea433318262d26f940b106d79e48d7
Signed-off-by: default avatarSriharsha Allenki <sallenki@codeaurora.org>
parent d9eff3c4
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -22,6 +22,10 @@ Optional Properties:
	value is 0 or device mode if 1.
- qcom,disable-device-mode: If present then don't treat GPIO high as Vbus high.
	And only notify host mode similar to ID line.
-qcom,id-det-gpio: If present, specifies a gpio that is tied to ID, switch
	to host mode if value is 0.
-qcom,dpdm_switch_gpio: If present, specifies a gpio that drives a switch
	which routes DPDM lines to different ports.

Example:

+89 −4
Original line number Diff line number Diff line
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -37,8 +37,15 @@ struct gpio_usbdetect {
	struct regulator	*vdd33;
	struct regulator	*vdd12;
	int			gpio_usbdetect;

	int			id;
	int			id_det_gpio;
	int			id_det_irq;

	bool			notify_host_mode;
	bool			disable_device_mode;

	int			dpdm_switch_gpio;
};

static int gpio_enable_ldos(struct gpio_usbdetect *usb, int on)
@@ -154,15 +161,28 @@ disable_vin:
	return ret;
}

static irqreturn_t gpio_usbdetect_vbus_irq(int irq, void *data)
static irqreturn_t gpio_usbdetect_irq(int irq, void *data)
{
	struct gpio_usbdetect *usb = data;

	if (gpio_is_valid(usb->id_det_gpio)) {
		usb->id = gpio_get_value(usb->id_det_gpio);
		if (usb->id) {
			dev_dbg(&usb->pdev->dev, "ID\n");
			usb->vbus = gpio_get_value(usb->gpio_usbdetect);
			goto queue_chg_work;
		}
		dev_dbg(&usb->pdev->dev, "!ID\n");
		schedule_delayed_work(&usb->chg_work, 0);
		return IRQ_HANDLED;
	}

	if (gpio_is_valid(usb->gpio_usbdetect))
		usb->vbus = gpio_get_value(usb->gpio_usbdetect);
	else
		usb->vbus = !!irq_read_line(irq);

queue_chg_work:
	pm_wakeup_event(&usb->pdev->dev, WAKEUP_SRC_TIMEOUT_MS);
	if (!usb->vbus)
		schedule_delayed_work(&usb->chg_work, 0);
@@ -178,6 +198,37 @@ static void gpio_usbdetect_chg_work(struct work_struct *w)
	struct gpio_usbdetect *usb = container_of(w, struct gpio_usbdetect,
							chg_work.work);

	if (gpio_is_valid(usb->id_det_gpio)) {
		dev_dbg(&usb->pdev->dev, "ID:%d VBUS:%d\n",
						usb->id, usb->vbus);
		if (!usb->id) {
			power_supply_set_supply_type(usb->usb_psy,
					POWER_SUPPLY_TYPE_UNKNOWN);
			power_supply_set_present(usb->usb_psy, 0);
			power_supply_set_usb_otg(usb->usb_psy, 1);
			if (gpio_is_valid(usb->dpdm_switch_gpio))
				gpio_set_value(usb->dpdm_switch_gpio, 1);

			return;
		}

		power_supply_set_usb_otg(usb->usb_psy, 0);
		if (gpio_is_valid(usb->dpdm_switch_gpio))
			gpio_set_value(usb->dpdm_switch_gpio, 0);

		if (usb->vbus) {
			power_supply_set_supply_type(usb->usb_psy,
						POWER_SUPPLY_TYPE_USB);
			power_supply_set_present(usb->usb_psy, usb->vbus);
		} else {
			power_supply_set_supply_type(usb->usb_psy,
					POWER_SUPPLY_TYPE_UNKNOWN);
			power_supply_set_present(usb->usb_psy, usb->vbus);
		}

		return;
	}

	if (usb->vbus) {
		if (usb->notify_host_mode)
			power_supply_set_usb_otg(usb->usb_psy, 0);
@@ -254,7 +305,7 @@ static int gpio_usbdetect_probe(struct platform_device *pdev)
	}

	rc = devm_request_irq(&pdev->dev, usb->vbus_det_irq,
			      gpio_usbdetect_vbus_irq,
			      gpio_usbdetect_irq,
			      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
			      "vbus_det_irq", usb);
	if (rc) {
@@ -263,14 +314,48 @@ static int gpio_usbdetect_probe(struct platform_device *pdev)
		goto disable_ldo;
	}

	usb->id_det_gpio = of_get_named_gpio(pdev->dev.of_node,
						"qcom,id-det-gpio", 0);
	if (gpio_is_valid(usb->id_det_gpio)) {
		rc = devm_gpio_request(&pdev->dev, usb->id_det_gpio,
					"GPIO_ID_DET");
		if (rc) {
			dev_err(&pdev->dev, "gpio req failed for gpio_%d\n",
							usb->id_det_gpio);
			goto disable_ldo;
		}

		usb->id_det_irq = gpio_to_irq(usb->id_det_gpio);
		if (usb->id_det_irq < 0) {
			dev_err(&pdev->dev, "get id_det_irq failed\n");
			goto disable_ldo;
		}
		rc = devm_request_irq(&pdev->dev, usb->id_det_irq,
					gpio_usbdetect_irq,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING, "id_det_irq",
					usb);
		if (rc) {
			dev_err(&pdev->dev, "request for id_det_irq failed:%d\n",
					rc);
			goto disable_ldo;
		}
	}

	usb->dpdm_switch_gpio = of_get_named_gpio(pdev->dev.of_node,
						"qcom,dpdm_switch_gpio", 0);
	dev_dbg(&pdev->dev, "is dpdm_switch_gpio valid:%d\n",
					gpio_is_valid(usb->dpdm_switch_gpio));

	device_init_wakeup(&pdev->dev, 1);

	enable_irq_wake(usb->vbus_det_irq);
	enable_irq_wake(usb->id_det_irq);
	dev_set_drvdata(&pdev->dev, usb);

	/* Read and report initial VBUS state */
	local_irq_save(flags);
	gpio_usbdetect_vbus_irq(usb->vbus_det_irq, usb);
	gpio_usbdetect_irq(usb->vbus_det_irq, usb);
	local_irq_restore(flags);

	return 0;