[PATCH v2 2/3] usb: phy: add lubbock phy driver
Dmitry Eremin-Solenikov
dbaryshkov at gmail.com
Sat Nov 29 14:02:04 PST 2014
Extract lubbock-specific code from pxa25x_udc driver. As a bonus, phy
driver determines connector/VBUS status by reading CPLD register. Also
it uses a work to call into udc stack, instead of pinging vbus session
right from irq handler.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>
---
drivers/usb/phy/Kconfig | 10 ++
drivers/usb/phy/Makefile | 1 +
drivers/usb/phy/phy-lubbock.c | 220 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 231 insertions(+)
create mode 100644 drivers/usb/phy/phy-lubbock.c
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 0cd1f44..98b1682 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -137,6 +137,16 @@ config USB_ISP1301
To compile this driver as a module, choose M here: the
module will be called phy-isp1301.
+config USB_LUBBOCK
+ tristate "USB VBUS PHY Driver for DBPXA250 Lubbock platform"
+ depends on ARCH_LUBBOCK
+ help
+ Say Y here to add support for the USB Gadget VBUS tranceiver driver
+ for DBPXA250 (Lubbock) platform.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phy-lubbock.
+
config USB_MSM_OTG
tristate "Qualcomm on-chip USB OTG controller support"
depends on (USB || USB_GADGET) && (ARCH_MSM || ARCH_QCOM || COMPILE_TEST)
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 75f2bba..0fe461c 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o
obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o
obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o
obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
+obj-$(CONFIG_USB_LUBBOCK) += phy-lubbock.o
obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
diff --git a/drivers/usb/phy/phy-lubbock.c b/drivers/usb/phy/phy-lubbock.c
new file mode 100644
index 0000000..f73c1a6
--- /dev/null
+++ b/drivers/usb/phy/phy-lubbock.c
@@ -0,0 +1,220 @@
+/*
+ * gpio-lubbock.c - VBUS handling code for DBPXA250 platform (lubbock)
+ *
+ * Based on lubbock-vbus.c:
+ * Copyright (c) 2008 Philipp Zabel <philipp.zabel at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+#include <mach/hardware.h>
+#include <mach/lubbock.h>
+
+struct lubbock_vbus_data {
+ struct usb_phy phy;
+ struct device *dev;
+ int vbus;
+};
+
+static int is_vbus_powered(void)
+{
+ return !(LUB_MISC_RD && BIT(9));
+}
+
+static void lubbock_vbus_handle(struct lubbock_vbus_data *lubbock_vbus)
+{
+ int status, vbus;
+
+ if (!lubbock_vbus->phy.otg->gadget)
+ return;
+
+ vbus = is_vbus_powered();
+ if ((vbus ^ lubbock_vbus->vbus) == 0)
+ return;
+ lubbock_vbus->vbus = vbus;
+
+ if (vbus) {
+ status = USB_EVENT_VBUS;
+ lubbock_vbus->phy.state = OTG_STATE_B_PERIPHERAL;
+ lubbock_vbus->phy.last_event = status;
+ usb_gadget_vbus_connect(lubbock_vbus->phy.otg->gadget);
+
+ atomic_notifier_call_chain(&lubbock_vbus->phy.notifier,
+ status, lubbock_vbus->phy.otg->gadget);
+ } else {
+ usb_gadget_vbus_disconnect(lubbock_vbus->phy.otg->gadget);
+ status = USB_EVENT_NONE;
+ lubbock_vbus->phy.state = OTG_STATE_B_IDLE;
+ lubbock_vbus->phy.last_event = status;
+
+ atomic_notifier_call_chain(&lubbock_vbus->phy.notifier,
+ status, lubbock_vbus->phy.otg->gadget);
+ }
+}
+
+/* VBUS change IRQ handler */
+static irqreturn_t lubbock_vbus_irq(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct lubbock_vbus_data *lubbock_vbus = platform_get_drvdata(pdev);
+ struct usb_otg *otg = lubbock_vbus->phy.otg;
+
+ dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
+ is_vbus_powered() ? "supplied" : "inactive",
+ otg->gadget ? otg->gadget->name : "none");
+
+ switch (irq) {
+ case LUBBOCK_USB_IRQ:
+ disable_irq(LUBBOCK_USB_IRQ);
+ enable_irq(LUBBOCK_USB_DISC_IRQ);
+ break;
+ case LUBBOCK_USB_DISC_IRQ:
+ disable_irq(LUBBOCK_USB_DISC_IRQ);
+ enable_irq(LUBBOCK_USB_IRQ);
+ break;
+ default:
+ return IRQ_NONE;
+ }
+
+ /*
+ * No need to use workqueue here - we are in a threded handler,
+ * so we can sleep.
+ */
+ if (otg->gadget)
+ lubbock_vbus_handle(lubbock_vbus);
+
+ return IRQ_HANDLED;
+}
+
+/* OTG transceiver interface */
+
+/* bind/unbind the peripheral controller */
+static int lubbock_vbus_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct lubbock_vbus_data *lubbock_vbus;
+ struct platform_device *pdev;
+
+ lubbock_vbus = container_of(otg->phy, struct lubbock_vbus_data, phy);
+ pdev = to_platform_device(lubbock_vbus->dev);
+
+ if (!gadget) {
+ dev_dbg(&pdev->dev, "unregistering gadget '%s'\n",
+ otg->gadget->name);
+
+ usb_gadget_vbus_disconnect(otg->gadget);
+ otg->phy->state = OTG_STATE_UNDEFINED;
+
+ otg->gadget = NULL;
+ return 0;
+ }
+
+ otg->gadget = gadget;
+ dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name);
+
+ /* initialize connection state */
+ lubbock_vbus->vbus = 0; /* start with disconnected */
+ lubbock_vbus_irq(LUBBOCK_USB_DISC_IRQ, pdev);
+
+ return 0;
+}
+
+/* for non-OTG B devices: set/clear transceiver suspend mode */
+static int lubbock_vbus_set_suspend(struct usb_phy *phy, int suspend)
+{
+ return 0;
+}
+
+/* platform driver interface */
+
+static int lubbock_vbus_probe(struct platform_device *pdev)
+{
+ struct lubbock_vbus_data *lubbock_vbus;
+ int err;
+
+ lubbock_vbus = devm_kzalloc(&pdev->dev,
+ sizeof(struct lubbock_vbus_data),
+ GFP_KERNEL);
+ if (!lubbock_vbus)
+ return -ENOMEM;
+
+ lubbock_vbus->phy.otg = devm_kzalloc(&pdev->dev,
+ sizeof(struct usb_otg),
+ GFP_KERNEL);
+ if (!lubbock_vbus->phy.otg)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, lubbock_vbus);
+ lubbock_vbus->dev = &pdev->dev;
+ lubbock_vbus->phy.label = "lubbock-vbus";
+ lubbock_vbus->phy.dev = lubbock_vbus->dev;
+ lubbock_vbus->phy.set_suspend = lubbock_vbus_set_suspend;
+ lubbock_vbus->phy.state = OTG_STATE_UNDEFINED;
+
+ lubbock_vbus->phy.otg->phy = &lubbock_vbus->phy;
+ lubbock_vbus->phy.otg->set_peripheral = lubbock_vbus_set_peripheral;
+
+ err = devm_request_threaded_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ,
+ NULL, lubbock_vbus_irq, 0, "vbus disconnect", pdev);
+ if (err) {
+ dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+ LUBBOCK_USB_DISC_IRQ, err);
+ return err;
+ }
+
+ err = devm_request_threaded_irq(&pdev->dev, LUBBOCK_USB_IRQ,
+ NULL, lubbock_vbus_irq, 0, "vbus connect", pdev);
+ if (err) {
+ dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+ LUBBOCK_USB_IRQ, err);
+ return err;
+ }
+
+ /* only active when a gadget is registered */
+ err = usb_add_phy(&lubbock_vbus->phy, USB_PHY_TYPE_USB2);
+ if (err) {
+ dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
+ err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int lubbock_vbus_remove(struct platform_device *pdev)
+{
+ struct lubbock_vbus_data *lubbock_vbus = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&lubbock_vbus->phy);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:lubbock-vbus");
+
+static struct platform_driver lubbock_vbus_driver = {
+ .driver = {
+ .name = "lubbock-vbus",
+ .owner = THIS_MODULE,
+ },
+ .probe = lubbock_vbus_probe,
+ .remove = lubbock_vbus_remove,
+};
+
+module_platform_driver(lubbock_vbus_driver);
+
+MODULE_DESCRIPTION("OTG transceiver driver for DBPXA250 Lubbock platform");
+MODULE_AUTHOR("Dmitry Eremin-Solenikov");
+MODULE_LICENSE("GPL v2");
--
2.1.3
More information about the linux-arm-kernel
mailing list