Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56c1e26d authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman
Browse files

[PATCH] USB: ehci power fixes



Miscellaneous updates for EHCI.

 - Mostly updates the power switching on EHCI controllers.  One routine
   centralizes the "power on/off all ports" logic, and the capability to
   do that is reported more correctly.

 - Courtesy Colin Leroy, a patch to always power up ports after resumes
   which didn't keep a USB device suspended.  The reset-everything logic
   powers down those ports (on some hardware) so something needs to turn
   them back on.

 - Minor tweaks/bugfixes for the debug port support.

Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent e2e66446
Loading
Loading
Loading
Loading
+44 −21
Original line number Diff line number Diff line
@@ -346,6 +346,22 @@ ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
	return 0;
}

static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
{
	unsigned port;

	if (!HCS_PPC (ehci->hcs_params))
		return;

	ehci_dbg (ehci, "...power%s ports...\n", is_on ? "up" : "down");
	for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
		(void) ehci_hub_control(ehci_to_hcd(ehci),
				is_on ? SetPortFeature : ClearPortFeature,
				USB_PORT_FEAT_POWER,
				port--, NULL, 0);
	msleep(20);
}


/* called by khubd or root hub init threads */

@@ -362,8 +378,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
	dbg_hcs_params (ehci, "reset");
	dbg_hcc_params (ehci, "reset");

	/* cache this readonly data; minimize chip reads */
	ehci->hcs_params = readl (&ehci->caps->hcs_params);

#ifdef	CONFIG_PCI
	/* EHCI 0.96 and later may have "extended capabilities" */
	if (hcd->self.controller->bus == &pci_bus_type) {
		struct pci_dev	*pdev = to_pci_dev(hcd->self.controller);

@@ -383,9 +401,30 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
			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));
	} else
		temp = 0;

	/* EHCI 0.96 and later may have "extended capabilities" */
	while (temp && count--) {
		u32		cap;

@@ -414,8 +453,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
		ehci_reset (ehci);
#endif

	/* cache this readonly data; minimize PCI reads */
	ehci->hcs_params = readl (&ehci->caps->hcs_params);
	ehci_port_power (ehci, 0);

	/* at least the Genesys GL880S needs fixup here */
	temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
@@ -657,16 +695,11 @@ static int ehci_start (struct usb_hcd *hcd)
static void ehci_stop (struct usb_hcd *hcd)
{
	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
	u8			rh_ports, port;

	ehci_dbg (ehci, "stop\n");

	/* Turn off port power on all root hub ports. */
	rh_ports = HCS_N_PORTS (ehci->hcs_params);
	for (port = 1; port <= rh_ports; port++)
		(void) ehci_hub_control(hcd,
			ClearPortFeature, USB_PORT_FEAT_POWER,
			port, NULL, 0);
	ehci_port_power (ehci, 0);

	/* no more interrupts ... */
	del_timer_sync (&ehci->watchdog);
@@ -748,7 +781,6 @@ static int ehci_resume (struct usb_hcd *hcd)
	unsigned		port;
	struct usb_device	*root = hcd->self.root_hub;
	int			retval = -EINVAL;
	int			powerup = 0;

	// maybe restore (PCI) FLADJ

@@ -766,8 +798,6 @@ static int ehci_resume (struct usb_hcd *hcd)
			up (&hcd->self.root_hub->serialize);
			break;
		}
		if ((status & PORT_POWER) == 0)
			powerup = 1;
		if (!root->children [port])
			continue;
		dbg_port (ehci, __FUNCTION__, port + 1, status);
@@ -794,16 +824,9 @@ static int ehci_resume (struct usb_hcd *hcd)
		retval = ehci_start (hcd);

		/* here we "know" root ports should always stay powered;
		 * but some controllers may lost all power.
		 * but some controllers may lose all power.
		 */
		if (powerup) {
			ehci_dbg (ehci, "...powerup ports...\n");
			for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
				(void) ehci_hub_control(hcd,
					SetPortFeature, USB_PORT_FEAT_POWER,
						port--, NULL, 0);
			msleep(20);
		}
		ehci_port_power (ehci, 1);
	}

	return retval;
+2 −0
Original line number Diff line number Diff line
@@ -281,6 +281,8 @@ ehci_hub_descriptor (
	temp = 0x0008;			/* per-port overcurrent reporting */
	if (HCS_PPC (ehci->hcs_params))
		temp |= 0x0001;		/* per-port power control */
	else
		temp |= 0x0002;		/* no power switching */
#if 0
// re-enable when we support USB_PORT_FEAT_INDICATOR below.
	if (HCS_INDICATOR (ehci->hcs_params))
+10 −9
Original line number Diff line number Diff line
@@ -47,6 +47,12 @@ struct ehci_stats {
#define	EHCI_MAX_ROOT_PORTS	15		/* see HCS_N_PORTS */

struct ehci_hcd {			/* one per controller */
	/* glue to PCI and HCD framework */
	struct ehci_caps __iomem *caps;
	struct ehci_regs __iomem *regs;
	struct ehci_dbg_port __iomem *debug;

	__u32			hcs_params;	/* cached register copy */
	spinlock_t		lock;

	/* async schedule support */
@@ -84,11 +90,6 @@ struct ehci_hcd { /* one per controller */

	unsigned		is_tdi_rh_tt:1;	/* TDI roothub with TT */

	/* glue to PCI and HCD framework */
	struct ehci_caps __iomem *caps;
	struct ehci_regs __iomem *regs;
	__u32			hcs_params;	/* cached register copy */

	/* irq statistics */
#ifdef EHCI_STATS
	struct ehci_stats	stats;
@@ -273,7 +274,7 @@ struct ehci_dbg_port {
#define DBGP_ENABLED	(1<<28)
#define DBGP_DONE	(1<<16)
#define DBGP_INUSE	(1<<10)
#define DBGP_ERRCODE(x)	(((x)>>7)&0x0f)
#define DBGP_ERRCODE(x)	(((x)>>7)&0x07)
#	define DBGP_ERR_BAD	1
#	define DBGP_ERR_SIGNAL	2
#define DBGP_ERROR	(1<<6)
@@ -282,11 +283,11 @@ struct ehci_dbg_port {
#define DBGP_LEN(x)	(((x)>>0)&0x0f)
	u32	pids;
#define DBGP_PID_GET(x)		(((x)>>16)&0xff)
#define DBGP_PID_SET(data,tok)	(((data)<<8)|(tok));
#define DBGP_PID_SET(data,tok)	(((data)<<8)|(tok))
	u32	data03;
	u32	data47;
	u32	address;
#define DBGP_EPADDR(dev,ep)	(((dev)<<8)|(ep));
#define DBGP_EPADDR(dev,ep)	(((dev)<<8)|(ep))
} __attribute__ ((packed));

/*-------------------------------------------------------------------------*/