[v6,3/8] usb: chipidea: Add support for Tegra20/30/114/124

Message ID 2b091105e45234dca93051862e76e711acf482cc.1502879078.git.digetx@gmail.com
State Accepted
Headers show

Commit Message

Dmitry Osipenko Aug. 16, 2017, 10:32 a.m.
From: Thierry Reding <treding@nvidia.com>

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 <mirq-linux@rere.qmqm.pl>
Signed-off-by: Thierry Reding <treding@nvidia.com>
[digetx@gmail.com: rebased patches and added DMA alignment quirk for Tegra20]
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
---
 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

Comments

Dmitry Osipenko Aug. 16, 2017, 10:38 a.m. | #1
Peter, you've applied the v3 of this patch to your local tree and so it is
already in linux-next. Don't you mind to pick up a newer version of the patch?
Peter Chen Aug. 25, 2017, 3:07 a.m. | #2
On Wed, Aug 16, 2017 at 01:38:54PM +0300, Dmitry Osipenko wrote:
> Peter, you've applied the v3 of this patch to your local tree and so it is
> already in linux-next. Don't you mind to pick up a newer version of the patch?
> 

Your patches are updated at my tree.
Dmitry Osipenko Aug. 25, 2017, 6:41 a.m. | #3
On 25.08.2017 06:07, Peter Chen wrote:
> On Wed, Aug 16, 2017 at 01:38:54PM +0300, Dmitry Osipenko wrote:
>> Peter, you've applied the v3 of this patch to your local tree and so it is
>> already in linux-next. Don't you mind to pick up a newer version of the patch?
>>
> 
> Your patches are updated at my tree.
> 

Thank you very much!
Jon Hunter Sept. 6, 2017, 1:21 p.m. | #4
On 16/08/17 11:32, Dmitry Osipenko wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> 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 <mirq-linux@rere.qmqm.pl>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> [digetx@gmail.com: rebased patches and added DMA alignment quirk for Tegra20]
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> Acked-by: Peter Chen <peter.chen@nxp.com>

Currently, I am seeing that system suspend on Tegra30/114/124 is broken
on -next (looks like it broke starting with next-20170823). Bisect shows
that is broke with this series and reverting this patch of top of tree
does workaround the problem.

Looking a bit deeper, it appears that the boards are hanging when the
set_phcd() function is called in the tegra phy driver to disable the phy
clock. This function is called by usb_phy_set_suspend() in the chipidea
driver.

Thierry, any thoughts?

Cheers
Jon
Dmitry Osipenko Sept. 6, 2017, 4:36 p.m. | #5
On 06.09.2017 16:21, Jon Hunter wrote:
> 
> On 16/08/17 11:32, Dmitry Osipenko wrote:
>> From: Thierry Reding <treding@nvidia.com>
>>
>> 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 <mirq-linux@rere.qmqm.pl>
>> Signed-off-by: Thierry Reding <treding@nvidia.com>
>> [digetx@gmail.com: rebased patches and added DMA alignment quirk for Tegra20]
>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
>> Acked-by: Peter Chen <peter.chen@nxp.com>
> 
> Currently, I am seeing that system suspend on Tegra30/114/124 is broken
> on -next (looks like it broke starting with next-20170823). Bisect shows
> that is broke with this series and reverting this patch of top of tree
> does workaround the problem.
> 
> Looking a bit deeper, it appears that the boards are hanging when the
> set_phcd() function is called in the tegra phy driver to disable the phy
> clock. This function is called by usb_phy_set_suspend() in the chipidea
> driver.

As been discussed on IRC, this issue is caused by entering LPM twice: first time
ChipIdea driver enters LPM and then Tegra's PHY does it too, touching registers
that seems shouldn't be touched after entering LPM. As Thierry suggested, one
possible and quick solution is to *not* set udc->data.usb_phy for Tegra30+.

Patch

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 <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+
+#include <linux/usb/chipidea.h>
+
+#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 <treding@nvidia.com>");
+MODULE_ALIAS("platform:tegra-udc");
+MODULE_LICENSE("GPL v2");