ehci-hub.c 27.2 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 * Copyright (C) 2001-2004 by David Brownell
3
 *
Linus Torvalds's avatar
Linus Torvalds committed
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

Alan Stern's avatar
Alan Stern committed
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;
Alan Stern's avatar
Alan Stern committed
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);
Alan Stern's avatar
Alan Stern committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
110
111
112
{
	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
	int			port;
113
	int			mask;
114
	u32 __iomem		*hostpc_reg = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
115

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

Linus Torvalds's avatar
Linus Torvalds committed
118
119
	if (time_before (jiffies, ehci->next_statechange))
		msleep(5);
120
121
	del_timer_sync(&ehci->watchdog);
	del_timer_sync(&ehci->iaa_watchdog);
Linus Torvalds's avatar
Linus Torvalds committed
122
123
124

	spin_lock_irq (&ehci->lock);

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	/* Once the controller is stopped, port resumes that are already
	 * in progress won't complete.  Hence if remote wakeup is enabled
	 * for the root hub and any ports are in the middle of a resume or
	 * remote wakeup, we must fail the suspend.
	 */
	if (hcd->self.root_hub->do_remote_wakeup) {
		port = HCS_N_PORTS(ehci->hcs_params);
		while (port--) {
			if (ehci->reset_done[port] != 0) {
				spin_unlock_irq(&ehci->lock);
				ehci_dbg(ehci, "suspend failed because "
						"port %d is resuming\n",
						port + 1);
				return -EBUSY;
			}
		}
	}

Linus Torvalds's avatar
Linus Torvalds committed
143
144
145
146
147
	/* stop schedules, clean any completed work */
	if (HC_IS_RUNNING(hcd->state)) {
		ehci_quiesce (ehci);
		hcd->state = HC_STATE_QUIESCING;
	}
148
	ehci->command = ehci_readl(ehci, &ehci->regs->command);
149
	ehci_work(ehci);
Linus Torvalds's avatar
Linus Torvalds committed
150

151
152
153
154
155
156
	/* 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;
Alan Stern's avatar
Alan Stern committed
157
	ehci->owned_ports = 0;
158
	port = HCS_N_PORTS(ehci->hcs_params);
Linus Torvalds's avatar
Linus Torvalds committed
159
160
	while (port--) {
		u32 __iomem	*reg = &ehci->regs->port_status [port];
161
		u32		t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
Linus Torvalds's avatar
Linus Torvalds committed
162
163
		u32		t2 = t1;

164
165
166
		if (ehci->has_hostpc)
			hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
				+ HOSTPC0 + 4 * (port & 0xff));
167
		/* keep track of which ports we suspend */
Alan Stern's avatar
Alan Stern committed
168
169
170
		if (t1 & PORT_OWNER)
			set_bit(port, &ehci->owned_ports);
		else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
Linus Torvalds's avatar
Linus Torvalds committed
171
			t2 |= PORT_SUSPEND;
172
173
174
175
			set_bit(port, &ehci->bus_suspended);
		}

		/* enable remote wakeup on all ports */
176
177
178
179
180
181
182
183
184
185
186
187
188
189
		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
190
			t2 &= ~PORT_WAKE_BITS;
Linus Torvalds's avatar
Linus Torvalds committed
191
192
193
194

		if (t1 != t2) {
			ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
				port + 1, t1, t2);
195
			ehci_writel(ehci, t2, reg);
196
197
198
199
200
201
202
203
204
205
206
			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");
			}
Linus Torvalds's avatar
Linus Torvalds committed
207
208
209
		}
	}

210
211
212
213
	/* Apparently some devices need a >= 1-uframe delay here */
	if (ehci->bus_suspended)
		udelay(150);

Linus Torvalds's avatar
Linus Torvalds committed
214
215
216
217
	/* turn off now-idle HC */
	ehci_halt (ehci);
	hcd->state = HC_STATE_SUSPENDED;

218
219
220
	if (ehci->reclaim)
		end_unlink_async(ehci);

221
222
	/* allow remote wakeup */
	mask = INTR_MASK;
223
	if (!hcd->self.root_hub->do_remote_wakeup)
224
		mask &= ~STS_PCD;
225
226
	ehci_writel(ehci, mask, &ehci->regs->intr_enable);
	ehci_readl(ehci, &ehci->regs->intr_enable);
227

Linus Torvalds's avatar
Linus Torvalds committed
228
229
	ehci->next_statechange = jiffies + msecs_to_jiffies(10);
	spin_unlock_irq (&ehci->lock);
230
231
232
233
234

	/* 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);
Linus Torvalds's avatar
Linus Torvalds committed
235
236
237
238
239
	return 0;
}


/* caller has locked the root hub, and should reset/reinit on error */
240
static int ehci_bus_resume (struct usb_hcd *hcd)
Linus Torvalds's avatar
Linus Torvalds committed
241
242
243
{
	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
	u32			temp;
Alan Stern's avatar
Alan Stern committed
244
	u32			power_okay;
Linus Torvalds's avatar
Linus Torvalds committed
245
	int			i;
246
	u8			resume_needed = 0;
Linus Torvalds's avatar
Linus Torvalds committed
247
248
249
250

	if (time_before (jiffies, ehci->next_statechange))
		msleep(5);
	spin_lock_irq (&ehci->lock);
251
252
253
254
	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
		spin_unlock_irq(&ehci->lock);
		return -ESHUTDOWN;
	}
Linus Torvalds's avatar
Linus Torvalds committed
255

256
	if (unlikely(ehci->debug)) {
257
		if (!dbgp_reset_prep())
258
259
260
261
262
			ehci->debug = NULL;
		else
			dbgp_external_startup();
	}

David Brownell's avatar
David Brownell committed
263
264
265
266
267
268
	/* 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.
	 */
Alan Stern's avatar
Alan Stern committed
269
270
271
	power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
	ehci_dbg(ehci, "resume root hub%s\n",
			power_okay ? "" : " after power loss");
David Brownell's avatar
David Brownell committed
272

273
274
275
	/* at least some APM implementations will try to deliver
	 * IRQs right away, so delay them until we're ready.
	 */
276
	ehci_writel(ehci, 0, &ehci->regs->intr_enable);
277
278

	/* re-init operational registers */
279
280
281
	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);
Linus Torvalds's avatar
Linus Torvalds committed
282
283

	/* restore CMD_RUN, framelist size, and irq threshold */
284
	ehci_writel(ehci, ehci->command, &ehci->regs->command);
Linus Torvalds's avatar
Linus Torvalds committed
285

286
287
	/* Some controller/firmware combinations need a delay during which
	 * they set up the port statuses.  See Bugzilla #8190. */
288
289
290
	spin_unlock_irq(&ehci->lock);
	msleep(8);
	spin_lock_irq(&ehci->lock);
291

292
	/* manually resume the ports we suspended during bus_suspend() */
Linus Torvalds's avatar
Linus Torvalds committed
293
294
	i = HCS_N_PORTS (ehci->hcs_params);
	while (i--) {
295
		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
296
		temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
297
		if (test_bit(i, &ehci->bus_suspended) &&
298
				(temp & PORT_SUSPEND)) {
Linus Torvalds's avatar
Linus Torvalds committed
299
			temp |= PORT_RESUME;
300
301
			resume_needed = 1;
		}
302
		ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
Linus Torvalds's avatar
Linus Torvalds committed
303
	}
304
305
306
307
308
309
310
311

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

Linus Torvalds's avatar
Linus Torvalds committed
312
313
	i = HCS_N_PORTS (ehci->hcs_params);
	while (i--) {
314
		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
315
316
317
		if (test_bit(i, &ehci->bus_suspended) &&
				(temp & PORT_SUSPEND)) {
			temp &= ~(PORT_RWC_BITS | PORT_RESUME);
318
			ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
319
320
			ehci_vdbg (ehci, "resumed port %d\n", i + 1);
		}
Linus Torvalds's avatar
Linus Torvalds committed
321
	}
322
	(void) ehci_readl(ehci, &ehci->regs->command);
Linus Torvalds's avatar
Linus Torvalds committed
323
324
325
326
327
328
329
330
331

	/* 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;
332
		ehci_writel(ehci, ehci->command, &ehci->regs->command);
Linus Torvalds's avatar
Linus Torvalds committed
333
334
335
336
337
338
	}

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

	/* Now we can safely re-enable irqs */
339
	ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
Linus Torvalds's avatar
Linus Torvalds committed
340
341

	spin_unlock_irq (&ehci->lock);
342
	ehci_handover_companion_ports(ehci);
Linus Torvalds's avatar
Linus Torvalds committed
343
344
345
346
347
	return 0;
}

#else

348
349
#define ehci_bus_suspend	NULL
#define ehci_bus_resume		NULL
Linus Torvalds's avatar
Linus Torvalds committed
350
351
352

#endif	/* CONFIG_PM */

