From patchwork Sat Oct 27 09:58:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chen Yu X-Patchwork-Id: 10658317 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8526C1734 for ; Sat, 27 Oct 2018 10:00:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 76A702BD18 for ; Sat, 27 Oct 2018 10:00:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6AB7A2BD27; Sat, 27 Oct 2018 10:00:45 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 88C3E2BD18 for ; Sat, 27 Oct 2018 10:00:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728544AbeJ0Sk3 (ORCPT ); Sat, 27 Oct 2018 14:40:29 -0400 Received: from szxga07-in.huawei.com ([45.249.212.35]:46153 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728437AbeJ0Sk3 (ORCPT ); Sat, 27 Oct 2018 14:40:29 -0400 Received: from DGGEMS406-HUB.china.huawei.com (unknown [172.30.72.59]) by Forcepoint Email with ESMTP id 463EF8D2C21A9; Sat, 27 Oct 2018 17:59:57 +0800 (CST) Received: from vm100-107-113-134.huawei.com (100.107.113.134) by DGGEMS406-HUB.china.huawei.com (10.3.19.206) with Microsoft SMTP Server id 14.3.408.0; Sat, 27 Oct 2018 17:59:49 +0800 From: Yu Chen To: , , CC: , , Yu Chen , Felipe Balbi , Greg Kroah-Hartman , "David S. Miller" , "Mauro Carvalho Chehab" , Andrew Morton , Arnd Bergmann , John Stultz , Binghui Wang Subject: [PATCH 04/10] usb: dwc3: dwc3-hisi: Add code for dwc3 of Hisilicon Soc Platform Date: Sat, 27 Oct 2018 17:58:14 +0800 Message-ID: <20181027095820.40056-5-chenyu56@huawei.com> X-Mailer: git-send-email 2.15.0-rc2 In-Reply-To: <20181027095820.40056-1-chenyu56@huawei.com> References: <20181027095820.40056-1-chenyu56@huawei.com> MIME-Version: 1.0 X-Originating-IP: [100.107.113.134] X-CFilter-Loop: Reflected Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This driver handles the poweron and shutdown of dwc3 core on Hisilicon Soc Platform. Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: "David S. Miller" Cc: Mauro Carvalho Chehab Cc: Andrew Morton Cc: Arnd Bergmann Cc: John Stultz Cc: Binghui Wang Signed-off-by: Yu Chen --- MAINTAINERS | 8 ++ drivers/usb/dwc3/Kconfig | 8 ++ drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-hisi.c | 335 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 352 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-hisi.c diff --git a/MAINTAINERS b/MAINTAINERS index fdb6a298c7e7..e485449f7811 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15288,6 +15288,14 @@ L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/roles/intel-xhci-usb-role-switch.c +USB IP DRIVER FOR HISILICON KIRIN +M: Yu Chen +M: Binghui Wang +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/usb/dwc3-hisi.txt +F: drivers/usb/dwc3/dwc3-hisi.c + USB ISP116X DRIVER M: Olav Kongas L: linux-usb@vger.kernel.org diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 1a0404fda596..09d105a2b53a 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -123,4 +123,12 @@ config USB_DWC3_QCOM for peripheral mode support. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_HISI + tristate "HiSilicon Kirin Platforms" + depends on ((ARCH_HISI && ARM64) || COMPILE_TEST) && OF + default USB_DWC3 + help + Support USB2/3 functionality in HiSilicon Kirin platforms. + Say 'Y' or 'M' if you have one such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3ef6144e5d..15781455e3f0 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o +obj-$(CONFIG_USB_DWC3_HISI) += dwc3-hisi.o diff --git a/drivers/usb/dwc3/dwc3-hisi.c b/drivers/usb/dwc3/dwc3-hisi.c new file mode 100644 index 000000000000..6e9787d420a7 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-hisi.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0+ +/** + * dwc3-hisi.c - Support for dwc3 platform devices on HiSilicon platforms + * + * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd. + * http://www.huawei.com + * + * Authors: Yu Chen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct dwc3_hisi { + struct device *dev; + struct clk **clks; + int num_clocks; + struct reset_control **rstcs; + int num_rstcs; +}; + +struct dwc3_hisi *g_dwc3_hisi; + +static int dwc3_hisi_init_clks(struct dwc3_hisi *dwc3_hisi, + struct device_node *np) +{ + struct device *dev = dwc3_hisi->dev; + int i; + + dwc3_hisi->num_clocks = of_clk_get_parent_count(np); + if (!dwc3_hisi->num_clocks) + return -ENOENT; + + dwc3_hisi->clks = devm_kcalloc(dev, dwc3_hisi->num_clocks, + sizeof(struct clk *), GFP_KERNEL); + if (!dwc3_hisi->clks) + return -ENOMEM; + + for (i = 0; i < dwc3_hisi->num_clocks; i++) { + struct clk *clk; + + clk = of_clk_get(np, i); + if (IS_ERR(clk)) { + while (--i >= 0) + clk_put(dwc3_hisi->clks[i]); + + return PTR_ERR(clk); + } + + dwc3_hisi->clks[i] = clk; + } + + return 0; +} + +static int dwc3_hisi_enable_clks(struct dwc3_hisi *dwc3_hisi) +{ + int i; + int ret; + + for (i = 0; i < dwc3_hisi->num_clocks; i++) { + ret = clk_prepare_enable(dwc3_hisi->clks[i]); + if (ret < 0) { + while (--i >= 0) + clk_disable_unprepare(dwc3_hisi->clks[i]); + + return ret; + } + } + + return 0; +} + +static void dwc3_hisi_disable_clks(struct dwc3_hisi *dwc3_hisi) +{ + int i; + + for (i = 0; i < dwc3_hisi->num_clocks; i++) + clk_disable_unprepare(dwc3_hisi->clks[i]); +} + +static int dwc3_hisi_get_rstcs(struct dwc3_hisi *dwc3_hisi, + struct device_node *np) +{ + struct device *dev = dwc3_hisi->dev; + int i; + + dwc3_hisi->num_rstcs = of_count_phandle_with_args(np, + "resets", "#reset-cells"); + if (!dwc3_hisi->num_rstcs) + return -ENOENT; + + dwc3_hisi->rstcs = devm_kcalloc(dev, dwc3_hisi->num_rstcs, + sizeof(struct reset_control *), GFP_KERNEL); + if (!dwc3_hisi->rstcs) + return -ENOMEM; + + for (i = 0; i < dwc3_hisi->num_rstcs; i++) { + struct reset_control *rstc; + + rstc = of_reset_control_get_shared_by_index(np, i); + if (IS_ERR(rstc)) { + while (--i >= 0) + reset_control_put(dwc3_hisi->rstcs[i]); + return PTR_ERR(rstc); + } + + dwc3_hisi->rstcs[i] = rstc; + } + + return 0; +} + +static int dwc3_hisi_reset_control_assert(struct dwc3_hisi *dwc3_hisi) +{ + int i, ret; + + for (i = dwc3_hisi->num_rstcs - 1; i >= 0 ; i--) { + ret = reset_control_assert(dwc3_hisi->rstcs[i]); + if (ret) { + while (--i >= 0) + reset_control_deassert(dwc3_hisi->rstcs[i]); + return ret; + } + udelay(10); + } + + return 0; +} + +static int dwc3_hisi_reset_control_deassert(struct dwc3_hisi *dwc3_hisi) +{ + int i, ret; + + for (i = 0; i < dwc3_hisi->num_rstcs; i++) { + ret = reset_control_deassert(dwc3_hisi->rstcs[i]); + if (ret) { + while (--i >= 0) + reset_control_assert(dwc3_hisi->rstcs[i]); + return ret; + } + udelay(10); + } + + return 0; +} + +static int dwc3_hisi_probe(struct platform_device *pdev) +{ + struct dwc3_hisi *dwc3_hisi; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + int ret; + int i; + + dwc3_hisi = devm_kzalloc(dev, sizeof(*dwc3_hisi), GFP_KERNEL); + if (!dwc3_hisi) + return -ENOMEM; + + platform_set_drvdata(pdev, dwc3_hisi); + dwc3_hisi->dev = dev; + + ret = dwc3_hisi_init_clks(dwc3_hisi, np); + if (ret) { + dev_err(dev, "could not get clocks\n"); + return ret; + } + + ret = dwc3_hisi_enable_clks(dwc3_hisi); + if (ret) { + dev_err(dev, "could not enable clocks\n"); + goto err_put_clks; + } + + ret = dwc3_hisi_get_rstcs(dwc3_hisi, np); + if (ret) { + dev_err(dev, "could not get reset controllers\n"); + goto err_disable_clks; + } + ret = dwc3_hisi_reset_control_deassert(dwc3_hisi); + if (ret) { + dev_err(dev, "reset control deassert failed\n"); + goto err_put_rstcs; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to add dwc3 core\n"); + goto err_reset_assert; + } + + dev_err(dev, "finish dwc3 hisi probe\n"); + + g_dwc3_hisi = dwc3_hisi; + return 0; + +err_reset_assert: + ret = dwc3_hisi_reset_control_assert(dwc3_hisi); + if (ret) + dev_err(dev, "reset control assert failed\n"); +err_put_rstcs: + for (i = 0; i < dwc3_hisi->num_rstcs; i++) + reset_control_put(dwc3_hisi->rstcs[i]); +err_disable_clks: + dwc3_hisi_disable_clks(dwc3_hisi); +err_put_clks: + for (i = 0; i < dwc3_hisi->num_clocks; i++) + clk_put(dwc3_hisi->clks[i]); + + return ret; +} + +static int dwc3_hisi_remove(struct platform_device *pdev) +{ + struct dwc3_hisi *dwc3_hisi = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i, ret; + + of_platform_depopulate(dev); + + ret = dwc3_hisi_reset_control_assert(dwc3_hisi); + if (ret) { + dev_err(dev, "reset control assert failed\n"); + return ret; + } + + for (i = 0; i < dwc3_hisi->num_clocks; i++) { + clk_disable_unprepare(dwc3_hisi->clks[i]); + clk_put(dwc3_hisi->clks[i]); + } + + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dwc3_hisi_suspend(struct device *dev) +{ + struct dwc3_hisi *dwc3_hisi = dev_get_drvdata(dev); + int ret; + + dev_info(dev, "%s\n", __func__); + + ret = dwc3_hisi_reset_control_assert(dwc3_hisi); + if (ret) { + dev_err(dev, "reset control assert failed\n"); + return ret; + } + + dwc3_hisi_disable_clks(dwc3_hisi); + + pinctrl_pm_select_default_state(dev); + + return 0; +} + +static int dwc3_hisi_resume(struct device *dev) +{ + struct dwc3_hisi *dwc3_hisi = dev_get_drvdata(dev); + int ret; + + dev_info(dev, "%s\n", __func__); + pinctrl_pm_select_default_state(dev); + + ret = dwc3_hisi_enable_clks(dwc3_hisi); + if (ret) { + dev_err(dev, "could not enable clocks\n"); + return ret; + } + + ret = dwc3_hisi_reset_control_deassert(dwc3_hisi); + if (ret) { + dev_err(dev, "reset control deassert failed\n"); + return ret; + } + + /* Wait for clock stable */ + msleep(100); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dwc3_hisi_dev_pm_ops, + dwc3_hisi_suspend, dwc3_hisi_resume); + +static const struct of_device_id dwc3_hisi_match[] = { + { .compatible = "hisilicon,hi3660-dwc3" }, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, dwc3_hisi_match); + +static struct platform_driver dwc3_hisi_driver = { + .probe = dwc3_hisi_probe, + .remove = dwc3_hisi_remove, + .driver = { + .name = "usb-hisi-dwc3", + .of_match_table = dwc3_hisi_match, + .pm = &dwc3_hisi_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_hisi_driver); + +MODULE_AUTHOR("Yu Chen "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 HiSilicon Glue Layer");