> >> > >> struct tegra_usb_soc_info { > >> unsigned long flags; > >> + unsigned int txfifothresh; > >> + enum usb_dr_mode dr_mode; > >> +}; > >> + > >> +static const struct tegra_usb_soc_info tegra20_ehci_soc_info = { > >> + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA | > >> + CI_HDRC_OVERRIDE_PHY_CONTROL, > >> + .dr_mode = USB_DR_MODE_HOST, > >> + .txfifothresh = 10, > >> +}; > >> + > >> +static const struct tegra_usb_soc_info tegra30_ehci_soc_info = { > >> + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA | > >> + CI_HDRC_OVERRIDE_PHY_CONTROL > >> + .dr_mode = USB_DR_MODE_HOST, > >> + .txfifothresh = 16, > >> }; > >> > >> static const struct tegra_usb_soc_info tegra_udc_soc_info = { > >> - .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, > >> + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA | > >> + CI_HDRC_OVERRIDE_PHY_CONTROL, > >> + .dr_mode = USB_DR_MODE_UNKNOWN, > >> }; > > > > What's the usage for this dr_mode? Does these three SoCs only supports > > single mode, it usually sets dr_mode at board dts file to indicate USB > > role for this board. > > All Tegra SoCs have three USB controllers. Only one of these three > controllers > supports peripheral / OTG modes, the other two controllers are fixed to the > host mode and device trees do not specify the dr_mode for the host > controllers. > > Hence we need to enforce the dr_mode=host for the host-mode controllers. > > For UDC the default mode is "unknown" because actual mode comes from a > board's device tree. >
You could add dr_mode property at usb node of soc.dtsi to fulfill that. > >> static const struct of_device_id tegra_usb_of_match[] = { > >> { > >> + .compatible = "nvidia,tegra20-ehci", > >> + .data = &tegra20_ehci_soc_info, > >> + }, { > >> + .compatible = "nvidia,tegra30-ehci", > >> + .data = &tegra30_ehci_soc_info, > >> + }, { > >> .compatible = "nvidia,tegra20-udc", > >> .data = &tegra_udc_soc_info, > > > > Your compatible indicates this controller is single USB role, is it on > > purpose? > > Yes, the "tegra20/30-ehci" controllers are fixed to the host-mode in hardware. > > ... > >> +static int tegra_usb_internal_port_reset(struct ehci_hcd *ehci, > >> + u32 __iomem *portsc_reg, > >> + unsigned long *flags) > >> +{ > >> + u32 saved_usbintr, temp; > >> + unsigned int i, tries; > >> + int retval = 0; > >> + > >> + saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable); > >> + /* disable USB interrupt */ > >> + ehci_writel(ehci, 0, &ehci->regs->intr_enable); > >> + spin_unlock_irqrestore(&ehci->lock, *flags); > >> + > >> + /* > >> + * Here we have to do Port Reset at most twice for > >> + * Port Enable bit to be set. > >> + */ > >> + for (i = 0; i < 2; i++) { > >> + temp = ehci_readl(ehci, portsc_reg); > >> + temp |= PORT_RESET; > >> + ehci_writel(ehci, temp, portsc_reg); > >> + mdelay(10); > > > > Needs accurate delay? How about usleep_range? > > To be hones I don't know for sure whether hub_control() could be used from > interrupt context. This mdelay is borrowed from the older ehci-tegra driver. > > Are you suggesting that it should be safe to sleep here? > I see msleep is called at tegra_ehci_hub_control at ehci-tegra.c. .hub_control is not called at interrupt context. > ... > >> static int tegra_usb_probe(struct platform_device *pdev) { > >> const struct tegra_usb_soc_info *soc; @@ -83,24 +292,49 @@ > static > >> int tegra_usb_probe(struct platform_device *pdev) > >> return err; > >> } > >> > >> + if (device_property_present(&pdev->dev, "nvidia,needs-double-reset")) > >> + usb->needs_double_reset = true; > >> + > >> + err = tegra_usb_reset_controller(&pdev->dev); > >> + if (err) { > >> + dev_err(&pdev->dev, "failed to reset controller: %d\n", err); > >> + goto fail_power_off; > >> + } > >> + > >> + /* > >> + * USB controller registers shan't be touched before PHY is > > > > %s/shan't/shouldn't > > ok > > ... > >> static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool > >> enable) { > >> struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -160,14 +166,14 @@ > >> static int host_start(struct ci_hdrc *ci) > >> pinctrl_select_state(ci->platdata->pctl, > >> ci->platdata->pins_host); > >> > >> + ci->hcd = hcd; > >> + > >> ret = usb_add_hcd(hcd, 0, 0); > >> if (ret) { > >> goto disable_reg; > >> } else { > >> struct usb_otg *otg = &ci->otg; > >> > >> - ci->hcd = hcd; > >> - > > > > Why this changed? > > The ci->hcd is used by tegra_usb_notify_event() to handle reset event and the > reset happens once usb_add_hcd() is invoked. Hence we need to pre-assign > the hcd pointer, otherwise there will be a NULL dereference. Get it, please set ci->hcd as NULL at error path. Peter