/*
* Copyright (C) 2001-2004 by David Brownell
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this file is part of ehci-hcd.c */
/*-------------------------------------------------------------------------*/
/*
* EHCI Root Hub ... the nonsharable stuff
*
* Registers don't need cpu_to_le32, that happens transparently
*/
/*-------------------------------------------------------------------------*/
#include <linux/usb/otg.h>
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
#ifdef CONFIG_PM
static int persist_enabled_on_companion(struct usb_device *udev, void *unused)
{
return !udev->maxchild && udev->persist_enabled &&
udev->bus->root_hub->speed < USB_SPEED_HIGH;
}
/* After a power loss, ports that were owned by the companion must be
* reset so that the companion can still own them.
*/
static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
{
u32 __iomem *reg;
u32 status;
int port;
__le32 buf;
struct usb_hcd *hcd = ehci_to_hcd(ehci);
if (!ehci->owned_ports)
return;
/*
* USB 1.1 devices are mostly HIDs, which don't need to persist across
* suspends. If we ensure that none of our companion's devices have
* persist_enabled (by looking through all USB 1.1 buses in the system),
* we can skip this and avoid slowing resume down. Devices without
* persist will just get reenumerated shortly after resume anyway.
*/
if (!usb_for_each_dev(NULL, persist_enabled_on_companion))
return;
/* Make sure the ports are powered */
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
if (!(status & PORT_POWER))
ehci_port_power(ehci, port, true);
}
}
/* Give the connections some time to appear */
msleep(20);
spin_lock_irq(&ehci->lock);
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
/* Port already owned by companion? */