2 SerialIo implementation for PCI or SIO UARTs.
4 Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
12 Skip the optional Controller device path node and return the
13 pointer to the next device path node.
15 @param DevicePath Pointer to the device path.
16 @param ContainsControllerNode Returns TRUE if the Controller device path exists.
17 @param ControllerNumber Returns the Controller Number if Controller device path exists.
19 @return Pointer to the next device path node.
22 SkipControllerDevicePathNode (
23 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
24 BOOLEAN
*ContainsControllerNode
,
25 UINT32
*ControllerNumber
28 if ((DevicePathType (DevicePath
) == HARDWARE_DEVICE_PATH
) &&
29 (DevicePathSubType (DevicePath
) == HW_CONTROLLER_DP
)
31 if (ContainsControllerNode
!= NULL
) {
32 *ContainsControllerNode
= TRUE
;
34 if (ControllerNumber
!= NULL
) {
35 *ControllerNumber
= ((CONTROLLER_DEVICE_PATH
*) DevicePath
)->ControllerNumber
;
37 DevicePath
= NextDevicePathNode (DevicePath
);
39 if (ContainsControllerNode
!= NULL
) {
40 *ContainsControllerNode
= FALSE
;
43 return (UART_DEVICE_PATH
*) DevicePath
;
47 Checks whether the UART parameters are valid and computes the Divisor.
49 @param ClockRate The clock rate of the serial device used to verify
50 the BaudRate. Do not verify the BaudRate if it's 0.
51 @param BaudRate The requested baudrate of the serial device.
52 @param DataBits Number of databits used in serial device.
53 @param Parity The type of parity used in serial device.
54 @param StopBits Number of stopbits used in serial device.
55 @param Divisor Return the divisor if ClockRate is not 0.
56 @param ActualBaudRate Return the actual supported baudrate without
57 exceeding BaudRate. NULL means baudrate degradation
59 If the requested BaudRate is not supported, the routine
60 returns TRUE and the Actual Baud Rate when ActualBaudRate
61 is not NULL, returns FALSE when ActualBaudRate is NULL.
63 @retval TRUE The UART parameters are valid.
64 @retval FALSE The UART parameters are not valid.
67 VerifyUartParameters (
71 IN EFI_PARITY_TYPE Parity
,
72 IN EFI_STOP_BITS_TYPE StopBits
,
74 OUT UINT64
*ActualBaudRate
78 UINT32 ComputedBaudRate
;
79 UINT64 ComputedDivisor
;
82 if ((DataBits
< 5) || (DataBits
> 8) ||
83 (Parity
< NoParity
) || (Parity
> SpaceParity
) ||
84 (StopBits
< OneStopBit
) || (StopBits
> TwoStopBits
) ||
85 ((DataBits
== 5) && (StopBits
== TwoStopBits
)) ||
86 ((DataBits
>= 6) && (DataBits
<= 8) && (StopBits
== OneFiveStopBits
))
92 // Do not verify the baud rate if clock rate is unknown (0).
99 // Compute divisor use to program the baud rate using a round determination
100 // Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate)
101 // = ClockRate / (BaudRate << 4)
103 ComputedDivisor
= DivU64x64Remainder (ClockRate
, LShiftU64 (BaudRate
, 4), &Remainder
);
105 // Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate)
106 // BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3)
108 if (Remainder
>= LShiftU64 (BaudRate
, 3)) {
112 // If the computed divisor is larger than the maximum value that can be programmed
113 // into the UART, then the requested baud rate can not be supported.
115 if (ComputedDivisor
> MAX_UINT16
) {
120 // If the computed divisor is 0, then use a computed divisor of 1, which will select
121 // the maximum supported baud rate.
123 if (ComputedDivisor
== 0) {
128 // Actual baud rate that the serial port will be programmed for
129 // should be with in 4% of requested one.
131 ComputedBaudRate
= ClockRate
/ ((UINT16
) ComputedDivisor
<< 4);
132 if (ComputedBaudRate
== 0) {
136 Percent
= DivU64x32 (MultU64x32 (BaudRate
, 100), ComputedBaudRate
);
137 DEBUG ((EFI_D_INFO
, "ClockRate = %d\n", ClockRate
));
138 DEBUG ((EFI_D_INFO
, "Divisor = %ld\n", ComputedDivisor
));
139 DEBUG ((EFI_D_INFO
, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate
, ComputedBaudRate
, Percent
));
142 // If the requested BaudRate is not supported:
143 // Returns TRUE and the Actual Baud Rate when ActualBaudRate is not NULL;
144 // Returns FALSE when ActualBaudRate is NULL.
146 if ((Percent
>= 96) && (Percent
<= 104)) {
147 if (ActualBaudRate
!= NULL
) {
148 *ActualBaudRate
= BaudRate
;
150 if (Divisor
!= NULL
) {
151 *Divisor
= ComputedDivisor
;
155 if (ComputedBaudRate
< BaudRate
) {
156 if (ActualBaudRate
!= NULL
) {
157 *ActualBaudRate
= ComputedBaudRate
;
159 if (Divisor
!= NULL
) {
160 *Divisor
= ComputedDivisor
;
166 // ActualBaudRate is higher than requested baud rate and more than 4%
167 // higher than the requested value. Increment Divisor if it is less
168 // than MAX_UINT16 and computed baud rate with new divisor.
170 if (ComputedDivisor
== MAX_UINT16
) {
174 ComputedBaudRate
= ClockRate
/ ((UINT16
) ComputedDivisor
<< 4);
175 if (ComputedBaudRate
== 0) {
179 DEBUG ((EFI_D_INFO
, "ClockRate = %d\n", ClockRate
));
180 DEBUG ((EFI_D_INFO
, "Divisor = %ld\n", ComputedDivisor
));
181 DEBUG ((EFI_D_INFO
, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate
, ComputedBaudRate
, Percent
));
183 if (ActualBaudRate
!= NULL
) {
184 *ActualBaudRate
= ComputedBaudRate
;
186 if (Divisor
!= NULL
) {
187 *Divisor
= ComputedDivisor
;
193 Detect whether specific FIFO is full or not.
195 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
197 @return whether specific FIFO is full or not
201 IN SERIAL_DEV_FIFO
*Fifo
204 return (BOOLEAN
) (((Fifo
->Tail
+ 1) % SERIAL_MAX_FIFO_SIZE
) == Fifo
->Head
);
208 Detect whether specific FIFO is empty or not.
210 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
212 @return whether specific FIFO is empty or not
216 IN SERIAL_DEV_FIFO
*Fifo
220 return (BOOLEAN
) (Fifo
->Head
== Fifo
->Tail
);
224 Add data to specific FIFO.
226 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
227 @param Data the data added to FIFO
229 @retval EFI_SUCCESS Add data to specific FIFO successfully
230 @retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full
234 IN OUT SERIAL_DEV_FIFO
*Fifo
,
239 // if FIFO full can not add data
241 if (SerialFifoFull (Fifo
)) {
242 return EFI_OUT_OF_RESOURCES
;
245 // FIFO is not full can add data
247 Fifo
->Data
[Fifo
->Tail
] = Data
;
248 Fifo
->Tail
= (Fifo
->Tail
+ 1) % SERIAL_MAX_FIFO_SIZE
;
253 Remove data from specific FIFO.
255 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
256 @param Data the data removed from FIFO
258 @retval EFI_SUCCESS Remove data from specific FIFO successfully
259 @retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty
264 IN OUT SERIAL_DEV_FIFO
*Fifo
,
269 // if FIFO is empty, no data can remove
271 if (SerialFifoEmpty (Fifo
)) {
272 return EFI_OUT_OF_RESOURCES
;
275 // FIFO is not empty, can remove data
277 *Data
= Fifo
->Data
[Fifo
->Head
];
278 Fifo
->Head
= (Fifo
->Head
+ 1) % SERIAL_MAX_FIFO_SIZE
;
283 Reads and writes all available data.
285 @param SerialDevice The device to transmit.
287 @retval EFI_SUCCESS Data was read/written successfully.
288 @retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when
289 this happens, pending writes are not done.
293 SerialReceiveTransmit (
294 IN SERIAL_DEV
*SerialDevice
300 BOOLEAN ReceiveFifoFull
;
308 // Begin the read or write
310 if (SerialDevice
->SoftwareLoopbackEnable
) {
312 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
313 if (!SerialFifoEmpty (&SerialDevice
->Transmit
)) {
314 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
315 if (ReceiveFifoFull
) {
316 return EFI_OUT_OF_RESOURCES
;
319 SerialFifoAdd (&SerialDevice
->Receive
, Data
);
321 } while (!SerialFifoEmpty (&SerialDevice
->Transmit
));
323 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
325 // For full handshake flow control, tell the peer to send data
326 // if receive buffer is available.
328 if (SerialDevice
->HardwareFlowControl
&&
329 !FeaturePcdGet(PcdSerialUseHalfHandshake
)&&
332 Mcr
.Data
= READ_MCR (SerialDevice
);
334 WRITE_MCR (SerialDevice
, Mcr
.Data
);
337 Lsr
.Data
= READ_LSR (SerialDevice
);
340 // Flush incomming data to prevent a an overrun during a long write
342 if ((Lsr
.Bits
.Dr
== 1) && !ReceiveFifoFull
) {
343 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
344 if (!ReceiveFifoFull
) {
345 if (Lsr
.Bits
.FIFOe
== 1 || Lsr
.Bits
.Oe
== 1 || Lsr
.Bits
.Pe
== 1 || Lsr
.Bits
.Fe
== 1 || Lsr
.Bits
.Bi
== 1) {
346 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
348 EFI_P_EC_INPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
349 SerialDevice
->DevicePath
351 if (Lsr
.Bits
.FIFOe
== 1 || Lsr
.Bits
.Pe
== 1|| Lsr
.Bits
.Fe
== 1 || Lsr
.Bits
.Bi
== 1) {
352 Data
= READ_RBR (SerialDevice
);
357 Data
= READ_RBR (SerialDevice
);
359 SerialFifoAdd (&SerialDevice
->Receive
, Data
);
362 // For full handshake flow control, if receive buffer full
363 // tell the peer to stop sending data.
365 if (SerialDevice
->HardwareFlowControl
&&
366 !FeaturePcdGet(PcdSerialUseHalfHandshake
) &&
367 SerialFifoFull (&SerialDevice
->Receive
)
369 Mcr
.Data
= READ_MCR (SerialDevice
);
371 WRITE_MCR (SerialDevice
, Mcr
.Data
);
377 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
379 EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER
| EFI_PERIPHERAL_SERIAL_PORT
,
380 SerialDevice
->DevicePath
387 if (Lsr
.Bits
.Thre
== 1 && !SerialFifoEmpty (&SerialDevice
->Transmit
)) {
389 // Make sure the transmit data will not be missed
391 if (SerialDevice
->HardwareFlowControl
) {
393 // For half handshake flow control assert RTS before sending.
395 if (FeaturePcdGet(PcdSerialUseHalfHandshake
)) {
396 Mcr
.Data
= READ_MCR (SerialDevice
);
398 WRITE_MCR (SerialDevice
, Mcr
.Data
);
404 Msr
.Data
= READ_MSR (SerialDevice
);
405 while ((Msr
.Bits
.Dcd
== 1) && ((Msr
.Bits
.Cts
== 0) ^ FeaturePcdGet(PcdSerialUseHalfHandshake
))) {
406 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
412 Msr
.Data
= READ_MSR (SerialDevice
);
415 if ((Msr
.Bits
.Dcd
== 0) || ((Msr
.Bits
.Cts
== 1) ^ FeaturePcdGet(PcdSerialUseHalfHandshake
))) {
416 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
417 WRITE_THR (SerialDevice
, Data
);
421 // For half handshake flow control, tell DCE we are done.
423 if (FeaturePcdGet(PcdSerialUseHalfHandshake
)) {
424 Mcr
.Data
= READ_MCR (SerialDevice
);
426 WRITE_MCR (SerialDevice
, Mcr
.Data
);
429 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
430 WRITE_THR (SerialDevice
, Data
);
433 } while (Lsr
.Bits
.Thre
== 1 && !SerialFifoEmpty (&SerialDevice
->Transmit
));
440 Flush the serial hardware transmit FIFO, holding register, and shift register.
442 @param SerialDevice The device to flush.
444 @retval EFI_SUCCESS The transmit FIFO is completely flushed.
445 @retval EFI_TIMEOUT A timeout occured waiting for the transmit FIFO to flush.
448 SerialFlushTransmitFifo (
449 SERIAL_DEV
*SerialDevice
457 // If this is the first time the UART is being configured, then the current
458 // UART settings are not known, so compute a timeout to wait for the Tx FIFO
459 // assuming the worst case current settings.
461 // Timeout = (Max Bits per Char) * (Max Pending Chars) / (Slowest Baud Rate)
462 // Max Bits per Char = Start bit + 8 data bits + parity + 2 stop bits = 12
463 // Max Pending Chars = Largest Tx FIFO + hold + shift = 64 + 1 + 1 = 66
464 // Slowest Reasonable Baud Rate = 300 baud
465 // Timeout = 12 * 66 / 300 = 2.64 seconds = 2,640,000 uS
470 // Use the largest of the computed timeout, the default timeout, and the
471 // currently set timeout.
473 Timeout
= MAX (Timeout
, SERIAL_PORT_DEFAULT_TIMEOUT
);
474 Timeout
= MAX (Timeout
, SerialDevice
->SerialMode
.Timeout
);
477 // Wait for the shortest time possible for the serial port to be ready making
478 // sure the transmit FIFO, holding register, and shift register are all
479 // empty. The actual wait time is expected to be very small because the
480 // number characters currently in the FIFO should be small when a
481 // configuration change is requested.
483 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
484 // in the rest of this function that may send additional characters to this
485 // UART device invalidating the flush operation.
488 Lsr
.Data
= READ_LSR (SerialDevice
);
489 while (Lsr
.Bits
.Temt
== 0 || Lsr
.Bits
.Thre
== 0) {
490 if (Elapsed
>= Timeout
) {
493 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
494 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
495 Lsr
.Data
= READ_LSR (SerialDevice
);
502 // Interface Functions
507 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
509 @retval EFI_SUCCESS Reset successfully
510 @retval EFI_DEVICE_ERROR Failed to reset
516 IN EFI_SERIAL_IO_PROTOCOL
*This
520 SERIAL_DEV
*SerialDevice
;
528 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
531 // Report the status code reset the serial
533 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
535 EFI_P_PC_RESET
| EFI_PERIPHERAL_SERIAL_PORT
,
536 SerialDevice
->DevicePath
539 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
542 // Wait for all data to be transmitted before changing the UART configuration.
544 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
545 // that may send additional characters to this UART device until the UART
546 // configuration change is complete.
548 SerialFlushTransmitFifo (SerialDevice
);
551 // Make sure DLAB is 0.
553 Lcr
.Data
= READ_LCR (SerialDevice
);
555 WRITE_LCR (SerialDevice
, Lcr
.Data
);
558 // Turn off all interrupts
560 Ier
.Data
= READ_IER (SerialDevice
);
565 WRITE_IER (SerialDevice
, Ier
.Data
);
571 Fcr
.Bits
.TrFIFOE
= 0;
572 WRITE_FCR (SerialDevice
, Fcr
.Data
);
575 // Turn off loopback and disable device interrupt.
577 Mcr
.Data
= READ_MCR (SerialDevice
);
581 WRITE_MCR (SerialDevice
, Mcr
.Data
);
584 // Clear the scratch pad register
586 WRITE_SCR (SerialDevice
, 0);
591 Fcr
.Bits
.TrFIFOE
= 1;
592 if (SerialDevice
->ReceiveFifoDepth
> 16) {
593 Fcr
.Bits
.TrFIFO64
= 1;
595 Fcr
.Bits
.ResetRF
= 1;
596 Fcr
.Bits
.ResetTF
= 1;
597 WRITE_FCR (SerialDevice
, Fcr
.Data
);
600 // Go set the current attributes
602 Status
= This
->SetAttributes (
604 This
->Mode
->BaudRate
,
605 This
->Mode
->ReceiveFifoDepth
,
607 (EFI_PARITY_TYPE
) This
->Mode
->Parity
,
608 (UINT8
) This
->Mode
->DataBits
,
609 (EFI_STOP_BITS_TYPE
) This
->Mode
->StopBits
612 if (EFI_ERROR (Status
)) {
613 gBS
->RestoreTPL (Tpl
);
614 return EFI_DEVICE_ERROR
;
617 // Go set the current control bits
620 if (SerialDevice
->HardwareFlowControl
) {
621 Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
623 if (SerialDevice
->SoftwareLoopbackEnable
) {
624 Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
626 Status
= This
->SetControl (
631 if (EFI_ERROR (Status
)) {
632 gBS
->RestoreTPL (Tpl
);
633 return EFI_DEVICE_ERROR
;
637 // Reset the software FIFO
639 SerialDevice
->Receive
.Head
= SerialDevice
->Receive
.Tail
= 0;
640 SerialDevice
->Transmit
.Head
= SerialDevice
->Transmit
.Tail
= 0;
641 gBS
->RestoreTPL (Tpl
);
644 // Device reset is complete
650 Set new attributes to a serial device.
652 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
653 @param BaudRate The baudrate of the serial device
654 @param ReceiveFifoDepth The depth of receive FIFO buffer
655 @param Timeout The request timeout for a single char
656 @param Parity The type of parity used in serial device
657 @param DataBits Number of databits used in serial device
658 @param StopBits Number of stopbits used in serial device
660 @retval EFI_SUCCESS The new attributes were set
661 @retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value
662 @retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6
663 @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return)
668 SerialSetAttributes (
669 IN EFI_SERIAL_IO_PROTOCOL
*This
,
671 IN UINT32 ReceiveFifoDepth
,
673 IN EFI_PARITY_TYPE Parity
,
675 IN EFI_STOP_BITS_TYPE StopBits
679 SERIAL_DEV
*SerialDevice
;
682 UART_DEVICE_PATH
*Uart
;
685 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
688 // Check for default settings and fill in actual values.
691 BaudRate
= PcdGet64 (PcdUartDefaultBaudRate
);
694 if (ReceiveFifoDepth
== 0) {
695 ReceiveFifoDepth
= SerialDevice
->ReceiveFifoDepth
;
699 Timeout
= SERIAL_PORT_DEFAULT_TIMEOUT
;
702 if (Parity
== DefaultParity
) {
703 Parity
= (EFI_PARITY_TYPE
) PcdGet8 (PcdUartDefaultParity
);
707 DataBits
= PcdGet8 (PcdUartDefaultDataBits
);
710 if (StopBits
== DefaultStopBits
) {
711 StopBits
= (EFI_STOP_BITS_TYPE
) PcdGet8 (PcdUartDefaultStopBits
);
714 if (!VerifyUartParameters (SerialDevice
->ClockRate
, BaudRate
, DataBits
, Parity
, StopBits
, &Divisor
, &BaudRate
)) {
715 return EFI_INVALID_PARAMETER
;
718 if ((ReceiveFifoDepth
== 0) || (ReceiveFifoDepth
> SerialDevice
->ReceiveFifoDepth
)) {
719 return EFI_INVALID_PARAMETER
;
722 if ((Timeout
< SERIAL_PORT_MIN_TIMEOUT
) || (Timeout
> SERIAL_PORT_MAX_TIMEOUT
)) {
723 return EFI_INVALID_PARAMETER
;
726 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
729 // Wait for all data to be transmitted before changing the UART configuration.
731 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
732 // that may send additional characters to this UART device until the UART
733 // configuration change is complete.
735 SerialFlushTransmitFifo (SerialDevice
);
738 // Put serial port on Divisor Latch Mode
740 Lcr
.Data
= READ_LCR (SerialDevice
);
742 WRITE_LCR (SerialDevice
, Lcr
.Data
);
745 // Write the divisor to the serial port
747 WRITE_DLL (SerialDevice
, (UINT8
) Divisor
);
748 WRITE_DLM (SerialDevice
, (UINT8
) ((UINT16
) Divisor
>> 8));
751 // Put serial port back in normal mode and set remaining attributes.
758 Lcr
.Bits
.EvenPar
= 0;
759 Lcr
.Bits
.SticPar
= 0;
764 Lcr
.Bits
.EvenPar
= 1;
765 Lcr
.Bits
.SticPar
= 0;
770 Lcr
.Bits
.EvenPar
= 0;
771 Lcr
.Bits
.SticPar
= 0;
776 Lcr
.Bits
.EvenPar
= 1;
777 Lcr
.Bits
.SticPar
= 1;
782 Lcr
.Bits
.EvenPar
= 0;
783 Lcr
.Bits
.SticPar
= 1;
795 case OneFiveStopBits
:
806 Lcr
.Bits
.SerialDB
= (UINT8
) ((DataBits
- 5) & 0x03);
807 WRITE_LCR (SerialDevice
, Lcr
.Data
);
810 // Set the Serial I/O mode
812 This
->Mode
->BaudRate
= BaudRate
;
813 This
->Mode
->ReceiveFifoDepth
= ReceiveFifoDepth
;
814 This
->Mode
->Timeout
= Timeout
;
815 This
->Mode
->Parity
= Parity
;
816 This
->Mode
->DataBits
= DataBits
;
817 This
->Mode
->StopBits
= StopBits
;
820 // See if Device Path Node has actually changed
822 if (SerialDevice
->UartDevicePath
.BaudRate
== BaudRate
&&
823 SerialDevice
->UartDevicePath
.DataBits
== DataBits
&&
824 SerialDevice
->UartDevicePath
.Parity
== Parity
&&
825 SerialDevice
->UartDevicePath
.StopBits
== StopBits
827 gBS
->RestoreTPL (Tpl
);
831 // Update the device path
833 SerialDevice
->UartDevicePath
.BaudRate
= BaudRate
;
834 SerialDevice
->UartDevicePath
.DataBits
= DataBits
;
835 SerialDevice
->UartDevicePath
.Parity
= (UINT8
) Parity
;
836 SerialDevice
->UartDevicePath
.StopBits
= (UINT8
) StopBits
;
838 Status
= EFI_SUCCESS
;
839 if (SerialDevice
->Handle
!= NULL
) {
842 // Skip the optional Controller device path node
844 Uart
= SkipControllerDevicePathNode (
845 (EFI_DEVICE_PATH_PROTOCOL
*) (
846 (UINT8
*) SerialDevice
->DevicePath
+ GetDevicePathSize (SerialDevice
->ParentDevicePath
) - END_DEVICE_PATH_LENGTH
851 CopyMem (Uart
, &SerialDevice
->UartDevicePath
, sizeof (UART_DEVICE_PATH
));
852 Status
= gBS
->ReinstallProtocolInterface (
853 SerialDevice
->Handle
,
854 &gEfiDevicePathProtocolGuid
,
855 SerialDevice
->DevicePath
,
856 SerialDevice
->DevicePath
860 gBS
->RestoreTPL (Tpl
);
868 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
869 @param Control Control bits that can be settable
871 @retval EFI_SUCCESS New Control bits were set successfully
872 @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported
878 IN EFI_SERIAL_IO_PROTOCOL
*This
,
882 SERIAL_DEV
*SerialDevice
;
885 UART_FLOW_CONTROL_DEVICE_PATH
*FlowControl
;
889 // The control bits that can be set are :
890 // EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO
891 // EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO
892 // EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW
893 // EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW
894 // EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW
896 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
899 // first determine the parameter is invalid
901 if ((Control
& (~(EFI_SERIAL_REQUEST_TO_SEND
| EFI_SERIAL_DATA_TERMINAL_READY
|
902 EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
| EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
|
903 EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
))) != 0) {
904 return EFI_UNSUPPORTED
;
907 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
910 // Wait for all data to be transmitted before changing the UART configuration.
912 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
913 // that may send additional characters to this UART device until the UART
914 // configuration change is complete.
916 SerialFlushTransmitFifo (SerialDevice
);
918 Mcr
.Data
= READ_MCR (SerialDevice
);
922 SerialDevice
->SoftwareLoopbackEnable
= FALSE
;
923 SerialDevice
->HardwareFlowControl
= FALSE
;
925 if ((Control
& EFI_SERIAL_DATA_TERMINAL_READY
) == EFI_SERIAL_DATA_TERMINAL_READY
) {
929 if ((Control
& EFI_SERIAL_REQUEST_TO_SEND
) == EFI_SERIAL_REQUEST_TO_SEND
) {
933 if ((Control
& EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) {
937 if ((Control
& EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) {
938 SerialDevice
->HardwareFlowControl
= TRUE
;
941 WRITE_MCR (SerialDevice
, Mcr
.Data
);
943 if ((Control
& EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) {
944 SerialDevice
->SoftwareLoopbackEnable
= TRUE
;
947 Status
= EFI_SUCCESS
;
948 if (SerialDevice
->Handle
!= NULL
) {
949 FlowControl
= (UART_FLOW_CONTROL_DEVICE_PATH
*) (
950 (UINTN
) SerialDevice
->DevicePath
951 + GetDevicePathSize (SerialDevice
->ParentDevicePath
)
952 - END_DEVICE_PATH_LENGTH
953 + sizeof (UART_DEVICE_PATH
)
955 if (IsUartFlowControlDevicePathNode (FlowControl
) &&
956 ((BOOLEAN
) (ReadUnaligned32 (&FlowControl
->FlowControlMap
) == UART_FLOW_CONTROL_HARDWARE
) != SerialDevice
->HardwareFlowControl
)) {
958 // Flow Control setting is changed, need to reinstall device path protocol
960 WriteUnaligned32 (&FlowControl
->FlowControlMap
, SerialDevice
->HardwareFlowControl
? UART_FLOW_CONTROL_HARDWARE
: 0);
961 Status
= gBS
->ReinstallProtocolInterface (
962 SerialDevice
->Handle
,
963 &gEfiDevicePathProtocolGuid
,
964 SerialDevice
->DevicePath
,
965 SerialDevice
->DevicePath
970 gBS
->RestoreTPL (Tpl
);
978 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
979 @param Control Control signals of the serial device
981 @retval EFI_SUCCESS Get Control signals successfully
987 IN EFI_SERIAL_IO_PROTOCOL
*This
,
991 SERIAL_DEV
*SerialDevice
;
996 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
998 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1003 // Read the Modem Status Register
1005 Msr
.Data
= READ_MSR (SerialDevice
);
1007 if (Msr
.Bits
.Cts
== 1) {
1008 *Control
|= EFI_SERIAL_CLEAR_TO_SEND
;
1011 if (Msr
.Bits
.Dsr
== 1) {
1012 *Control
|= EFI_SERIAL_DATA_SET_READY
;
1015 if (Msr
.Bits
.Ri
== 1) {
1016 *Control
|= EFI_SERIAL_RING_INDICATE
;
1019 if (Msr
.Bits
.Dcd
== 1) {
1020 *Control
|= EFI_SERIAL_CARRIER_DETECT
;
1023 // Read the Modem Control Register
1025 Mcr
.Data
= READ_MCR (SerialDevice
);
1027 if (Mcr
.Bits
.DtrC
== 1) {
1028 *Control
|= EFI_SERIAL_DATA_TERMINAL_READY
;
1031 if (Mcr
.Bits
.Rts
== 1) {
1032 *Control
|= EFI_SERIAL_REQUEST_TO_SEND
;
1035 if (Mcr
.Bits
.Lme
== 1) {
1036 *Control
|= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
;
1039 if (SerialDevice
->HardwareFlowControl
) {
1040 *Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
1043 // Update FIFO status
1045 SerialReceiveTransmit (SerialDevice
);
1048 // See if the Transmit FIFO is empty
1050 if (SerialFifoEmpty (&SerialDevice
->Transmit
)) {
1051 *Control
|= EFI_SERIAL_OUTPUT_BUFFER_EMPTY
;
1055 // See if the Receive FIFO is empty.
1057 if (SerialFifoEmpty (&SerialDevice
->Receive
)) {
1058 *Control
|= EFI_SERIAL_INPUT_BUFFER_EMPTY
;
1061 if (SerialDevice
->SoftwareLoopbackEnable
) {
1062 *Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
1065 gBS
->RestoreTPL (Tpl
);
1071 Write the specified number of bytes to serial device.
1073 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1074 @param BufferSize On input the size of Buffer, on output the amount of
1075 data actually written
1076 @param Buffer The buffer of data to write
1078 @retval EFI_SUCCESS The data were written successfully
1079 @retval EFI_DEVICE_ERROR The device reported an error
1080 @retval EFI_TIMEOUT The write operation was stopped due to timeout
1086 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1087 IN OUT UINTN
*BufferSize
,
1091 SERIAL_DEV
*SerialDevice
;
1098 UINTN BitsPerCharacter
;
1100 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1104 if (*BufferSize
== 0) {
1108 if (Buffer
== NULL
) {
1109 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1111 EFI_P_EC_OUTPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1112 SerialDevice
->DevicePath
1115 return EFI_DEVICE_ERROR
;
1118 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1120 CharBuffer
= (UINT8
*) Buffer
;
1123 // Compute the number of bits in a single character. This is a start bit,
1124 // followed by the number of data bits, followed by the number of stop bits.
1125 // The number of stop bits is specified by an enumeration that includes
1126 // support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits.
1130 This
->Mode
->DataBits
+
1131 ((This
->Mode
->StopBits
== TwoStopBits
) ? 2 : This
->Mode
->StopBits
);
1134 // Compute the timeout in microseconds to wait for a single byte to be
1135 // transmitted. The Mode structure contans a Timeout field that is the
1136 // maximum time to transmit or receive a character. However, many UARTs
1137 // have a FIFO for transmits, so the time required to add one new character
1138 // to the transmit FIFO may be the time required to flush a full FIFO. If
1139 // the Timeout in the Mode structure is smaller than the time required to
1140 // flush a full FIFO at the current baud rate, then use a timeout value that
1141 // is required to flush a full transmit FIFO.
1144 This
->Mode
->Timeout
,
1145 (UINTN
)DivU64x64Remainder (
1146 BitsPerCharacter
* (SerialDevice
->TransmitFifoDepth
+ 1) * 1000000,
1147 This
->Mode
->BaudRate
,
1152 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1153 SerialFifoAdd (&SerialDevice
->Transmit
, CharBuffer
[Index
]);
1155 while (SerialReceiveTransmit (SerialDevice
) != EFI_SUCCESS
|| !SerialFifoEmpty (&SerialDevice
->Transmit
)) {
1157 // Unsuccessful write so check if timeout has expired, if not,
1158 // stall for a bit, increment time elapsed, and try again
1160 if (Elapsed
>= Timeout
) {
1161 *BufferSize
= ActualWrite
;
1162 gBS
->RestoreTPL (Tpl
);
1166 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1168 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1173 // Successful write so reset timeout
1178 gBS
->RestoreTPL (Tpl
);
1184 Read the specified number of bytes from serial device.
1186 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1187 @param BufferSize On input the size of Buffer, on output the amount of
1188 data returned in buffer
1189 @param Buffer The buffer to return the data into
1191 @retval EFI_SUCCESS The data were read successfully
1192 @retval EFI_DEVICE_ERROR The device reported an error
1193 @retval EFI_TIMEOUT The read operation was stopped due to timeout
1199 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1200 IN OUT UINTN
*BufferSize
,
1204 SERIAL_DEV
*SerialDevice
;
1211 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1214 if (*BufferSize
== 0) {
1218 if (Buffer
== NULL
) {
1219 return EFI_DEVICE_ERROR
;
1222 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1224 Status
= SerialReceiveTransmit (SerialDevice
);
1226 if (EFI_ERROR (Status
)) {
1229 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1231 EFI_P_EC_INPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1232 SerialDevice
->DevicePath
1235 gBS
->RestoreTPL (Tpl
);
1237 return EFI_DEVICE_ERROR
;
1240 CharBuffer
= (UINT8
*) Buffer
;
1241 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1242 while (SerialFifoRemove (&SerialDevice
->Receive
, &(CharBuffer
[Index
])) != EFI_SUCCESS
) {
1244 // Unsuccessful read so check if timeout has expired, if not,
1245 // stall for a bit, increment time elapsed, and try again
1246 // Need this time out to get conspliter to work.
1248 if (Elapsed
>= This
->Mode
->Timeout
) {
1249 *BufferSize
= Index
;
1250 gBS
->RestoreTPL (Tpl
);
1254 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1255 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1257 Status
= SerialReceiveTransmit (SerialDevice
);
1258 if (Status
== EFI_DEVICE_ERROR
) {
1259 *BufferSize
= Index
;
1260 gBS
->RestoreTPL (Tpl
);
1261 return EFI_DEVICE_ERROR
;
1265 // Successful read so reset timeout
1270 SerialReceiveTransmit (SerialDevice
);
1272 gBS
->RestoreTPL (Tpl
);
1278 Use scratchpad register to test if this serial port is present.
1280 @param SerialDevice Pointer to serial device structure
1282 @return if this serial port is present
1286 IN SERIAL_DEV
*SerialDevice
1298 Temp
= READ_SCR (SerialDevice
);
1299 WRITE_SCR (SerialDevice
, 0xAA);
1301 if (READ_SCR (SerialDevice
) != 0xAA) {
1305 WRITE_SCR (SerialDevice
, 0x55);
1307 if (READ_SCR (SerialDevice
) != 0x55) {
1313 WRITE_SCR (SerialDevice
, Temp
);
1320 @param SerialDev Pointer to serial device
1321 @param Offset Offset in register group
1323 @return Data read from serial port
1327 SerialReadRegister (
1328 IN SERIAL_DEV
*SerialDev
,
1335 if (SerialDev
->PciDeviceInfo
== NULL
) {
1336 return IoRead8 ((UINTN
) SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
);
1338 if (SerialDev
->MmioAccess
) {
1339 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Read (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1340 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1342 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Read (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1343 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1345 ASSERT_EFI_ERROR (Status
);
1353 @param SerialDev Pointer to serial device
1354 @param Offset Offset in register group
1355 @param Data data which is to be written to some serial port register
1358 SerialWriteRegister (
1359 IN SERIAL_DEV
*SerialDev
,
1366 if (SerialDev
->PciDeviceInfo
== NULL
) {
1367 IoWrite8 ((UINTN
) SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, Data
);
1369 if (SerialDev
->MmioAccess
) {
1370 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Write (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1371 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1373 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Write (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1374 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1376 ASSERT_EFI_ERROR (Status
);