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

Commit b74f7d47 authored by Jack Pham's avatar Jack Pham
Browse files

misc: Add APQ8084 Docking Station driver



This driver is used to coordinate the GPIOs used to power
up and reset the USB hub and ethernet ports on the QTI
APQ8084 Docking Station.

Change-Id: Ie1baf294396086aaaa342025349aa146bef8d0b5
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent 674c8b07
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
QTI APQ8084 Docking Station

This device describes the interface used when connecting to the
Docking Station's USB hub and Ethernet ports. The interface
consists of GPIOs used for controlling the main power supply
and reset lines.

Required properties:
 - compatible: Should be "qti,apq8084-dock"
 - qti,dock-detect-gpio: phandle to a GPIO node corresponding to the input
                         signal indicating when the dock is connected
 - qti,dock-enable-gpio: phandle to a GPIO node corresponding to the output
                         signal that turns on/off power to the ports
 - qti,dock-hub-reset-gpio: phandle to a GPIO node corresponding to the output
                            signal that resets the USB ports
 - qti,dock-eth-reset-gpio: phandle to a GPIO node corresponding to the output
                            signal that resets the Ethernet ports
+7 −0
Original line number Diff line number Diff line
@@ -602,6 +602,13 @@ config TI_DRV2667
	  To compile this driver as a module, choose M here: the
	  module will be called ti_drv2667.

config APQ8084_DOCKING_STATION
	tristate "QTI APQ8084 Docking Station USB/Ethernet support"
	depends on OF_GPIO
	help
	  This option enables support for the USB and Ethernet ports found on
	  the QTI APQ8084 Docking Station.

source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
+1 −0
Original line number Diff line number Diff line
@@ -61,3 +61,4 @@ obj-$(CONFIG_QSEECOM) += qseecom.o
obj-$(CONFIG_QFP_FUSE) += qfp_fuse.o
obj-$(CONFIG_TI_DRV2667) += ti_drv2667.o
obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o
obj-$(CONFIG_APQ8084_DOCKING_STATION)	+= apq8084_dock.o
+168 −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/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeup.h>
#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>

struct apq8084_dock {
	struct device		*dev;
	struct work_struct	dock_work;
	int			dock_detect;
	int			dock_hub_reset;
	int			dock_eth_reset;
	int			dock_enable;
};

static void dock_detected_work(struct work_struct *w)
{
	struct apq8084_dock *dock = container_of(w, struct apq8084_dock,
						 dock_work);
	int docked;

	docked = gpio_get_value(dock->dock_detect);
	gpio_direction_output(dock->dock_enable, 0);

	if (docked) {
		/* assert RESETs before turning on power */
		gpio_direction_output(dock->dock_hub_reset, 1);
		gpio_direction_output(dock->dock_eth_reset, 1);
		gpio_direction_output(dock->dock_enable, 1);

		msleep(20); /* short delay before de-asserting RESETs */
		gpio_direction_output(dock->dock_hub_reset, 0);
		gpio_direction_output(dock->dock_eth_reset, 0);
	}

	/* Allow system suspend */
	pm_relax(dock->dev);
}

static irqreturn_t dock_detected(int irq, void *data)
{
	struct apq8084_dock *dock = data;

	/* Ensure suspend can't happen until after work function commpletes */
	pm_stay_awake(dock->dev);
	schedule_work(&dock->dock_work);
	return IRQ_HANDLED;
}

static int apq8084_dock_probe(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	struct apq8084_dock *dock;
	int ret;

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

	dock->dev = &pdev->dev;
	platform_set_drvdata(pdev, dock);
	INIT_WORK(&dock->dock_work, dock_detected_work);

	dock->dock_detect = of_get_named_gpio(node, "qti,dock-detect-gpio", 0);
	if (dock->dock_detect < 0) {
		dev_err(dock->dev, "unable to get dock-detect-gpio\n");
		return dock->dock_detect;
	}

	ret = devm_gpio_request(dock->dev, dock->dock_detect, "dock_detect");
	if (ret)
		return ret;

	ret = devm_request_irq(&pdev->dev, gpio_to_irq(dock->dock_detect),
				dock_detected, IRQF_TRIGGER_RISING |
				IRQF_TRIGGER_FALLING | IRQF_SHARED,
				"dock_detect_irq", dock);
	if (ret)
		return ret;

	dock->dock_hub_reset = of_get_named_gpio(node,
						 "qti,dock-hub-reset-gpio", 0);
	if (dock->dock_hub_reset < 0) {
		dev_err(dock->dev, "unable to get dock-hub-reset-gpio\n");
		return dock->dock_hub_reset;
	}

	ret = devm_gpio_request(dock->dev, dock->dock_hub_reset,
				"dock_hub_reset");
	if (ret)
		return ret;

	dock->dock_eth_reset = of_get_named_gpio(node,
						 "qti,dock-eth-reset-gpio", 0);
	if (dock->dock_eth_reset < 0) {
		dev_err(dock->dev, "unable to get dock-eth-reset-gpio\n");
		return dock->dock_eth_reset;
	}

	ret = devm_gpio_request(dock->dev, dock->dock_eth_reset,
				"dock_eth_reset");
	if (ret)
		return ret;

	dock->dock_enable = of_get_named_gpio(node, "qti,dock-enable-gpio", 0);
	if (dock->dock_enable < 0) {
		dev_err(dock->dev, "unable to get dock-enable-gpio\n");
		return dock->dock_enable;
	}

	ret = devm_gpio_request(dock->dev, dock->dock_enable, "dock_enable");
	if (ret)
		return ret;

	schedule_work(&dock->dock_work);
	device_init_wakeup(dock->dev, true);
	enable_irq_wake(gpio_to_irq(dock->dock_detect));

	return 0;
}

static int apq8084_dock_remove(struct platform_device *pdev)
{
	struct apq8084_dock *dock = platform_get_drvdata(pdev);

	disable_irq_wake(gpio_to_irq(dock->dock_detect));
	cancel_work_sync(&dock->dock_work);

	return 0;
}

static struct of_device_id of_match_table[] = {
	{       .compatible = "qti,apq8084-dock",
	}
};

static struct platform_driver apq8084_dock_driver = {
	.driver         = {
		.name   = "apq8084-dock-driver",
		.of_match_table = of_match_table,
	},
	.probe          = apq8084_dock_probe,
	.remove		= apq8084_dock_remove,
};

module_platform_driver(apq8084_dock_driver);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("QTI APQ8084 Docking Station driver");