]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c
MdeModulePkg/PciSioSerialDxe: Flush Tx before config change
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciSioSerialDxe / SerialIo.c
index 8377ffa13c7aab5a0ea8869c5feecce6846c590e..56c5faf5db1f7f379dc6fd3edba7ca1236ed4025 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   SerialIo implementation for PCI or SIO UARTs.\r
 \r
-Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR>\r
 SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
@@ -436,6 +436,68 @@ SerialReceiveTransmit (
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Flush the serial hardware transmit FIFO, holding register, and shift register.\r
+\r
+  @param SerialDevice  The device to flush.\r
+\r
+  @retval  EFI_SUCCESS  The transmit FIFO is completely flushed.\r
+  @retval  EFI_TIMEOUT  A timeout occured waiting for the transmit FIFO to flush.\r
+**/\r
+EFI_STATUS\r
+SerialFlushTransmitFifo (\r
+  SERIAL_DEV  *SerialDevice\r
+  )\r
+{\r
+  SERIAL_PORT_LSR  Lsr;\r
+  UINTN            Timeout;\r
+  UINTN            Elapsed;\r
+\r
+  //\r
+  // If this is the first time the UART is being configured, then the current\r
+  // UART settings are not known, so compute a timeout to wait for the Tx FIFO\r
+  // assuming the worst case current settings.\r
+  //\r
+  // Timeout = (Max Bits per Char) * (Max Pending Chars) / (Slowest Baud Rate)\r
+  //   Max Bits per Char = Start bit + 8 data bits + parity + 2 stop bits = 12\r
+  //   Max Pending Chars = Largest Tx FIFO + hold + shift = 64 + 1 + 1 = 66\r
+  //   Slowest Reasonable Baud Rate = 300 baud\r
+  // Timeout = 12 * 66 / 300 = 2.64 seconds = 2,640,000 uS\r
+  //\r
+  Timeout = 2640000;\r
+\r
+  //\r
+  // Use the largest of the computed timeout, the default timeout, and the\r
+  // currently set timeout.\r
+  //\r
+  Timeout = MAX (Timeout, SERIAL_PORT_DEFAULT_TIMEOUT);\r
+  Timeout = MAX (Timeout, SerialDevice->SerialMode.Timeout);\r
+\r
+  //\r
+  // Wait for the shortest time possible for the serial port to be ready making\r
+  // sure the transmit FIFO, holding register, and shift register are all\r
+  // empty.  The actual wait time is expected to be very small because the\r
+  // number characters currently in the FIFO should be small when a\r
+  // configuration change is requested.\r
+  //\r
+  // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
+  // in the rest of this function that may send additional characters to this\r
+  // UART device invalidating the flush operation.\r
+  //\r
+  Elapsed = 0;\r
+  Lsr.Data = READ_LSR (SerialDevice);\r
+  while (Lsr.Bits.Temt == 0 || Lsr.Bits.Thre == 0) {\r
+    if (Elapsed >= Timeout) {\r
+      return EFI_TIMEOUT;\r
+    }\r
+    gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
+    Elapsed += TIMEOUT_STALL_INTERVAL;\r
+    Lsr.Data = READ_LSR (SerialDevice);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 //\r
 // Interface Functions\r
 //\r
@@ -476,6 +538,15 @@ SerialReset (
 \r
   Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
 \r
+  //\r
+  // Wait for all data to be transmitted before changing the UART configuration.\r
+  //\r
+  // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
+  // that may send additional characters to this UART device until the UART\r
+  // configuration change is complete.\r
+  //\r
+  SerialFlushTransmitFifo (SerialDevice);\r
+\r
   //\r
   // Make sure DLAB is 0.\r
   //\r
@@ -654,6 +725,15 @@ SerialSetAttributes (
 \r
   Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
 \r
+  //\r
+  // Wait for all data to be transmitted before changing the UART configuration.\r
+  //\r
+  // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
+  // that may send additional characters to this UART device until the UART\r
+  // configuration change is complete.\r
+  //\r
+  SerialFlushTransmitFifo (SerialDevice);\r
+\r
   //\r
   // Put serial port on Divisor Latch Mode\r
   //\r
@@ -826,6 +906,15 @@ SerialSetControl (
 \r
   Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
 \r
+  //\r
+  // Wait for all data to be transmitted before changing the UART configuration.\r
+  //\r
+  // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
+  // that may send additional characters to this UART device until the UART\r
+  // configuration change is complete.\r
+  //\r
+  SerialFlushTransmitFifo (SerialDevice);\r
+\r
   Mcr.Data = READ_MCR (SerialDevice);\r
   Mcr.Bits.DtrC = 0;\r
   Mcr.Bits.Rts = 0;\r