From patchwork Sat Nov 29 22:02:04 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 5407141 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id EBCE19F6CD for ; Sat, 29 Nov 2014 22:06:18 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C919F20204 for ; Sat, 29 Nov 2014 22:06:17 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 018362016C for ; Sat, 29 Nov 2014 22:06:15 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Xuq6h-0004EV-W6; Sat, 29 Nov 2014 22:03:11 +0000 Received: from mail-wi0-x230.google.com ([2a00:1450:400c:c05::230]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Xuq6L-00048e-E6 for linux-arm-kernel@lists.infradead.org; Sat, 29 Nov 2014 22:02:51 +0000 Received: by mail-wi0-f176.google.com with SMTP id ex7so21272842wid.9 for ; Sat, 29 Nov 2014 14:02:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references; bh=F+RiEQBlThDXU9lKrH6WhkR+UV4zD/o3ZYbtk3M+DZQ=; b=0/BLIpHmI9Sdf7lnNfTTxM6JvG62/auFzyGprCM4SWojUSXmxu4+stkUUmSId8wgZp 0LP1/2ol7f8ZKIk+Sw3o7VYkjDnh/OMSFAB5p4V79pajdHmj5aToLVKM+MnTL+cWAcPw 3vmuvrgrsqphzgArXJfzZL/H1MZfBjKLez0345SjLVdzFtkUbD1taOdE/VFXyigEx5aI 8rnGPPZ2UovelXHbX7NyBj01vldlRdWk7ph8CHH1Gl03regFQ1AqPSwaRmkit/PecahU 7lZxGPgg5kIB7XGXRjXI4mbJJxTp2HE3Ecik4uze4xLrmXeHrldkxXPEwe3OyT4KFOUw 1kSw== X-Received: by 10.180.108.167 with SMTP id hl7mr70781010wib.68.1417298547687; Sat, 29 Nov 2014 14:02:27 -0800 (PST) Received: from fangorn.rup.mentorg.com (nat-min.mentorg.com. [139.181.32.34]) by mx.google.com with ESMTPSA id t10sm17285285wix.15.2014.11.29.14.02.26 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 29 Nov 2014 14:02:26 -0800 (PST) From: Dmitry Eremin-Solenikov To: Robert Jarzmik , Felipe Balbi , linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v2 2/3] usb: phy: add lubbock phy driver Date: Sun, 30 Nov 2014 01:02:04 +0300 Message-Id: <1417298525-5587-3-git-send-email-dbaryshkov@gmail.com> X-Mailer: git-send-email 2.1.3 In-Reply-To: <1417298525-5587-1-git-send-email-dbaryshkov@gmail.com> References: <1417298525-5587-1-git-send-email-dbaryshkov@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20141129_140249_828048_DA4A6875 X-CRM114-Status: GOOD ( 19.71 ) X-Spam-Score: -0.8 (/) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +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");