Merge "misc: Add APQ8084 Docking Station driver"

This commit is contained in:
Linux Build Service Account 2013-11-29 02:25:12 -08:00 committed by Gerrit - the friendly Code Review server
commit b6296469c2
4 changed files with 193 additions and 0 deletions

View file

@ -0,0 +1,17 @@
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

View file

@ -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"

View file

@ -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
drivers/misc/apq8084_dock.c Normal file
View file

@ -0,0 +1,168 @@
/* 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");