Commit 6c932443 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB OTG: ohci reset updates (2/5)

Generic OTG and reset support for OTG.

 - Declare and use a start_hnp() board-specific procedure.  The OMAP
   implementation will come separately, it can't yet be configured.

 - When OTG is configured, implement the usb_bus_start_enum() hook;
   that just starts a root port reset.

 - When some task (usually khubd) resets a root port, make sure it takes
   the full 50 msec.  The OHCI reset timer is in hardware, and the host
   may need to issue multiple resets to guard against concurrent resume.

 - For backward compatibility, don't kick in the new 50 msec logic unless
   it could be needed:  without CONFIG_USB_SUSPEND, nothing will suspend
   so nothing could resume.  Which is good, at least until we start to
   measure how long a reset takes ... it seems chip-specific.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 2b33fdb4
......@@ -435,6 +435,89 @@ ohci_hub_descriptor (
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_OTG
static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u32 status;
if (!port)
return -EINVAL;
port--;
/* start port reset before HNP protocol times out */
status = ohci_readl(&ohci->regs->roothub.portstatus [port]);
if (!(status & RH_PS_CCS))
return -ENODEV;
/* khubd will finish the reset later */
writel(RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
return 0;
}
static void start_hnp(struct ohci_hcd *ohci);
#else
#define ohci_start_port_reset NULL
#endif
/*-------------------------------------------------------------------------*/
/* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling,
* not necessarily continuous ... to guard against resume signaling.
* The short timeout is safe for non-root hubs, and is backward-compatible
* with earlier Linux hosts.
*/
#ifdef CONFIG_USB_SUSPEND
#define PORT_RESET_MSEC 50
#else
#define PORT_RESET_MSEC 10
#endif
/* this timer value might be vendor-specific ... */
#define PORT_RESET_HW_MSEC 10
/* wrap-aware logic stolen from <linux/jiffies.h> */
#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
/* called from some task, normally khubd */
static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
{
u32 *portstat = &ohci->regs->roothub.portstatus [port];
u32 temp;
u16 now = readl(&ohci->regs->fmnumber);
u16 reset_done = now + PORT_RESET_MSEC;
/* build a "continuous enough" reset signal, with up to
* 3msec gap between pulses. scheduler HZ==100 must work;
* this might need to be deadline-scheduled.
*/
do {
/* spin until any current reset finishes */
for (;;) {
temp = ohci_readl (portstat);
if (!(temp & RH_PS_PRS))
break;
udelay (500);
}
if (!(temp & RH_PS_CCS))
break;
if (temp & RH_PS_PRSC)
writel (RH_PS_PRSC, portstat);
/* start the next reset, sleep till it's probably done */
writel (RH_PS_PRS, portstat);
msleep(PORT_RESET_HW_MSEC);
now = readl(&ohci->regs->fmnumber);
} while (tick_before(now, reset_done));
/* caller synchronizes using PRSC */
}
static int ohci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
......@@ -533,6 +616,12 @@ static int ohci_hub_control (
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
#ifdef CONFIG_USB_OTG
if (ohci->hcd.self.otg_port == (wIndex + 1)
&& ohci->hcd.self.b_hnp_enable)
start_hnp(ohci);
else
#endif
writel (RH_PS_PSS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
......@@ -541,10 +630,7 @@ static int ohci_hub_control (
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_RESET:
temp = ohci_readl (&ohci->regs->roothub.portstatus [wIndex]);
if (temp & RH_PS_CCS)
writel (RH_PS_PRS,
&ohci->regs->roothub.portstatus [wIndex]);
root_port_reset (ohci, wIndex);
break;
default:
goto error;
......
......@@ -566,6 +566,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
......
......@@ -242,6 +242,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
......@@ -276,7 +277,7 @@ static int __init ohci_hcd_pci_init (void)
if (usb_disabled())
return -ENODEV;
printk (KERN_DEBUG "%s: block sizes: ed %Zd td %Zd\n", hcd_name,
pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
return pci_module_init (&ohci_pci_driver);
}
......
......@@ -900,9 +900,6 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
/*-------------------------------------------------------------------------*/
/* wrap-aware logic stolen from <linux/jiffies.h> */
#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
static void
finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment