Blame view

common/usb_hub.c 15 KB
23faf2bc9   Marek Vasut   USB: Separate out...
1
  /*
23faf2bc9   Marek Vasut   USB: Separate out...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   * Most of this source has been derived from the Linux USB
   * project:
   * (C) Copyright Linus Torvalds 1999
   * (C) Copyright Johannes Erdfelt 1999-2001
   * (C) Copyright Andreas Gal 1999
   * (C) Copyright Gregory P. Smith 1999
   * (C) Copyright Deti Fliegl 1999 (new USB architecture)
   * (C) Copyright Randy Dunlap 2000
   * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
   * (C) Copyright Yggdrasil Computing, Inc. 2000
   *     (usb_device_id matching changes by Adam J. Richter)
   *
   * Adapted for U-Boot:
   * (C) Copyright 2001 Denis Peter, MPL AG Switzerland
   *
1a4596601   Wolfgang Denk   Add GPL-2.0+ SPDX...
17
   * SPDX-License-Identifier:	GPL-2.0+
23faf2bc9   Marek Vasut   USB: Separate out...
18
19
20
21
22
23
24
25
26
27
   */
  
  /****************************************************************************
   * HUB "Driver"
   * Probes device for being a hub and configurate it
   */
  
  #include <common.h>
  #include <command.h>
  #include <asm/processor.h>
93ad908c4   Lucas Stach   usb: do explicit ...
28
  #include <asm/unaligned.h>
23faf2bc9   Marek Vasut   USB: Separate out...
29
30
31
32
33
34
35
36
  #include <linux/ctype.h>
  #include <asm/byteorder.h>
  #include <asm/unaligned.h>
  
  #include <usb.h>
  #ifdef CONFIG_4xx
  #include <asm/4xx_pci.h>
  #endif
aa1550588   Kuo-Jung Su   usb: hub: make mi...
37
38
39
  #ifndef CONFIG_USB_HUB_MIN_POWER_ON_DELAY
  #define CONFIG_USB_HUB_MIN_POWER_ON_DELAY	100
  #endif
23faf2bc9   Marek Vasut   USB: Separate out...
40
41
42
43
  #define USB_BUFSIZ	512
  
  static struct usb_hub_device hub_dev[USB_MAX_HUB];
  static int usb_hub_index;
3615a996a   Dan Murphy   USB: usb-hub: Add...
44
45
46
47
  __weak void usb_hub_reset_devices(int port)
  {
  	return;
  }
23faf2bc9   Marek Vasut   USB: Separate out...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  
  static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
  {
  	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
  		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
  		USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT);
  }
  
  static int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
  {
  	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
  				USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature,
  				port, NULL, 0, USB_CNTL_TIMEOUT);
  }
  
  static int usb_set_port_feature(struct usb_device *dev, int port, int feature)
  {
  	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
  				USB_REQ_SET_FEATURE, USB_RT_PORT, feature,
  				port, NULL, 0, USB_CNTL_TIMEOUT);
  }
  
  static int usb_get_hub_status(struct usb_device *dev, void *data)
  {
  	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
  			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
  			data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
  }
  
  static int usb_get_port_status(struct usb_device *dev, int port, void *data)
  {
  	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
  			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
  			data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
  }
  
  
  static void usb_hub_power_on(struct usb_hub_device *hub)
  {
  	int i;
  	struct usb_device *dev;
a1a28c6e6   Wolfgang Grandegger   USB: relax usbcor...
89
  	unsigned pgood_delay = hub->desc.bPwrOn2PwrGood * 2;
020bbcb76   Vivek Gautam   usb: hub: Power-c...
90
91
92
  	ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
  	unsigned short portstatus;
  	int ret;
23faf2bc9   Marek Vasut   USB: Separate out...
93
94
  
  	dev = hub->pusb_dev;
0bf796f7a   Vivek Gautam   usb: hub: Paralle...
95
96
97
98
99
100
  
  	/*
  	 * Enable power to the ports:
  	 * Here we Power-cycle the ports: aka,
  	 * turning them off and turning on again.
  	 */
ceb4972a8   Vivek Gautam   usb: common: Weed...
101
102
  	debug("enabling power on all ports
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
103
  	for (i = 0; i < dev->maxchild; i++) {
020bbcb76   Vivek Gautam   usb: hub: Power-c...
104
105
106
  		usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
  		debug("port %d returns %lX
  ", i + 1, dev->status);
0bf796f7a   Vivek Gautam   usb: hub: Paralle...
107
  	}
020bbcb76   Vivek Gautam   usb: hub: Power-c...
108

0bf796f7a   Vivek Gautam   usb: hub: Paralle...
109
110
  	/* Wait at least 2*bPwrOn2PwrGood for PP to change */
  	mdelay(pgood_delay);
020bbcb76   Vivek Gautam   usb: hub: Power-c...
111

0bf796f7a   Vivek Gautam   usb: hub: Paralle...
112
  	for (i = 0; i < dev->maxchild; i++) {
020bbcb76   Vivek Gautam   usb: hub: Power-c...
113
114
115
116
  		ret = usb_get_port_status(dev, i + 1, portsts);
  		if (ret < 0) {
  			debug("port %d: get_port_status failed
  ", i + 1);
0adc331b3   Nikita Kiryanov   usb_hub: fix powe...
117
  			continue;
020bbcb76   Vivek Gautam   usb: hub: Power-c...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  		}
  
  		/*
  		 * Check to confirm the state of Port Power:
  		 * xHCI says "After modifying PP, s/w shall read
  		 * PP and confirm that it has reached the desired state
  		 * before modifying it again, undefined behavior may occur
  		 * if this procedure is not followed".
  		 * EHCI doesn't say anything like this, but no harm in keeping
  		 * this.
  		 */
  		portstatus = le16_to_cpu(portsts->wPortStatus);
  		if (portstatus & (USB_PORT_STAT_POWER << 1)) {
  			debug("port %d: Port power change failed
  ", i + 1);
0adc331b3   Nikita Kiryanov   usb_hub: fix powe...
133
  			continue;
020bbcb76   Vivek Gautam   usb: hub: Power-c...
134
  		}
0bf796f7a   Vivek Gautam   usb: hub: Paralle...
135
  	}
020bbcb76   Vivek Gautam   usb: hub: Power-c...
136

0bf796f7a   Vivek Gautam   usb: hub: Paralle...
137
  	for (i = 0; i < dev->maxchild; i++) {
23faf2bc9   Marek Vasut   USB: Separate out...
138
  		usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
ceb4972a8   Vivek Gautam   usb: common: Weed...
139
140
  		debug("port %d returns %lX
  ", i + 1, dev->status);
23faf2bc9   Marek Vasut   USB: Separate out...
141
  	}
a1a28c6e6   Wolfgang Grandegger   USB: relax usbcor...
142

aa1550588   Kuo-Jung Su   usb: hub: make mi...
143
144
  	/* Wait for power to become stable */
  	mdelay(max(pgood_delay, CONFIG_USB_HUB_MIN_POWER_ON_DELAY));
23faf2bc9   Marek Vasut   USB: Separate out...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  }
  
  void usb_hub_reset(void)
  {
  	usb_hub_index = 0;
  }
  
  static struct usb_hub_device *usb_hub_allocate(void)
  {
  	if (usb_hub_index < USB_MAX_HUB)
  		return &hub_dev[usb_hub_index++];
  
  	printf("ERROR: USB_MAX_HUB (%d) reached
  ", USB_MAX_HUB);
  	return NULL;
  }
  
  #define MAX_TRIES 5
  
  static inline char *portspeed(int portstatus)
  {
55f4b5754   Vivek Gautam   usb: fix: Fixing ...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  	char *speed_str;
  
  	switch (portstatus & USB_PORT_STAT_SPEED_MASK) {
  	case USB_PORT_STAT_SUPER_SPEED:
  		speed_str = "5 Gb/s";
  		break;
  	case USB_PORT_STAT_HIGH_SPEED:
  		speed_str = "480 Mb/s";
  		break;
  	case USB_PORT_STAT_LOW_SPEED:
  		speed_str = "1.5 Mb/s";
  		break;
  	default:
  		speed_str = "12 Mb/s";
  		break;
  	}
  
  	return speed_str;
23faf2bc9   Marek Vasut   USB: Separate out...
184
185
186
187
188
189
  }
  
  int hub_port_reset(struct usb_device *dev, int port,
  			unsigned short *portstat)
  {
  	int tries;
f57661394   Puneet Saxena   USB: Align buffer...
190
  	ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
23faf2bc9   Marek Vasut   USB: Separate out...
191
  	unsigned short portstatus, portchange;
ceb4972a8   Vivek Gautam   usb: common: Weed...
192
193
  	debug("hub_port_reset: resetting port %d...
  ", port);
23faf2bc9   Marek Vasut   USB: Separate out...
194
195
196
  	for (tries = 0; tries < MAX_TRIES; tries++) {
  
  		usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET);
5b84dd67c   Mike Frysinger   usb: replace wait...
197
  		mdelay(200);
23faf2bc9   Marek Vasut   USB: Separate out...
198

f57661394   Puneet Saxena   USB: Align buffer...
199
  		if (usb_get_port_status(dev, port + 1, portsts) < 0) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
200
201
202
  			debug("get_port_status failed status %lX
  ",
  			      dev->status);
23faf2bc9   Marek Vasut   USB: Separate out...
203
204
  			return -1;
  		}
f57661394   Puneet Saxena   USB: Align buffer...
205
206
  		portstatus = le16_to_cpu(portsts->wPortStatus);
  		portchange = le16_to_cpu(portsts->wPortChange);
23faf2bc9   Marek Vasut   USB: Separate out...
207

ceb4972a8   Vivek Gautam   usb: common: Weed...
208
209
210
  		debug("portstatus %x, change %x, %s
  ", portstatus, portchange,
  							portspeed(portstatus));
23faf2bc9   Marek Vasut   USB: Separate out...
211

ceb4972a8   Vivek Gautam   usb: common: Weed...
212
213
214
215
216
217
  		debug("STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \
  		      "  USB_PORT_STAT_ENABLE %d
  ",
  		      (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0,
  		      (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0,
  		      (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0);
23faf2bc9   Marek Vasut   USB: Separate out...
218
219
220
221
222
223
224
  
  		if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
  		    !(portstatus & USB_PORT_STAT_CONNECTION))
  			return -1;
  
  		if (portstatus & USB_PORT_STAT_ENABLE)
  			break;
5b84dd67c   Mike Frysinger   usb: replace wait...
225
  		mdelay(200);
23faf2bc9   Marek Vasut   USB: Separate out...
226
227
228
  	}
  
  	if (tries == MAX_TRIES) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
229
230
231
232
233
  		debug("Cannot enable port %i after %i retries, " \
  		      "disabling port.
  ", port + 1, MAX_TRIES);
  		debug("Maybe the USB cable is bad?
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
234
235
236
237
238
239
240
241
242
243
244
245
  		return -1;
  	}
  
  	usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET);
  	*portstat = portstatus;
  	return 0;
  }
  
  
  void usb_hub_port_connect_change(struct usb_device *dev, int port)
  {
  	struct usb_device *usb;
f57661394   Puneet Saxena   USB: Align buffer...
246
  	ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
23faf2bc9   Marek Vasut   USB: Separate out...
247
248
249
  	unsigned short portstatus;
  
  	/* Check status */
f57661394   Puneet Saxena   USB: Align buffer...
250
  	if (usb_get_port_status(dev, port + 1, portsts) < 0) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
251
252
  		debug("get_port_status failed
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
253
254
  		return;
  	}
f57661394   Puneet Saxena   USB: Align buffer...
255
  	portstatus = le16_to_cpu(portsts->wPortStatus);
ceb4972a8   Vivek Gautam   usb: common: Weed...
256
257
258
259
260
  	debug("portstatus %x, change %x, %s
  ",
  	      portstatus,
  	      le16_to_cpu(portsts->wPortChange),
  	      portspeed(portstatus));
23faf2bc9   Marek Vasut   USB: Separate out...
261
262
263
264
265
266
267
  
  	/* Clear the connection change status */
  	usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION);
  
  	/* Disconnect any existing devices under this port */
  	if (((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
  	     (!(portstatus & USB_PORT_STAT_ENABLE))) || (dev->children[port])) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
268
269
  		debug("usb_disconnect(&hub->children[port]);
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
270
271
272
273
  		/* Return now if nothing is connected */
  		if (!(portstatus & USB_PORT_STAT_CONNECTION))
  			return;
  	}
5b84dd67c   Mike Frysinger   usb: replace wait...
274
  	mdelay(200);
23faf2bc9   Marek Vasut   USB: Separate out...
275
276
277
278
279
280
281
  
  	/* Reset the port */
  	if (hub_port_reset(dev, port, &portstatus) < 0) {
  		printf("cannot reset port %i!?
  ", port + 1);
  		return;
  	}
5b84dd67c   Mike Frysinger   usb: replace wait...
282
  	mdelay(200);
23faf2bc9   Marek Vasut   USB: Separate out...
283
284
  
  	/* Allocate a new device struct for it */
c7e3b2b58   Lucas Stach   usb: lowlevel int...
285
  	usb = usb_alloc_new_device(dev->controller);
23faf2bc9   Marek Vasut   USB: Separate out...
286

55f4b5754   Vivek Gautam   usb: fix: Fixing ...
287
288
  	switch (portstatus & USB_PORT_STAT_SPEED_MASK) {
  	case USB_PORT_STAT_SUPER_SPEED:
6497c6670   Vivek Gautam   USB: SS: Add supp...
289
  		usb->speed = USB_SPEED_SUPER;
55f4b5754   Vivek Gautam   usb: fix: Fixing ...
290
291
  		break;
  	case USB_PORT_STAT_HIGH_SPEED:
23faf2bc9   Marek Vasut   USB: Separate out...
292
  		usb->speed = USB_SPEED_HIGH;
55f4b5754   Vivek Gautam   usb: fix: Fixing ...
293
294
  		break;
  	case USB_PORT_STAT_LOW_SPEED:
23faf2bc9   Marek Vasut   USB: Separate out...
295
  		usb->speed = USB_SPEED_LOW;
55f4b5754   Vivek Gautam   usb: fix: Fixing ...
296
297
  		break;
  	default:
23faf2bc9   Marek Vasut   USB: Separate out...
298
  		usb->speed = USB_SPEED_FULL;
55f4b5754   Vivek Gautam   usb: fix: Fixing ...
299
300
  		break;
  	}
23faf2bc9   Marek Vasut   USB: Separate out...
301
302
303
304
305
306
307
  
  	dev->children[port] = usb;
  	usb->parent = dev;
  	usb->portnr = port + 1;
  	/* Run it through the hoops (find a driver, etc) */
  	if (usb_new_device(usb)) {
  		/* Woops, disable the port */
359439d28   Milind Choudhary   usb: Clean up new...
308
309
  		usb_free_device();
  		dev->children[port] = NULL;
ceb4972a8   Vivek Gautam   usb: common: Weed...
310
311
  		debug("hub: disabling port %d
  ", port + 1);
23faf2bc9   Marek Vasut   USB: Separate out...
312
313
314
315
316
317
318
  		usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE);
  	}
  }
  
  
  static int usb_hub_configure(struct usb_device *dev)
  {
eaf3e613e   Julius Werner   usb: Use well-kno...
319
  	int i, length;
f57661394   Puneet Saxena   USB: Align buffer...
320
321
  	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ);
  	unsigned char *bitmap;
93ad908c4   Lucas Stach   usb: do explicit ...
322
  	short hubCharacteristics;
23faf2bc9   Marek Vasut   USB: Separate out...
323
324
  	struct usb_hub_descriptor *descriptor;
  	struct usb_hub_device *hub;
ceb4972a8   Vivek Gautam   usb: common: Weed...
325
  	__maybe_unused struct usb_hub_status *hubsts;
23faf2bc9   Marek Vasut   USB: Separate out...
326
327
328
329
330
331
332
333
  
  	/* "allocate" Hub device */
  	hub = usb_hub_allocate();
  	if (hub == NULL)
  		return -1;
  	hub->pusb_dev = dev;
  	/* Get the the hub descriptor */
  	if (usb_get_hub_descriptor(dev, buffer, 4) < 0) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
334
335
336
  		debug("usb_hub_configure: failed to get hub " \
  		      "descriptor, giving up %lX
  ", dev->status);
23faf2bc9   Marek Vasut   USB: Separate out...
337
338
339
  		return -1;
  	}
  	descriptor = (struct usb_hub_descriptor *)buffer;
eaf3e613e   Julius Werner   usb: Use well-kno...
340
  	length = min(descriptor->bLength, sizeof(struct usb_hub_descriptor));
23faf2bc9   Marek Vasut   USB: Separate out...
341

eaf3e613e   Julius Werner   usb: Use well-kno...
342
  	if (usb_get_hub_descriptor(dev, buffer, length) < 0) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
343
344
345
  		debug("usb_hub_configure: failed to get hub " \
  		      "descriptor 2nd giving up %lX
  ", dev->status);
23faf2bc9   Marek Vasut   USB: Separate out...
346
347
  		return -1;
  	}
eaf3e613e   Julius Werner   usb: Use well-kno...
348
  	memcpy((unsigned char *)&hub->desc, buffer, length);
23faf2bc9   Marek Vasut   USB: Separate out...
349
  	/* adjust 16bit values */
93ad908c4   Lucas Stach   usb: do explicit ...
350
351
352
  	put_unaligned(le16_to_cpu(get_unaligned(
  			&descriptor->wHubCharacteristics)),
  			&hub->desc.wHubCharacteristics);
23faf2bc9   Marek Vasut   USB: Separate out...
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  	/* set the bitmap */
  	bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0];
  	/* devices not removable by default */
  	memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8);
  	bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0];
  	memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */
  
  	for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++)
  		hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i];
  
  	for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++)
  		hub->desc.PortPowerCtrlMask[i] = descriptor->PortPowerCtrlMask[i];
  
  	dev->maxchild = descriptor->bNbrPorts;
ceb4972a8   Vivek Gautam   usb: common: Weed...
367
368
  	debug("%d ports detected
  ", dev->maxchild);
23faf2bc9   Marek Vasut   USB: Separate out...
369

93ad908c4   Lucas Stach   usb: do explicit ...
370
371
  	hubCharacteristics = get_unaligned(&hub->desc.wHubCharacteristics);
  	switch (hubCharacteristics & HUB_CHAR_LPSM) {
23faf2bc9   Marek Vasut   USB: Separate out...
372
  	case 0x00:
ceb4972a8   Vivek Gautam   usb: common: Weed...
373
374
  		debug("ganged power switching
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
375
376
  		break;
  	case 0x01:
ceb4972a8   Vivek Gautam   usb: common: Weed...
377
378
  		debug("individual port power switching
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
379
380
381
  		break;
  	case 0x02:
  	case 0x03:
ceb4972a8   Vivek Gautam   usb: common: Weed...
382
383
  		debug("unknown reserved power switching mode
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
384
385
  		break;
  	}
93ad908c4   Lucas Stach   usb: do explicit ...
386
  	if (hubCharacteristics & HUB_CHAR_COMPOUND)
ceb4972a8   Vivek Gautam   usb: common: Weed...
387
388
  		debug("part of a compound device
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
389
  	else
ceb4972a8   Vivek Gautam   usb: common: Weed...
390
391
  		debug("standalone hub
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
392

93ad908c4   Lucas Stach   usb: do explicit ...
393
  	switch (hubCharacteristics & HUB_CHAR_OCPM) {
23faf2bc9   Marek Vasut   USB: Separate out...
394
  	case 0x00:
ceb4972a8   Vivek Gautam   usb: common: Weed...
395
396
  		debug("global over-current protection
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
397
398
  		break;
  	case 0x08:
ceb4972a8   Vivek Gautam   usb: common: Weed...
399
400
  		debug("individual port over-current protection
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
401
402
403
  		break;
  	case 0x10:
  	case 0x18:
ceb4972a8   Vivek Gautam   usb: common: Weed...
404
405
  		debug("no over-current protection
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
406
407
  		break;
  	}
ceb4972a8   Vivek Gautam   usb: common: Weed...
408
409
410
411
412
413
  	debug("power on to power good time: %dms
  ",
  	      descriptor->bPwrOn2PwrGood * 2);
  	debug("hub controller current requirement: %dmA
  ",
  	      descriptor->bHubContrCurrent);
23faf2bc9   Marek Vasut   USB: Separate out...
414
415
  
  	for (i = 0; i < dev->maxchild; i++)
ceb4972a8   Vivek Gautam   usb: common: Weed...
416
417
418
419
  		debug("port %d is%s removable
  ", i + 1,
  		      hub->desc.DeviceRemovable[(i + 1) / 8] & \
  		      (1 << ((i + 1) % 8)) ? " not" : "");
23faf2bc9   Marek Vasut   USB: Separate out...
420
421
  
  	if (sizeof(struct usb_hub_status) > USB_BUFSIZ) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
422
423
424
  		debug("usb_hub_configure: failed to get Status - " \
  		      "too long: %d
  ", descriptor->bLength);
23faf2bc9   Marek Vasut   USB: Separate out...
425
426
427
428
  		return -1;
  	}
  
  	if (usb_get_hub_status(dev, buffer) < 0) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
429
430
431
  		debug("usb_hub_configure: failed to get Status %lX
  ",
  		      dev->status);
23faf2bc9   Marek Vasut   USB: Separate out...
432
433
  		return -1;
  	}
ceb4972a8   Vivek Gautam   usb: common: Weed...
434
  #ifdef DEBUG
23faf2bc9   Marek Vasut   USB: Separate out...
435
436
  	hubsts = (struct usb_hub_status *)buffer;
  #endif
ceb4972a8   Vivek Gautam   usb: common: Weed...
437
438
439
440
441
442
443
444
445
446
447
448
449
  
  	debug("get_hub_status returned status %X, change %X
  ",
  	      le16_to_cpu(hubsts->wHubStatus),
  	      le16_to_cpu(hubsts->wHubChange));
  	debug("local power source is %s
  ",
  	      (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \
  	      "lost (inactive)" : "good");
  	debug("%sover-current condition exists
  ",
  	      (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \
  	      "" : "no ");
23faf2bc9   Marek Vasut   USB: Separate out...
450
  	usb_hub_power_on(hub);
3615a996a   Dan Murphy   USB: usb-hub: Add...
451
452
453
454
455
456
457
  	/*
  	 * Reset any devices that may be in a bad state when applying
  	 * the power.  This is a __weak function.  Resetting of the devices
  	 * should occur in the board file of the device.
  	 */
  	for (i = 0; i < dev->maxchild; i++)
  		usb_hub_reset_devices(i + 1);
23faf2bc9   Marek Vasut   USB: Separate out...
458
  	for (i = 0; i < dev->maxchild; i++) {
f57661394   Puneet Saxena   USB: Align buffer...
459
  		ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
23faf2bc9   Marek Vasut   USB: Separate out...
460
  		unsigned short portstatus, portchange;
b6d7852cf   Vipin Kumar   usbh/ehci: Increa...
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  		int ret;
  		ulong start = get_timer(0);
  
  		/*
  		 * Wait for (whichever finishes first)
  		 *  - A maximum of 10 seconds
  		 *    This is a purely observational value driven by connecting
  		 *    a few broken pen drives and taking the max * 1.5 approach
  		 *  - connection_change and connection state to report same
  		 *    state
  		 */
  		do {
  			ret = usb_get_port_status(dev, i + 1, portsts);
  			if (ret < 0) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
475
476
  				debug("get_port_status failed
  ");
b6d7852cf   Vipin Kumar   usbh/ehci: Increa...
477
478
479
480
481
482
483
484
485
  				break;
  			}
  
  			portstatus = le16_to_cpu(portsts->wPortStatus);
  			portchange = le16_to_cpu(portsts->wPortChange);
  
  			if ((portchange & USB_PORT_STAT_C_CONNECTION) ==
  				(portstatus & USB_PORT_STAT_CONNECTION))
  				break;
b6d7852cf   Vipin Kumar   usbh/ehci: Increa...
486
487
488
  		} while (get_timer(start) < CONFIG_SYS_HZ * 10);
  
  		if (ret < 0)
23faf2bc9   Marek Vasut   USB: Separate out...
489
  			continue;
23faf2bc9   Marek Vasut   USB: Separate out...
490

ceb4972a8   Vivek Gautam   usb: common: Weed...
491
492
493
  		debug("Port %d Status %X Change %X
  ",
  		      i + 1, portstatus, portchange);
23faf2bc9   Marek Vasut   USB: Separate out...
494
495
  
  		if (portchange & USB_PORT_STAT_C_CONNECTION) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
496
497
  			debug("port %d connection change
  ", i + 1);
23faf2bc9   Marek Vasut   USB: Separate out...
498
499
500
  			usb_hub_port_connect_change(dev, i);
  		}
  		if (portchange & USB_PORT_STAT_C_ENABLE) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
501
502
503
  			debug("port %d enable change, status %x
  ",
  			      i + 1, portstatus);
23faf2bc9   Marek Vasut   USB: Separate out...
504
505
  			usb_clear_port_feature(dev, i + 1,
  						USB_PORT_FEAT_C_ENABLE);
e82a316d7   Kuo-Jung Su   usb: ehci: add Fa...
506
507
508
509
510
  			/*
  			 * The following hack causes a ghost device problem
  			 * to Faraday EHCI
  			 */
  #ifndef CONFIG_USB_EHCI_FARADAY
23faf2bc9   Marek Vasut   USB: Separate out...
511
512
513
514
515
516
  			/* EM interference sometimes causes bad shielded USB
  			 * devices to be shutdown by the hub, this hack enables
  			 * them again. Works at least with mouse driver */
  			if (!(portstatus & USB_PORT_STAT_ENABLE) &&
  			     (portstatus & USB_PORT_STAT_CONNECTION) &&
  			     ((dev->children[i]))) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
517
518
519
520
521
  				debug("already running port %i "  \
  				      "disabled by hub (EMI?), " \
  				      "re-enabling...
  ", i + 1);
  				      usb_hub_port_connect_change(dev, i);
23faf2bc9   Marek Vasut   USB: Separate out...
522
  			}
e82a316d7   Kuo-Jung Su   usb: ehci: add Fa...
523
  #endif
23faf2bc9   Marek Vasut   USB: Separate out...
524
525
  		}
  		if (portstatus & USB_PORT_STAT_SUSPEND) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
526
527
  			debug("port %d suspend change
  ", i + 1);
23faf2bc9   Marek Vasut   USB: Separate out...
528
529
530
531
532
  			usb_clear_port_feature(dev, i + 1,
  						USB_PORT_FEAT_SUSPEND);
  		}
  
  		if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
533
534
  			debug("port %d over-current change
  ", i + 1);
23faf2bc9   Marek Vasut   USB: Separate out...
535
536
537
538
539
540
  			usb_clear_port_feature(dev, i + 1,
  						USB_PORT_FEAT_C_OVER_CURRENT);
  			usb_hub_power_on(hub);
  		}
  
  		if (portchange & USB_PORT_STAT_C_RESET) {
ceb4972a8   Vivek Gautam   usb: common: Weed...
541
542
  			debug("port %d reset change
  ", i + 1);
23faf2bc9   Marek Vasut   USB: Separate out...
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
  			usb_clear_port_feature(dev, i + 1,
  						USB_PORT_FEAT_C_RESET);
  		}
  	} /* end for i all ports */
  
  	return 0;
  }
  
  int usb_hub_probe(struct usb_device *dev, int ifnum)
  {
  	struct usb_interface *iface;
  	struct usb_endpoint_descriptor *ep;
  	int ret;
  
  	iface = &dev->config.if_desc[ifnum];
  	/* Is it a hub? */
  	if (iface->desc.bInterfaceClass != USB_CLASS_HUB)
  		return 0;
  	/* Some hubs have a subclass of 1, which AFAICT according to the */
  	/*  specs is not defined, but it works */
  	if ((iface->desc.bInterfaceSubClass != 0) &&
  	    (iface->desc.bInterfaceSubClass != 1))
  		return 0;
  	/* Multiple endpoints? What kind of mutant ninja-hub is this? */
  	if (iface->desc.bNumEndpoints != 1)
  		return 0;
  	ep = &iface->ep_desc[0];
  	/* Output endpoint? Curiousier and curiousier.. */
  	if (!(ep->bEndpointAddress & USB_DIR_IN))
  		return 0;
  	/* If it's not an interrupt endpoint, we'd better punt! */
  	if ((ep->bmAttributes & 3) != 3)
  		return 0;
  	/* We found a hub */
ceb4972a8   Vivek Gautam   usb: common: Weed...
577
578
  	debug("USB hub found
  ");
23faf2bc9   Marek Vasut   USB: Separate out...
579
580
581
  	ret = usb_hub_configure(dev);
  	return ret;
  }