1 /** @file
2 Usb Hub Request Support In PEI Phase
3 
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "UsbPeim.h"
11 #include "HubPeim.h"
12 #include "PeiUsbLib.h"
13 
14 /**
15   Get a given hub port status.
16 
17   @param  PeiServices   General-purpose services that are available to every PEIM.
18   @param  UsbIoPpi      Indicates the PEI_USB_IO_PPI instance.
19   @param  Port          Usb hub port number (starting from 1).
20   @param  PortStatus    Current Hub port status and change status.
21 
22   @retval EFI_SUCCESS       Port status is obtained successfully.
23   @retval EFI_DEVICE_ERROR  Cannot get the port status due to a hardware error.
24   @retval Others            Other failure occurs.
25 
26 **/
27 EFI_STATUS
PeiHubGetPortStatus(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_IO_PPI * UsbIoPpi,IN UINT8 Port,OUT UINT32 * PortStatus)28 PeiHubGetPortStatus (
29   IN  EFI_PEI_SERVICES    **PeiServices,
30   IN  PEI_USB_IO_PPI      *UsbIoPpi,
31   IN  UINT8               Port,
32   OUT UINT32              *PortStatus
33   )
34 {
35   EFI_USB_DEVICE_REQUEST  DeviceRequest;
36 
37   ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
38 
39   //
40   // Fill Device request packet
41   //
42   DeviceRequest.RequestType = USB_HUB_GET_PORT_STATUS_REQ_TYPE;
43   DeviceRequest.Request     = USB_HUB_GET_PORT_STATUS;
44   DeviceRequest.Index       = Port;
45   DeviceRequest.Length      = (UINT16) sizeof (UINT32);
46 
47 
48   return UsbIoPpi->UsbControlTransfer (
49                      PeiServices,
50                      UsbIoPpi,
51                      &DeviceRequest,
52                      EfiUsbDataIn,
53                      PcdGet32 (PcdUsbTransferTimeoutValue),
54                      PortStatus,
55                      sizeof (UINT32)
56                      );
57 
58 }
59 
60 /**
61   Set specified feature to a given hub port.
62 
63   @param  PeiServices   General-purpose services that are available to every PEIM.
64   @param  UsbIoPpi      Indicates the PEI_USB_IO_PPI instance.
65   @param  Port          Usb hub port number (starting from 1).
66   @param  Value         New feature value.
67 
68   @retval EFI_SUCCESS       Port feature is set successfully.
69   @retval EFI_DEVICE_ERROR  Cannot set the port feature due to a hardware error.
70   @retval Others            Other failure occurs.
71 
72 **/
73 EFI_STATUS
PeiHubSetPortFeature(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_IO_PPI * UsbIoPpi,IN UINT8 Port,IN UINT8 Value)74 PeiHubSetPortFeature (
75   IN EFI_PEI_SERVICES    **PeiServices,
76   IN PEI_USB_IO_PPI      *UsbIoPpi,
77   IN UINT8               Port,
78   IN UINT8               Value
79   )
80 {
81   EFI_USB_DEVICE_REQUEST      DeviceRequest;
82 
83   ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
84 
85   //
86   // Fill Device request packet
87   //
88   DeviceRequest.RequestType = USB_HUB_SET_PORT_FEATURE_REQ_TYPE;
89   DeviceRequest.Request     = USB_HUB_SET_PORT_FEATURE;
90   DeviceRequest.Value       = Value;
91   DeviceRequest.Index       = Port;
92 
93   return UsbIoPpi->UsbControlTransfer (
94                      PeiServices,
95                      UsbIoPpi,
96                      &DeviceRequest,
97                      EfiUsbNoData,
98                      PcdGet32 (PcdUsbTransferTimeoutValue),
99                      NULL,
100                      0
101                      );
102 }
103 
104 /**
105   Clear specified feature on a given hub port.
106 
107   @param  PeiServices   General-purpose services that are available to every PEIM.
108   @param  UsbIoPpi      Indicates the PEI_USB_IO_PPI instance.
109   @param  Port          Usb hub port number (starting from 1).
110   @param  Value         Feature value that will be cleared from the hub port.
111 
112   @retval EFI_SUCCESS       Port feature is cleared successfully.
113   @retval EFI_DEVICE_ERROR  Cannot clear the port feature due to a hardware error.
114   @retval Others            Other failure occurs.
115 
116 **/
117 EFI_STATUS
PeiHubClearPortFeature(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_IO_PPI * UsbIoPpi,IN UINT8 Port,IN UINT8 Value)118 PeiHubClearPortFeature (
119   IN EFI_PEI_SERVICES    **PeiServices,
120   IN PEI_USB_IO_PPI      *UsbIoPpi,
121   IN UINT8               Port,
122   IN UINT8               Value
123   )
124 {
125   EFI_USB_DEVICE_REQUEST      DeviceRequest;
126 
127   ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
128 
129   //
130   // Fill Device request packet
131   //
132   DeviceRequest.RequestType = USB_HUB_CLEAR_FEATURE_PORT_REQ_TYPE;
133   DeviceRequest.Request     = USB_HUB_CLEAR_FEATURE_PORT;
134   DeviceRequest.Value       = Value;
135   DeviceRequest.Index       = Port;
136 
137   return UsbIoPpi->UsbControlTransfer (
138                      PeiServices,
139                      UsbIoPpi,
140                      &DeviceRequest,
141                      EfiUsbNoData,
142                      PcdGet32 (PcdUsbTransferTimeoutValue),
143                      NULL,
144                      0
145                      );
146 }
147 
148 /**
149   Get a given hub status.
150 
151   @param  PeiServices   General-purpose services that are available to every PEIM.
152   @param  UsbIoPpi      Indicates the PEI_USB_IO_PPI instance.
153   @param  HubStatus     Current Hub status and change status.
154 
155   @retval EFI_SUCCESS       Hub status is obtained successfully.
156   @retval EFI_DEVICE_ERROR  Cannot get the hub status due to a hardware error.
157   @retval Others            Other failure occurs.
158 
159 **/
160 EFI_STATUS
PeiHubGetHubStatus(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_IO_PPI * UsbIoPpi,OUT UINT32 * HubStatus)161 PeiHubGetHubStatus (
162   IN  EFI_PEI_SERVICES    **PeiServices,
163   IN  PEI_USB_IO_PPI      *UsbIoPpi,
164   OUT UINT32              *HubStatus
165   )
166 {
167   EFI_USB_DEVICE_REQUEST  DeviceRequest;
168 
169   ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
170 
171   //
172   // Fill Device request packet
173   //
174   DeviceRequest.RequestType = USB_HUB_GET_HUB_STATUS_REQ_TYPE;
175   DeviceRequest.Request     = USB_HUB_GET_HUB_STATUS;
176   DeviceRequest.Length      = (UINT16) sizeof (UINT32);
177 
178   return UsbIoPpi->UsbControlTransfer (
179                      PeiServices,
180                      UsbIoPpi,
181                      &DeviceRequest,
182                      EfiUsbDataIn,
183                      PcdGet32 (PcdUsbTransferTimeoutValue),
184                      HubStatus,
185                      sizeof (UINT32)
186                      );
187 }
188 
189 
190 
191 /**
192   Clear specified feature on a given hub.
193 
194   @param  PeiServices   General-purpose services that are available to every PEIM.
195   @param  UsbIoPpi      Indicates the PEI_USB_IO_PPI instance.
196   @param  Value         Feature value that will be cleared from the hub port.
197 
198   @retval EFI_SUCCESS       Hub feature is cleared successfully.
199   @retval EFI_DEVICE_ERROR  Cannot clear the hub feature due to a hardware error.
200   @retval Others            Other failure occurs.
201 
202 **/
203 EFI_STATUS
PeiHubClearHubFeature(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_IO_PPI * UsbIoPpi,IN UINT8 Value)204 PeiHubClearHubFeature (
205   IN EFI_PEI_SERVICES    **PeiServices,
206   IN PEI_USB_IO_PPI      *UsbIoPpi,
207   IN UINT8               Value
208   )
209 {
210   EFI_USB_DEVICE_REQUEST      DeviceRequest;
211 
212   ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
213 
214   //
215   // Fill Device request packet
216   //
217   DeviceRequest.RequestType = USB_HUB_CLEAR_FEATURE_REQ_TYPE;
218   DeviceRequest.Request     = USB_HUB_CLEAR_FEATURE;
219   DeviceRequest.Value       = Value;
220 
221   return  UsbIoPpi->UsbControlTransfer (
222                       PeiServices,
223                       UsbIoPpi,
224                       &DeviceRequest,
225                       EfiUsbNoData,
226                       PcdGet32 (PcdUsbTransferTimeoutValue),
227                       NULL,
228                       0
229                       );
230 }
231 
232 /**
233   Get a given (SuperSpeed) hub descriptor.
234 
235   @param  PeiServices    General-purpose services that are available to every PEIM.
236   @param  PeiUsbDevice   Indicates the hub controller device.
237   @param  UsbIoPpi       Indicates the PEI_USB_IO_PPI instance.
238   @param  DescriptorSize The length of Hub Descriptor buffer.
239   @param  HubDescriptor  Caller allocated buffer to store the hub descriptor if
240                          successfully returned.
241 
242   @retval EFI_SUCCESS       Hub descriptor is obtained successfully.
243   @retval EFI_DEVICE_ERROR  Cannot get the hub descriptor due to a hardware error.
244   @retval Others            Other failure occurs.
245 
246 **/
247 EFI_STATUS
PeiGetHubDescriptor(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_DEVICE * PeiUsbDevice,IN PEI_USB_IO_PPI * UsbIoPpi,IN UINTN DescriptorSize,OUT EFI_USB_HUB_DESCRIPTOR * HubDescriptor)248 PeiGetHubDescriptor (
249   IN  EFI_PEI_SERVICES          **PeiServices,
250   IN  PEI_USB_DEVICE            *PeiUsbDevice,
251   IN  PEI_USB_IO_PPI            *UsbIoPpi,
252   IN  UINTN                     DescriptorSize,
253   OUT EFI_USB_HUB_DESCRIPTOR    *HubDescriptor
254   )
255 {
256   EFI_USB_DEVICE_REQUEST      DevReq;
257   UINT8                       DescType;
258 
259   ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
260 
261   DescType = (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) ?
262              USB_DT_SUPERSPEED_HUB :
263              USB_DT_HUB;
264 
265   //
266   // Fill Device request packet
267   //
268   DevReq.RequestType = USB_RT_HUB | 0x80;
269   DevReq.Request     = USB_HUB_GET_DESCRIPTOR;
270   DevReq.Value       = (UINT16) (DescType << 8);
271   DevReq.Length      = (UINT16) DescriptorSize;
272 
273   return  UsbIoPpi->UsbControlTransfer (
274                       PeiServices,
275                       UsbIoPpi,
276                       &DevReq,
277                       EfiUsbDataIn,
278                       PcdGet32 (PcdUsbTransferTimeoutValue),
279                       HubDescriptor,
280                       (UINT16)DescriptorSize
281                       );
282 }
283 
284 /**
285   Read the whole usb hub descriptor. It is necessary
286   to do it in two steps because hub descriptor is of
287   variable length.
288 
289   @param  PeiServices       General-purpose services that are available to every PEIM.
290   @param  PeiUsbDevice      Indicates the hub controller device.
291   @param  UsbIoPpi          Indicates the PEI_USB_IO_PPI instance.
292   @param  HubDescriptor     Caller allocated buffer to store the hub descriptor if
293                             successfully returned.
294 
295   @retval EFI_SUCCESS       Hub descriptor is obtained successfully.
296   @retval EFI_DEVICE_ERROR  Cannot get the hub descriptor due to a hardware error.
297   @retval Others            Other failure occurs.
298 
299 **/
300 EFI_STATUS
PeiUsbHubReadDesc(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_DEVICE * PeiUsbDevice,IN PEI_USB_IO_PPI * UsbIoPpi,OUT EFI_USB_HUB_DESCRIPTOR * HubDescriptor)301 PeiUsbHubReadDesc (
302   IN EFI_PEI_SERVICES           **PeiServices,
303   IN PEI_USB_DEVICE             *PeiUsbDevice,
304   IN PEI_USB_IO_PPI             *UsbIoPpi,
305   OUT EFI_USB_HUB_DESCRIPTOR    *HubDescriptor
306   )
307 {
308   EFI_STATUS Status;
309 
310   //
311   // First get the hub descriptor length
312   //
313   Status = PeiGetHubDescriptor (PeiServices, PeiUsbDevice, UsbIoPpi, 2, HubDescriptor);
314 
315   if (EFI_ERROR (Status)) {
316     return Status;
317   }
318 
319   //
320   // Get the whole hub descriptor
321   //
322   return PeiGetHubDescriptor (PeiServices, PeiUsbDevice, UsbIoPpi, HubDescriptor->Length, HubDescriptor);
323 }
324 
325 /**
326   USB hub control transfer to set the hub depth.
327 
328   @param  PeiServices       General-purpose services that are available to every PEIM.
329   @param  PeiUsbDevice      Indicates the hub controller device.
330   @param  UsbIoPpi          Indicates the PEI_USB_IO_PPI instance.
331 
332   @retval EFI_SUCCESS       Depth of the hub is set.
333   @retval Others            Failed to set the depth.
334 
335 **/
336 EFI_STATUS
PeiUsbHubCtrlSetHubDepth(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_DEVICE * PeiUsbDevice,IN PEI_USB_IO_PPI * UsbIoPpi)337 PeiUsbHubCtrlSetHubDepth (
338   IN EFI_PEI_SERVICES           **PeiServices,
339   IN PEI_USB_DEVICE             *PeiUsbDevice,
340   IN PEI_USB_IO_PPI             *UsbIoPpi
341   )
342 {
343   EFI_USB_DEVICE_REQUEST        DevReq;
344   ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
345 
346   //
347   // Fill Device request packet
348   //
349   DevReq.RequestType = USB_RT_HUB;
350   DevReq.Request     = USB_HUB_REQ_SET_DEPTH;
351   DevReq.Value       = PeiUsbDevice->Tier;
352   DevReq.Length      = 0;
353 
354   return  UsbIoPpi->UsbControlTransfer (
355                       PeiServices,
356                       UsbIoPpi,
357                       &DevReq,
358                       EfiUsbNoData,
359                       PcdGet32 (PcdUsbTransferTimeoutValue),
360                       NULL,
361                       0
362                       );
363 }
364 
365 /**
366   Configure a given hub.
367 
368   @param  PeiServices    General-purpose services that are available to every PEIM.
369   @param  PeiUsbDevice   Indicating the hub controller device that will be configured
370 
371   @retval EFI_SUCCESS       Hub configuration is done successfully.
372   @retval EFI_DEVICE_ERROR  Cannot configure the hub due to a hardware error.
373 
374 **/
375 EFI_STATUS
PeiDoHubConfig(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_DEVICE * PeiUsbDevice)376 PeiDoHubConfig (
377   IN EFI_PEI_SERVICES    **PeiServices,
378   IN PEI_USB_DEVICE      *PeiUsbDevice
379   )
380 {
381   UINT8                   HubDescBuffer[256];
382   EFI_USB_HUB_DESCRIPTOR  *HubDescriptor;
383   EFI_STATUS              Status;
384   EFI_USB_HUB_STATUS      HubStatus;
385   UINTN                   Index;
386   PEI_USB_IO_PPI          *UsbIoPpi;
387 
388   UsbIoPpi = &PeiUsbDevice->UsbIoPpi;
389 
390   //
391   // The length field of descriptor is UINT8 type, so the buffer
392   // with 256 bytes is enough to hold the descriptor data.
393   //
394   HubDescriptor = (EFI_USB_HUB_DESCRIPTOR *) HubDescBuffer;
395 
396   //
397   // Get the hub descriptor
398   //
399   Status = PeiUsbHubReadDesc (
400             PeiServices,
401             PeiUsbDevice,
402             UsbIoPpi,
403             HubDescriptor
404             );
405   if (EFI_ERROR (Status)) {
406     return EFI_DEVICE_ERROR;
407   }
408 
409   PeiUsbDevice->DownStreamPortNo = HubDescriptor->NbrPorts;
410 
411   if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) {
412     DEBUG ((EFI_D_INFO, "PeiDoHubConfig: Set Hub Depth as 0x%x\n", PeiUsbDevice->Tier));
413     PeiUsbHubCtrlSetHubDepth (
414       PeiServices,
415       PeiUsbDevice,
416       UsbIoPpi
417       );
418   } else {
419     //
420     //  Power all the hub ports
421     //
422     for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) {
423       Status = PeiHubSetPortFeature (
424                 PeiServices,
425                 UsbIoPpi,
426                 (UINT8) (Index + 1),
427                 EfiUsbPortPower
428                 );
429       if (EFI_ERROR (Status)) {
430         DEBUG (( EFI_D_ERROR, "PeiDoHubConfig: PeiHubSetPortFeature EfiUsbPortPower failed %x\n", Index));
431         continue;
432       }
433     }
434 
435     DEBUG (( EFI_D_INFO, "PeiDoHubConfig: HubDescriptor.PwrOn2PwrGood: 0x%x\n", HubDescriptor->PwrOn2PwrGood));
436     if (HubDescriptor->PwrOn2PwrGood > 0) {
437       MicroSecondDelay (HubDescriptor->PwrOn2PwrGood * USB_SET_PORT_POWER_STALL);
438     }
439 
440     //
441     // Clear Hub Status Change
442     //
443     Status = PeiHubGetHubStatus (
444               PeiServices,
445               UsbIoPpi,
446               (UINT32 *) &HubStatus
447               );
448     if (EFI_ERROR (Status)) {
449       return EFI_DEVICE_ERROR;
450     } else {
451       //
452       // Hub power supply change happens
453       //
454       if ((HubStatus.HubChangeStatus & HUB_CHANGE_LOCAL_POWER) != 0) {
455         PeiHubClearHubFeature (
456           PeiServices,
457           UsbIoPpi,
458           C_HUB_LOCAL_POWER
459           );
460       }
461       //
462       // Hub change overcurrent happens
463       //
464       if ((HubStatus.HubChangeStatus & HUB_CHANGE_OVERCURRENT) != 0) {
465         PeiHubClearHubFeature (
466           PeiServices,
467           UsbIoPpi,
468           C_HUB_OVER_CURRENT
469           );
470       }
471     }
472   }
473 
474   return EFI_SUCCESS;
475 }
476 
477 /**
478   Send reset signal over the given root hub port.
479 
480   @param  PeiServices    General-purpose services that are available to every PEIM.
481   @param  UsbIoPpi       Indicates the PEI_USB_IO_PPI instance.
482   @param  PortNum        Usb hub port number (starting from 1).
483 
484 **/
485 VOID
PeiResetHubPort(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_USB_IO_PPI * UsbIoPpi,IN UINT8 PortNum)486 PeiResetHubPort (
487   IN EFI_PEI_SERVICES    **PeiServices,
488   IN PEI_USB_IO_PPI      *UsbIoPpi,
489   IN UINT8               PortNum
490   )
491 {
492   EFI_STATUS          Status;
493   UINTN               Index;
494   EFI_USB_PORT_STATUS HubPortStatus;
495 
496   MicroSecondDelay (100 * 1000);
497 
498   //
499   // reset root port
500   //
501   PeiHubSetPortFeature (
502     PeiServices,
503     UsbIoPpi,
504     PortNum,
505     EfiUsbPortReset
506     );
507 
508   //
509   // Drive the reset signal for worst 20ms. Check USB 2.0 Spec
510   // section 7.1.7.5 for timing requirements.
511   //
512   MicroSecondDelay (USB_SET_PORT_RESET_STALL);
513 
514   //
515   // Check USB_PORT_STAT_C_RESET bit to see if the resetting state is done.
516   //
517   ZeroMem (&HubPortStatus, sizeof (EFI_USB_PORT_STATUS));
518 
519   for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) {
520     Status = PeiHubGetPortStatus (
521                PeiServices,
522                UsbIoPpi,
523                PortNum,
524                (UINT32 *) &HubPortStatus
525                );
526 
527     if (EFI_ERROR (Status)) {
528       return;
529     }
530 
531     if (USB_BIT_IS_SET (HubPortStatus.PortChangeStatus, USB_PORT_STAT_C_RESET)) {
532       break;
533     }
534 
535     MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL);
536   }
537 
538   if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) {
539     DEBUG ((EFI_D_ERROR, "PeiResetHubPort: reset not finished in time on port %d\n", PortNum));
540     return;
541   }
542 
543   //
544   // clear reset change root port
545   //
546   PeiHubClearPortFeature (
547     PeiServices,
548     UsbIoPpi,
549     PortNum,
550     EfiUsbPortResetChange
551     );
552 
553   MicroSecondDelay (1 * 1000);
554 
555   PeiHubClearPortFeature (
556     PeiServices,
557     UsbIoPpi,
558     PortNum,
559     EfiUsbPortConnectChange
560     );
561 
562   //
563   // Set port enable
564   //
565   PeiHubSetPortFeature (
566     PeiServices,
567     UsbIoPpi,
568     PortNum,
569     EfiUsbPortEnable
570     );
571 
572   //
573   // Clear any change status
574   //
575 
576   PeiHubClearPortFeature (
577     PeiServices,
578     UsbIoPpi,
579     PortNum,
580     EfiUsbPortEnableChange
581     );
582 
583   MicroSecondDelay (10 * 1000);
584 
585   return;
586 }
587