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

Commit e15fd97d authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: qpnp-usbdetect: Add USB VBUS detection driver"

parents 9e1c95d4 543e4d4f
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
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>;
	};
+10 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+126 −0
Original line number Diff line number Diff line
/* 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");