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

Commit 580a744f authored by Jack Pham's avatar Jack Pham
Browse files

platform: msm: Add snapshot of GPIO-based USB detection driver



This patch adds the GPIO USB detect driver as of msm-3.14 commit
e016c39467094409c9c872b02ec619164913054a ("Merge "msm: thermal: Fix
compilation issue when THERMAL_MONITOR is disabled"").

This driver was previously prefixed as QPNP, but as it does not
really tie in to Qualcomm Technologies' PMIC architectures at all,
rename it to a more generic 'gpio-usbdetect'.

Change-Id: Id86523f587d1684d4ff496ef6fb5b1fca48fda75
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent c67f84f6
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
GPIO USB VBUS Detection

Discrete USB VBUS detection circuitry can be connected to the AP or PMICs.
Such circuits can be used to detect the when a USB cable is connected to
an upstream port such as a standard host or a wall charger by detecting
the presence of VBUS voltage. The GPIO can be configured to trigger an
interrupt, and allow the software driver to in turn notify the USB
subsytem using the power_supply framework.

Required Properties:
 - compatible: must be "qcom,gpio-usbdetect"
 - interrupts: an interrupt triggered by the output of the detection circuit
 - interrupt-names: must be "vbus_det_irq"

Optional Properties:
 - vin-supply: phandle to a regulator that powers this circuit, if needed

Example:

	usb_detect {
		compatible = "qcom,gpio-usbdetect";
                interrupt-parent = <&spmi_bus>;
                interrupts = <0x0 0xCA 0x0>;  /* PMA8084 GPIO 11 */
                interrupt-names = "vbus_det_irq";
                vin-supply = <&vbus_det_reg>;
	};
+9 −0
Original line number Diff line number Diff line
@@ -166,6 +166,15 @@ config QPNP_HAPTIC
	  on the Qualcomm Technologies' QPNP PMICs. It uses the android
	  timed-output framework.

config GPIO_USB_DETECT
	tristate "GPIO-based USB VBUS Detection"
	depends on POWER_SUPPLY
	help
	  This driver supports external USB VBUS detection circuitry whose
	  output is connected to a GPIO. The driver in turn notifies the
	  USB driver of VBUS presence/disconnection using the power_supply
	  framework.

config MSM_11AD
	tristate "Platform driver for 11ad chip"
	depends on PCI
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPS) += sps/
obj-$(CONFIG_I2C_MSM_PROF_DBG) += i2c-msm-prof-dbg.o
obj-$(CONFIG_IPA) += ipa/
obj-$(CONFIG_QPNP_HAPTIC) += qpnp-haptic.o
obj-$(CONFIG_GPIO_USB_DETECT) += gpio-usbdetect.o
obj-$(CONFIG_USB_BAM) += usb_bam.o
obj-$(CONFIG_MSM_AVTIMER) += avtimer.o
obj-$(CONFIG_MSM_11AD) += msm_11ad/
+142 −0
Original line number Diff line number Diff line
/* Copyright (c) 2013-2015, 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
 * only 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/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/of_platform.h>
#include <linux/interrupt.h>
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>

struct gpio_usbdetect {
	struct platform_device	*pdev;
	struct regulator	*vin;
	struct power_supply	*usb_psy;
	int			vbus_det_irq;
};

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

	vbus = !!irq_read_line(irq);
	power_supply_set_present(usb->usb_psy, vbus);

	return IRQ_HANDLED;
}

static int gpio_usbdetect_probe(struct platform_device *pdev)
{
	struct gpio_usbdetect *usb;
	struct power_supply *usb_psy;
	int rc, vbus;
	unsigned long flags;

	usb_psy = power_supply_get_by_name("usb");
	if (!usb_psy) {
		dev_dbg(&pdev->dev, "USB power_supply not found, deferring probe\n");
		return -EPROBE_DEFER;
	}

	usb = devm_kzalloc(&pdev->dev, sizeof(*usb), GFP_KERNEL);
	if (!usb)
		return -ENOMEM;

	usb->pdev = pdev;
	usb->usb_psy = usb_psy;

	if (of_get_property(pdev->dev.of_node, "vin-supply", NULL)) {
		usb->vin = devm_regulator_get(&pdev->dev, "vin");
		if (IS_ERR(usb->vin)) {
			dev_err(&pdev->dev, "Failed to get VIN regulator: %ld\n",
				PTR_ERR(usb->vin));
			return PTR_ERR(usb->vin);
		}
	}

	if (usb->vin) {
		rc = regulator_enable(usb->vin);
		if (rc) {
			dev_err(&pdev->dev, "Failed to enable VIN regulator: %d\n",
				rc);
			return rc;
		}
	}

	usb->vbus_det_irq = platform_get_irq_byname(pdev, "vbus_det_irq");
	if (usb->vbus_det_irq < 0) {
		if (usb->vin)
			regulator_disable(usb->vin);
		return usb->vbus_det_irq;
	}

	rc = devm_request_irq(&pdev->dev, usb->vbus_det_irq,
			      gpio_usbdetect_vbus_irq,
			      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
			      "vbus_det_irq", usb);
	if (rc) {
		dev_err(&pdev->dev, "request for vbus_det_irq failed: %d\n",
			rc);
		if (usb->vin)
			regulator_disable(usb->vin);
		return rc;
	}

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

	/* Read and report initial VBUS state */
	local_irq_save(flags);
	vbus = !!irq_read_line(usb->vbus_det_irq);
	local_irq_restore(flags);

	power_supply_set_present(usb->usb_psy, vbus);

	return 0;
}

static int gpio_usbdetect_remove(struct platform_device *pdev)
{
	struct gpio_usbdetect *usb = dev_get_drvdata(&pdev->dev);

	disable_irq_wake(usb->vbus_det_irq);
	disable_irq(usb->vbus_det_irq);
	if (usb->vin)
		regulator_disable(usb->vin);

	return 0;
}

static struct of_device_id of_match_table[] = {
	{ .compatible = "qcom,gpio-usbdetect", },
	{}
};

static struct platform_driver gpio_usbdetect_driver = {
	.driver		= {
		.name	= "qcom,gpio-usbdetect",
		.of_match_table = of_match_table,
	},
	.probe		= gpio_usbdetect_probe,
	.remove		= gpio_usbdetect_remove,
};

module_driver(gpio_usbdetect_driver, platform_driver_register,
		platform_driver_unregister);

MODULE_DESCRIPTION("GPIO USB VBUS Detection driver");
MODULE_LICENSE("GPL v2");