353
354
355
/*-------------------------------------------------------------------------*/

/* Display the ports dedicated to the companion controller */
356
357
358
static ssize_t show_companion(struct device *dev,
			      struct device_attribute *attr,
			      char *buf)
359
360
361
362
363
364
{
	struct ehci_hcd		*ehci;
	int			nports, index, n;
	int			count = PAGE_SIZE;
	char			*ptr = buf;

365
	ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
366
367
368
369
370
371
372
373
374
375
376
377
378
	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;
}

/*
379
 * Sets the owner of a port
380
 */
381
static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
382
383
384
{
	u32 __iomem		*status_reg;
	u32			port_status;
385
	int 			try;
386

387
	status_reg = &ehci->regs->port_status[portnum];
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409

	/*
	 * 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);
	}
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
}

/*
 * 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);
440
441
	return count;
}
442
static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
443
444
445
446
447
448
449

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

	/* with integrated TT there is no companion! */
	if (!ehci_is_TDI(ehci))
450
		i = device_create_file(ehci_to_hcd(ehci)->self.controller,
451
				       &dev_attr_companion);
452
453
454
455
456
457
}

static inline void remove_companion_file(struct ehci_hcd *ehci)
{
	/* with integrated TT there is no companion! */
	if (!ehci_is_TDI(ehci))
458
		device_remove_file(ehci_to_hcd(ehci)->self.controller,
459
				   &dev_attr_companion);
460
461
462
}


Linus Torvalds's avatar
Linus Torvalds committed
463
464
465
466
467
/*-------------------------------------------------------------------------*/

static int check_reset_complete (
	struct ehci_hcd	*ehci,
	int		index,
468
	u32 __iomem	*status_reg,
Linus Torvalds's avatar
Linus Torvalds committed
469
470
	int		port_status
) {
471
	if (!(port_status & PORT_CONNECT))
Linus Torvalds's avatar
Linus Torvalds committed
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
		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;
David Brownell's avatar
David Brownell committed
490
		port_status &= ~PORT_RWC_BITS;
491
		ehci_writel(ehci, port_status, status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
492

493
494
495
496
		/* ensure 440EPX ohci controller state is operational */
		if (ehci->has_amcc_usb23)
			set_ohci_hcfs(ehci, 1);
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
497
		ehci_dbg (ehci, "port %d high speed\n", index + 1);
498
499
500
501
		/* ensure 440EPx ohci controller state is suspended */
		if (ehci->has_amcc_usb23)
			set_ohci_hcfs(ehci, 0);
	}
Linus Torvalds's avatar
Linus Torvalds committed
502
503
504
505
506
507
508
509
510
511
512
513
514
515

	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;
516
	u32		mask;
Linus Torvalds's avatar
Linus Torvalds committed
517
518
519
520
521
522
523
524
525
526
527
528
529
530
	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++;
	}
531

532
533
	/* Some boards (mostly VIA?) report bogus overcurrent indications,
	 * causing massive log spam unless we completely ignore them.  It
534
	 * may be relevant that VIA VT8235 controllers, where PORT_POWER is
535
536
537
538
539
540
541
542
543
	 * 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

Linus Torvalds's avatar
Linus Torvalds committed
544
545
546
547
548
	/* 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++) {
549
		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
550
551
552
553
554
555
556
557

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

558
559
560
		if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
				|| (ehci->reset_done[i] && time_after_eq(
					jiffies, ehci->reset_done[i]))) {
Linus Torvalds's avatar
Linus Torvalds committed
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
			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 */
David Brownell's avatar
David Brownell committed
598
599
	else
		temp |= 0x0002;		/* no power switching */
Linus Torvalds's avatar
Linus Torvalds committed
600
601
602
603
604
#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
Al Viro's avatar
Al Viro committed
605
	desc->wHubCharacteristics = cpu_to_le16(temp);
Linus Torvalds's avatar
Linus Torvalds committed
606
607
608
609
610
611
612
613
614
615
616
617
618
619
}

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

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);
Alan Stern's avatar
Alan Stern committed
620
621
	u32 __iomem	*status_reg = &ehci->regs->port_status[
				(wIndex & 0xff) - 1];
622
623
	u32 __iomem	*hostpc_reg = NULL;
	u32		temp, temp1, status;
Linus Torvalds's avatar
Linus Torvalds committed
624
625
	unsigned long	flags;
	int		retval = 0;
626
	unsigned	selector;
Linus Torvalds's avatar
Linus Torvalds committed
627
628
629
630
631
632
633
634

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

