]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c
MdeModulePkg: Add PciSioSerialDxe driver
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciSioSerialDxe / SerialIo.c
diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c
new file mode 100644 (file)
index 0000000..f1870f3
--- /dev/null
@@ -0,0 +1,1320 @@
+/** @file\r
+  SerialIo implementation for PCI or SIO UARTs.\r
+\r
+Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Serial.h"\r
+\r
+/**\r
+  Skip the optional Controller device path node and return the\r
+  pointer to the next device path node.\r
+\r
+  @param DevicePath             Pointer to the device path.\r
+  @param ContainsControllerNode Returns TRUE if the Controller device path exists.\r
+  @param ControllerNumber       Returns the Controller Number if Controller device path exists.\r
+\r
+  @return     Pointer to the next device path node.\r
+**/\r
+UART_DEVICE_PATH *\r
+SkipControllerDevicePathNode (\r
+  EFI_DEVICE_PATH_PROTOCOL          *DevicePath,\r
+  BOOLEAN                           *ContainsControllerNode,\r
+  UINT32                            *ControllerNumber\r
+  )\r
+{\r
+  if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) &&\r
+      (DevicePathSubType (DevicePath) == HW_CONTROLLER_DP)\r
+      ) {\r
+    if (ContainsControllerNode != NULL) {\r
+      *ContainsControllerNode = TRUE;\r
+    }\r
+    if (ControllerNumber != NULL) {\r
+      *ControllerNumber = ((CONTROLLER_DEVICE_PATH *) DevicePath)->ControllerNumber;\r
+    }\r
+    DevicePath = NextDevicePathNode (DevicePath);\r
+  } else {\r
+    if (ContainsControllerNode != NULL) {\r
+      *ContainsControllerNode = FALSE;\r
+    }\r
+  }\r
+  return (UART_DEVICE_PATH *) DevicePath;\r
+}\r
+\r
+/**\r
+  Checks whether the UART parameters are valid and computes the Divisor.\r
+\r
+  @param  ClockRate      The clock rate of the serial device used to verify\r
+                         the BaudRate. Do not verify the BaudRate if it's 0.\r
+  @param  BaudRate       The requested baudrate of the serial device.\r
+  @param  DataBits       Number of databits used in serial device.\r
+  @param  Parity         The type of parity used in serial device.\r
+  @param  StopBits       Number of stopbits used in serial device.\r
+  @param  Divisor        Return the divisor if ClockRate is not 0.\r
+  @param  ActualBaudRate Return the actual supported baudrate without\r
+                         exceeding BaudRate. NULL means baudrate degradation\r
+                         is not allowed.\r
+                         If the requested BaudRate is not supported, the routine\r
+                         returns TRUE and the Actual Baud Rate when ActualBaudRate\r
+                         is not NULL, returns FALSE when ActualBaudRate is NULL.\r
+\r
+  @retval TRUE   The UART parameters are valid.\r
+  @retval FALSE  The UART parameters are not valid.\r
+**/\r
+BOOLEAN\r
+VerifyUartParameters (\r
+  IN     UINT32                  ClockRate,\r
+  IN     UINT64                  BaudRate,\r
+  IN     UINT8                   DataBits,\r
+  IN     EFI_PARITY_TYPE         Parity,\r
+  IN     EFI_STOP_BITS_TYPE      StopBits,\r
+     OUT UINT64                  *Divisor,\r
+     OUT UINT64                  *ActualBaudRate\r
+  )\r
+{\r
+  UINT64                     Remainder;\r
+  UINT32                     ComputedBaudRate;\r
+  UINT64                     ComputedDivisor;\r
+  UINT64                     Percent;\r
+\r
+  if ((DataBits < 5) || (DataBits > 8) ||\r
+      (Parity < NoParity) || (Parity > SpaceParity) ||\r
+      (StopBits < OneStopBit) || (StopBits > TwoStopBits) ||\r
+      ((DataBits == 5) && (StopBits == TwoStopBits)) ||\r
+      ((DataBits >= 6) && (DataBits <= 8) && (StopBits == OneFiveStopBits))\r
+      ) {\r
+    return FALSE;\r
+  } \r
+\r
+  //\r
+  // Do not verify the baud rate if clock rate is unknown (0).\r
+  //\r
+  if (ClockRate == 0) {\r
+    return TRUE;\r
+  }\r
+\r
+  //\r
+  // Compute divisor use to program the baud rate using a round determination\r
+  // Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate)\r
+  //         = ClockRate / (BaudRate << 4)\r
+  //\r
+  ComputedDivisor = DivU64x64Remainder (ClockRate, LShiftU64 (BaudRate, 4), &Remainder);\r
+  //\r
+  // Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate)\r
+  // BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3)\r
+  //\r
+  if (Remainder >= LShiftU64 (BaudRate, 3)) {\r
+    ComputedDivisor++;\r
+  }\r
+  //\r
+  // If the computed divisor is larger than the maximum value that can be programmed\r
+  // into the UART, then the requested baud rate can not be supported.\r
+  //\r
+  if (ComputedDivisor > MAX_UINT16) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // If the computed divisor is 0, then use a computed divisor of 1, which will select\r
+  // the maximum supported baud rate.\r
+  //\r
+  if (ComputedDivisor == 0) {\r
+    ComputedDivisor = 1;\r
+  }\r
+\r
+  //\r
+  // Actual baud rate that the serial port will be programmed for\r
+  // should be with in 4% of requested one.\r
+  //\r
+  ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4);\r
+  if (ComputedBaudRate == 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  Percent = DivU64x32 (MultU64x32 (BaudRate, 100), ComputedBaudRate);\r
+  DEBUG ((EFI_D_INFO, "ClockRate = %d\n",  ClockRate));\r
+  DEBUG ((EFI_D_INFO, "Divisor   = %ld\n", ComputedDivisor));\r
+  DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));\r
+\r
+  //\r
+  // If the requested BaudRate is not supported:\r
+  //  Returns TRUE and the Actual Baud Rate when ActualBaudRate is not NULL;\r
+  //  Returns FALSE when ActualBaudRate is NULL.\r
+  //\r
+  if ((Percent >= 96) && (Percent <= 104)) {\r
+    if (ActualBaudRate != NULL) {\r
+      *ActualBaudRate = BaudRate;\r
+    }\r
+    if (Divisor != NULL) {\r
+      *Divisor = ComputedDivisor;\r
+    }\r
+    return TRUE;\r
+  }\r
+  if (ComputedBaudRate < BaudRate) {\r
+    if (ActualBaudRate != NULL) {\r
+      *ActualBaudRate = ComputedBaudRate;\r
+    }\r
+    if (Divisor != NULL) {\r
+      *Divisor = ComputedDivisor;\r
+    }\r
+    return TRUE;\r
+  }\r
+\r
+  //\r
+  // ActualBaudRate is higher than requested baud rate and more than 4% \r
+  // higher than the requested value.  Increment Divisor if it is less \r
+  // than MAX_UINT16 and computed baud rate with new divisor.\r
+  //\r
+  if (ComputedDivisor == MAX_UINT16) {\r
+    return FALSE;\r
+  }\r
+  ComputedDivisor++;\r
+  ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4);\r
+  if (ComputedBaudRate == 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  DEBUG ((EFI_D_INFO, "ClockRate = %d\n",  ClockRate));\r
+  DEBUG ((EFI_D_INFO, "Divisor   = %ld\n", ComputedDivisor));\r
+  DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));\r
+\r
+  if (ActualBaudRate != NULL) {\r
+    *ActualBaudRate = ComputedBaudRate;\r
+  }\r
+  if (Divisor != NULL) {\r
+    *Divisor = ComputedDivisor;\r
+  }\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Detect whether specific FIFO is full or not.\r
+\r
+  @param Fifo    A pointer to the Data Structure SERIAL_DEV_FIFO\r
+\r
+  @return whether specific FIFO is full or not\r
+**/\r
+BOOLEAN\r
+SerialFifoFull (\r
+  IN SERIAL_DEV_FIFO *Fifo\r
+  )\r
+{\r
+  return (BOOLEAN) (((Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE) == Fifo->Head);\r
+}\r
+\r
+/**\r
+  Detect whether specific FIFO is empty or not.\r
\r
+  @param  Fifo    A pointer to the Data Structure SERIAL_DEV_FIFO\r
+\r
+  @return whether specific FIFO is empty or not\r
+**/\r
+BOOLEAN\r
+SerialFifoEmpty (\r
+  IN SERIAL_DEV_FIFO *Fifo\r
+  )\r
+\r
+{\r
+  return (BOOLEAN) (Fifo->Head == Fifo->Tail);\r
+}\r
+\r
+/**\r
+  Add data to specific FIFO.\r
+\r
+  @param Fifo                  A pointer to the Data Structure SERIAL_DEV_FIFO\r
+  @param Data                  the data added to FIFO\r
+\r
+  @retval EFI_SUCCESS           Add data to specific FIFO successfully\r
+  @retval EFI_OUT_OF_RESOURCE   Failed to add data because FIFO is already full\r
+**/\r
+EFI_STATUS\r
+SerialFifoAdd (\r
+  IN OUT SERIAL_DEV_FIFO *Fifo,\r
+  IN     UINT8           Data\r
+  )\r
+{\r
+  //\r
+  // if FIFO full can not add data\r
+  //\r
+  if (SerialFifoFull (Fifo)) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // FIFO is not full can add data\r
+  //\r
+  Fifo->Data[Fifo->Tail] = Data;\r
+  Fifo->Tail = (Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Remove data from specific FIFO.\r
+\r
+  @param Fifo                  A pointer to the Data Structure SERIAL_DEV_FIFO\r
+  @param Data                  the data removed from FIFO\r
+\r
+  @retval EFI_SUCCESS           Remove data from specific FIFO successfully\r
+  @retval EFI_OUT_OF_RESOURCE   Failed to remove data because FIFO is empty\r
+\r
+**/\r
+EFI_STATUS\r
+SerialFifoRemove (\r
+  IN OUT SERIAL_DEV_FIFO *Fifo,\r
+  OUT    UINT8           *Data\r
+  )\r
+{\r
+  //\r
+  // if FIFO is empty, no data can remove\r
+  //\r
+  if (SerialFifoEmpty (Fifo)) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // FIFO is not empty, can remove data\r
+  //\r
+  *Data = Fifo->Data[Fifo->Head];\r
+  Fifo->Head = (Fifo->Head + 1) % SERIAL_MAX_FIFO_SIZE;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reads and writes all avaliable data.\r
+\r
+  @param SerialDevice           The device to transmit.\r
+\r
+  @retval EFI_SUCCESS           Data was read/written successfully.\r
+  @retval EFI_OUT_OF_RESOURCE   Failed because software receive FIFO is full.  Note, when\r
+                                this happens, pending writes are not done.\r
+\r
+**/\r
+EFI_STATUS\r
+SerialReceiveTransmit (\r
+  IN SERIAL_DEV *SerialDevice\r
+  )\r
+\r
+{\r
+  SERIAL_PORT_LSR Lsr;\r
+  UINT8           Data;\r
+  BOOLEAN         ReceiveFifoFull;\r
+  SERIAL_PORT_MSR Msr;\r
+  SERIAL_PORT_MCR Mcr;\r
+  UINTN           TimeOut;\r
+\r
+  Data = 0;\r
+\r
+  //\r
+  // Begin the read or write\r
+  //\r
+  if (SerialDevice->SoftwareLoopbackEnable) {\r
+    do {\r
+      ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);\r
+      if (!SerialFifoEmpty (&SerialDevice->Transmit)) {\r
+        SerialFifoRemove (&SerialDevice->Transmit, &Data);\r
+        if (ReceiveFifoFull) {\r
+          return EFI_OUT_OF_RESOURCES;\r
+        }\r
+\r
+        SerialFifoAdd (&SerialDevice->Receive, Data);\r
+      }\r
+    } while (!SerialFifoEmpty (&SerialDevice->Transmit));\r
+  } else {\r
+    ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);\r
+    //\r
+    // For full handshake flow control, tell the peer to send data\r
+    // if receive buffer is available.\r
+    //\r
+    if (SerialDevice->HardwareFlowControl &&\r
+        !FeaturePcdGet(PcdSerialUseHalfHandshake)&&\r
+        !ReceiveFifoFull\r
+        ) {\r
+      Mcr.Data     = READ_MCR (SerialDevice);\r
+      Mcr.Bits.Rts = 1;\r
+      WRITE_MCR (SerialDevice, Mcr.Data);\r
+    }\r
+    do {\r
+      Lsr.Data = READ_LSR (SerialDevice);\r
+\r
+      //\r
+      // Flush incomming data to prevent a an overrun during a long write\r
+      //\r
+      if ((Lsr.Bits.Dr == 1) && !ReceiveFifoFull) {\r
+        ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);\r
+        if (!ReceiveFifoFull) {\r
+          if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Oe == 1 || Lsr.Bits.Pe == 1 || Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {\r
+            REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+              EFI_ERROR_CODE,\r
+              EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,\r
+              SerialDevice->DevicePath\r
+              );\r
+            if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Pe == 1|| Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {\r
+              Data = READ_RBR (SerialDevice);\r
+              continue;\r
+            }\r
+          }\r
+\r
+          Data = READ_RBR (SerialDevice);\r
+\r
+          SerialFifoAdd (&SerialDevice->Receive, Data);\r
+          \r
+          //\r
+          // For full handshake flow control, if receive buffer full\r
+          // tell the peer to stop sending data.\r
+          //\r
+          if (SerialDevice->HardwareFlowControl &&\r
+              !FeaturePcdGet(PcdSerialUseHalfHandshake)   &&\r
+              SerialFifoFull (&SerialDevice->Receive)\r
+              ) {\r
+            Mcr.Data     = READ_MCR (SerialDevice);\r
+            Mcr.Bits.Rts = 0;\r
+            WRITE_MCR (SerialDevice, Mcr.Data);\r
+          }\r
+\r
+\r
+          continue;\r
+        } else {\r
+          REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+            EFI_PROGRESS_CODE,\r
+            EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER | EFI_PERIPHERAL_SERIAL_PORT,\r
+            SerialDevice->DevicePath\r
+            );\r
+        }\r
+      }\r
+      //\r
+      // Do the write\r
+      //\r
+      if (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit)) {\r
+        //\r
+        // Make sure the transmit data will not be missed\r
+        //\r
+        if (SerialDevice->HardwareFlowControl) {\r
+          //\r
+          // For half handshake flow control assert RTS before sending.\r
+          //\r
+          if (FeaturePcdGet(PcdSerialUseHalfHandshake)) {\r
+            Mcr.Data     = READ_MCR (SerialDevice);\r
+            Mcr.Bits.Rts= 0;\r
+            WRITE_MCR (SerialDevice, Mcr.Data);\r
+          }\r
+          //\r
+          // Wait for CTS\r
+          //\r
+          TimeOut   = 0;\r
+          Msr.Data  = READ_MSR (SerialDevice);\r
+          while ((Msr.Bits.Dcd == 1) && ((Msr.Bits.Cts == 0) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) {\r
+            gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
+            TimeOut++;\r
+            if (TimeOut > 5) {\r
+              break;\r
+            }\r
+\r
+            Msr.Data = READ_MSR (SerialDevice);\r
+          }\r
+\r
+          if ((Msr.Bits.Dcd == 0) || ((Msr.Bits.Cts == 1) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) {\r
+            SerialFifoRemove (&SerialDevice->Transmit, &Data);\r
+            WRITE_THR (SerialDevice, Data);\r
+          }\r
+\r
+          //\r
+          // For half handshake flow control, tell DCE we are done.\r
+          //\r
+          if (FeaturePcdGet(PcdSerialUseHalfHandshake)) {\r
+            Mcr.Data = READ_MCR (SerialDevice);\r
+            Mcr.Bits.Rts = 1;\r
+            WRITE_MCR (SerialDevice, Mcr.Data);\r
+          }\r
+        } else {\r
+          SerialFifoRemove (&SerialDevice->Transmit, &Data);\r
+          WRITE_THR (SerialDevice, Data);\r
+        }\r
+      }\r
+    } while (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit));\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Flush the serial hardware transmit FIFO and shift register.\r
+\r
+  @param SerialDevice  The device to flush.\r
+**/\r
+VOID\r
+SerialFlushTransmitFifo (\r
+  SERIAL_DEV  *SerialDevice\r
+  )\r
+{\r
+  SERIAL_PORT_LSR  Lsr;\r
+\r
+  //\r
+  // Wait for the serial port to be ready, to make sure both the transmit FIFO\r
+  // and shift register empty.\r
+  //\r
+  do {\r
+    Lsr.Data = READ_LSR (SerialDevice);\r
+  } while (Lsr.Bits.Temt == 0);\r
+}\r
+\r
+//\r
+// Interface Functions\r
+//\r
+/**\r
+  Reset serial device.\r
+\r
+  @param This               Pointer to EFI_SERIAL_IO_PROTOCOL\r
+\r
+  @retval EFI_SUCCESS        Reset successfully\r
+  @retval EFI_DEVICE_ERROR   Failed to reset\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialReset (\r
+  IN EFI_SERIAL_IO_PROTOCOL  *This\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  SERIAL_DEV      *SerialDevice;\r
+  SERIAL_PORT_LCR Lcr;\r
+  SERIAL_PORT_IER Ier;\r
+  SERIAL_PORT_MCR Mcr;\r
+  SERIAL_PORT_FCR Fcr;\r
+  EFI_TPL         Tpl;\r
+  UINT32          Control;\r
+\r
+  SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
+\r
+  //\r
+  // Report the status code reset the serial\r
+  //\r
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+    EFI_PROGRESS_CODE,\r
+    EFI_P_PC_RESET | EFI_PERIPHERAL_SERIAL_PORT,\r
+    SerialDevice->DevicePath\r
+    );\r
+\r
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+  SerialFlushTransmitFifo (SerialDevice);\r
+\r
+  //\r
+  // Make sure DLAB is 0.\r
+  //\r
+  Lcr.Data      = READ_LCR (SerialDevice);\r
+  Lcr.Bits.DLab = 0;\r
+  WRITE_LCR (SerialDevice, Lcr.Data);\r
+\r
+  //\r
+  // Turn off all interrupts\r
+  //\r
+  Ier.Data        = READ_IER (SerialDevice);\r
+  Ier.Bits.Ravie  = 0;\r
+  Ier.Bits.Theie  = 0;\r
+  Ier.Bits.Rie    = 0;\r
+  Ier.Bits.Mie    = 0;\r
+  WRITE_IER (SerialDevice, Ier.Data);\r
+\r
+  //\r
+  // Reset the FIFO\r
+  //\r
+  Fcr.Data = 0;\r
+  Fcr.Bits.TrFIFOE = 0;\r
+  WRITE_FCR (SerialDevice, Fcr.Data);\r
+\r
+  //\r
+  // Turn off loopback and disable device interrupt.\r
+  //\r
+  Mcr.Data      = READ_MCR (SerialDevice);\r
+  Mcr.Bits.Out1 = 0;\r
+  Mcr.Bits.Out2 = 0;\r
+  Mcr.Bits.Lme  = 0;\r
+  WRITE_MCR (SerialDevice, Mcr.Data);\r
+\r
+  //\r
+  // Clear the scratch pad register\r
+  //\r
+  WRITE_SCR (SerialDevice, 0);\r
+\r
+  //\r
+  // Enable FIFO\r
+  //\r
+  Fcr.Bits.TrFIFOE  = 1;\r
+  if (SerialDevice->ReceiveFifoDepth > 16) {\r
+    Fcr.Bits.TrFIFO64 = 1;\r
+  }\r
+  Fcr.Bits.ResetRF  = 1;\r
+  Fcr.Bits.ResetTF  = 1;\r
+  WRITE_FCR (SerialDevice, Fcr.Data);\r
+\r
+  //\r
+  // Go set the current attributes\r
+  //\r
+  Status = This->SetAttributes (\r
+                   This,\r
+                   This->Mode->BaudRate,\r
+                   This->Mode->ReceiveFifoDepth,\r
+                   This->Mode->Timeout,\r
+                   (EFI_PARITY_TYPE) This->Mode->Parity,\r
+                   (UINT8) This->Mode->DataBits,\r
+                   (EFI_STOP_BITS_TYPE) This->Mode->StopBits\r
+                   );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->RestoreTPL (Tpl);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Go set the current control bits\r
+  //\r
+  Control = 0;\r
+  if (SerialDevice->HardwareFlowControl) {\r
+    Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;\r
+  }\r
+  if (SerialDevice->SoftwareLoopbackEnable) {\r
+    Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;\r
+  }\r
+  Status = This->SetControl (\r
+                   This,\r
+                   Control\r
+                   );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->RestoreTPL (Tpl);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Reset the software FIFO\r
+  //\r
+  SerialDevice->Receive.Head = SerialDevice->Receive.Tail = 0;\r
+  SerialDevice->Transmit.Head = SerialDevice->Transmit.Tail = 0;\r
+  gBS->RestoreTPL (Tpl);\r
+\r
+  //\r
+  // Device reset is complete\r
+  //\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Set new attributes to a serial device.\r
+\r
+  @param This                     Pointer to EFI_SERIAL_IO_PROTOCOL\r
+  @param  BaudRate                 The baudrate of the serial device\r
+  @param  ReceiveFifoDepth         The depth of receive FIFO buffer\r
+  @param  Timeout                  The request timeout for a single char\r
+  @param  Parity                   The type of parity used in serial device\r
+  @param  DataBits                 Number of databits used in serial device\r
+  @param  StopBits                 Number of stopbits used in serial device\r
+\r
+  @retval  EFI_SUCCESS              The new attributes were set\r
+  @retval  EFI_INVALID_PARAMETERS   One or more attributes have an unsupported value\r
+  @retval  EFI_UNSUPPORTED          Data Bits can not set to 5 or 6\r
+  @retval  EFI_DEVICE_ERROR         The serial device is not functioning correctly (no return)\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialSetAttributes (\r
+  IN EFI_SERIAL_IO_PROTOCOL  *This,\r
+  IN UINT64                  BaudRate,\r
+  IN UINT32                  ReceiveFifoDepth,\r
+  IN UINT32                  Timeout,\r
+  IN EFI_PARITY_TYPE         Parity,\r
+  IN UINT8                   DataBits,\r
+  IN EFI_STOP_BITS_TYPE      StopBits\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  SERIAL_DEV                *SerialDevice;\r
+  UINT64                    Divisor;\r
+  SERIAL_PORT_LCR           Lcr;\r
+  UART_DEVICE_PATH          *Uart;\r
+  EFI_TPL                   Tpl;\r
+\r
+  SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
+\r
+  //\r
+  // Check for default settings and fill in actual values.\r
+  //\r
+  if (BaudRate == 0) {\r
+    BaudRate = PcdGet64 (PcdUartDefaultBaudRate);\r
+  }\r
+\r
+  if (ReceiveFifoDepth == 0) {\r
+    ReceiveFifoDepth = SerialDevice->ReceiveFifoDepth;\r
+  }\r
+\r
+  if (Timeout == 0) {\r
+    Timeout = SERIAL_PORT_DEFAULT_TIMEOUT;\r
+  }\r
+\r
+  if (Parity == DefaultParity) {\r
+    Parity = (EFI_PARITY_TYPE) PcdGet8 (PcdUartDefaultParity);\r
+  }\r
+\r
+  if (DataBits == 0) {\r
+    DataBits = PcdGet8 (PcdUartDefaultDataBits);\r
+  }\r
+\r
+  if (StopBits == DefaultStopBits) {\r
+    StopBits = (EFI_STOP_BITS_TYPE) PcdGet8 (PcdUartDefaultStopBits);\r
+  }\r
+\r
+  if (!VerifyUartParameters (SerialDevice->ClockRate, BaudRate, DataBits, Parity, StopBits, &Divisor, &BaudRate)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((ReceiveFifoDepth == 0) || (ReceiveFifoDepth > SerialDevice->ReceiveFifoDepth)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Timeout < SERIAL_PORT_MIN_TIMEOUT) || (Timeout > SERIAL_PORT_MAX_TIMEOUT)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+  SerialFlushTransmitFifo (SerialDevice);\r
+\r
+  //\r
+  // Put serial port on Divisor Latch Mode\r
+  //\r
+  Lcr.Data      = READ_LCR (SerialDevice);\r
+  Lcr.Bits.DLab = 1;\r
+  WRITE_LCR (SerialDevice, Lcr.Data);\r
+\r
+  //\r
+  // Write the divisor to the serial port\r
+  //\r
+  WRITE_DLL (SerialDevice, (UINT8) Divisor);\r
+  WRITE_DLM (SerialDevice, (UINT8) ((UINT16) Divisor >> 8));\r
+\r
+  //\r
+  // Put serial port back in normal mode and set remaining attributes.\r
+  //\r
+  Lcr.Bits.DLab = 0;\r
+\r
+  switch (Parity) {\r
+  case NoParity:\r
+    Lcr.Bits.ParEn    = 0;\r
+    Lcr.Bits.EvenPar  = 0;\r
+    Lcr.Bits.SticPar  = 0;\r
+    break;\r
+\r
+  case EvenParity:\r
+    Lcr.Bits.ParEn    = 1;\r
+    Lcr.Bits.EvenPar  = 1;\r
+    Lcr.Bits.SticPar  = 0;\r
+    break;\r
+\r
+  case OddParity:\r
+    Lcr.Bits.ParEn    = 1;\r
+    Lcr.Bits.EvenPar  = 0;\r
+    Lcr.Bits.SticPar  = 0;\r
+    break;\r
+\r
+  case SpaceParity:\r
+    Lcr.Bits.ParEn    = 1;\r
+    Lcr.Bits.EvenPar  = 1;\r
+    Lcr.Bits.SticPar  = 1;\r
+    break;\r
+\r
+  case MarkParity:\r
+    Lcr.Bits.ParEn    = 1;\r
+    Lcr.Bits.EvenPar  = 0;\r
+    Lcr.Bits.SticPar  = 1;\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  switch (StopBits) {\r
+  case OneStopBit:\r
+    Lcr.Bits.StopB = 0;\r
+    break;\r
+\r
+  case OneFiveStopBits:\r
+  case TwoStopBits:\r
+    Lcr.Bits.StopB = 1;\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+  //\r
+  // DataBits\r
+  //\r
+  Lcr.Bits.SerialDB = (UINT8) ((DataBits - 5) & 0x03);\r
+  WRITE_LCR (SerialDevice, Lcr.Data);\r
+\r
+  //\r
+  // Set the Serial I/O mode\r
+  //\r
+  This->Mode->BaudRate          = BaudRate;\r
+  This->Mode->ReceiveFifoDepth  = ReceiveFifoDepth;\r
+  This->Mode->Timeout           = Timeout;\r
+  This->Mode->Parity            = Parity;\r
+  This->Mode->DataBits          = DataBits;\r
+  This->Mode->StopBits          = StopBits;\r
+\r
+  //\r
+  // See if Device Path Node has actually changed\r
+  //\r
+  if (SerialDevice->UartDevicePath.BaudRate == BaudRate &&\r
+      SerialDevice->UartDevicePath.DataBits == DataBits &&\r
+      SerialDevice->UartDevicePath.Parity == Parity &&\r
+      SerialDevice->UartDevicePath.StopBits == StopBits\r
+      ) {\r
+    gBS->RestoreTPL (Tpl);\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Update the device path\r
+  //\r
+  SerialDevice->UartDevicePath.BaudRate = BaudRate;\r
+  SerialDevice->UartDevicePath.DataBits = DataBits;\r
+  SerialDevice->UartDevicePath.Parity   = (UINT8) Parity;\r
+  SerialDevice->UartDevicePath.StopBits = (UINT8) StopBits;\r
+\r
+  Status = EFI_SUCCESS;\r
+  if (SerialDevice->Handle != NULL) {\r
+\r
+    //\r
+    // Skip the optional Controller device path node\r
+    //\r
+    Uart = SkipControllerDevicePathNode (\r
+             (EFI_DEVICE_PATH_PROTOCOL *) (\r
+               (UINT8 *) SerialDevice->DevicePath + GetDevicePathSize (SerialDevice->ParentDevicePath) - END_DEVICE_PATH_LENGTH\r
+               ),\r
+             NULL,\r
+             NULL\r
+             );\r
+    CopyMem (Uart, &SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH));\r
+    Status = gBS->ReinstallProtocolInterface (\r
+                    SerialDevice->Handle,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    SerialDevice->DevicePath,\r
+                    SerialDevice->DevicePath\r
+                    );\r
+  }\r
+\r
+  gBS->RestoreTPL (Tpl);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set Control Bits.\r
+\r
+  @param This              Pointer to EFI_SERIAL_IO_PROTOCOL\r
+  @param Control           Control bits that can be settable\r
+\r
+  @retval EFI_SUCCESS       New Control bits were set successfully\r
+  @retval EFI_UNSUPPORTED   The Control bits wanted to set are not supported\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialSetControl (\r
+  IN EFI_SERIAL_IO_PROTOCOL  *This,\r
+  IN UINT32                  Control\r
+  )\r
+{\r
+  SERIAL_DEV                    *SerialDevice;\r
+  SERIAL_PORT_MCR               Mcr;\r
+  EFI_TPL                       Tpl;\r
+  UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;\r
+  EFI_STATUS                    Status;\r
+\r
+  //\r
+  // The control bits that can be set are :\r
+  //     EFI_SERIAL_DATA_TERMINAL_READY: 0x0001  // WO\r
+  //     EFI_SERIAL_REQUEST_TO_SEND: 0x0002  // WO\r
+  //     EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000  // RW\r
+  //     EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000  // RW\r
+  //     EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW\r
+  //\r
+  SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
+\r
+  //\r
+  // first determine the parameter is invalid\r
+  //\r
+  if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |\r
+                    EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |\r
+                    EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+  Mcr.Data = READ_MCR (SerialDevice);\r
+  Mcr.Bits.DtrC = 0;\r
+  Mcr.Bits.Rts = 0;\r
+  Mcr.Bits.Lme = 0;\r
+  SerialDevice->SoftwareLoopbackEnable = FALSE;\r
+  SerialDevice->HardwareFlowControl = FALSE;\r
+\r
+  if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {\r
+    Mcr.Bits.DtrC = 1;\r
+  }\r
+\r
+  if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {\r
+    Mcr.Bits.Rts = 1;\r
+  }\r
+\r
+  if ((Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) {\r
+    Mcr.Bits.Lme = 1;\r
+  }\r
+\r
+  if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {\r
+    SerialDevice->HardwareFlowControl = TRUE;\r
+  }\r
+\r
+  WRITE_MCR (SerialDevice, Mcr.Data);\r
+\r
+  if ((Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) {\r
+    SerialDevice->SoftwareLoopbackEnable = TRUE;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+  if (SerialDevice->Handle != NULL) {\r
+    FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) (\r
+                    (UINTN) SerialDevice->DevicePath\r
+                    + GetDevicePathSize (SerialDevice->ParentDevicePath)\r
+                    - END_DEVICE_PATH_LENGTH\r
+                    + sizeof (UART_DEVICE_PATH)\r
+                    );\r
+    if (IsUartFlowControlDevicePathNode (FlowControl) &&\r
+        ((BOOLEAN) (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) != SerialDevice->HardwareFlowControl)) {\r
+      //\r
+      // Flow Control setting is changed, need to reinstall device path protocol\r
+      //\r
+      WriteUnaligned32 (&FlowControl->FlowControlMap, SerialDevice->HardwareFlowControl ? UART_FLOW_CONTROL_HARDWARE : 0);\r
+      Status = gBS->ReinstallProtocolInterface (\r
+                      SerialDevice->Handle,\r
+                      &gEfiDevicePathProtocolGuid,\r
+                      SerialDevice->DevicePath,\r
+                      SerialDevice->DevicePath\r
+                      );\r
+    }\r
+  }\r
+\r
+  gBS->RestoreTPL (Tpl);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get ControlBits.\r
+\r
+  @param This          Pointer to EFI_SERIAL_IO_PROTOCOL\r
+  @param Control       Control signals of the serial device\r
+\r
+  @retval EFI_SUCCESS   Get Control signals successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialGetControl (\r
+  IN EFI_SERIAL_IO_PROTOCOL  *This,\r
+  OUT UINT32                 *Control\r
+  )\r
+{\r
+  SERIAL_DEV      *SerialDevice;\r
+  SERIAL_PORT_MSR Msr;\r
+  SERIAL_PORT_MCR Mcr;\r
+  EFI_TPL         Tpl;\r
+\r
+  Tpl           = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+  SerialDevice  = SERIAL_DEV_FROM_THIS (This);\r
+\r
+  *Control      = 0;\r
+\r
+  //\r
+  // Read the Modem Status Register\r
+  //\r
+  Msr.Data = READ_MSR (SerialDevice);\r
+\r
+  if (Msr.Bits.Cts == 1) {\r
+    *Control |= EFI_SERIAL_CLEAR_TO_SEND;\r
+  }\r
+\r
+  if (Msr.Bits.Dsr == 1) {\r
+    *Control |= EFI_SERIAL_DATA_SET_READY;\r
+  }\r
+\r
+  if (Msr.Bits.Ri == 1) {\r
+    *Control |= EFI_SERIAL_RING_INDICATE;\r
+  }\r
+\r
+  if (Msr.Bits.Dcd == 1) {\r
+    *Control |= EFI_SERIAL_CARRIER_DETECT;\r
+  }\r
+  //\r
+  // Read the Modem Control Register\r
+  //\r
+  Mcr.Data = READ_MCR (SerialDevice);\r
+\r
+  if (Mcr.Bits.DtrC == 1) {\r
+    *Control |= EFI_SERIAL_DATA_TERMINAL_READY;\r
+  }\r
+\r
+  if (Mcr.Bits.Rts == 1) {\r
+    *Control |= EFI_SERIAL_REQUEST_TO_SEND;\r
+  }\r
+\r
+  if (Mcr.Bits.Lme == 1) {\r
+    *Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;\r
+  }\r
+\r
+  if (SerialDevice->HardwareFlowControl) {\r
+    *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;\r
+  }\r
+  //\r
+  // Update FIFO status\r
+  //\r
+  SerialReceiveTransmit (SerialDevice);\r
+\r
+  //\r
+  // See if the Transmit FIFO is empty\r
+  //\r
+  if (SerialFifoEmpty (&SerialDevice->Transmit)) {\r
+    *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;\r
+  }\r
+\r
+  //\r
+  // See if the Receive FIFO is empty.\r
+  //\r
+  if (SerialFifoEmpty (&SerialDevice->Receive)) {\r
+    *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;\r
+  }\r
+\r
+  if (SerialDevice->SoftwareLoopbackEnable) {\r
+    *Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;\r
+  }\r
+\r
+  gBS->RestoreTPL (Tpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Write the specified number of bytes to serial device.\r
+\r
+  @param This               Pointer to EFI_SERIAL_IO_PROTOCOL\r
+  @param  BufferSize         On input the size of Buffer, on output the amount of\r
+                       data actually written\r
+  @param  Buffer             The buffer of data to write\r
+\r
+  @retval EFI_SUCCESS        The data were written successfully\r
+  @retval EFI_DEVICE_ERROR   The device reported an error\r
+  @retval EFI_TIMEOUT        The write operation was stopped due to timeout\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialWrite (\r
+  IN EFI_SERIAL_IO_PROTOCOL  *This,\r
+  IN OUT UINTN               *BufferSize,\r
+  IN VOID                    *Buffer\r
+  )\r
+{\r
+  SERIAL_DEV  *SerialDevice;\r
+  UINT8       *CharBuffer;\r
+  UINT32      Index;\r
+  UINTN       Elapsed;\r
+  UINTN       ActualWrite;\r
+  EFI_TPL     Tpl;\r
+  UINTN       Timeout;\r
+  UINTN       BitsPerCharacter;\r
+\r
+  SerialDevice  = SERIAL_DEV_FROM_THIS (This);\r
+  Elapsed       = 0;\r
+  ActualWrite   = 0;\r
+\r
+  if (*BufferSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+      EFI_ERROR_CODE,\r
+      EFI_P_EC_OUTPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,\r
+      SerialDevice->DevicePath\r
+      );\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Tpl         = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+  CharBuffer  = (UINT8 *) Buffer;\r
+\r
+  //\r
+  // Compute the number of bits in a single character.  This is a start bit,\r
+  // followed by the number of data bits, followed by the number of stop bits.\r
+  // The number of stop bits is specified by an enumeration that includes\r
+  // support for 1.5 stop bits.  Treat 1.5 stop bits as 2 stop bits.\r
+  //\r
+  BitsPerCharacter =\r
+    1 +\r
+    This->Mode->DataBits +\r
+    ((This->Mode->StopBits == TwoStopBits) ? 2 : This->Mode->StopBits);\r
+\r
+  //\r
+  // Compute the timeout in microseconds to wait for a single byte to be\r
+  // transmitted.  The Mode structure contans a Timeout field that is the\r
+  // maximum time to transmit or receive a character.  However, many UARTs\r
+  // have a FIFO for transmits, so the time required to add one new character\r
+  // to the transmit FIFO may be the time required to flush a full FIFO.  If\r
+  // the Timeout in the Mode structure is smaller than the time required to\r
+  // flush a full FIFO at the current baud rate, then use a timeout value that\r
+  // is required to flush a full transmit FIFO.\r
+  //\r
+  Timeout = MAX (\r
+              This->Mode->Timeout,\r
+              (UINTN)DivU64x64Remainder (\r
+                BitsPerCharacter * (SerialDevice->TransmitFifoDepth + 1) * 1000000,\r
+                This->Mode->BaudRate,\r
+                NULL\r
+                )\r
+              );\r
+  \r
+  for (Index = 0; Index < *BufferSize; Index++) {\r
+    SerialFifoAdd (&SerialDevice->Transmit, CharBuffer[Index]);\r
+\r
+    while (SerialReceiveTransmit (SerialDevice) != EFI_SUCCESS || !SerialFifoEmpty (&SerialDevice->Transmit)) {\r
+      //\r
+      //  Unsuccessful write so check if timeout has expired, if not,\r
+      //  stall for a bit, increment time elapsed, and try again\r
+      //\r
+      if (Elapsed >= Timeout) {\r
+        *BufferSize = ActualWrite;\r
+        gBS->RestoreTPL (Tpl);\r
+        return EFI_TIMEOUT;\r
+      }\r
+\r
+      gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
+\r
+      Elapsed += TIMEOUT_STALL_INTERVAL;\r
+    }\r
+\r
+    ActualWrite++;\r
+    //\r
+    //  Successful write so reset timeout\r
+    //\r
+    Elapsed = 0;\r
+  }\r
+\r
+  gBS->RestoreTPL (Tpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Read the specified number of bytes from serial device.\r
+\r
+  @param This               Pointer to EFI_SERIAL_IO_PROTOCOL\r
+  @param BufferSize         On input the size of Buffer, on output the amount of\r
+                            data returned in buffer\r
+  @param Buffer             The buffer to return the data into\r
+\r
+  @retval EFI_SUCCESS        The data were read successfully\r
+  @retval EFI_DEVICE_ERROR   The device reported an error\r
+  @retval EFI_TIMEOUT        The read operation was stopped due to timeout\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialRead (\r
+  IN EFI_SERIAL_IO_PROTOCOL  *This,\r
+  IN OUT UINTN               *BufferSize,\r
+  OUT VOID                   *Buffer\r
+  )\r
+{\r
+  SERIAL_DEV  *SerialDevice;\r
+  UINT32      Index;\r
+  UINT8       *CharBuffer;\r
+  UINTN       Elapsed;\r
+  EFI_STATUS  Status;\r
+  EFI_TPL     Tpl;\r
+\r
+  SerialDevice  = SERIAL_DEV_FROM_THIS (This);\r
+  Elapsed       = 0;\r
+\r
+  if (*BufferSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Tpl     = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+  Status  = SerialReceiveTransmit (SerialDevice);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    *BufferSize = 0;\r
+\r
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+      EFI_ERROR_CODE,\r
+      EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,\r
+      SerialDevice->DevicePath\r
+      );\r
+\r
+    gBS->RestoreTPL (Tpl);\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  CharBuffer = (UINT8 *) Buffer;\r
+  for (Index = 0; Index < *BufferSize; Index++) {\r
+    while (SerialFifoRemove (&SerialDevice->Receive, &(CharBuffer[Index])) != EFI_SUCCESS) {\r
+      //\r
+      //  Unsuccessful read so check if timeout has expired, if not,\r
+      //  stall for a bit, increment time elapsed, and try again\r
+      //  Need this time out to get conspliter to work.\r
+      //\r
+      if (Elapsed >= This->Mode->Timeout) {\r
+        *BufferSize = Index;\r
+        gBS->RestoreTPL (Tpl);\r
+        return EFI_TIMEOUT;\r
+      }\r
+\r
+      gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
+      Elapsed += TIMEOUT_STALL_INTERVAL;\r
+\r
+      Status = SerialReceiveTransmit (SerialDevice);\r
+      if (Status == EFI_DEVICE_ERROR) {\r
+        *BufferSize = Index;\r
+        gBS->RestoreTPL (Tpl);\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+    }\r
+    //\r
+    //  Successful read so reset timeout\r
+    //\r
+    Elapsed = 0;\r
+  }\r
+\r
+  SerialReceiveTransmit (SerialDevice);\r
+\r
+  gBS->RestoreTPL (Tpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Use scratchpad register to test if this serial port is present.\r
+\r
+  @param SerialDevice   Pointer to serial device structure\r
+\r
+  @return if this serial port is present\r
+**/\r
+BOOLEAN\r
+SerialPresent (\r
+  IN SERIAL_DEV *SerialDevice\r
+  )\r
+\r
+{\r
+  UINT8   Temp;\r
+  BOOLEAN Status;\r
+\r
+  Status = TRUE;\r
+\r
+  //\r
+  // Save SCR reg\r
+  //\r
+  Temp = READ_SCR (SerialDevice);\r
+  WRITE_SCR (SerialDevice, 0xAA);\r
+\r
+  if (READ_SCR (SerialDevice) != 0xAA) {\r
+    Status = FALSE;\r
+  }\r
+\r
+  WRITE_SCR (SerialDevice, 0x55);\r
+\r
+  if (READ_SCR (SerialDevice) != 0x55) {\r
+    Status = FALSE;\r
+  }\r
+  //\r
+  // Restore SCR\r
+  //\r
+  WRITE_SCR (SerialDevice, Temp);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read serial port.\r
+\r
+  @param SerialDev     Pointer to serial device\r
+  @param Offset        Offset in register group\r
+\r
+  @return Data read from serial port\r
+\r
+**/\r
+UINT8\r
+SerialReadRegister (\r
+  IN SERIAL_DEV                            *SerialDev,\r
+  IN UINT32                                Offset\r
+  )\r
+{\r
+  UINT8                                    Data;\r
+  EFI_STATUS                               Status;\r
+\r
+  if (SerialDev->PciDeviceInfo == NULL) {\r
+    return IoRead8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride);\r
+  } else {\r
+    if (SerialDev->MmioAccess) {\r
+      Status = SerialDev->PciDeviceInfo->PciIo->Mem.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,\r
+                                                          SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);\r
+    } else {\r
+      Status = SerialDev->PciDeviceInfo->PciIo->Io.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,\r
+                                                         SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);\r
+    }\r
+    ASSERT_EFI_ERROR (Status);\r
+    return Data;\r
+  }\r
+}\r
+\r
+/**\r
+  Write serial port.\r
+\r
+  @param  SerialDev     Pointer to serial device\r
+  @param  Offset        Offset in register group\r
+  @param  Data          data which is to be written to some serial port register\r
+**/\r
+VOID\r
+SerialWriteRegister (\r
+  IN SERIAL_DEV                            *SerialDev,\r
+  IN UINT32                                Offset,\r
+  IN UINT8                                 Data\r
+  )\r
+{\r
+  EFI_STATUS                               Status;\r
+\r
+  if (SerialDev->PciDeviceInfo == NULL) {\r
+    IoWrite8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, Data);\r
+  } else {\r
+    if (SerialDev->MmioAccess) {\r
+      Status = SerialDev->PciDeviceInfo->PciIo->Mem.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,\r
+                                                           SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);\r
+    } else {\r
+      Status = SerialDev->PciDeviceInfo->PciIo->Io.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,\r
+                                                          SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);\r
+    }\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+}\r