From patchwork Wed Dec 28 22:56:55 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Boyd X-Patchwork-Id: 9490733 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 755FF62AB0 for ; Wed, 28 Dec 2016 23:00:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6040126242 for ; Wed, 28 Dec 2016 23:00:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 54C8126490; Wed, 28 Dec 2016 23:00:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A89F126242 for ; Wed, 28 Dec 2016 23:00:38 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1cMNBb-0001lT-A3; Wed, 28 Dec 2016 22:59:07 +0000 Received: from mail-pg0-x22d.google.com ([2607:f8b0:400e:c05::22d]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1cMNAC-00011M-Pi for linux-arm-kernel@lists.infradead.org; Wed, 28 Dec 2016 22:57:46 +0000 Received: by mail-pg0-x22d.google.com with SMTP id i5so94980703pgh.2 for ; Wed, 28 Dec 2016 14:57:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=IipkOCbHsiuICxM/orviprW66sxxJwrLwAles3IebmY=; b=fytXFzEoY3LgBZ+PQzzowDkWhQcUnp0IjR6LZ0VfSFelaOyVMmRiK3AHxch8QBjdLp S5nu43yZkkWEaU4+qv8Y1xpvzJQmHW3kqH8X3FMCqPKRbk47IsomkLaFKp69nKbufXHl bs9bz9W1AIM+skZVvMQkFtuSgjVZIUe+dR+QY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=IipkOCbHsiuICxM/orviprW66sxxJwrLwAles3IebmY=; b=XYS74j0il09Eus2GADypsoienvElPZ4kLPPge1QLNh613nh42DSF5ctt/Qa9ckrIlW R8V4wN52ZSndaI2TrG+iJy1yv4K8Kw8sIDg+tlRjOWNnJGVEk/WzZnOkDgTcK/+ddGBC cJQi3WcylhocuGGNed9wZi2NqbDWmtNvYIz1iPZRzvMnjYU/wLF28wtMW2+WIMGorHXB lqxpwckNDueRti+L08CMhd7bFe7oDIGpXj6idy5L6T8/kRHXS79X1ReNopt8n6zQDjTy lOMzSUvFShKpwEuLcWplCCYrnCQfhyaOkdeOHcX1jmNS4GOGd3cqrtFSUPyBgonxUBRw CzxA== X-Gm-Message-State: AIkVDXJmkOZO846pto/6i9bREzTjJD1BTnvRSqlJykozYUqm/IFqnUVFsGqG/xwOeIgEQlOw X-Received: by 10.98.80.205 with SMTP id g74mr28691956pfj.151.1482965844138; Wed, 28 Dec 2016 14:57:24 -0800 (PST) Received: from localhost.localdomain (i-global254.qualcomm.com. [199.106.103.254]) by smtp.gmail.com with ESMTPSA id x4sm100355741pgc.14.2016.12.28.14.57.22 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 28 Dec 2016 14:57:23 -0800 (PST) From: Stephen Boyd To: linux-usb@vger.kernel.org Subject: [PATCH v6 09/25] usb: chipidea: Add support for ULPI PHY bus Date: Wed, 28 Dec 2016 14:56:55 -0800 Message-Id: <20161228225711.698-10-stephen.boyd@linaro.org> X-Mailer: git-send-email 2.10.0.297.gf6727b0 In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org> References: <20161228225711.698-1-stephen.boyd@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161228_145741_145446_6DD6B4C9 X-CRM114-Status: GOOD ( 25.13 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Felipe Balbi , Heikki Krogerus , Arnd Bergmann , Neil Armstrong , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, Bjorn Andersson , Peter Chen , Greg Kroah-Hartman , Andy Gross , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Some phys for the chipidea controller are controlled via the ULPI viewport. Add support for the ULPI bus so that these sorts of phys can be probed and read/written automatically without having to duplicate the viewport logic in each phy driver. Acked-by: Peter Chen Cc: Greg Kroah-Hartman Cc: Heikki Krogerus Signed-off-by: Stephen Boyd --- drivers/usb/chipidea/Kconfig | 7 +++ drivers/usb/chipidea/Makefile | 1 + drivers/usb/chipidea/ci.h | 21 ++++++++ drivers/usb/chipidea/core.c | 31 +++++++++--- drivers/usb/chipidea/ulpi.c | 113 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 drivers/usb/chipidea/ulpi.c diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index 5e5b9eb7ebf6..19c20eaa23f2 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -38,4 +38,11 @@ config USB_CHIPIDEA_HOST Say Y here to enable host controller functionality of the ChipIdea driver. +config USB_CHIPIDEA_ULPI + bool "ChipIdea ULPI PHY support" + depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_CHIPIDEA + help + Say Y here if you have a ULPI PHY attached to your ChipIdea + controller. + endif diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 518e445476c3..39fca5715ed3 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -4,6 +4,7 @@ ci_hdrc-y := core.o otg.o debug.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o +ci_hdrc-$(CONFIG_USB_CHIPIDEA_ULPI) += ulpi.o # Glue/Bridge layers go here diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 05bc4d631cb9..59e22389c10b 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -18,6 +18,8 @@ #include #include #include +#include +#include /****************************************************************************** * DEFINE @@ -52,6 +54,7 @@ enum ci_hw_regs { OP_ENDPTLISTADDR, OP_TTCTRL, OP_BURSTSIZE, + OP_ULPI_VIEWPORT, OP_PORTSC, OP_DEVLC, OP_OTGSC, @@ -187,6 +190,8 @@ struct hw_bank { * @test_mode: the selected test mode * @platdata: platform specific information supplied by parent device * @vbus_active: is VBUS active + * @ulpi: pointer to ULPI device, if any + * @ulpi_ops: ULPI read/write ops for this device * @phy: pointer to PHY, if any * @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework * @hcd: pointer to usb_hcd for ehci host driver @@ -236,6 +241,10 @@ struct ci_hdrc { struct ci_hdrc_platform_data *platdata; int vbus_active; +#ifdef CONFIG_USB_CHIPIDEA_ULPI + struct ulpi *ulpi; + struct ulpi_ops ulpi_ops; +#endif struct phy *phy; /* old usb_phy interface */ struct usb_phy *usb_phy; @@ -418,6 +427,16 @@ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci) #endif } +#if IS_ENABLED(CONFIG_USB_CHIPIDEA_ULPI) +int ci_ulpi_init(struct ci_hdrc *ci); +void ci_ulpi_exit(struct ci_hdrc *ci); +int ci_ulpi_resume(struct ci_hdrc *ci); +#else +static inline int ci_ulpi_init(struct ci_hdrc *ci) { return 0; } +static inline void ci_ulpi_exit(struct ci_hdrc *ci) { } +static inline int ci_ulpi_resume(struct ci_hdrc *ci) { return 0; } +#endif + u32 hw_read_intr_enable(struct ci_hdrc *ci); u32 hw_read_intr_status(struct ci_hdrc *ci); @@ -428,6 +447,8 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode); u8 hw_port_test_get(struct ci_hdrc *ci); +void hw_phymode_configure(struct ci_hdrc *ci); + void ci_platform_configure(struct ci_hdrc *ci); int dbg_create_files(struct ci_hdrc *ci); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 3d15aee84a6a..83adbb0ad509 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -86,6 +86,7 @@ static const u8 ci_regs_nolpm[] = { [OP_ENDPTLISTADDR] = 0x18U, [OP_TTCTRL] = 0x1CU, [OP_BURSTSIZE] = 0x20U, + [OP_ULPI_VIEWPORT] = 0x30U, [OP_PORTSC] = 0x44U, [OP_DEVLC] = 0x84U, [OP_OTGSC] = 0x64U, @@ -110,6 +111,7 @@ static const u8 ci_regs_lpm[] = { [OP_ENDPTLISTADDR] = 0x18U, [OP_TTCTRL] = 0x1CU, [OP_BURSTSIZE] = 0x20U, + [OP_ULPI_VIEWPORT] = 0x30U, [OP_PORTSC] = 0x44U, [OP_DEVLC] = 0x84U, [OP_OTGSC] = 0xC4U, @@ -285,7 +287,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) return 0; } -static void hw_phymode_configure(struct ci_hdrc *ci) +void hw_phymode_configure(struct ci_hdrc *ci) { u32 portsc, lpm, sts = 0; @@ -895,6 +897,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) CI_HDRC_IMX28_WRITE_FIX); ci->supports_runtime_pm = !!(ci->platdata->flags & CI_HDRC_SUPPORTS_RUNTIME_PM); + platform_set_drvdata(pdev, ci); ret = hw_device_init(ci, base); if (ret < 0) { @@ -902,6 +905,10 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } + ret = ci_ulpi_init(ci); + if (ret) + return ret; + if (ci->platdata->phy) { ci->phy = ci->platdata->phy; } else if (ci->platdata->usb_phy) { @@ -912,11 +919,15 @@ static int ci_hdrc_probe(struct platform_device *pdev) /* if both generic PHY and USB PHY layers aren't enabled */ if (PTR_ERR(ci->phy) == -ENOSYS && - PTR_ERR(ci->usb_phy) == -ENXIO) - return -ENXIO; + PTR_ERR(ci->usb_phy) == -ENXIO) { + ret = -ENXIO; + goto ulpi_exit; + } - if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) - return -EPROBE_DEFER; + if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) { + ret = -EPROBE_DEFER; + goto ulpi_exit; + } if (IS_ERR(ci->phy)) ci->phy = NULL; @@ -1001,7 +1012,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - platform_set_drvdata(pdev, ci); ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name, ci); if (ret) @@ -1033,6 +1043,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci_role_destroy(ci); deinit_phy: ci_usb_phy_exit(ci); +ulpi_exit: + ci_ulpi_exit(ci); return ret; } @@ -1052,6 +1064,7 @@ static int ci_hdrc_remove(struct platform_device *pdev) ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); ci_usb_phy_exit(ci); + ci_ulpi_exit(ci); return 0; } @@ -1099,6 +1112,7 @@ static void ci_controller_suspend(struct ci_hdrc *ci) static int ci_controller_resume(struct device *dev) { struct ci_hdrc *ci = dev_get_drvdata(dev); + int ret; dev_dbg(dev, "at %s\n", __func__); @@ -1108,6 +1122,11 @@ static int ci_controller_resume(struct device *dev) } ci_hdrc_enter_lpm(ci, false); + + ret = ci_ulpi_resume(ci); + if (ret) + return ret; + if (ci->usb_phy) { usb_phy_set_suspend(ci->usb_phy, 0); usb_phy_set_wakeup(ci->usb_phy, false); diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c new file mode 100644 index 000000000000..1219583dc1b2 --- /dev/null +++ b/drivers/usb/chipidea/ulpi.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016 Linaro Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include + +#include "ci.h" + +#define ULPI_WAKEUP BIT(31) +#define ULPI_RUN BIT(30) +#define ULPI_WRITE BIT(29) +#define ULPI_SYNC_STATE BIT(27) +#define ULPI_ADDR(n) ((n) << 16) +#define ULPI_DATA(n) (n) + +static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask) +{ + unsigned long usec = 10000; + + while (usec--) { + if (!hw_read(ci, OP_ULPI_VIEWPORT, mask)) + return 0; + + udelay(1); + } + + return -ETIMEDOUT; +} + +static int ci_ulpi_read(struct device *dev, u8 addr) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + int ret; + + hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP); + ret = ci_ulpi_wait(ci, ULPI_WAKEUP); + if (ret) + return ret; + + hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr)); + ret = ci_ulpi_wait(ci, ULPI_RUN); + if (ret) + return ret; + + return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8; +} + +static int ci_ulpi_write(struct device *dev, u8 addr, u8 val) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + int ret; + + hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP); + ret = ci_ulpi_wait(ci, ULPI_WAKEUP); + if (ret) + return ret; + + hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, + ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val); + return ci_ulpi_wait(ci, ULPI_RUN); +} + +int ci_ulpi_init(struct ci_hdrc *ci) +{ + if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI) + return 0; + + /* + * Set PORTSC correctly so we can read/write ULPI registers for + * identification purposes + */ + hw_phymode_configure(ci); + + ci->ulpi_ops.read = ci_ulpi_read; + ci->ulpi_ops.write = ci_ulpi_write; + ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops); + if (IS_ERR(ci->ulpi)) + dev_err(ci->dev, "failed to register ULPI interface"); + + return PTR_ERR_OR_ZERO(ci->ulpi); +} + +void ci_ulpi_exit(struct ci_hdrc *ci) +{ + if (ci->ulpi) { + ulpi_unregister_interface(ci->ulpi); + ci->ulpi = NULL; + } +} + +int ci_ulpi_resume(struct ci_hdrc *ci) +{ + int cnt = 100000; + + while (cnt-- > 0) { + if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +}