ehci-hub.c 26.6 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
D
David Brownell 已提交
2
 * Copyright (C) 2001-2004 by David Brownell
3
 *
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 * 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
 */

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

31 32
#define	PORT_WAKE_BITS	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)

33 34
#ifdef	CONFIG_PM

A
Alan Stern 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
static int ehci_hub_control(
	struct usb_hcd	*hcd,
	u16		typeReq,
	u16		wValue,
	u16		wIndex,
	char		*buf,
	u16		wLength
);

/* 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;

	/* Give the connections some time to appear */
	msleep(20);

	port = HCS_N_PORTS(ehci->hcs_params);
	while (port--) {
		if (test_bit(port, &ehci->owned_ports)) {
			reg = &ehci->regs->port_status[port];
65
			status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
A
Alan Stern 已提交
66 67 68 69

			/* Port already owned by companion? */
			if (status & PORT_OWNER)
				clear_bit(port, &ehci->owned_ports);
70 71
			else if (test_bit(port, &ehci->companion_ports))
				ehci_writel(ehci, status & ~PORT_PE, reg);
A
Alan Stern 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
			else
				ehci_hub_control(hcd, SetPortFeature,
						USB_PORT_FEAT_RESET, port + 1,
						NULL, 0);
		}
	}

	if (!ehci->owned_ports)
		return;
	msleep(90);		/* Wait for resets to complete */

	port = HCS_N_PORTS(ehci->hcs_params);
	while (port--) {
		if (test_bit(port, &ehci->owned_ports)) {
			ehci_hub_control(hcd, GetPortStatus,
					0, port + 1,
					(char *) &buf, sizeof(buf));

			/* The companion should now own the port,
			 * but if something went wrong the port must not
			 * remain enabled.
			 */
			reg = &ehci->regs->port_status[port];
			status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
			if (status & PORT_OWNER)
				ehci_writel(ehci, status | PORT_CSC, reg);
			else {
				ehci_dbg(ehci, "failed handover port %d: %x\n",
						port + 1, status);
				ehci_writel(ehci, status & ~PORT_PE, reg);
			}
		}
	}

	ehci->owned_ports = 0;
}

109
static int ehci_bus_suspend (struct usb_hcd *hcd)
L
Linus Torvalds 已提交
110 111 112
{
	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
	int			port;
113
	int			mask;
114
	u32 __iomem		*hostpc_reg = NULL;
L
Linus Torvalds 已提交
115

116 117
	ehci_dbg(ehci, "suspend root hub\n");

L
Linus Torvalds 已提交
118 119
	if (time_before (jiffies, ehci->next_statechange))
		msleep(5);
120 121
	del_timer_sync(&ehci->watchdog);
	del_timer_sync(&ehci->iaa_watchdog);
L
Linus Torvalds 已提交
122 123 124 125 126 127 128 129 130

	port = HCS_N_PORTS (ehci->hcs_params);
	spin_lock_irq (&ehci->lock);

	/* stop schedules, clean any completed work */
	if (HC_IS_RUNNING(hcd->state)) {
		ehci_quiesce (ehci);
		hcd->state = HC_STATE_QUIESCING;
	}
131
	ehci->command = ehci_readl(ehci, &ehci->regs->command);
132
	ehci_work(ehci);
L
Linus Torvalds 已提交
133

134 135 136 137 138 139
	/* Unlike other USB host controller types, EHCI doesn't have
	 * any notion of "global" or bus-wide suspend.  The driver has
	 * to manually suspend all the active unsuspended ports, and
	 * then manually resume them in the bus_resume() routine.
	 */
	ehci->bus_suspended = 0;
A
Alan Stern 已提交
140
	ehci->owned_ports = 0;
L
Linus Torvalds 已提交
141 142
	while (port--) {
		u32 __iomem	*reg = &ehci->regs->port_status [port];
143
		u32		t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
L
Linus Torvalds 已提交
144 145
		u32		t2 = t1;

146 147 148
		if (ehci->has_hostpc)
			hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
				+ HOSTPC0 + 4 * (port & 0xff));
149
		/* keep track of which ports we suspend */
A
Alan Stern 已提交
150 151 152
		if (t1 & PORT_OWNER)
			set_bit(port, &ehci->owned_ports);
		else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
L
Linus Torvalds 已提交
153
			t2 |= PORT_SUSPEND;
154 155 156 157
			set_bit(port, &ehci->bus_suspended);
		}

		/* enable remote wakeup on all ports */