635
636
637
	if (ehci->has_hostpc)
		hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
				+ HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
Linus Torvalds's avatar
Linus Torvalds committed
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
	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--;
654
		temp = ehci_readl(ehci, status_reg);
655
656
657
658
659
660
661

		/*
		 * 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).
		 */
Linus Torvalds's avatar
Linus Torvalds committed
662
663
664

		switch (wValue) {
		case USB_PORT_FEAT_ENABLE:
665
			ehci_writel(ehci, temp & ~PORT_PE, status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
666
667
			break;
		case USB_PORT_FEAT_C_ENABLE:
668
			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_PEC,
669
					status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
670
671
672
673
			break;
		case USB_PORT_FEAT_SUSPEND:
			if (temp & PORT_RESET)
				goto error;
674
675
			if (ehci->no_selective_suspend)
				break;
Linus Torvalds's avatar
Linus Torvalds committed
676
677
678
679
			if (temp & PORT_SUSPEND) {
				if ((temp & PORT_PE) == 0)
					goto error;
				/* resume signaling for 20 msec */
David Brownell's avatar
David Brownell committed
680
				temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
681
				ehci_writel(ehci, temp | PORT_RESUME,
682
						status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
683
684
685
686
687
				ehci->reset_done [wIndex] = jiffies
						+ msecs_to_jiffies (20);
			}
			break;
		case USB_PORT_FEAT_C_SUSPEND:
688
			clear_bit(wIndex, &ehci->port_c_suspend);
Linus Torvalds's avatar
Linus Torvalds committed
689
690
691
			break;
		case USB_PORT_FEAT_POWER:
			if (HCS_PPC (ehci->hcs_params))
692
693
				ehci_writel(ehci,
					  temp & ~(PORT_RWC_BITS | PORT_POWER),
694
					  status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
695
696
			break;
		case USB_PORT_FEAT_C_CONNECTION:
697
			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC,
698
					status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
699
700
			break;
		case USB_PORT_FEAT_C_OVER_CURRENT:
701
			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_OCC,
702
					status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
703
704
705
706
707
708
709
			break;
		case USB_PORT_FEAT_C_RESET:
			/* GetPortStatus clears reset */
			break;
		default:
			goto error;
		}
710
		ehci_readl(ehci, &ehci->regs->command);	/* unblock posted write */
Linus Torvalds's avatar
Linus Torvalds committed
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
		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;
726
		temp = ehci_readl(ehci, status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
727
728
729
730
731
732

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

		if ((temp & PORT_OCC) && !ignore_oc){
Linus Torvalds's avatar
Linus Torvalds committed
735
736
			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;

737
738
739
740
741
742
743
744
745
746
747
748
749
750
			/*
			 * 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);
			}
		}

Linus Torvalds's avatar
Linus Torvalds committed
751
		/* whoever resumes must GetPortStatus to complete it!! */
752
753
754
755
756
757
758
759
760
761
762
		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]);
			}
Linus Torvalds's avatar
Linus Torvalds committed
763

764
765
766
			/* resume completed? */
			else if (time_after_eq(jiffies,
					ehci->reset_done[wIndex])) {
767
				clear_bit(wIndex, &ehci->suspended_ports);
768
				set_bit(wIndex, &ehci->port_c_suspend);
769
770
771
772
773
				ehci->reset_done[wIndex] = 0;

				/* stop resume signaling */
				temp = ehci_readl(ehci, status_reg);
				ehci_writel(ehci,
774
775
					temp & ~(PORT_RWC_BITS | PORT_RESUME),
					status_reg);
776
				retval = handshake(ehci, status_reg,
777
					   PORT_RESUME, 0, 2000 /* 2msec */);
778
779
780
781
782
783
784
				if (retval != 0) {
					ehci_err(ehci,
						"port %d resume error %d\n",
						wIndex + 1, retval);
					goto error;
				}
				temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
Linus Torvalds's avatar
Linus Torvalds committed
785
786
787
788
789
			}
		}

		/* whoever resets must GetPortStatus to complete it!! */
		if ((temp & PORT_RESET)
790
791
				&& time_after_eq(jiffies,
					ehci->reset_done[wIndex])) {
Linus Torvalds's avatar
Linus Torvalds committed
792
793
794
795
			status |= 1 << USB_PORT_FEAT_C_RESET;
			ehci->reset_done [wIndex] = 0;

			/* force reset to complete */
796
			ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
797
					status_reg);
798
799
800
			/* REVISIT:  some hardware needs 550+ usec to clear
			 * this bit; seems too long to spin routinely...
			 */
801
			retval = handshake(ehci, status_reg,
802
					PORT_RESET, 0, 750);
Linus Torvalds's avatar
Linus Torvalds committed
803
804
805
806
807
808
809
			if (retval != 0) {
				ehci_err (ehci, "port %d reset error %d\n",
					wIndex + 1, retval);
				goto error;
			}

			/* see what we found out */
810
811
			temp = check_reset_complete (ehci, wIndex, status_reg,
					ehci_readl(ehci, status_reg));
Linus Torvalds's avatar
Linus Torvalds committed
812
813
		}

