2 SerialIo implementation for PCI or SIO UARTs.
4 Copyright (c) 2006 - 2018, 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 // Interface Functions
445 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
447 @retval EFI_SUCCESS Reset successfully
448 @retval EFI_DEVICE_ERROR Failed to reset
454 IN EFI_SERIAL_IO_PROTOCOL
*This
458 SERIAL_DEV
*SerialDevice
;
466 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
469 // Report the status code reset the serial
471 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
473 EFI_P_PC_RESET
| EFI_PERIPHERAL_SERIAL_PORT
,
474 SerialDevice
->DevicePath
477 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
480 // Make sure DLAB is 0.
482 Lcr
.Data
= READ_LCR (SerialDevice
);
484 WRITE_LCR (SerialDevice
, Lcr
.Data
);
487 // Turn off all interrupts
489 Ier
.Data
= READ_IER (SerialDevice
);
494 WRITE_IER (SerialDevice
, Ier
.Data
);
500 Fcr
.Bits
.TrFIFOE
= 0;
501 WRITE_FCR (SerialDevice
, Fcr
.Data
);
504 // Turn off loopback and disable device interrupt.
506 Mcr
.Data
= READ_MCR (SerialDevice
);
510 WRITE_MCR (SerialDevice
, Mcr
.Data
);
513 // Clear the scratch pad register
515 WRITE_SCR (SerialDevice
, 0);
520 Fcr
.Bits
.TrFIFOE
= 1;
521 if (SerialDevice
->ReceiveFifoDepth
> 16) {
522 Fcr
.Bits
.TrFIFO64
= 1;
524 Fcr
.Bits
.ResetRF
= 1;
525 Fcr
.Bits
.ResetTF
= 1;
526 WRITE_FCR (SerialDevice
, Fcr
.Data
);
529 // Go set the current attributes
531 Status
= This
->SetAttributes (
533 This
->Mode
->BaudRate
,
534 This
->Mode
->ReceiveFifoDepth
,
536 (EFI_PARITY_TYPE
) This
->Mode
->Parity
,
537 (UINT8
) This
->Mode
->DataBits
,
538 (EFI_STOP_BITS_TYPE
) This
->Mode
->StopBits
541 if (EFI_ERROR (Status
)) {
542 gBS
->RestoreTPL (Tpl
);
543 return EFI_DEVICE_ERROR
;
546 // Go set the current control bits
549 if (SerialDevice
->HardwareFlowControl
) {
550 Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
552 if (SerialDevice
->SoftwareLoopbackEnable
) {
553 Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
555 Status
= This
->SetControl (
560 if (EFI_ERROR (Status
)) {
561 gBS
->RestoreTPL (Tpl
);
562 return EFI_DEVICE_ERROR
;
566 // Reset the software FIFO
568 SerialDevice
->Receive
.Head
= SerialDevice
->Receive
.Tail
= 0;
569 SerialDevice
->Transmit
.Head
= SerialDevice
->Transmit
.Tail
= 0;
570 gBS
->RestoreTPL (Tpl
);
573 // Device reset is complete
579 Set new attributes to a serial device.
581 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
582 @param BaudRate The baudrate of the serial device
583 @param ReceiveFifoDepth The depth of receive FIFO buffer
584 @param Timeout The request timeout for a single char
585 @param Parity The type of parity used in serial device
586 @param DataBits Number of databits used in serial device
587 @param StopBits Number of stopbits used in serial device
589 @retval EFI_SUCCESS The new attributes were set
590 @retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value
591 @retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6
592 @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return)
597 SerialSetAttributes (
598 IN EFI_SERIAL_IO_PROTOCOL
*This
,
600 IN UINT32 ReceiveFifoDepth
,
602 IN EFI_PARITY_TYPE Parity
,
604 IN EFI_STOP_BITS_TYPE StopBits
608 SERIAL_DEV
*SerialDevice
;
611 UART_DEVICE_PATH
*Uart
;
614 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
617 // Check for default settings and fill in actual values.
620 BaudRate
= PcdGet64 (PcdUartDefaultBaudRate
);
623 if (ReceiveFifoDepth
== 0) {
624 ReceiveFifoDepth
= SerialDevice
->ReceiveFifoDepth
;
628 Timeout
= SERIAL_PORT_DEFAULT_TIMEOUT
;
631 if (Parity
== DefaultParity
) {
632 Parity
= (EFI_PARITY_TYPE
) PcdGet8 (PcdUartDefaultParity
);
636 DataBits
= PcdGet8 (PcdUartDefaultDataBits
);
639 if (StopBits
== DefaultStopBits
) {
640 StopBits
= (EFI_STOP_BITS_TYPE
) PcdGet8 (PcdUartDefaultStopBits
);
643 if (!VerifyUartParameters (SerialDevice
->ClockRate
, BaudRate
, DataBits
, Parity
, StopBits
, &Divisor
, &BaudRate
)) {
644 return EFI_INVALID_PARAMETER
;
647 if ((ReceiveFifoDepth
== 0) || (ReceiveFifoDepth
> SerialDevice
->ReceiveFifoDepth
)) {
648 return EFI_INVALID_PARAMETER
;
651 if ((Timeout
< SERIAL_PORT_MIN_TIMEOUT
) || (Timeout
> SERIAL_PORT_MAX_TIMEOUT
)) {
652 return EFI_INVALID_PARAMETER
;
655 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
658 // Put serial port on Divisor Latch Mode
660 Lcr
.Data
= READ_LCR (SerialDevice
);
662 WRITE_LCR (SerialDevice
, Lcr
.Data
);
665 // Write the divisor to the serial port
667 WRITE_DLL (SerialDevice
, (UINT8
) Divisor
);
668 WRITE_DLM (SerialDevice
, (UINT8
) ((UINT16
) Divisor
>> 8));
671 // Put serial port back in normal mode and set remaining attributes.
678 Lcr
.Bits
.EvenPar
= 0;
679 Lcr
.Bits
.SticPar
= 0;
684 Lcr
.Bits
.EvenPar
= 1;
685 Lcr
.Bits
.SticPar
= 0;
690 Lcr
.Bits
.EvenPar
= 0;
691 Lcr
.Bits
.SticPar
= 0;
696 Lcr
.Bits
.EvenPar
= 1;
697 Lcr
.Bits
.SticPar
= 1;
702 Lcr
.Bits
.EvenPar
= 0;
703 Lcr
.Bits
.SticPar
= 1;
715 case OneFiveStopBits
:
726 Lcr
.Bits
.SerialDB
= (UINT8
) ((DataBits
- 5) & 0x03);
727 WRITE_LCR (SerialDevice
, Lcr
.Data
);
730 // Set the Serial I/O mode
732 This
->Mode
->BaudRate
= BaudRate
;
733 This
->Mode
->ReceiveFifoDepth
= ReceiveFifoDepth
;
734 This
->Mode
->Timeout
= Timeout
;
735 This
->Mode
->Parity
= Parity
;
736 This
->Mode
->DataBits
= DataBits
;
737 This
->Mode
->StopBits
= StopBits
;
740 // See if Device Path Node has actually changed
742 if (SerialDevice
->UartDevicePath
.BaudRate
== BaudRate
&&
743 SerialDevice
->UartDevicePath
.DataBits
== DataBits
&&
744 SerialDevice
->UartDevicePath
.Parity
== Parity
&&
745 SerialDevice
->UartDevicePath
.StopBits
== StopBits
747 gBS
->RestoreTPL (Tpl
);
751 // Update the device path
753 SerialDevice
->UartDevicePath
.BaudRate
= BaudRate
;
754 SerialDevice
->UartDevicePath
.DataBits
= DataBits
;
755 SerialDevice
->UartDevicePath
.Parity
= (UINT8
) Parity
;
756 SerialDevice
->UartDevicePath
.StopBits
= (UINT8
) StopBits
;
758 Status
= EFI_SUCCESS
;
759 if (SerialDevice
->Handle
!= NULL
) {
762 // Skip the optional Controller device path node
764 Uart
= SkipControllerDevicePathNode (
765 (EFI_DEVICE_PATH_PROTOCOL
*) (
766 (UINT8
*) SerialDevice
->DevicePath
+ GetDevicePathSize (SerialDevice
->ParentDevicePath
) - END_DEVICE_PATH_LENGTH
771 CopyMem (Uart
, &SerialDevice
->UartDevicePath
, sizeof (UART_DEVICE_PATH
));
772 Status
= gBS
->ReinstallProtocolInterface (
773 SerialDevice
->Handle
,
774 &gEfiDevicePathProtocolGuid
,
775 SerialDevice
->DevicePath
,
776 SerialDevice
->DevicePath
780 gBS
->RestoreTPL (Tpl
);
788 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
789 @param Control Control bits that can be settable
791 @retval EFI_SUCCESS New Control bits were set successfully
792 @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported
798 IN EFI_SERIAL_IO_PROTOCOL
*This
,
802 SERIAL_DEV
*SerialDevice
;
805 UART_FLOW_CONTROL_DEVICE_PATH
*FlowControl
;
809 // The control bits that can be set are :
810 // EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO
811 // EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO
812 // EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW
813 // EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW
814 // EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW
816 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
819 // first determine the parameter is invalid
821 if ((Control
& (~(EFI_SERIAL_REQUEST_TO_SEND
| EFI_SERIAL_DATA_TERMINAL_READY
|
822 EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
| EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
|
823 EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
))) != 0) {
824 return EFI_UNSUPPORTED
;
827 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
829 Mcr
.Data
= READ_MCR (SerialDevice
);
833 SerialDevice
->SoftwareLoopbackEnable
= FALSE
;
834 SerialDevice
->HardwareFlowControl
= FALSE
;
836 if ((Control
& EFI_SERIAL_DATA_TERMINAL_READY
) == EFI_SERIAL_DATA_TERMINAL_READY
) {
840 if ((Control
& EFI_SERIAL_REQUEST_TO_SEND
) == EFI_SERIAL_REQUEST_TO_SEND
) {
844 if ((Control
& EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) {
848 if ((Control
& EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) {
849 SerialDevice
->HardwareFlowControl
= TRUE
;
852 WRITE_MCR (SerialDevice
, Mcr
.Data
);
854 if ((Control
& EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) {
855 SerialDevice
->SoftwareLoopbackEnable
= TRUE
;
858 Status
= EFI_SUCCESS
;
859 if (SerialDevice
->Handle
!= NULL
) {
860 FlowControl
= (UART_FLOW_CONTROL_DEVICE_PATH
*) (
861 (UINTN
) SerialDevice
->DevicePath
862 + GetDevicePathSize (SerialDevice
->ParentDevicePath
)
863 - END_DEVICE_PATH_LENGTH
864 + sizeof (UART_DEVICE_PATH
)
866 if (IsUartFlowControlDevicePathNode (FlowControl
) &&
867 ((BOOLEAN
) (ReadUnaligned32 (&FlowControl
->FlowControlMap
) == UART_FLOW_CONTROL_HARDWARE
) != SerialDevice
->HardwareFlowControl
)) {
869 // Flow Control setting is changed, need to reinstall device path protocol
871 WriteUnaligned32 (&FlowControl
->FlowControlMap
, SerialDevice
->HardwareFlowControl
? UART_FLOW_CONTROL_HARDWARE
: 0);
872 Status
= gBS
->ReinstallProtocolInterface (
873 SerialDevice
->Handle
,
874 &gEfiDevicePathProtocolGuid
,
875 SerialDevice
->DevicePath
,
876 SerialDevice
->DevicePath
881 gBS
->RestoreTPL (Tpl
);
889 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
890 @param Control Control signals of the serial device
892 @retval EFI_SUCCESS Get Control signals successfully
898 IN EFI_SERIAL_IO_PROTOCOL
*This
,
902 SERIAL_DEV
*SerialDevice
;
907 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
909 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
914 // Read the Modem Status Register
916 Msr
.Data
= READ_MSR (SerialDevice
);
918 if (Msr
.Bits
.Cts
== 1) {
919 *Control
|= EFI_SERIAL_CLEAR_TO_SEND
;
922 if (Msr
.Bits
.Dsr
== 1) {
923 *Control
|= EFI_SERIAL_DATA_SET_READY
;
926 if (Msr
.Bits
.Ri
== 1) {
927 *Control
|= EFI_SERIAL_RING_INDICATE
;
930 if (Msr
.Bits
.Dcd
== 1) {
931 *Control
|= EFI_SERIAL_CARRIER_DETECT
;
934 // Read the Modem Control Register
936 Mcr
.Data
= READ_MCR (SerialDevice
);
938 if (Mcr
.Bits
.DtrC
== 1) {
939 *Control
|= EFI_SERIAL_DATA_TERMINAL_READY
;
942 if (Mcr
.Bits
.Rts
== 1) {
943 *Control
|= EFI_SERIAL_REQUEST_TO_SEND
;
946 if (Mcr
.Bits
.Lme
== 1) {
947 *Control
|= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
;
950 if (SerialDevice
->HardwareFlowControl
) {
951 *Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
954 // Update FIFO status
956 SerialReceiveTransmit (SerialDevice
);
959 // See if the Transmit FIFO is empty
961 if (SerialFifoEmpty (&SerialDevice
->Transmit
)) {
962 *Control
|= EFI_SERIAL_OUTPUT_BUFFER_EMPTY
;
966 // See if the Receive FIFO is empty.
968 if (SerialFifoEmpty (&SerialDevice
->Receive
)) {
969 *Control
|= EFI_SERIAL_INPUT_BUFFER_EMPTY
;
972 if (SerialDevice
->SoftwareLoopbackEnable
) {
973 *Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
976 gBS
->RestoreTPL (Tpl
);
982 Write the specified number of bytes to serial device.
984 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
985 @param BufferSize On input the size of Buffer, on output the amount of
986 data actually written
987 @param Buffer The buffer of data to write
989 @retval EFI_SUCCESS The data were written successfully
990 @retval EFI_DEVICE_ERROR The device reported an error
991 @retval EFI_TIMEOUT The write operation was stopped due to timeout
997 IN EFI_SERIAL_IO_PROTOCOL
*This
,
998 IN OUT UINTN
*BufferSize
,
1002 SERIAL_DEV
*SerialDevice
;
1009 UINTN BitsPerCharacter
;
1011 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1015 if (*BufferSize
== 0) {
1019 if (Buffer
== NULL
) {
1020 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1022 EFI_P_EC_OUTPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1023 SerialDevice
->DevicePath
1026 return EFI_DEVICE_ERROR
;
1029 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1031 CharBuffer
= (UINT8
*) Buffer
;
1034 // Compute the number of bits in a single character. This is a start bit,
1035 // followed by the number of data bits, followed by the number of stop bits.
1036 // The number of stop bits is specified by an enumeration that includes
1037 // support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits.
1041 This
->Mode
->DataBits
+
1042 ((This
->Mode
->StopBits
== TwoStopBits
) ? 2 : This
->Mode
->StopBits
);
1045 // Compute the timeout in microseconds to wait for a single byte to be
1046 // transmitted. The Mode structure contans a Timeout field that is the
1047 // maximum time to transmit or receive a character. However, many UARTs
1048 // have a FIFO for transmits, so the time required to add one new character
1049 // to the transmit FIFO may be the time required to flush a full FIFO. If
1050 // the Timeout in the Mode structure is smaller than the time required to
1051 // flush a full FIFO at the current baud rate, then use a timeout value that
1052 // is required to flush a full transmit FIFO.
1055 This
->Mode
->Timeout
,
1056 (UINTN
)DivU64x64Remainder (
1057 BitsPerCharacter
* (SerialDevice
->TransmitFifoDepth
+ 1) * 1000000,
1058 This
->Mode
->BaudRate
,
1063 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1064 SerialFifoAdd (&SerialDevice
->Transmit
, CharBuffer
[Index
]);
1066 while (SerialReceiveTransmit (SerialDevice
) != EFI_SUCCESS
|| !SerialFifoEmpty (&SerialDevice
->Transmit
)) {
1068 // Unsuccessful write so check if timeout has expired, if not,
1069 // stall for a bit, increment time elapsed, and try again
1071 if (Elapsed
>= Timeout
) {
1072 *BufferSize
= ActualWrite
;
1073 gBS
->RestoreTPL (Tpl
);
1077 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1079 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1084 // Successful write so reset timeout
1089 gBS
->RestoreTPL (Tpl
);
1095 Read the specified number of bytes from serial device.
1097 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1098 @param BufferSize On input the size of Buffer, on output the amount of
1099 data returned in buffer
1100 @param Buffer The buffer to return the data into
1102 @retval EFI_SUCCESS The data were read successfully
1103 @retval EFI_DEVICE_ERROR The device reported an error
1104 @retval EFI_TIMEOUT The read operation was stopped due to timeout
1110 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1111 IN OUT UINTN
*BufferSize
,
1115 SERIAL_DEV
*SerialDevice
;
1122 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1125 if (*BufferSize
== 0) {
1129 if (Buffer
== NULL
) {
1130 return EFI_DEVICE_ERROR
;
1133 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1135 Status
= SerialReceiveTransmit (SerialDevice
);
1137 if (EFI_ERROR (Status
)) {
1140 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1142 EFI_P_EC_INPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1143 SerialDevice
->DevicePath
1146 gBS
->RestoreTPL (Tpl
);
1148 return EFI_DEVICE_ERROR
;
1151 CharBuffer
= (UINT8
*) Buffer
;
1152 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1153 while (SerialFifoRemove (&SerialDevice
->Receive
, &(CharBuffer
[Index
])) != EFI_SUCCESS
) {
1155 // Unsuccessful read so check if timeout has expired, if not,
1156 // stall for a bit, increment time elapsed, and try again
1157 // Need this time out to get conspliter to work.
1159 if (Elapsed
>= This
->Mode
->Timeout
) {
1160 *BufferSize
= Index
;
1161 gBS
->RestoreTPL (Tpl
);
1165 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1166 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1168 Status
= SerialReceiveTransmit (SerialDevice
);
1169 if (Status
== EFI_DEVICE_ERROR
) {
1170 *BufferSize
= Index
;
1171 gBS
->RestoreTPL (Tpl
);
1172 return EFI_DEVICE_ERROR
;
1176 // Successful read so reset timeout
1181 SerialReceiveTransmit (SerialDevice
);
1183 gBS
->RestoreTPL (Tpl
);
1189 Use scratchpad register to test if this serial port is present.
1191 @param SerialDevice Pointer to serial device structure
1193 @return if this serial port is present
1197 IN SERIAL_DEV
*SerialDevice
1209 Temp
= READ_SCR (SerialDevice
);
1210 WRITE_SCR (SerialDevice
, 0xAA);
1212 if (READ_SCR (SerialDevice
) != 0xAA) {
1216 WRITE_SCR (SerialDevice
, 0x55);
1218 if (READ_SCR (SerialDevice
) != 0x55) {
1224 WRITE_SCR (SerialDevice
, Temp
);
1231 @param SerialDev Pointer to serial device
1232 @param Offset Offset in register group
1234 @return Data read from serial port
1238 SerialReadRegister (
1239 IN SERIAL_DEV
*SerialDev
,
1246 if (SerialDev
->PciDeviceInfo
== NULL
) {
1247 return IoRead8 ((UINTN
) SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
);
1249 if (SerialDev
->MmioAccess
) {
1250 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Read (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1251 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1253 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Read (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1254 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1256 ASSERT_EFI_ERROR (Status
);
1264 @param SerialDev Pointer to serial device
1265 @param Offset Offset in register group
1266 @param Data data which is to be written to some serial port register
1269 SerialWriteRegister (
1270 IN SERIAL_DEV
*SerialDev
,
1277 if (SerialDev
->PciDeviceInfo
== NULL
) {
1278 IoWrite8 ((UINTN
) SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, Data
);
1280 if (SerialDev
->MmioAccess
) {
1281 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Write (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1282 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1284 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Write (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1285 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1287 ASSERT_EFI_ERROR (Status
);