158 159 160 161 162 163 164 165 166 167 168 169 170 171
		if (hcd->self.root_hub->do_remote_wakeup) {
			/* only enable appropriate wake bits, otherwise the
			 * hardware can not go phy low power mode. If a race
			 * condition happens here(connection change during bits
			 * set), the port change detection will finally fix it.
			 */
			if (t1 & PORT_CONNECT) {
				t2 |= PORT_WKOC_E | PORT_WKDISC_E;
				t2 &= ~PORT_WKCONN_E;
			} else {
				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
				t2 &= ~PORT_WKDISC_E;
			}
		} else
172
			t2 &= ~PORT_WAKE_BITS;
L
Linus Torvalds 已提交
173 174 175 176

		if (t1 != t2) {
			ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
				port + 1, t1, t2);
177
			ehci_writel(ehci, t2, reg);
178 179 180 181 182 183 184 185 186 187 188
			if (hostpc_reg) {
				u32	t3;

				msleep(5);/* 5ms for HCD enter low pwr mode */
				t3 = ehci_readl(ehci, hostpc_reg);
				ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
				t3 = ehci_readl(ehci, hostpc_reg);
				ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
					port, (t3 & HOSTPC_PHCD) ?
					"succeeded" : "failed");
			}
L
Linus Torvalds 已提交
189 190 191
		}
	}

192 193 194 195
	/* Apparently some devices need a >= 1-uframe delay here */
	if (ehci->bus_suspended)
		udelay(150);

L
Linus Torvalds 已提交
196 197 198 199
	/* turn off now-idle HC */
	ehci_halt (ehci);
	hcd->state = HC_STATE_SUSPENDED;

200 201 202
	if (ehci->reclaim)
		end_unlink_async(ehci);

203 204
	/* allow remote wakeup */
	mask = INTR_MASK;
205
	if (!hcd->self.root_hub->do_remote_wakeup)
206
		mask &= ~STS_PCD;
207 208
	ehci_writel(ehci, mask, &ehci->regs->intr_enable);
	ehci_readl(ehci, &ehci->regs->intr_enable);
209

L
Linus Torvalds 已提交
210 211
	ehci->next_statechange = jiffies + msecs_to_jiffies(10);
	spin_unlock_irq (&ehci->lock);
212 213 214 215 216

	/* ehci_work() may have re-enabled the watchdog timer, which we do not
	 * want, and so we must delete any pending watchdog timer events.
	 */
	del_timer_sync(&ehci->watchdog);
L
Linus Torvalds 已提交
217 218 219 220 221
	return 0;
}


/* caller has locked the root hub, and should reset/reinit on error */
222
static int ehci_bus_resume (struct usb_hcd *hcd)
L
Linus Torvalds 已提交
223 224 225
{
	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
	u32			temp;
A
Alan Stern 已提交
226
	u32			power_okay;
L
Linus Torvalds 已提交
227
	int			i;
228
	u8			resume_needed = 0;
L
Linus Torvalds 已提交
229 230 231 232

	if (time_before (jiffies, ehci->next_statechange))
		msleep(5);
	spin_lock_irq (&ehci->lock);
233 234 235 236
	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
		spin_unlock_irq(&ehci->lock);
		return -ESHUTDOWN;
	}
L
Linus Torvalds 已提交
237

238 239 240 241 242 243 244
	if (unlikely(ehci->debug)) {
		if (ehci->debug && !dbgp_reset_prep())
			ehci->debug = NULL;
		else
			dbgp_external_startup();
	}

D
David Brownell 已提交
245 246 247 248 249 250
	/* Ideally and we've got a real resume here, and no port's power
	 * was lost.  (For PCI, that means Vaux was maintained.)  But we
	 * could instead be restoring a swsusp snapshot -- so that BIOS was
	 * the last user of the controller, not reset/pm hardware keeping
	 * state we gave to it.
	 */
A
Alan Stern 已提交
251 252 253
	power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
	ehci_dbg(ehci, "resume root hub%s\n",
			power_okay ? "" : " after power loss");
D
David Brownell 已提交
254

255 256 257
	/* at least some APM implementations will try to deliver
	 * IRQs right away, so delay them until we're ready.
	 */
258
	ehci_writel(ehci, 0, &ehci->regs->intr_enable);
259 260

	/* re-init operational registers */
261 262 263
	ehci_writel(ehci, 0, &ehci->regs->segment);
	ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
	ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next);
L
Linus Torvalds 已提交
264 265

	/* restore CMD_RUN, framelist size, and irq threshold */
266
	ehci_writel(ehci, ehci->command, &ehci->regs->command);
L
Linus Torvalds 已提交
267

268 269
	/* Some controller/firmware combinations need a delay during which
	 * they set up the port statuses.  See Bugzilla #8190. */
270 271 272
	spin_unlock_irq(&ehci->lock);
	msleep(8);
	spin_lock_irq(&ehci->lock);
273

274
	/* manually resume the ports we suspended during bus_suspend() */
L
Linus Torvalds 已提交
275 276
	i = HCS_N_PORTS (ehci->hcs_params);
	while (i--) {
277
		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
278
		temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
279
		if (test_bit(i, &ehci->bus_suspended) &&
280
				(temp & PORT_SUSPEND)) {
L
Linus Torvalds 已提交
281
			temp |= PORT_RESUME;
282 283
			resume_needed = 1;
		}
284
		ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
L
Linus Torvalds 已提交
285
	}
286 287 288 289 290 291 292 293

	/* msleep for 20ms only if code is trying to resume port */
	if (resume_needed) {
		spin_unlock_irq(&ehci->lock);
		msleep(20);
		spin_lock_irq(&ehci->lock);
	}

L
Linus Torvalds 已提交
294 295
	i = HCS_N_PORTS (ehci->hcs_params);
	while (i--) {
296
		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
297 298 299
		if (test_bit(i, &ehci->bus_suspended) &&
				(temp & PORT_SUSPEND)) {
			temp &= ~(PORT_RWC_BITS | PORT_RESUME);
300
			ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
301 302
			ehci_vdbg (ehci, "resumed port %d\n", i + 1);
		}
L
Linus Torvalds 已提交
303
	}
304
	(void) ehci_readl(ehci, &ehci->regs->command);
L
Linus Torvalds 已提交
305 306 307 308 309 310 311 312 313

	/* maybe re-activate the schedule(s) */
	temp = 0;
	if (ehci->async->qh_next.qh)
		temp |= CMD_ASE;
	if (ehci->periodic_sched)
		temp |= CMD_PSE;
	if (temp) {
		ehci->command |= temp;
314
		ehci_writel(ehci, ehci->command, &ehci->regs->command);
L
Linus Torvalds 已提交
315 316 317 318 319 320
	}

	ehci->next_statechange = jiffies + msecs_to_jiffies(5);
	hcd->state = HC_STATE_RUNNING;

	/* Now we can safely re-enable irqs */
321
	ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
L
Linus Torvalds 已提交
322 323

	spin_unlock_irq (&ehci->lock);
324
	ehci_handover_companion_ports(ehci);
L
Linus Torvalds 已提交
325 326 327 328 329
	return 0;
}

#else

330 331
#define ehci_bus_suspend	NULL
#define ehci_bus_resume		NULL
L
Linus Torvalds 已提交
332 333 334

#endif	/* CONFIG_PM */

335 336 337
/*-------------------------------------------------------------------------*/

/* Display the ports dedicated to the companion controller */
338 339 340
static ssize_t show_companion(struct device *dev,
			      struct device_attribute *attr,
			      char *buf)
341 342 343 344 345 346
{
	struct ehci_hcd		*ehci;
	int			nports, index, n;
	int			count = PAGE_SIZE;
	char			*ptr = buf;

347
	ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
348 349 350 351 352 353 354 355 356 357 358 359 360
	nports = HCS_N_PORTS(ehci->hcs_params);

	for (index = 0; index < nports; ++index) {
		if (test_bit(index, &ehci->companion_ports)) {
			n = scnprintf(ptr, count, "%d\n", index + 1);
			ptr += n;
			count -= n;
		}
	}
	return ptr - buf;
}

/*
361
 * Sets the owner of a port
362
 */
363
static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
364 365 366
{
	u32 __iomem		*status_reg;
	u32			port_status;
367
	int 			try;
368

369
	status_reg = &ehci->regs->port_status[portnum];
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391

	/*
	 * The controller won't set the OWNER bit if the port is
	 * enabled, so this loop will sometimes require at least two
	 * iterations: one to disable the port and one to set OWNER.
	 */
	for (try = 4; try > 0; --try) {
		spin_lock_irq(&ehci->lock);
		port_status = ehci_readl(ehci, status_reg);
		if ((port_status & PORT_OWNER) == new_owner
				|| (port_status & (PORT_OWNER | PORT_CONNECT))
					== 0)
			try = 0;
		else {
			port_status ^= PORT_OWNER;
			port_status &= ~(PORT_PE | PORT_RWC_BITS);
			ehci_writel(ehci, port_status, status_reg);
		}
		spin_unlock_irq(&ehci->lock);
		if (try > 1)
			msleep(5);
	}
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
}

/*
 * Dedicate or undedicate a port to the companion controller.
 * Syntax is "[-]portnum", where a leading '-' sign means
 * return control of the port to the EHCI controller.
 */
