Hi, Nagarjuna, On Fri, 2019-05-24 at 12:20 +0530, Nagarjuna Kristam wrote: > > On 16-05-2019 14:38, Chunfeng Yun wrote: > > On Thu, 2019-05-16 at 12:09 +0530, Nagarjuna Kristam wrote: > >> This patch adds UDC driver for tegra XUSB 3.0 device mode controller. > >> XUSB device mode controller supports SS, HS and FS modes > >> > >> Based on work by: > >> Mark Kuo <mkuo@xxxxxxxxxx> > >> Andrew Bresticker <abrestic@xxxxxxxxxxxx> > >> > >> Signed-off-by: Nagarjuna Kristam <nkristam@xxxxxxxxxx> > >> --- > >> drivers/usb/gadget/udc/Kconfig | 10 + > >> drivers/usb/gadget/udc/Makefile | 1 + > >> drivers/usb/gadget/udc/tegra_xudc.c | 3807 +++++++++++++++++++++++++++++++++++ > >> 3 files changed, 3818 insertions(+) > >> create mode 100644 drivers/usb/gadget/udc/tegra_xudc.c > >> > >> diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig > >> index ef0259a..b35856c 100644 > >> --- a/drivers/usb/gadget/udc/Kconfig > >> +++ b/drivers/usb/gadget/udc/Kconfig > >> @@ -440,6 +440,16 @@ config USB_GADGET_XILINX > >> dynamically linked module called "udc-xilinx" and force all > >> gadget drivers to also be dynamically linked. > >> > >> +config USB_TEGRA_XUDC > >> + tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller" > >> + depends on ARCH_TEGRA > >> + help > >> + Enables NVIDIA Tegra USB 3.0 device mode controller driver. > >> + > >> + Say "y" to link the driver statically, or "m" to build a > >> + dynamically linked module called "tegra_xudc" and force all > >> + gadget drivers to also be dynamically linked. > >> + > >> source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig" > >> [...] > >> +static int tegra_xudc_probe(struct platform_device *pdev) > >> +{ > >> + struct tegra_xudc *xudc; > >> + struct resource *res; > >> + unsigned int i; > >> + int err; > >> + > >> + xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_ATOMIC); > >> + if (!xudc) > >> + return -ENOMEM; > >> + > >> + xudc->dev = &pdev->dev; > >> + platform_set_drvdata(pdev, xudc); > >> + > >> + xudc->soc = of_device_get_match_data(&pdev->dev); > >> + if (!xudc->soc) > >> + return -ENODEV; > >> + > >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > >> + xudc->base = devm_ioremap_resource(&pdev->dev, res); > >> + if (IS_ERR(xudc->base)) > >> + return PTR_ERR(xudc->base); > >> + xudc->phys_base = res->start; > >> + > >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); > >> + xudc->fpci = devm_ioremap_resource(&pdev->dev, res); > >> + if (IS_ERR(xudc->fpci)) > >> + return PTR_ERR(xudc->fpci); > >> + > >> + if (xudc->soc->has_ipfs) { > >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); > >> + xudc->ipfs = devm_ioremap_resource(&pdev->dev, res); > >> + if (IS_ERR(xudc->ipfs)) > >> + return PTR_ERR(xudc->ipfs); > >> + } > > providing register names will make more clear > > will migrate to platform_get_resource_by_name > > >> + > >> + xudc->irq = platform_get_irq(pdev, 0); > >> + if (xudc->irq < 0) { > >> + dev_err(xudc->dev, "failed to get IRQ: %d\n", > >> + xudc->irq); > >> + return xudc->irq; > >> + } > >> + > >> + err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0, > >> + dev_name(&pdev->dev), xudc); > >> + if (err < 0) { > >> + dev_err(xudc->dev, "failed to claim IRQ#%u: %d\n", xudc->irq, > >> + err); > >> + return err; > >> + } > >> + > >> + xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, > >> + sizeof(*xudc->clks), GFP_KERNEL); > >> + if (!xudc->clks) > >> + return -ENOMEM; > >> + > >> + for (i = 0; i < xudc->soc->num_clks; i++) > >> + xudc->clks[i].id = xudc->soc->clock_names[i]; > >> + > >> + err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, > >> + xudc->clks); > >> + if (err) { > >> + dev_err(xudc->dev, "failed to request clks %d\n", err); > >> + return err; > >> + } > >> + > >> + xudc->supplies = devm_kcalloc(&pdev->dev, xudc->soc->num_supplies, > >> + sizeof(*xudc->supplies), GFP_KERNEL); > >> + if (!xudc->supplies) > >> + return -ENOMEM; > >> + > >> + for (i = 0; i < xudc->soc->num_supplies; i++) > >> + xudc->supplies[i].supply = xudc->soc->supply_names[i]; > >> + > >> + err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies, > >> + xudc->supplies); > >> + if (err) { > >> + dev_err(xudc->dev, "failed to request regulators %d\n", err); > >> + return err; > >> + } > >> + > >> + xudc->padctl = tegra_xusb_padctl_get(&pdev->dev); > >> + if (IS_ERR(xudc->padctl)) > >> + return PTR_ERR(xudc->padctl); > >> + > >> + err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies); > >> + if (err) { > >> + dev_err(xudc->dev, "failed to enable regulators %d\n", err); > >> + goto put_padctl; > >> + } > >> + > >> + xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3"); > >> + if (IS_ERR(xudc->usb3_phy)) { > >> + err = PTR_ERR(xudc->usb3_phy); > >> + dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err); > >> + goto disable_regulator; > >> + } > >> + > >> + xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2"); > >> + if (IS_ERR(xudc->utmi_phy)) { > >> + err = PTR_ERR(xudc->utmi_phy); > >> + dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err); > >> + goto disable_regulator; > >> + } > >> + > >> + xudc->data_role_extcon = extcon_get_edev_by_phandle(&pdev->dev, 0); > > extcon seems not allowed for new USB driver anymore, use usb-connector > > instead. > > Try to look into drivers/usb/roles, maybe you also need use USB role > > switch. Hope it's helpful to you. > > > Purpose of using extcon is to get hot plug interrupts. > Going through usb-connector dt-document doesnot provide fucntionlity of > generating a hot-plug interrupts of its own. > based on documentation for usb-sw-role driver in drivers/usb/roles is needed > when a mux switch is used. It is not needeed for current driver. > > Please share your inputs, if purpose of generating hot-plug interrupts can be met > using usb-connector, with out any additional driver. Sorry for the late reply. I introduce a new driver using usb-connector, and try to replace the function of extcon-usb-gpio.c, but still under review, you can try it. [v6,09/10] usb: roles: add USB Type-B GPIO connector driver https://patchwork.kernel.org/patch/10966361/ If you have any good idea, please let me know, thanks a lot > > -Nagarjuna > >> + if (IS_ERR(xudc->data_role_extcon)) { > >> + err = PTR_ERR(xudc->data_role_extcon); > >> + dev_err(xudc->dev, "extcon_get_edev_by_phandle failed %d\n", > >> + err); > >> + goto disable_regulator; > >> + } > >> + > >> + err = tegra_xudc_powerdomain_init(xudc); > >> + if (err) > >> + goto put_powerdomains; > >> + > >> + err = tegra_xudc_phy_init(xudc); > >> + if (err) > >> + goto put_powerdomains; > >> + > >> + err = tegra_xudc_alloc_event_ring(xudc); > >> + if (err) > >> + goto disable_phy; > >> + > >> + err = tegra_xudc_alloc_eps(xudc); > >> + if (err) > >> + goto free_event_ring; > >> + > >> + spin_lock_init(&xudc->lock); > >> + > >> + init_completion(&xudc->disconnect_complete); > >> + > >> + INIT_WORK(&xudc->data_role_work, tegra_xudc_data_role_work); > >> + > >> + xudc->data_role_nb.notifier_call = tegra_xudc_data_role_notifier; > >> + > >> + extcon_register_notifier(xudc->data_role_extcon, EXTCON_USB, > >> + &xudc->data_role_nb); > >> + > >> + INIT_DELAYED_WORK(&xudc->plc_reset_work, tegra_xudc_plc_reset_work); > >> + > >> + INIT_DELAYED_WORK(&xudc->port_reset_war_work, > >> + tegra_xudc_port_reset_war_work); > >> + > >> + pm_runtime_enable(&pdev->dev); > >> + > >> + xudc->gadget.ops = &tegra_xudc_gadget_ops; > >> + xudc->gadget.ep0 = &xudc->ep[0].usb_ep; > >> + xudc->gadget.name = "tegra-xudc"; > >> + xudc->gadget.max_speed = USB_SPEED_SUPER; > >> + > >> + err = usb_add_gadget_udc(&pdev->dev, &xudc->gadget); > >> + if (err) { > >> + dev_err(&pdev->dev, "failed to add USB gadget: %d\n", err); > >> + goto free_eps; > >> + } > >> + > >> + tegra_xudc_update_data_role(xudc); > >> + > >> + return 0; > >> + > >> +free_eps: > >> + tegra_xudc_free_eps(xudc); > >> +free_event_ring: > >> + tegra_xudc_free_event_ring(xudc); > >> +disable_phy: > >> + tegra_xudc_phy_exit(xudc); > >> +put_powerdomains: > >> + tegra_xudc_powerdomain_remove(xudc); > >> +disable_regulator: > >> + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); > >> +put_padctl: > >> + tegra_xusb_padctl_put(xudc->padctl); > >> + > >> + return err; > >> +} > >> + > >> +static int tegra_xudc_remove(struct platform_device *pdev) > >> +{ > >> + struct tegra_xudc *xudc = platform_get_drvdata(pdev); > >> + > >> + pm_runtime_get_sync(xudc->dev); > >> + > >> + cancel_delayed_work(&xudc->plc_reset_work); > >> + if (xudc->data_role_extcon) { > >> + extcon_unregister_notifier(xudc->data_role_extcon, EXTCON_USB, > >> + &xudc->data_role_nb); > >> + cancel_work_sync(&xudc->data_role_work); > >> + } > >> + > >> + usb_del_gadget_udc(&xudc->gadget); > >> + > >> + tegra_xudc_free_eps(xudc); > >> + tegra_xudc_free_event_ring(xudc); > >> + > >> + tegra_xudc_powerdomain_remove(xudc); > >> + > >> + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); > >> + > >> + phy_power_off(xudc->utmi_phy); > >> + phy_power_off(xudc->usb3_phy); > >> + > >> + tegra_xudc_phy_exit(xudc); > >> + > >> + pm_runtime_disable(xudc->dev); > >> + pm_runtime_put(xudc->dev); > >> + > >> + tegra_xusb_padctl_put(xudc->padctl); > >> + > >> + return 0; > >> +} > >> + > >> +static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc) > >> +{ > >> + unsigned long flags; > >> + > >> + dev_dbg(xudc->dev, "entering ELPG\n"); > >> + > >> + spin_lock_irqsave(&xudc->lock, flags); > >> + > >> + xudc->powergated = true; > >> + xudc->saved_regs.ctrl = xudc_readl(xudc, CTRL); > >> + xudc->saved_regs.portpm = xudc_readl(xudc, PORTPM); > >> + xudc_writel(xudc, 0, CTRL); > >> + > >> + spin_unlock_irqrestore(&xudc->lock, flags); > >> + > >> + clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks); > >> + > >> + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); > >> + > >> + dev_dbg(xudc->dev, "entering ELPG done\n"); > >> + return 0; > >> +} > >> + > >> +static int __maybe_unused tegra_xudc_unpowergate(struct tegra_xudc *xudc) > >> +{ > >> + unsigned long flags; > >> + int err; > >> + > >> + dev_dbg(xudc->dev, "exiting ELPG\n"); > >> + > >> + err = regulator_bulk_enable(xudc->soc->num_supplies, > >> + xudc->supplies); > >> + if (err < 0) > >> + return err; > >> + > >> + err = clk_bulk_prepare_enable(xudc->soc->num_clks, xudc->clks); > >> + if (err < 0) > >> + return err; > >> + > >> + tegra_xudc_fpci_ipfs_init(xudc); > >> + > >> + tegra_xudc_device_params_init(xudc); > >> + > >> + tegra_xudc_init_event_ring(xudc); > >> + > >> + tegra_xudc_init_eps(xudc); > >> + > >> + xudc_writel(xudc, xudc->saved_regs.portpm, PORTPM); > >> + xudc_writel(xudc, xudc->saved_regs.ctrl, CTRL); > >> + > >> + spin_lock_irqsave(&xudc->lock, flags); > >> + xudc->powergated = false; > >> + spin_unlock_irqrestore(&xudc->lock, flags); > >> + > >> + dev_dbg(xudc->dev, "exiting ELPG done\n"); > >> + return 0; > >> +} > >> + > >> +static int __maybe_unused tegra_xudc_suspend(struct device *dev) > >> +{ > >> + struct tegra_xudc *xudc = dev_get_drvdata(dev); > >> + unsigned long flags; > >> + > >> + spin_lock_irqsave(&xudc->lock, flags); > >> + xudc->suspended = true; > >> + spin_unlock_irqrestore(&xudc->lock, flags); > >> + > >> + if (xudc->data_role_extcon) > >> + flush_work(&xudc->data_role_work); > >> + > >> + /* Forcibly disconnect before powergating. */ > >> + tegra_xudc_device_mode_off(xudc); > >> + > >> + if (!pm_runtime_status_suspended(dev)) > >> + tegra_xudc_powergate(xudc); > >> + > >> + pm_runtime_disable(dev); > >> + > >> + return 0; > >> +} > >> + > >> +static int __maybe_unused tegra_xudc_resume(struct device *dev) > >> +{ > >> + struct tegra_xudc *xudc = dev_get_drvdata(dev); > >> + unsigned long flags; > >> + int err; > >> + > >> + err = tegra_xudc_unpowergate(xudc); > >> + if (err < 0) > >> + return err; > >> + > >> + spin_lock_irqsave(&xudc->lock, flags); > >> + xudc->suspended = false; > >> + spin_unlock_irqrestore(&xudc->lock, flags); > >> + > >> + tegra_xudc_update_data_role(xudc); > >> + > >> + pm_runtime_enable(dev); > >> + > >> + return 0; > >> +} > >> + > >> +static int __maybe_unused tegra_xudc_runtime_suspend(struct device *dev) > >> +{ > >> + struct tegra_xudc *xudc = dev_get_drvdata(dev); > >> + > >> + return tegra_xudc_powergate(xudc); > >> +} > >> + > >> +static int __maybe_unused tegra_xudc_runtime_resume(struct device *dev) > >> +{ > >> + struct tegra_xudc *xudc = dev_get_drvdata(dev); > >> + > >> + return tegra_xudc_unpowergate(xudc); > >> +} > >> + > >> +static const struct dev_pm_ops tegra_xudc_pm_ops = { > >> + SET_SYSTEM_SLEEP_PM_OPS(tegra_xudc_suspend, tegra_xudc_resume) > >> + SET_RUNTIME_PM_OPS(tegra_xudc_runtime_suspend, > >> + tegra_xudc_runtime_resume, NULL) > >> +}; > >> + > >> +static struct platform_driver tegra_xudc_driver = { > >> + .probe = tegra_xudc_probe, > >> + .remove = tegra_xudc_remove, > >> + .driver = { > >> + .name = "tegra-xudc", > >> + .pm = &tegra_xudc_pm_ops, > >> + .of_match_table = tegra_xudc_of_match, > >> + }, > >> +}; > >> +module_platform_driver(tegra_xudc_driver); > >> + > >> +MODULE_DESCRIPTION("NVIDIA Tegra XUSB Device Controller"); > >> +MODULE_AUTHOR("Andrew Bresticker <abrestic@xxxxxxxxxxxx>"); > >> +MODULE_AUTHOR("Hui Fu"); > >> +MODULE_LICENSE("GPL v2"); > > > >