[PATCH] USB: EHCI updates split init/reinit logic for resume
Moving the PCI-specific parts of the EHCI driver into their own file created a few issues ... notably on resume paths which (like swsusp) require re-initializing the controller. This patch: - Splits the EHCI startup code into run-once HCD setup code and separate "init the hardware" reinit code. (That reinit code is a superset of the "early usb handoff" code.) - Then it makes the PCI init code run both, and the resume code only run the reinit code. - It also removes needless pci wrappers around EHCI start/stop methods. - Removes a byteswap issue that would be seen on big-endian hardware. The HCD glue still doesn't actually provide a good way to do all this run-one init stuff in one place though. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
abcc944806
commit
188075211c
|
@ -411,50 +411,39 @@ static void ehci_stop (struct usb_hcd *hcd)
|
||||||
dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
|
dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_run (struct usb_hcd *hcd)
|
/* one-time init, only for memory state */
|
||||||
|
static int ehci_init(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
u32 temp;
|
u32 temp;
|
||||||
int retval;
|
int retval;
|
||||||
u32 hcc_params;
|
u32 hcc_params;
|
||||||
int first;
|
|
||||||
|
|
||||||
/* skip some things on restart paths */
|
spin_lock_init(&ehci->lock);
|
||||||
first = (ehci->watchdog.data == 0);
|
|
||||||
if (first) {
|
init_timer(&ehci->watchdog);
|
||||||
init_timer (&ehci->watchdog);
|
ehci->watchdog.function = ehci_watchdog;
|
||||||
ehci->watchdog.function = ehci_watchdog;
|
ehci->watchdog.data = (unsigned long) ehci;
|
||||||
ehci->watchdog.data = (unsigned long) ehci;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hw default: 1K periodic list heads, one per frame.
|
* hw default: 1K periodic list heads, one per frame.
|
||||||
* periodic_size can shrink by USBCMD update if hcc_params allows.
|
* periodic_size can shrink by USBCMD update if hcc_params allows.
|
||||||
*/
|
*/
|
||||||
ehci->periodic_size = DEFAULT_I_TDPS;
|
ehci->periodic_size = DEFAULT_I_TDPS;
|
||||||
if (first && (retval = ehci_mem_init (ehci, GFP_KERNEL)) < 0)
|
if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* controllers may cache some of the periodic schedule ... */
|
/* controllers may cache some of the periodic schedule ... */
|
||||||
hcc_params = readl (&ehci->caps->hcc_params);
|
hcc_params = readl(&ehci->caps->hcc_params);
|
||||||
if (HCC_ISOC_CACHE (hcc_params)) // full frame cache
|
if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
|
||||||
ehci->i_thresh = 8;
|
ehci->i_thresh = 8;
|
||||||
else // N microframes cached
|
else // N microframes cached
|
||||||
ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
|
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
|
||||||
|
|
||||||
ehci->reclaim = NULL;
|
ehci->reclaim = NULL;
|
||||||
ehci->reclaim_ready = 0;
|
ehci->reclaim_ready = 0;
|
||||||
ehci->next_uframe = -1;
|
ehci->next_uframe = -1;
|
||||||
|
|
||||||
/* controller state: unknown --> reset */
|
|
||||||
|
|
||||||
/* EHCI spec section 4.1 */
|
|
||||||
if ((retval = ehci_reset (ehci)) != 0) {
|
|
||||||
ehci_mem_cleanup (ehci);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
writel (ehci->periodic_dma, &ehci->regs->frame_list);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dedicate a qh for the async ring head, since we couldn't unlink
|
* dedicate a qh for the async ring head, since we couldn't unlink
|
||||||
* a 'real' qh without stopping the async schedule [4.8]. use it
|
* a 'real' qh without stopping the async schedule [4.8]. use it
|
||||||
|
@ -462,37 +451,13 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||||
* its dummy is used in hw_alt_next of many tds, to prevent the qh
|
* its dummy is used in hw_alt_next of many tds, to prevent the qh
|
||||||
* from automatically advancing to the next td after short reads.
|
* from automatically advancing to the next td after short reads.
|
||||||
*/
|
*/
|
||||||
if (first) {
|
ehci->async->qh_next.qh = NULL;
|
||||||
ehci->async->qh_next.qh = NULL;
|
ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma);
|
||||||
ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
|
ehci->async->hw_info1 = cpu_to_le32(QH_HEAD);
|
||||||
ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
|
ehci->async->hw_token = cpu_to_le32(QTD_STS_HALT);
|
||||||
ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
|
ehci->async->hw_qtd_next = EHCI_LIST_END;
|
||||||
ehci->async->hw_qtd_next = EHCI_LIST_END;
|
ehci->async->qh_state = QH_STATE_LINKED;
|
||||||
ehci->async->qh_state = QH_STATE_LINKED;
|
ehci->async->hw_alt_next = QTD_NEXT(ehci->async->dummy->qtd_dma);
|
||||||
ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
|
|
||||||
}
|
|
||||||
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* hcc_params controls whether ehci->regs->segment must (!!!)
|
|
||||||
* be used; it constrains QH/ITD/SITD and QTD locations.
|
|
||||||
* pci_pool consistent memory always uses segment zero.
|
|
||||||
* streaming mappings for I/O buffers, like pci_map_single(),
|
|
||||||
* can return segments above 4GB, if the device allows.
|
|
||||||
*
|
|
||||||
* NOTE: the dma mask is visible through dma_supported(), so
|
|
||||||
* drivers can pass this info along ... like NETIF_F_HIGHDMA,
|
|
||||||
* Scsi_Host.highmem_io, and so forth. It's readonly to all
|
|
||||||
* host side drivers though.
|
|
||||||
*/
|
|
||||||
if (HCC_64BIT_ADDR (hcc_params)) {
|
|
||||||
writel (0, &ehci->regs->segment);
|
|
||||||
#if 0
|
|
||||||
// this is deeply broken on almost all architectures
|
|
||||||
if (!dma_set_mask (hcd->self.controller, DMA_64BIT_MASK))
|
|
||||||
ehci_info (ehci, "enabled 64bit DMA\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear interrupt enables, set irq latency */
|
/* clear interrupt enables, set irq latency */
|
||||||
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
|
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
|
||||||
|
@ -507,13 +472,13 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||||
* make problems: throughput reduction (!), data errors...
|
* make problems: throughput reduction (!), data errors...
|
||||||
*/
|
*/
|
||||||
if (park) {
|
if (park) {
|
||||||
park = min (park, (unsigned) 3);
|
park = min(park, (unsigned) 3);
|
||||||
temp |= CMD_PARK;
|
temp |= CMD_PARK;
|
||||||
temp |= park << 8;
|
temp |= park << 8;
|
||||||
}
|
}
|
||||||
ehci_info (ehci, "park %d\n", park);
|
ehci_dbg(ehci, "park %d\n", park);
|
||||||
}
|
}
|
||||||
if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
|
if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
|
||||||
/* periodic schedule size can be smaller than default */
|
/* periodic schedule size can be smaller than default */
|
||||||
temp &= ~(3 << 2);
|
temp &= ~(3 << 2);
|
||||||
temp |= (EHCI_TUNE_FLS << 2);
|
temp |= (EHCI_TUNE_FLS << 2);
|
||||||
|
@ -521,16 +486,63 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||||
case 0: ehci->periodic_size = 1024; break;
|
case 0: ehci->periodic_size = 1024; break;
|
||||||
case 1: ehci->periodic_size = 512; break;
|
case 1: ehci->periodic_size = 512; break;
|
||||||
case 2: ehci->periodic_size = 256; break;
|
case 2: ehci->periodic_size = 256; break;
|
||||||
default: BUG ();
|
default: BUG();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ehci->command = temp;
|
||||||
|
|
||||||
|
ehci->reboot_notifier.notifier_call = ehci_reboot;
|
||||||
|
register_reboot_notifier(&ehci->reboot_notifier);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start HC running; it's halted, ehci_init() has been run (once) */
|
||||||
|
static int ehci_run (struct usb_hcd *hcd)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||||
|
int retval;
|
||||||
|
u32 temp;
|
||||||
|
u32 hcc_params;
|
||||||
|
|
||||||
|
/* EHCI spec section 4.1 */
|
||||||
|
if ((retval = ehci_reset(ehci)) != 0) {
|
||||||
|
unregister_reboot_notifier(&ehci->reboot_notifier);
|
||||||
|
ehci_mem_cleanup(ehci);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
writel(ehci->periodic_dma, &ehci->regs->frame_list);
|
||||||
|
writel((u32)ehci->async->qh_dma, &ehci->regs->async_next);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hcc_params controls whether ehci->regs->segment must (!!!)
|
||||||
|
* be used; it constrains QH/ITD/SITD and QTD locations.
|
||||||
|
* pci_pool consistent memory always uses segment zero.
|
||||||
|
* streaming mappings for I/O buffers, like pci_map_single(),
|
||||||
|
* can return segments above 4GB, if the device allows.
|
||||||
|
*
|
||||||
|
* NOTE: the dma mask is visible through dma_supported(), so
|
||||||
|
* drivers can pass this info along ... like NETIF_F_HIGHDMA,
|
||||||
|
* Scsi_Host.highmem_io, and so forth. It's readonly to all
|
||||||
|
* host side drivers though.
|
||||||
|
*/
|
||||||
|
hcc_params = readl(&ehci->caps->hcc_params);
|
||||||
|
if (HCC_64BIT_ADDR(hcc_params)) {
|
||||||
|
writel(0, &ehci->regs->segment);
|
||||||
|
#if 0
|
||||||
|
// this is deeply broken on almost all architectures
|
||||||
|
if (!dma_set_mask(hcd->self.controller, DMA_64BIT_MASK))
|
||||||
|
ehci_info(ehci, "enabled 64bit DMA\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Philips, Intel, and maybe others need CMD_RUN before the
|
// Philips, Intel, and maybe others need CMD_RUN before the
|
||||||
// root hub will detect new devices (why?); NEC doesn't
|
// root hub will detect new devices (why?); NEC doesn't
|
||||||
temp |= CMD_RUN;
|
ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
|
||||||
writel (temp, &ehci->regs->command);
|
ehci->command |= CMD_RUN;
|
||||||
dbg_cmd (ehci, "init", temp);
|
writel (ehci->command, &ehci->regs->command);
|
||||||
|
dbg_cmd (ehci, "init", ehci->command);
|
||||||
/* set async sleep time = 10 us ... ? */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
|
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
|
||||||
|
@ -538,26 +550,23 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||||
* involved with the root hub. (Except where one is integrated,
|
* involved with the root hub. (Except where one is integrated,
|
||||||
* and there's no companion controller unless maybe for USB OTG.)
|
* and there's no companion controller unless maybe for USB OTG.)
|
||||||
*/
|
*/
|
||||||
if (first) {
|
|
||||||
ehci->reboot_notifier.notifier_call = ehci_reboot;
|
|
||||||
register_reboot_notifier (&ehci->reboot_notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
hcd->state = HC_STATE_RUNNING;
|
hcd->state = HC_STATE_RUNNING;
|
||||||
writel (FLAG_CF, &ehci->regs->configured_flag);
|
writel (FLAG_CF, &ehci->regs->configured_flag);
|
||||||
readl (&ehci->regs->command); /* unblock posted write */
|
readl (&ehci->regs->command); /* unblock posted writes */
|
||||||
|
|
||||||
temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
|
temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
|
||||||
ehci_info (ehci,
|
ehci_info (ehci,
|
||||||
"USB %x.%x %s, EHCI %x.%02x, driver %s\n",
|
"USB %x.%x started, EHCI %x.%02x, driver %s\n",
|
||||||
((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
|
((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
|
||||||
first ? "initialized" : "restarted",
|
|
||||||
temp >> 8, temp & 0xff, DRIVER_VERSION);
|
temp >> 8, temp & 0xff, DRIVER_VERSION);
|
||||||
|
|
||||||
writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
|
writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
|
||||||
|
|
||||||
if (first)
|
/* GRR this is run-once init(), being done every time the HC starts.
|
||||||
create_debug_files (ehci);
|
* So long as they're part of class devices, we can't do it init()
|
||||||
|
* since the class device isn't created that early.
|
||||||
|
*/
|
||||||
|
create_debug_files(ehci);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,15 +58,76 @@ static int bios_handoff(struct ehci_hcd *ehci, int where, u32 cap)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* called by khubd or root hub init threads */
|
/* called after powerup, by probe or system-pm "wakeup" */
|
||||||
|
static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
u32 temp;
|
||||||
|
int retval;
|
||||||
|
unsigned count = 256/4;
|
||||||
|
|
||||||
|
/* optional debug port, normally in the first BAR */
|
||||||
|
temp = pci_find_capability(pdev, 0x0a);
|
||||||
|
if (temp) {
|
||||||
|
pci_read_config_dword(pdev, temp, &temp);
|
||||||
|
temp >>= 16;
|
||||||
|
if ((temp & (3 << 13)) == (1 << 13)) {
|
||||||
|
temp &= 0x1fff;
|
||||||
|
ehci->debug = ehci_to_hcd(ehci)->regs + temp;
|
||||||
|
temp = readl(&ehci->debug->control);
|
||||||
|
ehci_info(ehci, "debug port %d%s\n",
|
||||||
|
HCS_DEBUG_PORT(ehci->hcs_params),
|
||||||
|
(temp & DBGP_ENABLED)
|
||||||
|
? " IN USE"
|
||||||
|
: "");
|
||||||
|
if (!(temp & DBGP_ENABLED))
|
||||||
|
ehci->debug = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = HCC_EXT_CAPS(readl(&ehci->caps->hcc_params));
|
||||||
|
|
||||||
|
/* EHCI 0.96 and later may have "extended capabilities" */
|
||||||
|
while (temp && count--) {
|
||||||
|
u32 cap;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, temp, &cap);
|
||||||
|
ehci_dbg(ehci, "capability %04x at %02x\n", cap, temp);
|
||||||
|
switch (cap & 0xff) {
|
||||||
|
case 1: /* BIOS/SMM/... handoff */
|
||||||
|
if (bios_handoff(ehci, temp, cap) != 0)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
case 0: /* illegal reserved capability */
|
||||||
|
ehci_dbg(ehci, "illegal capability!\n");
|
||||||
|
cap = 0;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
default: /* unknown */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
temp = (cap >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
if (!count) {
|
||||||
|
ehci_err(ehci, "bogus capabilities ... PCI problems!\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
|
||||||
|
retval = pci_set_mwi(pdev);
|
||||||
|
if (!retval)
|
||||||
|
ehci_dbg(ehci, "MWI active\n");
|
||||||
|
|
||||||
|
ehci_port_power(ehci, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* called by khubd or root hub (re)init threads; leaves HC in halt state */
|
||||||
static int ehci_pci_reset(struct usb_hcd *hcd)
|
static int ehci_pci_reset(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||||
u32 temp;
|
u32 temp;
|
||||||
unsigned count = 256/4;
|
int retval;
|
||||||
|
|
||||||
spin_lock_init (&ehci->lock);
|
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
|
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
|
||||||
|
@ -76,6 +137,10 @@ static int ehci_pci_reset(struct usb_hcd *hcd)
|
||||||
/* cache this readonly data; minimize chip reads */
|
/* cache this readonly data; minimize chip reads */
|
||||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
||||||
|
|
||||||
|
retval = ehci_halt(ehci);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
/* NOTE: only the parts below this line are PCI-specific */
|
/* NOTE: only the parts below this line are PCI-specific */
|
||||||
|
|
||||||
switch (pdev->vendor) {
|
switch (pdev->vendor) {
|
||||||
|
@ -111,57 +176,9 @@ static int ehci_pci_reset(struct usb_hcd *hcd)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* optional debug port, normally in the first BAR */
|
|
||||||
temp = pci_find_capability(pdev, 0x0a);
|
|
||||||
if (temp) {
|
|
||||||
pci_read_config_dword(pdev, temp, &temp);
|
|
||||||
temp >>= 16;
|
|
||||||
if ((temp & (3 << 13)) == (1 << 13)) {
|
|
||||||
temp &= 0x1fff;
|
|
||||||
ehci->debug = hcd->regs + temp;
|
|
||||||
temp = readl(&ehci->debug->control);
|
|
||||||
ehci_info(ehci, "debug port %d%s\n",
|
|
||||||
HCS_DEBUG_PORT(ehci->hcs_params),
|
|
||||||
(temp & DBGP_ENABLED)
|
|
||||||
? " IN USE"
|
|
||||||
: "");
|
|
||||||
if (!(temp & DBGP_ENABLED))
|
|
||||||
ehci->debug = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = HCC_EXT_CAPS(readl(&ehci->caps->hcc_params));
|
|
||||||
|
|
||||||
/* EHCI 0.96 and later may have "extended capabilities" */
|
|
||||||
while (temp && count--) {
|
|
||||||
u32 cap;
|
|
||||||
|
|
||||||
pci_read_config_dword(to_pci_dev(hcd->self.controller),
|
|
||||||
temp, &cap);
|
|
||||||
ehci_dbg(ehci, "capability %04x at %02x\n", cap, temp);
|
|
||||||
switch (cap & 0xff) {
|
|
||||||
case 1: /* BIOS/SMM/... handoff */
|
|
||||||
if (bios_handoff(ehci, temp, cap) != 0)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
break;
|
|
||||||
case 0: /* illegal reserved capability */
|
|
||||||
ehci_warn(ehci, "illegal capability!\n");
|
|
||||||
cap = 0;
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
default: /* unknown */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
temp = (cap >> 8) & 0xff;
|
|
||||||
}
|
|
||||||
if (!count) {
|
|
||||||
ehci_err(ehci, "bogus capabilities ... PCI problems!\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
if (ehci_is_TDI(ehci))
|
if (ehci_is_TDI(ehci))
|
||||||
ehci_reset(ehci);
|
ehci_reset(ehci);
|
||||||
|
|
||||||
ehci_port_power(ehci, 0);
|
|
||||||
|
|
||||||
/* at least the Genesys GL880S needs fixup here */
|
/* at least the Genesys GL880S needs fixup here */
|
||||||
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
||||||
temp &= 0x0f;
|
temp &= 0x0f;
|
||||||
|
@ -184,39 +201,15 @@ static int ehci_pci_reset(struct usb_hcd *hcd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* force HC to halt state */
|
|
||||||
return ehci_halt(ehci);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ehci_pci_start(struct usb_hcd *hcd)
|
|
||||||
{
|
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
int result = 0;
|
|
||||||
struct pci_dev *pdev;
|
|
||||||
u16 port_wake;
|
|
||||||
|
|
||||||
pdev = to_pci_dev(hcd->self.controller);
|
|
||||||
|
|
||||||
/* Serial Bus Release Number is at PCI 0x60 offset */
|
/* Serial Bus Release Number is at PCI 0x60 offset */
|
||||||
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
|
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
|
||||||
|
|
||||||
/* port wake capability, reported by boot firmware */
|
/* REVISIT: per-port wake capability (PCI 0x62) currently unused */
|
||||||
pci_read_config_word(pdev, 0x62, &port_wake);
|
|
||||||
hcd->can_wakeup = (port_wake & 1) != 0;
|
|
||||||
|
|
||||||
/* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
|
retval = ehci_pci_reinit(ehci, pdev);
|
||||||
result = pci_set_mwi(pdev);
|
|
||||||
if (!result)
|
|
||||||
ehci_dbg(ehci, "MWI active\n");
|
|
||||||
|
|
||||||
return ehci_run(hcd);
|
/* finish init */
|
||||||
}
|
return ehci_init(hcd);
|
||||||
|
|
||||||
/* always called by thread; normally rmmod */
|
|
||||||
|
|
||||||
static void ehci_pci_stop(struct usb_hcd *hcd)
|
|
||||||
{
|
|
||||||
ehci_stop(hcd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -250,6 +243,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
unsigned port;
|
unsigned port;
|
||||||
struct usb_device *root = hcd->self.root_hub;
|
struct usb_device *root = hcd->self.root_hub;
|
||||||
|
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||||
int retval = -EINVAL;
|
int retval = -EINVAL;
|
||||||
|
|
||||||
// maybe restore FLADJ
|
// maybe restore FLADJ
|
||||||
|
@ -258,7 +252,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
|
||||||
msleep(100);
|
msleep(100);
|
||||||
|
|
||||||
/* If CF is clear, we lost PCI Vaux power and need to restart. */
|
/* If CF is clear, we lost PCI Vaux power and need to restart. */
|
||||||
if (readl(&ehci->regs->configured_flag) != cpu_to_le32(FLAG_CF))
|
if (readl(&ehci->regs->configured_flag) != FLAG_CF)
|
||||||
goto restart;
|
goto restart;
|
||||||
|
|
||||||
/* If any port is suspended (or owned by the companion),
|
/* If any port is suspended (or owned by the companion),
|
||||||
|
@ -292,7 +286,7 @@ restart:
|
||||||
*/
|
*/
|
||||||
(void) ehci_halt(ehci);
|
(void) ehci_halt(ehci);
|
||||||
(void) ehci_reset(ehci);
|
(void) ehci_reset(ehci);
|
||||||
(void) ehci_pci_reset(hcd);
|
(void) ehci_pci_reinit(ehci, pdev);
|
||||||
|
|
||||||
/* emptying the schedule aborts any urbs */
|
/* emptying the schedule aborts any urbs */
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
|
@ -304,9 +298,7 @@ restart:
|
||||||
/* restart; khubd will disconnect devices */
|
/* restart; khubd will disconnect devices */
|
||||||
retval = ehci_run(hcd);
|
retval = ehci_run(hcd);
|
||||||
|
|
||||||
/* here we "know" root ports should always stay powered;
|
/* here we "know" root ports should always stay powered */
|
||||||
* but some controllers may lose all power.
|
|
||||||
*/
|
|
||||||
ehci_port_power(ehci, 1);
|
ehci_port_power(ehci, 1);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -328,12 +320,12 @@ static const struct hc_driver ehci_pci_hc_driver = {
|
||||||
* basic lifecycle operations
|
* basic lifecycle operations
|
||||||
*/
|
*/
|
||||||
.reset = ehci_pci_reset,
|
.reset = ehci_pci_reset,
|
||||||
.start = ehci_pci_start,
|
.start = ehci_run,
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
.suspend = ehci_pci_suspend,
|
.suspend = ehci_pci_suspend,
|
||||||
.resume = ehci_pci_resume,
|
.resume = ehci_pci_resume,
|
||||||
#endif
|
#endif
|
||||||
.stop = ehci_pci_stop,
|
.stop = ehci_stop,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* managing i/o requests and associated device resources
|
* managing i/o requests and associated device resources
|
||||||
|
|
Loading…
Reference in a new issue