static ssize_t store_companion(struct device *dev,
			       struct device_attribute *attr,
			       const char *buf, size_t count)
{
	struct ehci_hcd		*ehci;
	int			portnum, new_owner;

	ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
	new_owner = PORT_OWNER;		/* Owned by companion */
	if (sscanf(buf, "%d", &portnum) != 1)
		return -EINVAL;
	if (portnum < 0) {
		portnum = - portnum;
		new_owner = 0;		/* Owned by EHCI */
	}
	if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
		return -ENOENT;
	portnum--;
	if (new_owner)
		set_bit(portnum, &ehci->companion_ports);
	else
		clear_bit(portnum, &ehci->companion_ports);
	set_owner(ehci, portnum, new_owner);
422 423
	return count;
}
424
static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
425 426 427 428 429 430 431

static inline void create_companion_file(struct ehci_hcd *ehci)
{
	int	i;

	/* with integrated TT there is no companion! */
	if (!ehci_is_TDI(ehci))
432
		i = device_create_file(ehci_to_hcd(ehci)->self.controller,
433
				       &dev_attr_companion);
434 435 436 437 438 439
}

static inline void remove_companion_file(struct ehci_hcd *ehci)
{
	/* with integrated TT there is no companion! */
	if (!ehci_is_TDI(ehci))
440
		device_remove_file(ehci_to_hcd(ehci)->self.controller,
441
				   &dev_attr_companion);
442 443 444
}


L
Linus Torvalds 已提交
445 446 447 448 449
/*-------------------------------------------------------------------------*/

static int check_reset_complete (
	struct ehci_hcd	*ehci,
	int		index,
450
	u32 __iomem	*status_reg,
L
Linus Torvalds 已提交
451 452
	int		port_status
) {
453
	if (!(port_status & PORT_CONNECT))
L
Linus Torvalds 已提交
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
		return port_status;

	/* if reset finished and it's still not enabled -- handoff */
	if (!(port_status & PORT_PE)) {

		/* with integrated TT, there's nobody to hand it to! */
		if (ehci_is_TDI(ehci)) {
			ehci_dbg (ehci,
				"Failed to enable port %d on root hub TT\n",
				index+1);
			return port_status;
		}

		ehci_dbg (ehci, "port %d full speed --> companion\n",
			index + 1);

		// what happens if HCS_N_CC(params) == 0 ?
		port_status |= PORT_OWNER;
D
David Brownell 已提交
472
		port_status &= ~PORT_RWC_BITS;
473
		ehci_writel(ehci, port_status, status_reg);
L
Linus Torvalds 已提交
474

475 476 477 478
		/* ensure 440EPX ohci controller state is operational */
		if (ehci->has_amcc_usb23)
			set_ohci_hcfs(ehci, 1);
	} else {
L
Linus Torvalds 已提交
479
		ehci_dbg (ehci, "port %d high speed\n", index + 1);
480 481 482 483
		/* ensure 440EPx ohci controller state is suspended */
		if (ehci->has_amcc_usb23)
			set_ohci_hcfs(ehci, 0);
	}
L
Linus Torvalds 已提交
484 485 486 487 488 489 490 491 492 493 494 495 496 497

	return port_status;
}

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


/* build "status change" packet (one or two bytes) from HC registers */

static int
ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
	u32		temp, status = 0;
498
	u32		mask;
L
Linus Torvalds 已提交
499 500 501 502 503 504 505 506 507 508 509 510 511 512
	int		ports, i, retval = 1;
	unsigned long	flags;

	/* if !USB_SUSPEND, root hub timers won't get shut down ... */
	if (!HC_IS_RUNNING(hcd->state))
		return 0;

	/* init status to no-changes */
	buf [0] = 0;
	ports = HCS_N_PORTS (ehci->hcs_params);
	if (ports > 7) {
		buf [1] = 0;
		retval++;
	}
513

514 515
	/* Some boards (mostly VIA?) report bogus overcurrent indications,
	 * causing massive log spam unless we completely ignore them.  It
516
	 * may be relevant that VIA VT8235 controllers, where PORT_POWER is
517 518 519 520 521 522 523 524 525
	 * always set, seem to clear PORT_OCC and PORT_CSC when writing to
	 * PORT_POWER; that's surprising, but maybe within-spec.
	 */
	if (!ignore_oc)
		mask = PORT_CSC | PORT_PEC | PORT_OCC;
	else
		mask = PORT_CSC | PORT_PEC;
	// PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND

L
Linus Torvalds 已提交
526 527 528 529 530
	/* no hub change reports (bit 0) for now (power, ...) */

	/* port N changes (bit N)? */
	spin_lock_irqsave (&ehci->lock, flags);
	for (i = 0; i < ports; i++) {
531
		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
532 533 534 535 536 537 538 539

		/*
		 * Return status information even for ports with OWNER set.
		 * Otherwise khubd wouldn't see the disconnect event when a
		 * high-speed device is switched over to the companion
		 * controller by the user.
		 */

540 541 542
		if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
				|| (ehci->reset_done[i] && time_after_eq(
					jiffies, ehci->reset_done[i]))) {
L
Linus Torvalds 已提交
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
			if (i < 7)
			    buf [0] |= 1 << (i + 1);
			else
			    buf [1] |= 1 << (i - 7);
			status = STS_PCD;
		}
	}
	/* FIXME autosuspend idle root hubs */
	spin_unlock_irqrestore (&ehci->lock, flags);
	return status ? retval : 0;
}

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

static void
ehci_hub_descriptor (
	struct ehci_hcd			*ehci,
	struct usb_hub_descriptor	*desc
) {
	int		ports = HCS_N_PORTS (ehci->hcs_params);
	u16		temp;

	desc->bDescriptorType = 0x29;
	desc->bPwrOn2PwrGood = 10;	/* ehci 1.0, 2.3.9 says 20ms max */
	desc->bHubContrCurrent = 0;

	desc->bNbrPorts = ports;
	temp = 1 + (ports / 8);
	desc->bDescLength = 7 + 2 * temp;

	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
	memset (&desc->bitmap [0], 0, temp);
	memset (&desc->bitmap [temp], 0xff, temp);

	temp = 0x0008;			/* per-port overcurrent reporting */
	if (HCS_PPC (ehci->hcs_params))
		temp |= 0x0001;		/* per-port power control */
D
David Brownell 已提交
580 581
	else
		temp |= 0x0002;		/* no power switching */
L
Linus Torvalds 已提交
582 583 584 585 586
#if 0
// re-enable when we support USB_PORT_FEAT_INDICATOR below.
	if (HCS_INDICATOR (ehci->hcs_params))
		temp |= 0x0080;		/* per-port indicators (LEDs) */
#endif
A
Al Viro 已提交
587
	desc->wHubCharacteristics = cpu_to_le16(temp);
L
Linus Torvalds 已提交
588 589 590 591 592 593 594 595 596 597 598 599 600 601
}

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

static int ehci_hub_control (
	struct usb_hcd	*hcd,
	u16		typeReq,
	u16		wValue,
	u16		wIndex,
	char		*buf,
	u16		wLength
) {
	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
	int		ports = HCS_N_PORTS (ehci->hcs_params);
A
Alan Stern 已提交
602 603
	u32 __iomem	*status_reg = &ehci->regs->port_status[
				(wIndex & 0xff) - 1];
604 605
	u32 __iomem	*hostpc_reg = NULL;
	u32		temp, temp1, status;
L
Linus Torvalds 已提交
606 607
	unsigned long	flags;
	int		retval = 0;
608
	unsigned	selector;
L
Linus Torvalds 已提交
609 610 611 612 613 614 615 616

	/*
	 * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
	 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
	 * (track current state ourselves) ... blink for diagnostics,
	 * power, "this is the one", etc.  EHCI spec supports this.
	 */

617 618 619
	if (ehci->has_hostpc)
		hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
				+ HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
L
Linus Torvalds 已提交
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
	spin_lock_irqsave (&ehci->lock, flags);
	switch (typeReq) {
	case ClearHubFeature:
		switch (wValue) {
		case C_HUB_LOCAL_POWER:
		case C_HUB_OVER_CURRENT:
			/* no hub-wide feature/status flags */
			break;
		default:
			goto error;
		}
		break;
	case ClearPortFeature:
		if (!wIndex || wIndex > ports)
			goto error;
		wIndex--;
636
		temp = ehci_readl(ehci, status_reg);
637 638 639 640 641 642 643

		/*
		 * Even if OWNER is set, so the port is owned by the
		 * companion controller, khubd needs to be able to clear
		 * the port-change status bits (especially
		 * USB_PORT_FEAT_C_CONNECTION).
		 */
L
Linus Torvalds 已提交
644 645 646

		switch (wValue) {
		case USB_PORT_FEAT_ENABLE:
647
			ehci_writel(ehci, temp & ~PORT_PE, status_reg);
L
Linus Torvalds 已提交
648 649
			break;
		case USB_PORT_FEAT_C_ENABLE:
650
			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_PEC,
651
					status_reg);
L
Linus Torvalds 已提交
652 653 654 655
			break;
		case USB_PORT_FEAT_SUSPEND:
			if (temp & PORT_RESET)
				goto error;
D
David Brownell 已提交
656 657
			if (ehci->no_selective_suspend)
				break;
L
Linus Torvalds 已提交
658 659 660 661
			if (temp & PORT_SUSPEND) {
				if ((temp & PORT_PE) == 0)
					goto error;
				/* resume signaling for 20 msec */
D
David Brownell 已提交
662
				temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
663
				ehci_writel(ehci, temp | PORT_RESUME,
664
						status_reg);
L
Linus Torvalds 已提交
665 666 667 668 669
				ehci->reset_done [wIndex] = jiffies
						+ msecs_to_jiffies (20);
			}
			break;
		case USB_PORT_FEAT_C_SUSPEND:
670
			clear_bit(wIndex, &ehci->port_c_suspend);
L
Linus Torvalds 已提交
671 672 673
			break;
		case USB_PORT_FEAT_POWER:
			if (HCS_PPC (ehci->hcs_params))
674 675
				ehci_writel(ehci,
					  temp & ~(PORT_RWC_BITS | PORT_POWER),
676
					  status_reg);
L
Linus Torvalds 已提交
677 678
			break;
		case USB_PORT_FEAT_C_CONNECTION:
679
			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC,
680
					status_reg);
L
Linus Torvalds 已提交
681 682
			break;
		case USB_PORT_FEAT_C_OVER_CURRENT:
683
			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_OCC,
684
					status_reg);
