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
)
32 if (ContainsControllerNode
!= NULL
) {
33 *ContainsControllerNode
= TRUE
;
36 if (ControllerNumber
!= NULL
) {
37 *ControllerNumber
= ((CONTROLLER_DEVICE_PATH
*)DevicePath
)->ControllerNumber
;
40 DevicePath
= NextDevicePathNode (DevicePath
);
42 if (ContainsControllerNode
!= NULL
) {
43 *ContainsControllerNode
= FALSE
;
47 return (UART_DEVICE_PATH
*)DevicePath
;
51 Checks whether the UART parameters are valid and computes the Divisor.
53 @param ClockRate The clock rate of the serial device used to verify
54 the BaudRate. Do not verify the BaudRate if it's 0.
55 @param BaudRate The requested baudrate of the serial device.
56 @param DataBits Number of databits used in serial device.
57 @param Parity The type of parity used in serial device.
58 @param StopBits Number of stopbits used in serial device.
59 @param Divisor Return the divisor if ClockRate is not 0.
60 @param ActualBaudRate Return the actual supported baudrate without
61 exceeding BaudRate. NULL means baudrate degradation
63 If the requested BaudRate is not supported, the routine
64 returns TRUE and the Actual Baud Rate when ActualBaudRate
65 is not NULL, returns FALSE when ActualBaudRate is NULL.
67 @retval TRUE The UART parameters are valid.
68 @retval FALSE The UART parameters are not valid.
71 VerifyUartParameters (
75 IN EFI_PARITY_TYPE Parity
,
76 IN EFI_STOP_BITS_TYPE StopBits
,
78 OUT UINT64
*ActualBaudRate
82 UINT32 ComputedBaudRate
;
83 UINT64 ComputedDivisor
;
86 if ((DataBits
< 5) || (DataBits
> 8) ||
87 (Parity
< NoParity
) || (Parity
> SpaceParity
) ||
88 (StopBits
< OneStopBit
) || (StopBits
> TwoStopBits
) ||
89 ((DataBits
== 5) && (StopBits
== TwoStopBits
)) ||
90 ((DataBits
>= 6) && (DataBits
<= 8) && (StopBits
== OneFiveStopBits
))
97 // Do not verify the baud rate if clock rate is unknown (0).
104 // Compute divisor use to program the baud rate using a round determination
105 // Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate)
106 // = ClockRate / (BaudRate << 4)
108 ComputedDivisor
= DivU64x64Remainder (ClockRate
, LShiftU64 (BaudRate
, 4), &Remainder
);
110 // Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate)
111 // BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3)
113 if (Remainder
>= LShiftU64 (BaudRate
, 3)) {
118 // If the computed divisor is larger than the maximum value that can be programmed
119 // into the UART, then the requested baud rate can not be supported.
121 if (ComputedDivisor
> MAX_UINT16
) {
126 // If the computed divisor is 0, then use a computed divisor of 1, which will select
127 // the maximum supported baud rate.
129 if (ComputedDivisor
== 0) {
134 // Actual baud rate that the serial port will be programmed for
135 // should be with in 4% of requested one.
137 ComputedBaudRate
= ClockRate
/ ((UINT16
)ComputedDivisor
<< 4);
138 if (ComputedBaudRate
== 0) {
142 Percent
= DivU64x32 (MultU64x32 (BaudRate
, 100), ComputedBaudRate
);
143 DEBUG ((DEBUG_INFO
, "ClockRate = %d\n", ClockRate
));
144 DEBUG ((DEBUG_INFO
, "Divisor = %ld\n", ComputedDivisor
));
145 DEBUG ((DEBUG_INFO
, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate
, ComputedBaudRate
, Percent
));
148 // If the requested BaudRate is not supported:
149 // Returns TRUE and the Actual Baud Rate when ActualBaudRate is not NULL;
150 // Returns FALSE when ActualBaudRate is NULL.
152 if ((Percent
>= 96) && (Percent
<= 104)) {
153 if (ActualBaudRate
!= NULL
) {
154 *ActualBaudRate
= BaudRate
;
157 if (Divisor
!= NULL
) {
158 *Divisor
= ComputedDivisor
;
164 if (ComputedBaudRate
< BaudRate
) {
165 if (ActualBaudRate
!= NULL
) {
166 *ActualBaudRate
= ComputedBaudRate
;
169 if (Divisor
!= NULL
) {
170 *Divisor
= ComputedDivisor
;
177 // ActualBaudRate is higher than requested baud rate and more than 4%
178 // higher than the requested value. Increment Divisor if it is less
179 // than MAX_UINT16 and computed baud rate with new divisor.
181 if (ComputedDivisor
== MAX_UINT16
) {
186 ComputedBaudRate
= ClockRate
/ ((UINT16
)ComputedDivisor
<< 4);
187 if (ComputedBaudRate
== 0) {
191 DEBUG ((DEBUG_INFO
, "ClockRate = %d\n", ClockRate
));
192 DEBUG ((DEBUG_INFO
, "Divisor = %ld\n", ComputedDivisor
));
193 DEBUG ((DEBUG_INFO
, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate
, ComputedBaudRate
, Percent
));
195 if (ActualBaudRate
!= NULL
) {
196 *ActualBaudRate
= ComputedBaudRate
;
199 if (Divisor
!= NULL
) {
200 *Divisor
= ComputedDivisor
;
207 Detect whether specific FIFO is full or not.
209 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
211 @return whether specific FIFO is full or not
215 IN SERIAL_DEV_FIFO
*Fifo
218 return (BOOLEAN
)(((Fifo
->Tail
+ 1) % SERIAL_MAX_FIFO_SIZE
) == Fifo
->Head
);
222 Detect whether specific FIFO is empty or not.
224 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
226 @return whether specific FIFO is empty or not
230 IN SERIAL_DEV_FIFO
*Fifo
234 return (BOOLEAN
)(Fifo
->Head
== Fifo
->Tail
);
238 Add data to specific FIFO.
240 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
241 @param Data the data added to FIFO
243 @retval EFI_SUCCESS Add data to specific FIFO successfully
244 @retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full
248 IN OUT SERIAL_DEV_FIFO
*Fifo
,
253 // if FIFO full can not add data
255 if (SerialFifoFull (Fifo
)) {
256 return EFI_OUT_OF_RESOURCES
;
260 // FIFO is not full can add data
262 Fifo
->Data
[Fifo
->Tail
] = Data
;
263 Fifo
->Tail
= (Fifo
->Tail
+ 1) % SERIAL_MAX_FIFO_SIZE
;
268 Remove data from specific FIFO.
270 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
271 @param Data the data removed from FIFO
273 @retval EFI_SUCCESS Remove data from specific FIFO successfully
274 @retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty
279 IN OUT SERIAL_DEV_FIFO
*Fifo
,
284 // if FIFO is empty, no data can remove
286 if (SerialFifoEmpty (Fifo
)) {
287 return EFI_OUT_OF_RESOURCES
;
291 // FIFO is not empty, can remove data
293 *Data
= Fifo
->Data
[Fifo
->Head
];
294 Fifo
->Head
= (Fifo
->Head
+ 1) % SERIAL_MAX_FIFO_SIZE
;
299 Reads and writes all available data.
301 @param SerialDevice The device to transmit.
303 @retval EFI_SUCCESS Data was read/written successfully.
304 @retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when
305 this happens, pending writes are not done.
309 SerialReceiveTransmit (
310 IN SERIAL_DEV
*SerialDevice
316 BOOLEAN ReceiveFifoFull
;
324 // Begin the read or write
326 if (SerialDevice
->SoftwareLoopbackEnable
) {
328 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
329 if (!SerialFifoEmpty (&SerialDevice
->Transmit
)) {
330 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
331 if (ReceiveFifoFull
) {
332 return EFI_OUT_OF_RESOURCES
;
335 SerialFifoAdd (&SerialDevice
->Receive
, Data
);
337 } while (!SerialFifoEmpty (&SerialDevice
->Transmit
));
339 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
341 // For full handshake flow control, tell the peer to send data
342 // if receive buffer is available.
344 if (SerialDevice
->HardwareFlowControl
&&
345 !FeaturePcdGet (PcdSerialUseHalfHandshake
) &&
349 Mcr
.Data
= READ_MCR (SerialDevice
);
351 WRITE_MCR (SerialDevice
, Mcr
.Data
);
355 Lsr
.Data
= READ_LSR (SerialDevice
);
358 // Flush incomming data to prevent a an overrun during a long write
360 if ((Lsr
.Bits
.Dr
== 1) && !ReceiveFifoFull
) {
361 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
362 if (!ReceiveFifoFull
) {
363 if ((Lsr
.Bits
.FIFOe
== 1) || (Lsr
.Bits
.Oe
== 1) || (Lsr
.Bits
.Pe
== 1) || (Lsr
.Bits
.Fe
== 1) || (Lsr
.Bits
.Bi
== 1)) {
364 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
366 EFI_P_EC_INPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
367 SerialDevice
->DevicePath
369 if ((Lsr
.Bits
.FIFOe
== 1) || (Lsr
.Bits
.Pe
== 1) || (Lsr
.Bits
.Fe
== 1) || (Lsr
.Bits
.Bi
== 1)) {
370 Data
= READ_RBR (SerialDevice
);
375 Data
= READ_RBR (SerialDevice
);
377 SerialFifoAdd (&SerialDevice
->Receive
, Data
);
380 // For full handshake flow control, if receive buffer full
381 // tell the peer to stop sending data.
383 if (SerialDevice
->HardwareFlowControl
&&
384 !FeaturePcdGet (PcdSerialUseHalfHandshake
) &&
385 SerialFifoFull (&SerialDevice
->Receive
)
388 Mcr
.Data
= READ_MCR (SerialDevice
);
390 WRITE_MCR (SerialDevice
, Mcr
.Data
);
395 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
397 EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER
| EFI_PERIPHERAL_SERIAL_PORT
,
398 SerialDevice
->DevicePath
406 if ((Lsr
.Bits
.Thre
== 1) && !SerialFifoEmpty (&SerialDevice
->Transmit
)) {
408 // Make sure the transmit data will not be missed
410 if (SerialDevice
->HardwareFlowControl
) {
412 // For half handshake flow control assert RTS before sending.
414 if (FeaturePcdGet (PcdSerialUseHalfHandshake
)) {
415 Mcr
.Data
= READ_MCR (SerialDevice
);
417 WRITE_MCR (SerialDevice
, Mcr
.Data
);
424 Msr
.Data
= READ_MSR (SerialDevice
);
425 while ((Msr
.Bits
.Dcd
== 1) && ((Msr
.Bits
.Cts
== 0) ^ FeaturePcdGet (PcdSerialUseHalfHandshake
))) {
426 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
432 Msr
.Data
= READ_MSR (SerialDevice
);
435 if ((Msr
.Bits
.Dcd
== 0) || ((Msr
.Bits
.Cts
== 1) ^ FeaturePcdGet (PcdSerialUseHalfHandshake
))) {
436 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
437 WRITE_THR (SerialDevice
, Data
);
441 // For half handshake flow control, tell DCE we are done.
443 if (FeaturePcdGet (PcdSerialUseHalfHandshake
)) {
444 Mcr
.Data
= READ_MCR (SerialDevice
);
446 WRITE_MCR (SerialDevice
, Mcr
.Data
);
449 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
450 WRITE_THR (SerialDevice
, Data
);
453 } while (Lsr
.Bits
.Thre
== 1 && !SerialFifoEmpty (&SerialDevice
->Transmit
));
460 Flush the serial hardware transmit FIFO, holding register, and shift register.
462 @param SerialDevice The device to flush.
464 @retval EFI_SUCCESS The transmit FIFO is completely flushed.
465 @retval EFI_TIMEOUT A timeout occured waiting for the transmit FIFO to flush.
468 SerialFlushTransmitFifo (
469 SERIAL_DEV
*SerialDevice
477 // If this is the first time the UART is being configured, then the current
478 // UART settings are not known, so compute a timeout to wait for the Tx FIFO
479 // assuming the worst case current settings.
481 // Timeout = (Max Bits per Char) * (Max Pending Chars) / (Slowest Baud Rate)
482 // Max Bits per Char = Start bit + 8 data bits + parity + 2 stop bits = 12
483 // Max Pending Chars = Largest Tx FIFO + hold + shift = 64 + 1 + 1 = 66
484 // Slowest Reasonable Baud Rate = 300 baud
485 // Timeout = 12 * 66 / 300 = 2.64 seconds = 2,640,000 uS
490 // Use the largest of the computed timeout, the default timeout, and the
491 // currently set timeout.
493 Timeout
= MAX (Timeout
, SERIAL_PORT_DEFAULT_TIMEOUT
);
494 Timeout
= MAX (Timeout
, SerialDevice
->SerialMode
.Timeout
);
497 // Wait for the shortest time possible for the serial port to be ready making
498 // sure the transmit FIFO, holding register, and shift register are all
499 // empty. The actual wait time is expected to be very small because the
500 // number characters currently in the FIFO should be small when a
501 // configuration change is requested.
503 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
504 // in the rest of this function that may send additional characters to this
505 // UART device invalidating the flush operation.
508 Lsr
.Data
= READ_LSR (SerialDevice
);
509 while (Lsr
.Bits
.Temt
== 0 || Lsr
.Bits
.Thre
== 0) {
510 if (Elapsed
>= Timeout
) {
514 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
515 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
516 Lsr
.Data
= READ_LSR (SerialDevice
);
523 // Interface Functions
529 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
531 @retval EFI_SUCCESS Reset successfully
532 @retval EFI_DEVICE_ERROR Failed to reset
538 IN EFI_SERIAL_IO_PROTOCOL
*This
542 SERIAL_DEV
*SerialDevice
;
550 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
553 // Report the status code reset the serial
555 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
557 EFI_P_PC_RESET
| EFI_PERIPHERAL_SERIAL_PORT
,
558 SerialDevice
->DevicePath
561 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
564 // Wait for all data to be transmitted before changing the UART configuration.
566 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
567 // that may send additional characters to this UART device until the UART
568 // configuration change is complete.
570 SerialFlushTransmitFifo (SerialDevice
);
573 // Make sure DLAB is 0.
575 Lcr
.Data
= READ_LCR (SerialDevice
);
577 WRITE_LCR (SerialDevice
, Lcr
.Data
);
580 // Turn off all interrupts
582 Ier
.Data
= READ_IER (SerialDevice
);
587 WRITE_IER (SerialDevice
, Ier
.Data
);
593 Fcr
.Bits
.TrFIFOE
= 0;
594 WRITE_FCR (SerialDevice
, Fcr
.Data
);
597 // Turn off loopback and disable device interrupt.
599 Mcr
.Data
= READ_MCR (SerialDevice
);
603 WRITE_MCR (SerialDevice
, Mcr
.Data
);
606 // Clear the scratch pad register
608 WRITE_SCR (SerialDevice
, 0);
613 Fcr
.Bits
.TrFIFOE
= 1;
614 if (SerialDevice
->ReceiveFifoDepth
> 16) {
615 Fcr
.Bits
.TrFIFO64
= 1;
618 Fcr
.Bits
.ResetRF
= 1;
619 Fcr
.Bits
.ResetTF
= 1;
620 WRITE_FCR (SerialDevice
, Fcr
.Data
);
623 // Go set the current attributes
625 Status
= This
->SetAttributes (
627 This
->Mode
->BaudRate
,
628 This
->Mode
->ReceiveFifoDepth
,
630 (EFI_PARITY_TYPE
)This
->Mode
->Parity
,
631 (UINT8
)This
->Mode
->DataBits
,
632 (EFI_STOP_BITS_TYPE
)This
->Mode
->StopBits
635 if (EFI_ERROR (Status
)) {
636 gBS
->RestoreTPL (Tpl
);
637 return EFI_DEVICE_ERROR
;
641 // Go set the current control bits
644 if (SerialDevice
->HardwareFlowControl
) {
645 Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
648 if (SerialDevice
->SoftwareLoopbackEnable
) {
649 Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
652 Status
= This
->SetControl (
657 if (EFI_ERROR (Status
)) {
658 gBS
->RestoreTPL (Tpl
);
659 return EFI_DEVICE_ERROR
;
663 // Reset the software FIFO
665 SerialDevice
->Receive
.Head
= SerialDevice
->Receive
.Tail
= 0;
666 SerialDevice
->Transmit
.Head
= SerialDevice
->Transmit
.Tail
= 0;
667 gBS
->RestoreTPL (Tpl
);
670 // Device reset is complete
676 Set new attributes to a serial device.
678 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
679 @param BaudRate The baudrate of the serial device
680 @param ReceiveFifoDepth The depth of receive FIFO buffer
681 @param Timeout The request timeout for a single char
682 @param Parity The type of parity used in serial device
683 @param DataBits Number of databits used in serial device
684 @param StopBits Number of stopbits used in serial device
686 @retval EFI_SUCCESS The new attributes were set
687 @retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value
688 @retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6
689 @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return)
694 SerialSetAttributes (
695 IN EFI_SERIAL_IO_PROTOCOL
*This
,
697 IN UINT32 ReceiveFifoDepth
,
699 IN EFI_PARITY_TYPE Parity
,
701 IN EFI_STOP_BITS_TYPE StopBits
705 SERIAL_DEV
*SerialDevice
;
708 UART_DEVICE_PATH
*Uart
;
711 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
714 // Check for default settings and fill in actual values.
717 BaudRate
= PcdGet64 (PcdUartDefaultBaudRate
);
720 if (ReceiveFifoDepth
== 0) {
721 ReceiveFifoDepth
= SerialDevice
->ReceiveFifoDepth
;
725 Timeout
= SERIAL_PORT_DEFAULT_TIMEOUT
;
728 if (Parity
== DefaultParity
) {
729 Parity
= (EFI_PARITY_TYPE
)PcdGet8 (PcdUartDefaultParity
);
733 DataBits
= PcdGet8 (PcdUartDefaultDataBits
);
736 if (StopBits
== DefaultStopBits
) {
737 StopBits
= (EFI_STOP_BITS_TYPE
)PcdGet8 (PcdUartDefaultStopBits
);
740 if (!VerifyUartParameters (SerialDevice
->ClockRate
, BaudRate
, DataBits
, Parity
, StopBits
, &Divisor
, &BaudRate
)) {
741 return EFI_INVALID_PARAMETER
;
744 if ((ReceiveFifoDepth
== 0) || (ReceiveFifoDepth
> SerialDevice
->ReceiveFifoDepth
)) {
745 return EFI_INVALID_PARAMETER
;
748 if ((Timeout
< SERIAL_PORT_MIN_TIMEOUT
) || (Timeout
> SERIAL_PORT_MAX_TIMEOUT
)) {
749 return EFI_INVALID_PARAMETER
;
752 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
755 // Wait for all data to be transmitted before changing the UART configuration.
757 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
758 // that may send additional characters to this UART device until the UART
759 // configuration change is complete.
761 SerialFlushTransmitFifo (SerialDevice
);
764 // Put serial port on Divisor Latch Mode
766 Lcr
.Data
= READ_LCR (SerialDevice
);
768 WRITE_LCR (SerialDevice
, Lcr
.Data
);
771 // Write the divisor to the serial port
773 WRITE_DLL (SerialDevice
, (UINT8
)Divisor
);
774 WRITE_DLM (SerialDevice
, (UINT8
)((UINT16
)Divisor
>> 8));
777 // Put serial port back in normal mode and set remaining attributes.
784 Lcr
.Bits
.EvenPar
= 0;
785 Lcr
.Bits
.SticPar
= 0;
790 Lcr
.Bits
.EvenPar
= 1;
791 Lcr
.Bits
.SticPar
= 0;
796 Lcr
.Bits
.EvenPar
= 0;
797 Lcr
.Bits
.SticPar
= 0;
802 Lcr
.Bits
.EvenPar
= 1;
803 Lcr
.Bits
.SticPar
= 1;
808 Lcr
.Bits
.EvenPar
= 0;
809 Lcr
.Bits
.SticPar
= 1;
821 case OneFiveStopBits
:
833 Lcr
.Bits
.SerialDB
= (UINT8
)((DataBits
- 5) & 0x03);
834 WRITE_LCR (SerialDevice
, Lcr
.Data
);
837 // Set the Serial I/O mode
839 This
->Mode
->BaudRate
= BaudRate
;
840 This
->Mode
->ReceiveFifoDepth
= ReceiveFifoDepth
;
841 This
->Mode
->Timeout
= Timeout
;
842 This
->Mode
->Parity
= Parity
;
843 This
->Mode
->DataBits
= DataBits
;
844 This
->Mode
->StopBits
= StopBits
;
847 // See if Device Path Node has actually changed
849 if ((SerialDevice
->UartDevicePath
.BaudRate
== BaudRate
) &&
850 (SerialDevice
->UartDevicePath
.DataBits
== DataBits
) &&
851 (SerialDevice
->UartDevicePath
.Parity
== Parity
) &&
852 (SerialDevice
->UartDevicePath
.StopBits
== StopBits
)
855 gBS
->RestoreTPL (Tpl
);
860 // Update the device path
862 SerialDevice
->UartDevicePath
.BaudRate
= BaudRate
;
863 SerialDevice
->UartDevicePath
.DataBits
= DataBits
;
864 SerialDevice
->UartDevicePath
.Parity
= (UINT8
)Parity
;
865 SerialDevice
->UartDevicePath
.StopBits
= (UINT8
)StopBits
;
867 Status
= EFI_SUCCESS
;
868 if (SerialDevice
->Handle
!= NULL
) {
870 // Skip the optional Controller device path node
872 Uart
= SkipControllerDevicePathNode (
873 (EFI_DEVICE_PATH_PROTOCOL
*)(
874 (UINT8
*)SerialDevice
->DevicePath
+ GetDevicePathSize (SerialDevice
->ParentDevicePath
) - END_DEVICE_PATH_LENGTH
879 CopyMem (Uart
, &SerialDevice
->UartDevicePath
, sizeof (UART_DEVICE_PATH
));
880 Status
= gBS
->ReinstallProtocolInterface (
881 SerialDevice
->Handle
,
882 &gEfiDevicePathProtocolGuid
,
883 SerialDevice
->DevicePath
,
884 SerialDevice
->DevicePath
888 gBS
->RestoreTPL (Tpl
);
896 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
897 @param Control Control bits that can be settable
899 @retval EFI_SUCCESS New Control bits were set successfully
900 @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported
906 IN EFI_SERIAL_IO_PROTOCOL
*This
,
910 SERIAL_DEV
*SerialDevice
;
913 UART_FLOW_CONTROL_DEVICE_PATH
*FlowControl
;
917 // The control bits that can be set are :
918 // EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO
919 // EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO
920 // EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW
921 // EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW
922 // EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW
924 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
927 // first determine the parameter is invalid
929 if ((Control
& (~(EFI_SERIAL_REQUEST_TO_SEND
| EFI_SERIAL_DATA_TERMINAL_READY
|
930 EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
| EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
|
931 EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
))) != 0)
933 return EFI_UNSUPPORTED
;
936 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
939 // Wait for all data to be transmitted before changing the UART configuration.
941 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
942 // that may send additional characters to this UART device until the UART
943 // configuration change is complete.
945 SerialFlushTransmitFifo (SerialDevice
);
947 Mcr
.Data
= READ_MCR (SerialDevice
);
951 SerialDevice
->SoftwareLoopbackEnable
= FALSE
;
952 SerialDevice
->HardwareFlowControl
= FALSE
;
954 if ((Control
& EFI_SERIAL_DATA_TERMINAL_READY
) == EFI_SERIAL_DATA_TERMINAL_READY
) {
958 if ((Control
& EFI_SERIAL_REQUEST_TO_SEND
) == EFI_SERIAL_REQUEST_TO_SEND
) {
962 if ((Control
& EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) {
966 if ((Control
& EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) {
967 SerialDevice
->HardwareFlowControl
= TRUE
;
970 WRITE_MCR (SerialDevice
, Mcr
.Data
);
972 if ((Control
& EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) {
973 SerialDevice
->SoftwareLoopbackEnable
= TRUE
;
976 Status
= EFI_SUCCESS
;
977 if (SerialDevice
->Handle
!= NULL
) {
978 FlowControl
= (UART_FLOW_CONTROL_DEVICE_PATH
*)(
979 (UINTN
)SerialDevice
->DevicePath
980 + GetDevicePathSize (SerialDevice
->ParentDevicePath
)
981 - END_DEVICE_PATH_LENGTH
982 + sizeof (UART_DEVICE_PATH
)
984 if (IsUartFlowControlDevicePathNode (FlowControl
) &&
985 ((BOOLEAN
)(ReadUnaligned32 (&FlowControl
->FlowControlMap
) == UART_FLOW_CONTROL_HARDWARE
) != SerialDevice
->HardwareFlowControl
))
988 // Flow Control setting is changed, need to reinstall device path protocol
990 WriteUnaligned32 (&FlowControl
->FlowControlMap
, SerialDevice
->HardwareFlowControl
? UART_FLOW_CONTROL_HARDWARE
: 0);
991 Status
= gBS
->ReinstallProtocolInterface (
992 SerialDevice
->Handle
,
993 &gEfiDevicePathProtocolGuid
,
994 SerialDevice
->DevicePath
,
995 SerialDevice
->DevicePath
1000 gBS
->RestoreTPL (Tpl
);
1008 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1009 @param Control Control signals of the serial device
1011 @retval EFI_SUCCESS Get Control signals successfully
1017 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1021 SERIAL_DEV
*SerialDevice
;
1022 SERIAL_PORT_MSR Msr
;
1023 SERIAL_PORT_MCR Mcr
;
1026 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1028 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1033 // Read the Modem Status Register
1035 Msr
.Data
= READ_MSR (SerialDevice
);
1037 if (Msr
.Bits
.Cts
== 1) {
1038 *Control
|= EFI_SERIAL_CLEAR_TO_SEND
;
1041 if (Msr
.Bits
.Dsr
== 1) {
1042 *Control
|= EFI_SERIAL_DATA_SET_READY
;
1045 if (Msr
.Bits
.Ri
== 1) {
1046 *Control
|= EFI_SERIAL_RING_INDICATE
;
1049 if (Msr
.Bits
.Dcd
== 1) {
1050 *Control
|= EFI_SERIAL_CARRIER_DETECT
;
1054 // Read the Modem Control Register
1056 Mcr
.Data
= READ_MCR (SerialDevice
);
1058 if (Mcr
.Bits
.DtrC
== 1) {
1059 *Control
|= EFI_SERIAL_DATA_TERMINAL_READY
;
1062 if (Mcr
.Bits
.Rts
== 1) {
1063 *Control
|= EFI_SERIAL_REQUEST_TO_SEND
;
1066 if (Mcr
.Bits
.Lme
== 1) {
1067 *Control
|= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
;
1070 if (SerialDevice
->HardwareFlowControl
) {
1071 *Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
1075 // Update FIFO status
1077 SerialReceiveTransmit (SerialDevice
);
1080 // See if the Transmit FIFO is empty
1082 if (SerialFifoEmpty (&SerialDevice
->Transmit
)) {
1083 *Control
|= EFI_SERIAL_OUTPUT_BUFFER_EMPTY
;
1087 // See if the Receive FIFO is empty.
1089 if (SerialFifoEmpty (&SerialDevice
->Receive
)) {
1090 *Control
|= EFI_SERIAL_INPUT_BUFFER_EMPTY
;
1093 if (SerialDevice
->SoftwareLoopbackEnable
) {
1094 *Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
1097 gBS
->RestoreTPL (Tpl
);
1103 Write the specified number of bytes to serial device.
1105 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1106 @param BufferSize On input the size of Buffer, on output the amount of
1107 data actually written
1108 @param Buffer The buffer of data to write
1110 @retval EFI_SUCCESS The data were written successfully
1111 @retval EFI_DEVICE_ERROR The device reported an error
1112 @retval EFI_TIMEOUT The write operation was stopped due to timeout
1118 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1119 IN OUT UINTN
*BufferSize
,
1123 SERIAL_DEV
*SerialDevice
;
1130 UINTN BitsPerCharacter
;
1132 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1136 if (*BufferSize
== 0) {
1140 if (Buffer
== NULL
) {
1141 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1143 EFI_P_EC_OUTPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1144 SerialDevice
->DevicePath
1147 return EFI_DEVICE_ERROR
;
1150 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1152 CharBuffer
= (UINT8
*)Buffer
;
1155 // Compute the number of bits in a single character. This is a start bit,
1156 // followed by the number of data bits, followed by the number of stop bits.
1157 // The number of stop bits is specified by an enumeration that includes
1158 // support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits.
1162 This
->Mode
->DataBits
+
1163 ((This
->Mode
->StopBits
== TwoStopBits
) ? 2 : This
->Mode
->StopBits
);
1166 // Compute the timeout in microseconds to wait for a single byte to be
1167 // transmitted. The Mode structure contans a Timeout field that is the
1168 // maximum time to transmit or receive a character. However, many UARTs
1169 // have a FIFO for transmits, so the time required to add one new character
1170 // to the transmit FIFO may be the time required to flush a full FIFO. If
1171 // the Timeout in the Mode structure is smaller than the time required to
1172 // flush a full FIFO at the current baud rate, then use a timeout value that
1173 // is required to flush a full transmit FIFO.
1176 This
->Mode
->Timeout
,
1177 (UINTN
)DivU64x64Remainder (
1178 BitsPerCharacter
* (SerialDevice
->TransmitFifoDepth
+ 1) * 1000000,
1179 This
->Mode
->BaudRate
,
1184 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1185 SerialFifoAdd (&SerialDevice
->Transmit
, CharBuffer
[Index
]);
1187 while (SerialReceiveTransmit (SerialDevice
) != EFI_SUCCESS
|| !SerialFifoEmpty (&SerialDevice
->Transmit
)) {
1189 // Unsuccessful write so check if timeout has expired, if not,
1190 // stall for a bit, increment time elapsed, and try again
1192 if (Elapsed
>= Timeout
) {
1193 *BufferSize
= ActualWrite
;
1194 gBS
->RestoreTPL (Tpl
);
1198 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1200 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1205 // Successful write so reset timeout
1210 gBS
->RestoreTPL (Tpl
);
1216 Read the specified number of bytes from serial device.
1218 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1219 @param BufferSize On input the size of Buffer, on output the amount of
1220 data returned in buffer
1221 @param Buffer The buffer to return the data into
1223 @retval EFI_SUCCESS The data were read successfully
1224 @retval EFI_DEVICE_ERROR The device reported an error
1225 @retval EFI_TIMEOUT The read operation was stopped due to timeout
1231 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1232 IN OUT UINTN
*BufferSize
,
1236 SERIAL_DEV
*SerialDevice
;
1243 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1246 if (*BufferSize
== 0) {
1250 if (Buffer
== NULL
) {
1251 return EFI_DEVICE_ERROR
;
1254 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1256 Status
= SerialReceiveTransmit (SerialDevice
);
1258 if (EFI_ERROR (Status
)) {
1261 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1263 EFI_P_EC_INPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1264 SerialDevice
->DevicePath
1267 gBS
->RestoreTPL (Tpl
);
1269 return EFI_DEVICE_ERROR
;
1272 CharBuffer
= (UINT8
*)Buffer
;
1273 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1274 while (SerialFifoRemove (&SerialDevice
->Receive
, &(CharBuffer
[Index
])) != EFI_SUCCESS
) {
1276 // Unsuccessful read so check if timeout has expired, if not,
1277 // stall for a bit, increment time elapsed, and try again
1278 // Need this time out to get conspliter to work.
1280 if (Elapsed
>= This
->Mode
->Timeout
) {
1281 *BufferSize
= Index
;
1282 gBS
->RestoreTPL (Tpl
);
1286 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1287 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1289 Status
= SerialReceiveTransmit (SerialDevice
);
1290 if (Status
== EFI_DEVICE_ERROR
) {
1291 *BufferSize
= Index
;
1292 gBS
->RestoreTPL (Tpl
);
1293 return EFI_DEVICE_ERROR
;
1298 // Successful read so reset timeout
1303 SerialReceiveTransmit (SerialDevice
);
1305 gBS
->RestoreTPL (Tpl
);
1311 Use scratchpad register to test if this serial port is present.
1313 @param SerialDevice Pointer to serial device structure
1315 @return if this serial port is present
1319 IN SERIAL_DEV
*SerialDevice
1331 Temp
= READ_SCR (SerialDevice
);
1332 WRITE_SCR (SerialDevice
, 0xAA);
1334 if (READ_SCR (SerialDevice
) != 0xAA) {
1338 WRITE_SCR (SerialDevice
, 0x55);
1340 if (READ_SCR (SerialDevice
) != 0x55) {
1347 WRITE_SCR (SerialDevice
, Temp
);
1354 @param SerialDev Pointer to serial device
1355 @param Offset Offset in register group
1357 @return Data read from serial port
1361 SerialReadRegister (
1362 IN SERIAL_DEV
*SerialDev
,
1369 if (SerialDev
->PciDeviceInfo
== NULL
) {
1370 return IoRead8 ((UINTN
)SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
);
1372 if (SerialDev
->MmioAccess
) {
1373 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Read (
1374 SerialDev
->PciDeviceInfo
->PciIo
,
1376 EFI_PCI_IO_PASS_THROUGH_BAR
,
1377 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
,
1382 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Read (
1383 SerialDev
->PciDeviceInfo
->PciIo
,
1385 EFI_PCI_IO_PASS_THROUGH_BAR
,
1386 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
,
1392 ASSERT_EFI_ERROR (Status
);
1400 @param SerialDev Pointer to serial device
1401 @param Offset Offset in register group
1402 @param Data data which is to be written to some serial port register
1405 SerialWriteRegister (
1406 IN SERIAL_DEV
*SerialDev
,
1413 if (SerialDev
->PciDeviceInfo
== NULL
) {
1414 IoWrite8 ((UINTN
)SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, Data
);
1416 if (SerialDev
->MmioAccess
) {
1417 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Write (
1418 SerialDev
->PciDeviceInfo
->PciIo
,
1420 EFI_PCI_IO_PASS_THROUGH_BAR
,
1421 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
,
1426 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Write (
1427 SerialDev
->PciDeviceInfo
->PciIo
,
1429 EFI_PCI_IO_PASS_THROUGH_BAR
,
1430 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
,
1436 ASSERT_EFI_ERROR (Status
);