On 09.08.2017 14:49, Dmitry Osipenko wrote: > From: Thierry Reding <treding@xxxxxxxxxx> > > 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@xxxxxxxxxxxx> > Signed-off-by: Thierry Reding <treding@xxxxxxxxxx> > [digetx@xxxxxxxxx: rebased patches and added DMA alignment quirk for Tegra20] > Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx> > Acked-by: Peter Chen <peter.chen@xxxxxxx> > --- > 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..fc55743bd768 > --- /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; > + > + 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; > + } > + > + soc = of_device_get_match_data(&pdev->dev); > + if (!soc) { > + dev_err(&pdev->dev, "failed to match OF data\n"); > + return -EINVAL; > + } Oh, I made a mistake here. The code above should be before clock enabling. I'll send V5 shortly. > + > + /* > + * 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 power_off; > + } > + > + platform_set_drvdata(pdev, udc); > + > + return 0; > + > +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@xxxxxxxxxx>"); > +MODULE_ALIAS("platform:tegra-udc"); > +MODULE_LICENSE("GPL v2"); > -- Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html