L
Linus Torvalds 已提交
685 686 687 688 689 690 691
			break;
		case USB_PORT_FEAT_C_RESET:
			/* GetPortStatus clears reset */
			break;
		default:
			goto error;
		}
692
		ehci_readl(ehci, &ehci->regs->command);	/* unblock posted write */
L
Linus Torvalds 已提交
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
		break;
	case GetHubDescriptor:
		ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
			buf);
		break;
	case GetHubStatus:
		/* no hub-wide feature/status flags */
		memset (buf, 0, 4);
		//cpu_to_le32s ((u32 *) buf);
		break;
	case GetPortStatus:
		if (!wIndex || wIndex > ports)
			goto error;
		wIndex--;
		status = 0;
708
		temp = ehci_readl(ehci, status_reg);
L
Linus Torvalds 已提交
709 710 711 712 713 714

		// wPortChange bits
		if (temp & PORT_CSC)
			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
		if (temp & PORT_PEC)
			status |= 1 << USB_PORT_FEAT_C_ENABLE;
715 716

		if ((temp & PORT_OCC) && !ignore_oc){
L
Linus Torvalds 已提交
717 718
			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;

719 720 721 722 723 724 725 726 727 728 729 730 731 732
			/*
			 * Hubs should disable port power on over-current.
			 * However, not all EHCI implementations do this
			 * automatically, even if they _do_ support per-port
			 * power switching; they're allowed to just limit the
			 * current.  khubd will turn the power back on.
			 */
			if (HCS_PPC (ehci->hcs_params)){
				ehci_writel(ehci,
					temp & ~(PORT_RWC_BITS | PORT_POWER),
					status_reg);
			}
		}

L
Linus Torvalds 已提交
733
		/* whoever resumes must GetPortStatus to complete it!! */
734 735 736 737 738 739 740 741 742 743 744
		if (temp & PORT_RESUME) {

			/* Remote Wakeup received? */
			if (!ehci->reset_done[wIndex]) {
				/* resume signaling for 20 msec */
				ehci->reset_done[wIndex] = jiffies
						+ msecs_to_jiffies(20);
				/* check the port again */
				mod_timer(&ehci_to_hcd(ehci)->rh_timer,
						ehci->reset_done[wIndex]);
			}
L
Linus Torvalds 已提交
745

746 747 748
			/* resume completed? */
			else if (time_after_eq(jiffies,
					ehci->reset_done[wIndex])) {
749
				clear_bit(wIndex, &ehci->suspended_ports);
750
				set_bit(wIndex, &ehci->port_c_suspend);
751 752 753 754 755
				ehci->reset_done[wIndex] = 0;

				/* stop resume signaling */
				temp = ehci_readl(ehci, status_reg);
				ehci_writel(ehci,
756 757
					temp & ~(PORT_RWC_BITS | PORT_RESUME),
					status_reg);
758
				retval = handshake(ehci, status_reg,
759
					   PORT_RESUME, 0, 2000 /* 2msec */);
760 761 762 763 764 765 766
				if (retval != 0) {
					ehci_err(ehci,
						"port %d resume error %d\n",
						wIndex + 1, retval);
					goto error;
				}
				temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
L
Linus Torvalds 已提交
767 768 769 770 771
			}
		}

		/* whoever resets must GetPortStatus to complete it!! */
		if ((temp & PORT_RESET)
772 773
				&& time_after_eq(jiffies,
					ehci->reset_done[wIndex])) {
L
Linus Torvalds 已提交
774 775 776 777
			status |= 1 << USB_PORT_FEAT_C_RESET;
			ehci->reset_done [wIndex] = 0;

			/* force reset to complete */
778
			ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
779
					status_reg);