814
815
816
		if (!(temp & (PORT_RESUME|PORT_RESET)))
			ehci->reset_done[wIndex] = 0;

817
818
819
820
821
822
823
824
825
826
		/* 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);
		}

827
828
829
830
831
832
833
834
835
		/*
		 * 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
836
837
838
839
840
			if (ehci->has_hostpc) {
				temp1 = ehci_readl(ehci, hostpc_reg);
				status |= ehci_port_speed(ehci, temp1);
			} else
				status |= ehci_port_speed(ehci, temp);
Linus Torvalds's avatar
Linus Torvalds committed
841
		}
842
843
		if (temp & PORT_PE)
			status |= 1 << USB_PORT_FEAT_ENABLE;
844
845
846

		/* maybe the port was unsuspended without our knowledge */
		if (temp & (PORT_SUSPEND|PORT_RESUME)) {
847
			status |= 1 << USB_PORT_FEAT_SUSPEND;
848
849
850
851
852
853
854
		} 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);
		}

855
856
857
858
859
860
		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;
861
862
		if (test_bit(wIndex, &ehci->port_c_suspend))
			status |= 1 << USB_PORT_FEAT_C_SUSPEND;
Linus Torvalds's avatar
Linus Torvalds committed
863

David Brownell's avatar
David Brownell committed
864
#ifndef	VERBOSE_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
865
866
867
	if (status & ~0xffff)	/* only if wPortChange is interesting */
#endif
		dbg_port (ehci, "GetStatus", wIndex + 1, temp);
868
		put_unaligned_le32(status, buf);
Linus Torvalds's avatar
Linus Torvalds committed
869
870
871
872
873
874
875
876
877
878
879
880
		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:
881
882
		selector = wIndex >> 8;
		wIndex &= 0xff;
883
884
885
886
887
888
889
890
891
		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;
			}
		}
Linus Torvalds's avatar
Linus Torvalds committed
892
893
894
		if (!wIndex || wIndex > ports)
			goto error;
		wIndex--;
895
		temp = ehci_readl(ehci, status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
896
897
898
		if (temp & PORT_OWNER)
			break;

David Brownell's avatar
David Brownell committed
899
		temp &= ~PORT_RWC_BITS;
Linus Torvalds's avatar
Linus Torvalds committed
900
901
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
902
903
			if (ehci->no_selective_suspend)
				break;
Linus Torvalds's avatar
Linus Torvalds committed
904
905
906
			if ((temp & PORT_PE) == 0
					|| (temp & PORT_RESET) != 0)
				goto error;
907
			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
			/* 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");
			}
926
			set_bit(wIndex, &ehci->suspended_ports);
Linus Torvalds's avatar
Linus Torvalds committed
927
928
929
			break;
		case USB_PORT_FEAT_POWER:
			if (HCS_PPC (ehci->hcs_params))
930
				ehci_writel(ehci, temp | PORT_POWER,
931
						status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
			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);
			}
959
			ehci_writel(ehci, temp, status_reg);
Linus Torvalds's avatar
Linus Torvalds committed
960
			break;
961
962
963
964
965
966
967
968
969
970
971
972
973

		/* 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;
974
			ehci_writel(ehci, temp, status_reg);
975
976
			break;

Linus Torvalds's avatar
Linus Torvalds committed
977
978
979
		default:
			goto error;
		}
980
		ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
Linus Torvalds's avatar
Linus Torvalds committed
981
982
983
984
985
986
987
		break;

	default:
error:
		/* "stall" on error */
		retval = -EPIPE;
	}
988
error_exit:
Linus Torvalds's avatar
Linus Torvalds committed
989
990
991
	spin_unlock_irqrestore (&ehci->lock, flags);
	return retval;
}
992
993
994
995
996
997
998
999
1000
1001

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);
}

1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
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;
}