Home | History | Annotate | Download | only in EhciPei
      1 /** @file
      2 PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
      3 which is used to enable recovery function from USB Drivers.
      4 
      5 Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.<BR>
      6 
      7 This program and the accompanying materials
      8 are licensed and made available under the terms and conditions
      9 of the BSD License which accompanies this distribution.  The
     10 full text of the license may be found at
     11 http://opensource.org/licenses/bsd-license.php
     12 
     13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 
     16 **/
     17 
     18 #include "EhcPeim.h"
     19 
     20 /**
     21   Create helper QTD/QH for the EHCI device.
     22 
     23   @param  Ehc         The EHCI device.
     24 
     25   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource for helper QTD/QH.
     26   @retval EFI_SUCCESS           Helper QH/QTD are created.
     27 
     28 **/
     29 EFI_STATUS
     30 EhcCreateHelpQ (
     31   IN PEI_USB2_HC_DEV      *Ehc
     32   )
     33 {
     34   USB_ENDPOINT            Ep;
     35   PEI_EHC_QH              *Qh;
     36   QH_HW                   *QhHw;
     37   PEI_EHC_QTD             *Qtd;
     38 
     39   //
     40   // Create an inactive Qtd to terminate the short packet read.
     41   //
     42   Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);
     43 
     44   if (Qtd == NULL) {
     45     return EFI_OUT_OF_RESOURCES;
     46   }
     47 
     48   Qtd->QtdHw.Status   = QTD_STAT_HALTED;
     49   Ehc->ShortReadStop  = Qtd;
     50 
     51   //
     52   // Create a QH to act as the EHC reclamation header.
     53   // Set the header to loopback to itself.
     54   //
     55   Ep.DevAddr    = 0;
     56   Ep.EpAddr     = 1;
     57   Ep.Direction  = EfiUsbDataIn;
     58   Ep.DevSpeed   = EFI_USB_SPEED_HIGH;
     59   Ep.MaxPacket  = 64;
     60   Ep.HubAddr    = 0;
     61   Ep.HubPort    = 0;
     62   Ep.Toggle     = 0;
     63   Ep.Type       = EHC_BULK_TRANSFER;
     64   Ep.PollRate   = 1;
     65 
     66   Qh            = EhcCreateQh (Ehc, &Ep);
     67 
     68   if (Qh == NULL) {
     69     return EFI_OUT_OF_RESOURCES;
     70   }
     71 
     72   QhHw              = &Qh->QhHw;
     73   QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);
     74   QhHw->Status      = QTD_STAT_HALTED;
     75   QhHw->ReclaimHead = 1;
     76   Ehc->ReclaimHead  = Qh;
     77 
     78   //
     79   // Create a dummy QH to act as the terminator for periodical schedule
     80   //
     81   Ep.EpAddr   = 2;
     82   Ep.Type     = EHC_INT_TRANSFER_SYNC;
     83 
     84   Qh          = EhcCreateQh (Ehc, &Ep);
     85 
     86   if (Qh == NULL) {
     87     return EFI_OUT_OF_RESOURCES;
     88   }
     89 
     90   Qh->QhHw.Status = QTD_STAT_HALTED;
     91   Ehc->PeriodOne  = Qh;
     92 
     93   return EFI_SUCCESS;
     94 }
     95 
     96 /**
     97   Initialize the schedule data structure such as frame list.
     98 
     99   @param  Ehc                   The EHCI device to init schedule data for.
    100 
    101   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to init schedule data.
    102   @retval EFI_SUCCESS           The schedule data is initialized.
    103 
    104 **/
    105 EFI_STATUS
    106 EhcInitSched (
    107   IN PEI_USB2_HC_DEV      *Ehc
    108   )
    109 {
    110   EFI_PHYSICAL_ADDRESS  PhyAddr;
    111   VOID                  *Map;
    112   UINTN                 Index;
    113   UINT32                *Desc;
    114   EFI_STATUS            Status;
    115 
    116   //
    117   // First initialize the periodical schedule data:
    118   // 1. Allocate and map the memory for the frame list
    119   // 2. Create the help QTD/QH
    120   // 3. Initialize the frame entries
    121   // 4. Set the frame list register
    122   //
    123   //
    124   // The Frame List ocupies 4K bytes,
    125   // and must be aligned on 4-Kbyte boundaries.
    126   //
    127   Status = PeiServicesAllocatePages (
    128              EfiBootServicesCode,
    129              1,
    130              &PhyAddr
    131              );
    132 
    133   Map = NULL;
    134   Ehc->PeriodFrameHost  = (VOID *)(UINTN)PhyAddr;
    135   Ehc->PeriodFrame      = (VOID *)(UINTN)PhyAddr;
    136   Ehc->PeriodFrameMap   = Map;
    137   Ehc->High32bitAddr    = EHC_HIGH_32BIT (PhyAddr);
    138 
    139   //
    140   // Init memory pool management then create the helper
    141   // QTD/QH. If failed, previously allocated resources
    142   // will be freed by EhcFreeSched
    143   //
    144   Ehc->MemPool = UsbHcInitMemPool (
    145                    Ehc,
    146                    EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),
    147                    Ehc->High32bitAddr
    148                    );
    149 
    150   if (Ehc->MemPool == NULL) {
    151     return EFI_OUT_OF_RESOURCES;
    152   }
    153 
    154   Status = EhcCreateHelpQ (Ehc);
    155 
    156   if (EFI_ERROR (Status)) {
    157     return Status;
    158   }
    159 
    160   //
    161   // Initialize the frame list entries then set the registers
    162   //
    163   Desc = (UINT32 *) Ehc->PeriodFrame;
    164 
    165   for (Index = 0; Index < EHC_FRAME_LEN; Index++) {
    166     Desc[Index] = QH_LINK (Ehc->PeriodOne, EHC_TYPE_QH, FALSE);
    167   }
    168 
    169   EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (Ehc->PeriodFrame));
    170 
    171   //
    172   // Second initialize the asynchronous schedule:
    173   // Only need to set the AsynListAddr register to
    174   // the reclamation header
    175   //
    176   EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (Ehc->ReclaimHead));
    177   return EFI_SUCCESS;
    178 }
    179 
    180 /**
    181   Free the schedule data. It may be partially initialized.
    182 
    183   @param  Ehc   The EHCI device.
    184 
    185 **/
    186 VOID
    187 EhcFreeSched (
    188   IN PEI_USB2_HC_DEV      *Ehc
    189   )
    190 {
    191   EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
    192   EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
    193 
    194   if (Ehc->PeriodOne != NULL) {
    195     UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
    196     Ehc->PeriodOne = NULL;
    197   }
    198 
    199   if (Ehc->ReclaimHead != NULL) {
    200     UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
    201     Ehc->ReclaimHead = NULL;
    202   }
    203 
    204   if (Ehc->ShortReadStop != NULL) {
    205     UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));
    206     Ehc->ShortReadStop = NULL;
    207   }
    208 
    209   if (Ehc->MemPool != NULL) {
    210     UsbHcFreeMemPool (Ehc->MemPool);
    211     Ehc->MemPool = NULL;
    212   }
    213 
    214   if (Ehc->PeriodFrame != NULL) {
    215     Ehc->PeriodFrame = NULL;
    216   }
    217 }
    218 
    219 /**
    220   Link the queue head to the asynchronous schedule list.
    221   UEFI only supports one CTRL/BULK transfer at a time
    222   due to its interfaces. This simplifies the AsynList
    223   management: A reclamation header is always linked to
    224   the AsyncListAddr, the only active QH is appended to it.
    225 
    226   @param  Ehc   The EHCI device.
    227   @param  Qh    The queue head to link.
    228 
    229 **/
    230 VOID
    231 EhcLinkQhToAsync (
    232   IN PEI_USB2_HC_DEV      *Ehc,
    233   IN PEI_EHC_QH           *Qh
    234   )
    235 {
    236   PEI_EHC_QH               *Head;
    237 
    238   //
    239   // Append the queue head after the reclaim header, then
    240   // fix the hardware visiable parts (EHCI R1.0 page 72).
    241   // ReclaimHead is always linked to the EHCI's AsynListAddr.
    242   //
    243   Head                    = Ehc->ReclaimHead;
    244 
    245   Qh->NextQh              = Head->NextQh;
    246   Head->NextQh            = Qh;
    247 
    248   Qh->QhHw.HorizonLink    = QH_LINK (Head, EHC_TYPE_QH, FALSE);;
    249   Head->QhHw.HorizonLink  = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
    250 }
    251 
    252 /**
    253   Unlink a queue head from the asynchronous schedule list.
    254   Need to synchronize with hardware.
    255 
    256   @param  Ehc   The EHCI device.
    257   @param  Qh    The queue head to unlink.
    258 
    259 **/
    260 VOID
    261 EhcUnlinkQhFromAsync (
    262   IN PEI_USB2_HC_DEV      *Ehc,
    263   IN PEI_EHC_QH           *Qh
    264   )
    265 {
    266   PEI_EHC_QH              *Head;
    267 
    268   ASSERT (Ehc->ReclaimHead->NextQh == Qh);
    269 
    270   //
    271   // Remove the QH from reclamation head, then update the hardware
    272   // visiable part: Only need to loopback the ReclaimHead. The Qh
    273   // is pointing to ReclaimHead (which is staill in the list).
    274   //
    275   Head                    = Ehc->ReclaimHead;
    276 
    277   Head->NextQh            = Qh->NextQh;
    278   Qh->NextQh              = NULL;
    279 
    280   Head->QhHw.HorizonLink  = QH_LINK (Head, EHC_TYPE_QH, FALSE);
    281 
    282   //
    283   // Set and wait the door bell to synchronize with the hardware
    284   //
    285   EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);
    286 
    287   return;
    288 }
    289 
    290 /**
    291   Check the URB's execution result and update the URB's
    292   result accordingly.
    293 
    294   @param Ehc   The EHCI device.
    295   @param Urb   The URB to check result.
    296 
    297   @retval TRUE    URB transfer is finialized.
    298   @retval FALSE   URB transfer is not finialized.
    299 
    300 **/
    301 BOOLEAN
    302 EhcCheckUrbResult (
    303   IN  PEI_USB2_HC_DEV     *Ehc,
    304   IN  PEI_URB             *Urb
    305   )
    306 {
    307   EFI_LIST_ENTRY          *Entry;
    308   PEI_EHC_QTD             *Qtd;
    309   QTD_HW                  *QtdHw;
    310   UINT8                   State;
    311   BOOLEAN                 Finished;
    312 
    313   ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));
    314 
    315   Finished        = TRUE;
    316   Urb->Completed  = 0;
    317 
    318   Urb->Result     = EFI_USB_NOERROR;
    319 
    320   if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
    321     Urb->Result |= EFI_USB_ERR_SYSTEM;
    322     goto ON_EXIT;
    323   }
    324 
    325   EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
    326     Qtd   = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
    327     QtdHw = &Qtd->QtdHw;
    328     State = (UINT8) QtdHw->Status;
    329 
    330     if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
    331       //
    332       // EHCI will halt the queue head when met some error.
    333       // If it is halted, the result of URB is finialized.
    334       //
    335       if ((State & QTD_STAT_ERR_MASK) == 0) {
    336         Urb->Result |= EFI_USB_ERR_STALL;
    337       }
    338 
    339       if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
    340         Urb->Result |= EFI_USB_ERR_BABBLE;
    341       }
    342 
    343       if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
    344         Urb->Result |= EFI_USB_ERR_BUFFER;
    345       }
    346 
    347       if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {
    348         Urb->Result |= EFI_USB_ERR_TIMEOUT;
    349       }
    350 
    351       Finished = TRUE;
    352       goto ON_EXIT;
    353 
    354     } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
    355       //
    356       // The QTD is still active, no need to check furthur.
    357       //
    358       Urb->Result |= EFI_USB_ERR_NOTEXECUTE;
    359 
    360       Finished = FALSE;
    361       goto ON_EXIT;
    362 
    363     } else {
    364       //
    365       // This QTD is finished OK or met short packet read. Update the
    366       // transfer length if it isn't a setup.
    367       //
    368       if (QtdHw->Pid != QTD_PID_SETUP) {
    369         Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;
    370       }
    371 
    372       if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {
    373         //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
    374 
    375         //
    376         // Short packet read condition. If it isn't a setup transfer,
    377         // no need to check furthur: the queue head will halt at the
    378         // ShortReadStop. If it is a setup transfer, need to check the
    379         // Status Stage of the setup transfer to get the finial result
    380         //
    381         if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {
    382 
    383           Finished = TRUE;
    384           goto ON_EXIT;
    385         }
    386       }
    387     }
    388   }
    389 
    390 ON_EXIT:
    391   //
    392   // Return the data toggle set by EHCI hardware, bulk and interrupt
    393   // transfer will use this to initialize the next transaction. For
    394   // Control transfer, it always start a new data toggle sequence for
    395   // new transfer.
    396   //
    397   // NOTICE: don't move DT update before the loop, otherwise there is
    398   // a race condition that DT is wrong.
    399   //
    400   Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;
    401 
    402   return Finished;
    403 }
    404 
    405 /**
    406   Execute the transfer by polling the URB. This is a synchronous operation.
    407 
    408   @param  Ehc               The EHCI device.
    409   @param  Urb               The URB to execute.
    410   @param  TimeOut           The time to wait before abort, in millisecond.
    411 
    412   @retval EFI_DEVICE_ERROR  The transfer failed due to transfer error.
    413   @retval EFI_TIMEOUT       The transfer failed due to time out.
    414   @retval EFI_SUCCESS       The transfer finished OK.
    415 
    416 **/
    417 EFI_STATUS
    418 EhcExecTransfer (
    419   IN  PEI_USB2_HC_DEV     *Ehc,
    420   IN  PEI_URB             *Urb,
    421   IN  UINTN               TimeOut
    422   )
    423 {
    424   EFI_STATUS              Status;
    425   UINTN                   Index;
    426   UINTN                   Loop;
    427   BOOLEAN                 Finished;
    428   BOOLEAN                 InfiniteLoop;
    429 
    430   Status    = EFI_SUCCESS;
    431   Loop      = TimeOut * EHC_1_MILLISECOND;
    432   Finished     = FALSE;
    433   InfiniteLoop = FALSE;
    434 
    435   //
    436   // If Timeout is 0, then the caller must wait for the function to be completed
    437   // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
    438   //
    439   if (TimeOut == 0) {
    440     InfiniteLoop = TRUE;
    441   }
    442 
    443   for (Index = 0; InfiniteLoop || (Index < Loop); Index++) {
    444     Finished = EhcCheckUrbResult (Ehc, Urb);
    445 
    446     if (Finished) {
    447       break;
    448     }
    449 
    450     MicroSecondDelay (EHC_1_MICROSECOND);
    451   }
    452 
    453   if (!Finished) {
    454     Status = EFI_TIMEOUT;
    455   } else if (Urb->Result != EFI_USB_NOERROR) {
    456     Status = EFI_DEVICE_ERROR;
    457   }
    458 
    459   return Status;
    460 }
    461 
    462