780 781 782
			/* REVISIT:  some hardware needs 550+ usec to clear
			 * this bit; seems too long to spin routinely...
			 */
783
			retval = handshake(ehci, status_reg,
784
					PORT_RESET, 0, 750);
L
Linus Torvalds 已提交
785 786 787 788 789 790 791
			if (retval != 0) {
				ehci_err (ehci, "port %d reset error %d\n",
					wIndex + 1, retval);
				goto error;
			}

			/* see what we found out */
792 793
			temp = check_reset_complete (ehci, wIndex, status_reg,
					ehci_readl(ehci, status_reg));
L
Linus Torvalds 已提交
794 795
		}

796 797 798
		if (!(temp & (PORT_RESUME|PORT_RESET)))
			ehci->reset_done[wIndex] = 0;

799 800 801 802 803 804 805 806 807 808
		/* transfer dedicated ports to the companion hc */
		if ((temp & PORT_CONNECT) &&
				test_bit(wIndex, &ehci->companion_ports)) {
			temp &= ~PORT_RWC_BITS;
			temp |= PORT_OWNER;
			ehci_writel(ehci, temp, status_reg);
			ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1);
			temp = ehci_readl(ehci, status_reg);
		}

809 810 811 812 813 814 815 816 817
		/*
		 * Even if OWNER is set, there's no harm letting khubd
		 * see the wPortStatus values (they should all be 0 except
		 * for PORT_POWER anyway).
		 */

		if (temp & PORT_CONNECT) {
			status |= 1 << USB_PORT_FEAT_CONNECTION;
			// status may be from integrated TT
818 819 820 821 822
			if (ehci->has_hostpc) {
				temp1 = ehci_readl(ehci, hostpc_reg);
				status |= ehci_port_speed(ehci, temp1);
			} else
				status |= ehci_port_speed(ehci, temp);
L
Linus Torvalds 已提交
823
		}
824 825
		if (temp & PORT_PE)
			status |= 1 << USB_PORT_FEAT_ENABLE;
826 827 828

		/* maybe the port was unsuspended without our knowledge */
		if (temp & (PORT_SUSPEND|PORT_RESUME)) {
829
			status |= 1 << USB_PORT_FEAT_SUSPEND;
830 831 832 833 834 835 836
		} else if (test_bit(wIndex, &ehci->suspended_ports)) {
			clear_bit(wIndex, &ehci->suspended_ports);
			ehci->reset_done[wIndex] = 0;
			if (temp & PORT_PE)
				set_bit(wIndex, &ehci->port_c_suspend);
		}

837 838 839 840 841 842
		if (temp & PORT_OC)
			status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
		if (temp & PORT_RESET)
			status |= 1 << USB_PORT_FEAT_RESET;
		if (temp & PORT_POWER)
			status |= 1 << USB_PORT_FEAT_POWER;
843 844
		if (test_bit(wIndex, &ehci->port_c_suspend))
			status |= 1 << USB_PORT_FEAT_C_SUSPEND;
L
Linus Torvalds 已提交
845

D
David Brownell 已提交
846
#ifndef	VERBOSE_DEBUG
L
Linus Torvalds 已提交
847 848 849
	if (status & ~0xffff)	/* only if wPortChange is interesting */
#endif
		dbg_port (ehci, "GetStatus", wIndex + 1, temp);
850
		put_unaligned_le32(status, buf);
L
Linus Torvalds 已提交
851 852 853 854 855 856 857 858 859 860 861 862
		break;
	case SetHubFeature:
		switch (wValue) {
		case C_HUB_LOCAL_POWER:
		case C_HUB_OVER_CURRENT:
			/* no hub-wide feature/status flags */
			break;
		default:
			goto error;
		}
		break;
	case SetPortFeature:
863 864
		selector = wIndex >> 8;
		wIndex &= 0xff;
