From 734c58aefcc48f888e74fd082a0a2eca167e8816 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Sun, 9 Jul 2017 21:08:20 -0500 Subject: [PATCH 1/5] usb: chipidea: udc: compress return logic into line Simplify return logic to avoid unnecessary variable assignment. This issue was detected using Coccinelle and the following semantic patch: @@ local idexpression ret; expression e; @@ -ret = +return e; -return ret; Signed-off-by: Gustavo A. R. Silva Signed-off-by: Peter Chen --- drivers/usb/chipidea/udc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index d68b125796f9..6502c13331e8 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -944,7 +944,6 @@ isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) */ static int isr_setup_status_phase(struct ci_hdrc *ci) { - int retval; struct ci_hw_ep *hwep; /* @@ -960,9 +959,7 @@ static int isr_setup_status_phase(struct ci_hdrc *ci) ci->status->context = ci; ci->status->complete = isr_setup_status_complete; - retval = _ep_queue(&hwep->ep, ci->status, GFP_ATOMIC); - - return retval; + return _ep_queue(&hwep->ep, ci->status, GFP_ATOMIC); } /** From b74c43156c0caf392d93baae3df8e1788f799646 Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Tue, 18 Jul 2017 16:31:28 -0700 Subject: [PATCH 2/5] usb: chipidea: msm: ci_hdrc_msm_probe() missing of_node_get() Boot fails for qcom-apq8074-dragonboard on 4.13-rc1 with error: OF: ERROR: Bad of_node_put() on /soc/usb@f9a55000 The error will occur if the configuration is set to: CONFIG_OF_OVERLAY y CONFIG_OF_UNITTEST y CONFIG_OF_DYNAMIC y CONFIG_OF_RESOLVE y If CONFIG_OF_DYNAMIC is enabled then of_node_release() detects an attempt to release a node that is still attached to the device tree. Signed-off-by: Frank Rowand Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_msm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index 0bdfcdcbf7a5..bb626120296f 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -251,7 +251,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) if (ret) goto err_mux; - ulpi_node = of_find_node_by_name(pdev->dev.of_node, "ulpi"); + ulpi_node = of_find_node_by_name(of_node_get(pdev->dev.of_node), "ulpi"); if (ulpi_node) { phy_node = of_get_next_available_child(ulpi_node, NULL); ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy"); From 7c3a8b81503ce8e6ab22341f0267d30085d17194 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 23 Jun 2017 14:39:27 +0800 Subject: [PATCH 3/5] usb: chipidea: core: do not register extcon notifier if extcon device is not existed This issue is detected when the system has another device driver which registers USB connector extcon device, fix it by adding extcon device check. Signed-off-by: Peter Chen --- drivers/usb/chipidea/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index b17ed3a9a304..a91736bfc5c2 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -736,7 +736,7 @@ static int ci_extcon_register(struct ci_hdrc *ci) id = &ci->platdata->id_extcon; id->ci = ci; - if (!IS_ERR(id->edev)) { + if (!IS_ERR_OR_NULL(id->edev)) { ret = devm_extcon_register_notifier(ci->dev, id->edev, EXTCON_USB_HOST, &id->nb); if (ret < 0) { @@ -747,7 +747,7 @@ static int ci_extcon_register(struct ci_hdrc *ci) vbus = &ci->platdata->vbus_extcon; vbus->ci = ci; - if (!IS_ERR(vbus->edev)) { + if (!IS_ERR_OR_NULL(vbus->edev)) { ret = devm_extcon_register_notifier(ci->dev, vbus->edev, EXTCON_USB, &vbus->nb); if (ret < 0) { From 581821ae7f7e2c4547945c65f1bcd357f5915aa5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 16 Aug 2017 13:32:39 +0300 Subject: [PATCH 4/5] usb: chipidea: udc: Support SKB alignment quirk NVIDIA Tegra20 UDC can't cope with unaligned DMA and require a USB gadget quirk that avoids SKB buffer alignment to be set in order to make Ethernet Gadget working. Later Tegra generations do not require that quirk. Let's add a new platform data flag that allows to enable USB gadget quirk for platforms that require it. Signed-off-by: Dmitry Osipenko Acked-by: Peter Chen Signed-off-by: Peter Chen --- drivers/usb/chipidea/udc.c | 3 +++ include/linux/usb/chipidea.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 6502c13331e8..fe8a90543ea3 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1896,6 +1896,9 @@ static int udc_start(struct ci_hdrc *ci) ci->gadget.name = ci->platdata->name; ci->gadget.otg_caps = otg_caps; + if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA) + ci->gadget.quirk_avoids_skb_reserve = 1; + if (ci->is_otg && (otg_caps->hnp_support || otg_caps->srp_support || otg_caps->adp_support)) ci->gadget.is_otg = 1; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index c5fdfcf99828..d725cff7268d 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -58,6 +58,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_OVERRIDE_TX_BURST BIT(10) #define CI_HDRC_OVERRIDE_RX_BURST BIT(11) #define CI_HDRC_OVERRIDE_PHY_CONTROL BIT(12) /* Glue layer manages phy */ +#define CI_HDRC_REQUIRES_ALIGNED_DMA BIT(13) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 From dfebb5f43a78827a4a493048552e500f89fa3b31 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 16 Aug 2017 13:32:40 +0300 Subject: [PATCH 5/5] usb: chipidea: Add support for Tegra20/30/114/124 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of these Tegra SoC generations have a ChipIdea UDC IP block that can be used for device mode communication with a host. Implement rudimentary support that doesn't allow switching between host and device modes. Tested-by: Michał Mirosław Signed-off-by: Thierry Reding [digetx@gmail.com: rebased patches and added DMA alignment quirk for Tegra20] Signed-off-by: Dmitry Osipenko Acked-by: Peter Chen Signed-off-by: Peter Chen --- drivers/usb/chipidea/Makefile | 1 + drivers/usb/chipidea/ci_hdrc_tegra.c | 155 +++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 drivers/usb/chipidea/ci_hdrc_tegra.c diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 39fca5715ed3..ddcbddf8361a 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o obj-$(CONFIG_USB_CHIPIDEA_OF) += usbmisc_imx.o ci_hdrc_imx.o +obj-$(CONFIG_USB_CHIPIDEA_OF) += ci_hdrc_tegra.o diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c new file mode 100644 index 000000000000..bfcee2702d50 --- /dev/null +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2016, NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include + +#include "ci.h" + +struct tegra_udc { + struct ci_hdrc_platform_data data; + struct platform_device *dev; + + struct usb_phy *phy; + struct clk *clk; +}; + +struct tegra_udc_soc_info { + unsigned long flags; +}; + +static const struct tegra_udc_soc_info tegra20_udc_soc_info = { + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, +}; + +static const struct tegra_udc_soc_info tegra30_udc_soc_info = { + .flags = 0, +}; + +static const struct tegra_udc_soc_info tegra114_udc_soc_info = { + .flags = 0, +}; + +static const struct tegra_udc_soc_info tegra124_udc_soc_info = { + .flags = 0, +}; + +static const struct of_device_id tegra_udc_of_match[] = { + { + .compatible = "nvidia,tegra20-udc", + .data = &tegra20_udc_soc_info, + }, { + .compatible = "nvidia,tegra30-udc", + .data = &tegra30_udc_soc_info, + }, { + .compatible = "nvidia,tegra114-udc", + .data = &tegra114_udc_soc_info, + }, { + .compatible = "nvidia,tegra124-udc", + .data = &tegra124_udc_soc_info, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, tegra_udc_of_match); + +static int tegra_udc_probe(struct platform_device *pdev) +{ + const struct tegra_udc_soc_info *soc; + struct tegra_udc *udc; + int err; + + udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; + + soc = of_device_get_match_data(&pdev->dev); + if (!soc) { + dev_err(&pdev->dev, "failed to match OF data\n"); + return -EINVAL; + } + + udc->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); + if (IS_ERR(udc->phy)) { + err = PTR_ERR(udc->phy); + dev_err(&pdev->dev, "failed to get PHY: %d\n", err); + return err; + } + + udc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(udc->clk)) { + err = PTR_ERR(udc->clk); + dev_err(&pdev->dev, "failed to get clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(udc->clk); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable clock: %d\n", err); + return err; + } + + /* + * Tegra's USB PHY driver doesn't implement optional phy_init() + * hook, so we have to power on UDC controller before ChipIdea + * driver initialization kicks in. + */ + usb_phy_set_suspend(udc->phy, 0); + + /* setup and register ChipIdea HDRC device */ + udc->data.name = "tegra-udc"; + udc->data.flags = soc->flags; + udc->data.usb_phy = udc->phy; + udc->data.capoffset = DEF_CAPOFFSET; + + udc->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource, + pdev->num_resources, &udc->data); + if (IS_ERR(udc->dev)) { + err = PTR_ERR(udc->dev); + dev_err(&pdev->dev, "failed to add HDRC device: %d\n", err); + goto fail_power_off; + } + + platform_set_drvdata(pdev, udc); + + return 0; + +fail_power_off: + usb_phy_set_suspend(udc->phy, 1); + clk_disable_unprepare(udc->clk); + return err; +} + +static int tegra_udc_remove(struct platform_device *pdev) +{ + struct tegra_udc *udc = platform_get_drvdata(pdev); + + usb_phy_set_suspend(udc->phy, 1); + clk_disable_unprepare(udc->clk); + + return 0; +} + +static struct platform_driver tegra_udc_driver = { + .driver = { + .name = "tegra-udc", + .of_match_table = tegra_udc_of_match, + }, + .probe = tegra_udc_probe, + .remove = tegra_udc_remove, +}; +module_platform_driver(tegra_udc_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra USB device mode driver"); +MODULE_AUTHOR("Thierry Reding "); +MODULE_ALIAS("platform:tegra-udc"); +MODULE_LICENSE("GPL v2");