aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Pham <jackp@codeaurora.org>2013-09-27 15:05:01 -0700
committerJack Pham <jackp@codeaurora.org>2013-10-02 10:50:46 -0700
commit543e4d4f69a25d47089ea22dcc805981d43462fb (patch)
tree191d583464599c669e7b55390b42b01fabe788a8
parent16c09cc11cbba6962bf37e90ecf47aad64168b74 (diff)
msm: qpnp-usbdetect: Add USB VBUS detection driver
This driver is used to enable detection of USB cable connection based on VBUS voltage. The circuitry connects its detection output to a PMIC GPIO which in turn will notify USB through the power_supply_set_present() call. This will be used on platforms in which VBUS is not directly connected to the USB PHY and also in lieu of separate USB charging circuitry that can do additionally do detection. Change-Id: I6dd9e1a9740fc5af02b82a223eaeef2fafbf1057 Signed-off-by: Jack Pham <jackp@codeaurora.org>
-rw-r--r--Documentation/devicetree/bindings/platform/msm/qpnp-usbdetect.txt24
-rw-r--r--drivers/platform/msm/Kconfig10
-rw-r--r--drivers/platform/msm/Makefile1
-rw-r--r--drivers/platform/msm/qpnp-usbdetect.c126
4 files changed, 161 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-usbdetect.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-usbdetect.txt
new file mode 100644
index 00000000000..bf2e37abb23
--- /dev/null
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-usbdetect.txt
@@ -0,0 +1,24 @@
+QPNP External USB VBUS Detection Circuit
+
+Discrete USB VBUS detection circuitry can be connected to Qualcomm 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 PMIC is notified by a detection event
+via SPMI bus interrupt. A software driver can in turn notify the USB
+subsytem using the power_supply framework.
+
+Required Properties:
+ - compatible: must be "qcom,qpnp-usbdetect"
+ - interrupts: an interrupt triggered by the output of the detection circuit
+ - interrupt-names: must be "vbus_det_irq"
+ - vin-supply: phandle to a regulator that supplies 5V to this circuit
+
+Example:
+
+ usb_detect {
+ compatible = "qcom,qpnp-usbdetect";
+ interrupt-parent = <&spmi_bus>;
+ interrupts = <0x0 0xCA 0x0>; /* PMA8084 GPIO 11 */
+ interrupt-names = "vbus_det_irq";
+ vin-supply = <&vbus_det_reg>;
+ };
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 1fc21628399..44cbb3770d2 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -91,6 +91,16 @@ config QPNP_COINCELL
maintain PMIC register state when the main battery is removed from the
mobile device.
+config QPNP_USB_DETECT
+ tristate "Qualcomm QPNP USB VBUS Detection"
+ depends on POWER_SUPPLY
+ help
+ This driver supports external USB VBUS detection circuitry which
+ can be connected to Qualcomm QPNP PMIC devices. The driver will
+ configure a GPIO from the detector's output line and in turn notify
+ the USB driver of VBUS presence/disconnection using the power_supply
+ framework.
+
config IPA
tristate "IPA support"
depends on SPS
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index fc48eb5cf48..4ec81f66d85 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o
obj-$(CONFIG_MSM_AVTIMER) += avtimer.o
obj-$(CONFIG_SSM) += ssm.o
obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o
+obj-$(CONFIG_QPNP_USB_DETECT) += qpnp-usbdetect.o
diff --git a/drivers/platform/msm/qpnp-usbdetect.c b/drivers/platform/msm/qpnp-usbdetect.c
new file mode 100644
index 00000000000..9715553dae6
--- /dev/null
+++ b/drivers/platform/msm/qpnp-usbdetect.c
@@ -0,0 +1,126 @@
+/* Copyright (c) 2013, 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 qpnp_usbdetect {
+ struct platform_device *pdev;
+ struct regulator *vin;
+ struct power_supply *usb_psy;
+ int vbus_det_irq;
+};
+
+static irqreturn_t qpnp_usbdetect_vbus_irq(int irq, void *data)
+{
+ struct qpnp_usbdetect *usb = data;
+ int vbus;
+
+ vbus = !!irq_read_line(irq);
+ power_supply_set_present(usb->usb_psy, vbus);
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_usbdetect_probe(struct platform_device *pdev)
+{
+ struct qpnp_usbdetect *usb;
+ struct power_supply *usb_psy;
+ int rc;
+
+ 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;
+
+ 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);
+ }
+
+ 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) {
+ regulator_disable(usb->vin);
+ return usb->vbus_det_irq;
+ }
+
+ rc = devm_request_irq(&pdev->dev, usb->vbus_det_irq,
+ qpnp_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);
+ regulator_disable(usb->vin);
+ return rc;
+ }
+
+ enable_irq_wake(usb->vbus_det_irq);
+ dev_set_drvdata(&pdev->dev, usb);
+
+ return 0;
+}
+
+static int qpnp_usbdetect_remove(struct platform_device *pdev)
+{
+ struct qpnp_usbdetect *usb = dev_get_drvdata(&pdev->dev);
+
+ disable_irq_wake(usb->vbus_det_irq);
+ disable_irq(usb->vbus_det_irq);
+ regulator_disable(usb->vin);
+
+ return 0;
+}
+
+static struct of_device_id of_match_table[] = {
+ { .compatible = "qcom,qpnp-usbdetect",
+ }
+};
+
+static struct platform_driver qpnp_usbdetect_driver = {
+ .driver = {
+ .name = "qcom,qpnp-usbdetect",
+ .of_match_table = of_match_table,
+ },
+ .probe = qpnp_usbdetect_probe,
+ .remove = qpnp_usbdetect_remove,
+};
+
+module_driver(qpnp_usbdetect_driver, platform_driver_register,
+ platform_driver_unregister);
+
+MODULE_DESCRIPTION("QPNP PMIC USB VBUS Detection driver");
+MODULE_LICENSE("GPL v2");