865 866 867 868 869 870 871 872 873
		if (unlikely(ehci->debug)) {
			/* If the debug port is active any port
			 * feature requests should get denied */
			if (wIndex == HCS_DEBUG_PORT(ehci->hcs_params) &&
			    (readl(&ehci->debug->control) & DBGP_ENABLED)) {
				retval = -ENODEV;
				goto error_exit;
			}
		}
L
Linus Torvalds 已提交
874 875 876
		if (!wIndex || wIndex > ports)
			goto error;
		wIndex--;
877
		temp = ehci_readl(ehci, status_reg);
L
Linus Torvalds 已提交
878 879 880
		if (temp & PORT_OWNER)
			break;

D
David Brownell 已提交
881
		temp &= ~PORT_RWC_BITS;
L
Linus Torvalds 已提交
882 883
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
D
David Brownell 已提交
884 885
			if (ehci->no_selective_suspend)
				break;
L
Linus Torvalds 已提交
886 887 888
			if ((temp & PORT_PE) == 0
					|| (temp & PORT_RESET) != 0)
				goto error;
889
			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
			/* After above check the port must be connected.
			 * Set appropriate bit thus could put phy into low power
			 * mode if we have hostpc feature
			 */
			if (hostpc_reg) {
				temp &= ~PORT_WKCONN_E;
				temp |= (PORT_WKDISC_E | PORT_WKOC_E);
				ehci_writel(ehci, temp | PORT_SUSPEND,
							status_reg);
				msleep(5);/* 5ms for HCD enter low pwr mode */
				temp1 = ehci_readl(ehci, hostpc_reg);
				ehci_writel(ehci, temp1 | HOSTPC_PHCD,
					hostpc_reg);
				temp1 = ehci_readl(ehci, hostpc_reg);
				ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
					wIndex, (temp1 & HOSTPC_PHCD) ?
					"succeeded" : "failed");
			}
908
			set_bit(wIndex, &ehci->suspended_ports);
L
Linus Torvalds 已提交
909 910 911
			break;
		case USB_PORT_FEAT_POWER:
			if (HCS_PPC (ehci->hcs_params))
912
				ehci_writel(ehci, temp | PORT_POWER,
913
						status_reg);
L
Linus Torvalds 已提交
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
			break;
		case USB_PORT_FEAT_RESET:
			if (temp & PORT_RESUME)
				goto error;
			/* line status bits may report this as low speed,
			 * which can be fine if this root hub has a
			 * transaction translator built in.
			 */
			if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
					&& !ehci_is_TDI(ehci)
					&& PORT_USB11 (temp)) {
				ehci_dbg (ehci,
					"port %d low speed --> companion\n",
					wIndex + 1);
				temp |= PORT_OWNER;
			} else {
				ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
				temp |= PORT_RESET;
				temp &= ~PORT_PE;

				/*
				 * caller must wait, then call GetPortStatus
				 * usb 2.0 spec says 50 ms resets on root
				 */
				ehci->reset_done [wIndex] = jiffies
						+ msecs_to_jiffies (50);
			}
941
			ehci_writel(ehci, temp, status_reg);
L
Linus Torvalds 已提交
942
			break;
943 944 945 946 947 948 949 950 951 952 953 954 955

		/* For downstream facing ports (these):  one hub port is put
		 * into test mode according to USB2 11.24.2.13, then the hub
		 * must be reset (which for root hub now means rmmod+modprobe,
		 * or else system reboot).  See EHCI 2.3.9 and 4.14 for info
		 * about the EHCI-specific stuff.
		 */
		case USB_PORT_FEAT_TEST:
			if (!selector || selector > 5)
				goto error;
			ehci_quiesce(ehci);
			ehci_halt(ehci);
			temp |= selector << 16;
956
			ehci_writel(ehci, temp, status_reg);
957 958
			break;

L
Linus Torvalds 已提交
959 960 961
		default:
			goto error;
		}
962
		ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
L
Linus Torvalds 已提交
963 964 965 966 967 968 969
		break;

	default:
error:
		/* "stall" on error */
		retval = -EPIPE;
	}
970
error_exit:
L
Linus Torvalds 已提交
971 972 973
	spin_unlock_irqrestore (&ehci->lock, flags);
	return retval;
}
974 975 976 977 978 979 980 981 982 983

static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
{
	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);

	if (ehci_is_TDI(ehci))
		return;
	set_owner(ehci, --portnum, PORT_OWNER);
}

984 985 986 987 988 989 990 991 992 993
static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
{
	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
	u32 __iomem		*reg;

	if (ehci_is_TDI(ehci))
		return 0;
	reg = &ehci->regs->port_status[portnum - 1];
	return ehci_readl(ehci, reg) & PORT_OWNER;
}