2 SerialIo implementation for PCI or SIO UARTs.
4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Skip the optional Controller device path node and return the
19 pointer to the next device path node.
21 @param DevicePath Pointer to the device path.
22 @param ContainsControllerNode Returns TRUE if the Controller device path exists.
23 @param ControllerNumber Returns the Controller Number if Controller device path exists.
25 @return Pointer to the next device path node.
28 SkipControllerDevicePathNode (
29 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
30 BOOLEAN
*ContainsControllerNode
,
31 UINT32
*ControllerNumber
34 if ((DevicePathType (DevicePath
) == HARDWARE_DEVICE_PATH
) &&
35 (DevicePathSubType (DevicePath
) == HW_CONTROLLER_DP
)
37 if (ContainsControllerNode
!= NULL
) {
38 *ContainsControllerNode
= TRUE
;
40 if (ControllerNumber
!= NULL
) {
41 *ControllerNumber
= ((CONTROLLER_DEVICE_PATH
*) DevicePath
)->ControllerNumber
;
43 DevicePath
= NextDevicePathNode (DevicePath
);
45 if (ContainsControllerNode
!= NULL
) {
46 *ContainsControllerNode
= FALSE
;
49 return (UART_DEVICE_PATH
*) DevicePath
;
53 Checks whether the UART parameters are valid and computes the Divisor.
55 @param ClockRate The clock rate of the serial device used to verify
56 the BaudRate. Do not verify the BaudRate if it's 0.
57 @param BaudRate The requested baudrate of the serial device.
58 @param DataBits Number of databits used in serial device.
59 @param Parity The type of parity used in serial device.
60 @param StopBits Number of stopbits used in serial device.
61 @param Divisor Return the divisor if ClockRate is not 0.
62 @param ActualBaudRate Return the actual supported baudrate without
63 exceeding BaudRate. NULL means baudrate degradation
65 If the requested BaudRate is not supported, the routine
66 returns TRUE and the Actual Baud Rate when ActualBaudRate
67 is not NULL, returns FALSE when ActualBaudRate is NULL.
69 @retval TRUE The UART parameters are valid.
70 @retval FALSE The UART parameters are not valid.
73 VerifyUartParameters (
77 IN EFI_PARITY_TYPE Parity
,
78 IN EFI_STOP_BITS_TYPE StopBits
,
80 OUT UINT64
*ActualBaudRate
84 UINT32 ComputedBaudRate
;
85 UINT64 ComputedDivisor
;
88 if ((DataBits
< 5) || (DataBits
> 8) ||
89 (Parity
< NoParity
) || (Parity
> SpaceParity
) ||
90 (StopBits
< OneStopBit
) || (StopBits
> TwoStopBits
) ||
91 ((DataBits
== 5) && (StopBits
== TwoStopBits
)) ||
92 ((DataBits
>= 6) && (DataBits
<= 8) && (StopBits
== OneFiveStopBits
))
98 // Do not verify the baud rate if clock rate is unknown (0).
100 if (ClockRate
== 0) {
105 // Compute divisor use to program the baud rate using a round determination
106 // Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate)
107 // = ClockRate / (BaudRate << 4)
109 ComputedDivisor
= DivU64x64Remainder (ClockRate
, LShiftU64 (BaudRate
, 4), &Remainder
);
111 // Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate)
112 // BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3)
114 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 ((EFI_D_INFO
, "ClockRate = %d\n", ClockRate
));
144 DEBUG ((EFI_D_INFO
, "Divisor = %ld\n", ComputedDivisor
));
145 DEBUG ((EFI_D_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
;
156 if (Divisor
!= NULL
) {
157 *Divisor
= ComputedDivisor
;
161 if (ComputedBaudRate
< BaudRate
) {
162 if (ActualBaudRate
!= NULL
) {
163 *ActualBaudRate
= ComputedBaudRate
;
165 if (Divisor
!= NULL
) {
166 *Divisor
= ComputedDivisor
;
172 // ActualBaudRate is higher than requested baud rate and more than 4%
173 // higher than the requested value. Increment Divisor if it is less
174 // than MAX_UINT16 and computed baud rate with new divisor.
176 if (ComputedDivisor
== MAX_UINT16
) {
180 ComputedBaudRate
= ClockRate
/ ((UINT16
) ComputedDivisor
<< 4);
181 if (ComputedBaudRate
== 0) {
185 DEBUG ((EFI_D_INFO
, "ClockRate = %d\n", ClockRate
));
186 DEBUG ((EFI_D_INFO
, "Divisor = %ld\n", ComputedDivisor
));
187 DEBUG ((EFI_D_INFO
, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate
, ComputedBaudRate
, Percent
));
189 if (ActualBaudRate
!= NULL
) {
190 *ActualBaudRate
= ComputedBaudRate
;
192 if (Divisor
!= NULL
) {
193 *Divisor
= ComputedDivisor
;
199 Detect whether specific FIFO is full or not.
201 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
203 @return whether specific FIFO is full or not
207 IN SERIAL_DEV_FIFO
*Fifo
210 return (BOOLEAN
) (((Fifo
->Tail
+ 1) % SERIAL_MAX_FIFO_SIZE
) == Fifo
->Head
);
214 Detect whether specific FIFO is empty or not.
216 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
218 @return whether specific FIFO is empty or not
222 IN SERIAL_DEV_FIFO
*Fifo
226 return (BOOLEAN
) (Fifo
->Head
== Fifo
->Tail
);
230 Add data to specific FIFO.
232 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
233 @param Data the data added to FIFO
235 @retval EFI_SUCCESS Add data to specific FIFO successfully
236 @retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full
240 IN OUT SERIAL_DEV_FIFO
*Fifo
,
245 // if FIFO full can not add data
247 if (SerialFifoFull (Fifo
)) {
248 return EFI_OUT_OF_RESOURCES
;
251 // FIFO is not full can add data
253 Fifo
->Data
[Fifo
->Tail
] = Data
;
254 Fifo
->Tail
= (Fifo
->Tail
+ 1) % SERIAL_MAX_FIFO_SIZE
;
259 Remove data from specific FIFO.
261 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
262 @param Data the data removed from FIFO
264 @retval EFI_SUCCESS Remove data from specific FIFO successfully
265 @retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty
270 IN OUT SERIAL_DEV_FIFO
*Fifo
,
275 // if FIFO is empty, no data can remove
277 if (SerialFifoEmpty (Fifo
)) {
278 return EFI_OUT_OF_RESOURCES
;
281 // FIFO is not empty, can remove data
283 *Data
= Fifo
->Data
[Fifo
->Head
];
284 Fifo
->Head
= (Fifo
->Head
+ 1) % SERIAL_MAX_FIFO_SIZE
;
289 Reads and writes all avaliable data.
291 @param SerialDevice The device to transmit.
293 @retval EFI_SUCCESS Data was read/written successfully.
294 @retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when
295 this happens, pending writes are not done.
299 SerialReceiveTransmit (
300 IN SERIAL_DEV
*SerialDevice
306 BOOLEAN ReceiveFifoFull
;
314 // Begin the read or write
316 if (SerialDevice
->SoftwareLoopbackEnable
) {
318 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
319 if (!SerialFifoEmpty (&SerialDevice
->Transmit
)) {
320 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
321 if (ReceiveFifoFull
) {
322 return EFI_OUT_OF_RESOURCES
;
325 SerialFifoAdd (&SerialDevice
->Receive
, Data
);
327 } while (!SerialFifoEmpty (&SerialDevice
->Transmit
));
329 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
331 // For full handshake flow control, tell the peer to send data
332 // if receive buffer is available.
334 if (SerialDevice
->HardwareFlowControl
&&
335 !FeaturePcdGet(PcdSerialUseHalfHandshake
)&&
338 Mcr
.Data
= READ_MCR (SerialDevice
);
340 WRITE_MCR (SerialDevice
, Mcr
.Data
);
343 Lsr
.Data
= READ_LSR (SerialDevice
);
346 // Flush incomming data to prevent a an overrun during a long write
348 if ((Lsr
.Bits
.Dr
== 1) && !ReceiveFifoFull
) {
349 ReceiveFifoFull
= SerialFifoFull (&SerialDevice
->Receive
);
350 if (!ReceiveFifoFull
) {
351 if (Lsr
.Bits
.FIFOe
== 1 || Lsr
.Bits
.Oe
== 1 || Lsr
.Bits
.Pe
== 1 || Lsr
.Bits
.Fe
== 1 || Lsr
.Bits
.Bi
== 1) {
352 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
354 EFI_P_EC_INPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
355 SerialDevice
->DevicePath
357 if (Lsr
.Bits
.FIFOe
== 1 || Lsr
.Bits
.Pe
== 1|| Lsr
.Bits
.Fe
== 1 || Lsr
.Bits
.Bi
== 1) {
358 Data
= READ_RBR (SerialDevice
);
363 Data
= READ_RBR (SerialDevice
);
365 SerialFifoAdd (&SerialDevice
->Receive
, Data
);
368 // For full handshake flow control, if receive buffer full
369 // tell the peer to stop sending data.
371 if (SerialDevice
->HardwareFlowControl
&&
372 !FeaturePcdGet(PcdSerialUseHalfHandshake
) &&
373 SerialFifoFull (&SerialDevice
->Receive
)
375 Mcr
.Data
= READ_MCR (SerialDevice
);
377 WRITE_MCR (SerialDevice
, Mcr
.Data
);
383 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
385 EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER
| EFI_PERIPHERAL_SERIAL_PORT
,
386 SerialDevice
->DevicePath
393 if (Lsr
.Bits
.Thre
== 1 && !SerialFifoEmpty (&SerialDevice
->Transmit
)) {
395 // Make sure the transmit data will not be missed
397 if (SerialDevice
->HardwareFlowControl
) {
399 // For half handshake flow control assert RTS before sending.
401 if (FeaturePcdGet(PcdSerialUseHalfHandshake
)) {
402 Mcr
.Data
= READ_MCR (SerialDevice
);
404 WRITE_MCR (SerialDevice
, Mcr
.Data
);
410 Msr
.Data
= READ_MSR (SerialDevice
);
411 while ((Msr
.Bits
.Dcd
== 1) && ((Msr
.Bits
.Cts
== 0) ^ FeaturePcdGet(PcdSerialUseHalfHandshake
))) {
412 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
418 Msr
.Data
= READ_MSR (SerialDevice
);
421 if ((Msr
.Bits
.Dcd
== 0) || ((Msr
.Bits
.Cts
== 1) ^ FeaturePcdGet(PcdSerialUseHalfHandshake
))) {
422 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
423 WRITE_THR (SerialDevice
, Data
);
427 // For half handshake flow control, tell DCE we are done.
429 if (FeaturePcdGet(PcdSerialUseHalfHandshake
)) {
430 Mcr
.Data
= READ_MCR (SerialDevice
);
432 WRITE_MCR (SerialDevice
, Mcr
.Data
);
435 SerialFifoRemove (&SerialDevice
->Transmit
, &Data
);
436 WRITE_THR (SerialDevice
, Data
);
439 } while (Lsr
.Bits
.Thre
== 1 && !SerialFifoEmpty (&SerialDevice
->Transmit
));
446 Flush the serial hardware transmit FIFO and shift register.
448 @param SerialDevice The device to flush.
451 SerialFlushTransmitFifo (
452 SERIAL_DEV
*SerialDevice
458 // Wait for the serial port to be ready, to make sure both the transmit FIFO
459 // and shift register empty.
462 Lsr
.Data
= READ_LSR (SerialDevice
);
463 } while (Lsr
.Bits
.Temt
== 0);
467 // Interface Functions
472 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
474 @retval EFI_SUCCESS Reset successfully
475 @retval EFI_DEVICE_ERROR Failed to reset
481 IN EFI_SERIAL_IO_PROTOCOL
*This
485 SERIAL_DEV
*SerialDevice
;
493 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
496 // Report the status code reset the serial
498 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
500 EFI_P_PC_RESET
| EFI_PERIPHERAL_SERIAL_PORT
,
501 SerialDevice
->DevicePath
504 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
506 SerialFlushTransmitFifo (SerialDevice
);
509 // Make sure DLAB is 0.
511 Lcr
.Data
= READ_LCR (SerialDevice
);
513 WRITE_LCR (SerialDevice
, Lcr
.Data
);
516 // Turn off all interrupts
518 Ier
.Data
= READ_IER (SerialDevice
);
523 WRITE_IER (SerialDevice
, Ier
.Data
);
529 Fcr
.Bits
.TrFIFOE
= 0;
530 WRITE_FCR (SerialDevice
, Fcr
.Data
);
533 // Turn off loopback and disable device interrupt.
535 Mcr
.Data
= READ_MCR (SerialDevice
);
539 WRITE_MCR (SerialDevice
, Mcr
.Data
);
542 // Clear the scratch pad register
544 WRITE_SCR (SerialDevice
, 0);
549 Fcr
.Bits
.TrFIFOE
= 1;
550 if (SerialDevice
->ReceiveFifoDepth
> 16) {
551 Fcr
.Bits
.TrFIFO64
= 1;
553 Fcr
.Bits
.ResetRF
= 1;
554 Fcr
.Bits
.ResetTF
= 1;
555 WRITE_FCR (SerialDevice
, Fcr
.Data
);
558 // Go set the current attributes
560 Status
= This
->SetAttributes (
562 This
->Mode
->BaudRate
,
563 This
->Mode
->ReceiveFifoDepth
,
565 (EFI_PARITY_TYPE
) This
->Mode
->Parity
,
566 (UINT8
) This
->Mode
->DataBits
,
567 (EFI_STOP_BITS_TYPE
) This
->Mode
->StopBits
570 if (EFI_ERROR (Status
)) {
571 gBS
->RestoreTPL (Tpl
);
572 return EFI_DEVICE_ERROR
;
575 // Go set the current control bits
577 Control
= EFI_SERIAL_REQUEST_TO_SEND
| EFI_SERIAL_DATA_TERMINAL_READY
;
578 if (SerialDevice
->HardwareFlowControl
) {
579 Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
581 if (SerialDevice
->SoftwareLoopbackEnable
) {
582 Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
584 Status
= This
->SetControl (
589 if (EFI_ERROR (Status
)) {
590 gBS
->RestoreTPL (Tpl
);
591 return EFI_DEVICE_ERROR
;
595 // Reset the software FIFO
597 SerialDevice
->Receive
.Head
= SerialDevice
->Receive
.Tail
= 0;
598 SerialDevice
->Transmit
.Head
= SerialDevice
->Transmit
.Tail
= 0;
599 gBS
->RestoreTPL (Tpl
);
602 // Device reset is complete
608 Set new attributes to a serial device.
610 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
611 @param BaudRate The baudrate of the serial device
612 @param ReceiveFifoDepth The depth of receive FIFO buffer
613 @param Timeout The request timeout for a single char
614 @param Parity The type of parity used in serial device
615 @param DataBits Number of databits used in serial device
616 @param StopBits Number of stopbits used in serial device
618 @retval EFI_SUCCESS The new attributes were set
619 @retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value
620 @retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6
621 @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return)
626 SerialSetAttributes (
627 IN EFI_SERIAL_IO_PROTOCOL
*This
,
629 IN UINT32 ReceiveFifoDepth
,
631 IN EFI_PARITY_TYPE Parity
,
633 IN EFI_STOP_BITS_TYPE StopBits
637 SERIAL_DEV
*SerialDevice
;
640 UART_DEVICE_PATH
*Uart
;
643 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
646 // Check for default settings and fill in actual values.
649 BaudRate
= PcdGet64 (PcdUartDefaultBaudRate
);
652 if (ReceiveFifoDepth
== 0) {
653 ReceiveFifoDepth
= SerialDevice
->ReceiveFifoDepth
;
657 Timeout
= SERIAL_PORT_DEFAULT_TIMEOUT
;
660 if (Parity
== DefaultParity
) {
661 Parity
= (EFI_PARITY_TYPE
) PcdGet8 (PcdUartDefaultParity
);
665 DataBits
= PcdGet8 (PcdUartDefaultDataBits
);
668 if (StopBits
== DefaultStopBits
) {
669 StopBits
= (EFI_STOP_BITS_TYPE
) PcdGet8 (PcdUartDefaultStopBits
);
672 if (!VerifyUartParameters (SerialDevice
->ClockRate
, BaudRate
, DataBits
, Parity
, StopBits
, &Divisor
, &BaudRate
)) {
673 return EFI_INVALID_PARAMETER
;
676 if ((ReceiveFifoDepth
== 0) || (ReceiveFifoDepth
> SerialDevice
->ReceiveFifoDepth
)) {
677 return EFI_INVALID_PARAMETER
;
680 if ((Timeout
< SERIAL_PORT_MIN_TIMEOUT
) || (Timeout
> SERIAL_PORT_MAX_TIMEOUT
)) {
681 return EFI_INVALID_PARAMETER
;
684 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
686 SerialFlushTransmitFifo (SerialDevice
);
689 // Put serial port on Divisor Latch Mode
691 Lcr
.Data
= READ_LCR (SerialDevice
);
693 WRITE_LCR (SerialDevice
, Lcr
.Data
);
696 // Write the divisor to the serial port
698 WRITE_DLL (SerialDevice
, (UINT8
) Divisor
);
699 WRITE_DLM (SerialDevice
, (UINT8
) ((UINT16
) Divisor
>> 8));
702 // Put serial port back in normal mode and set remaining attributes.
709 Lcr
.Bits
.EvenPar
= 0;
710 Lcr
.Bits
.SticPar
= 0;
715 Lcr
.Bits
.EvenPar
= 1;
716 Lcr
.Bits
.SticPar
= 0;
721 Lcr
.Bits
.EvenPar
= 0;
722 Lcr
.Bits
.SticPar
= 0;
727 Lcr
.Bits
.EvenPar
= 1;
728 Lcr
.Bits
.SticPar
= 1;
733 Lcr
.Bits
.EvenPar
= 0;
734 Lcr
.Bits
.SticPar
= 1;
746 case OneFiveStopBits
:
757 Lcr
.Bits
.SerialDB
= (UINT8
) ((DataBits
- 5) & 0x03);
758 WRITE_LCR (SerialDevice
, Lcr
.Data
);
761 // Set the Serial I/O mode
763 This
->Mode
->BaudRate
= BaudRate
;
764 This
->Mode
->ReceiveFifoDepth
= ReceiveFifoDepth
;
765 This
->Mode
->Timeout
= Timeout
;
766 This
->Mode
->Parity
= Parity
;
767 This
->Mode
->DataBits
= DataBits
;
768 This
->Mode
->StopBits
= StopBits
;
771 // See if Device Path Node has actually changed
773 if (SerialDevice
->UartDevicePath
.BaudRate
== BaudRate
&&
774 SerialDevice
->UartDevicePath
.DataBits
== DataBits
&&
775 SerialDevice
->UartDevicePath
.Parity
== Parity
&&
776 SerialDevice
->UartDevicePath
.StopBits
== StopBits
778 gBS
->RestoreTPL (Tpl
);
782 // Update the device path
784 SerialDevice
->UartDevicePath
.BaudRate
= BaudRate
;
785 SerialDevice
->UartDevicePath
.DataBits
= DataBits
;
786 SerialDevice
->UartDevicePath
.Parity
= (UINT8
) Parity
;
787 SerialDevice
->UartDevicePath
.StopBits
= (UINT8
) StopBits
;
789 Status
= EFI_SUCCESS
;
790 if (SerialDevice
->Handle
!= NULL
) {
793 // Skip the optional Controller device path node
795 Uart
= SkipControllerDevicePathNode (
796 (EFI_DEVICE_PATH_PROTOCOL
*) (
797 (UINT8
*) SerialDevice
->DevicePath
+ GetDevicePathSize (SerialDevice
->ParentDevicePath
) - END_DEVICE_PATH_LENGTH
802 CopyMem (Uart
, &SerialDevice
->UartDevicePath
, sizeof (UART_DEVICE_PATH
));
803 Status
= gBS
->ReinstallProtocolInterface (
804 SerialDevice
->Handle
,
805 &gEfiDevicePathProtocolGuid
,
806 SerialDevice
->DevicePath
,
807 SerialDevice
->DevicePath
811 gBS
->RestoreTPL (Tpl
);
819 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
820 @param Control Control bits that can be settable
822 @retval EFI_SUCCESS New Control bits were set successfully
823 @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported
829 IN EFI_SERIAL_IO_PROTOCOL
*This
,
833 SERIAL_DEV
*SerialDevice
;
836 UART_FLOW_CONTROL_DEVICE_PATH
*FlowControl
;
840 // The control bits that can be set are :
841 // EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO
842 // EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO
843 // EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW
844 // EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW
845 // EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW
847 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
850 // first determine the parameter is invalid
852 if ((Control
& (~(EFI_SERIAL_REQUEST_TO_SEND
| EFI_SERIAL_DATA_TERMINAL_READY
|
853 EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
| EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
|
854 EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
))) != 0) {
855 return EFI_UNSUPPORTED
;
858 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
860 Mcr
.Data
= READ_MCR (SerialDevice
);
864 SerialDevice
->SoftwareLoopbackEnable
= FALSE
;
865 SerialDevice
->HardwareFlowControl
= FALSE
;
867 if ((Control
& EFI_SERIAL_DATA_TERMINAL_READY
) == EFI_SERIAL_DATA_TERMINAL_READY
) {
871 if ((Control
& EFI_SERIAL_REQUEST_TO_SEND
) == EFI_SERIAL_REQUEST_TO_SEND
) {
875 if ((Control
& EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
) {
879 if ((Control
& EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
) {
880 SerialDevice
->HardwareFlowControl
= TRUE
;
883 WRITE_MCR (SerialDevice
, Mcr
.Data
);
885 if ((Control
& EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
) {
886 SerialDevice
->SoftwareLoopbackEnable
= TRUE
;
889 Status
= EFI_SUCCESS
;
890 if (SerialDevice
->Handle
!= NULL
) {
891 FlowControl
= (UART_FLOW_CONTROL_DEVICE_PATH
*) (
892 (UINTN
) SerialDevice
->DevicePath
893 + GetDevicePathSize (SerialDevice
->ParentDevicePath
)
894 - END_DEVICE_PATH_LENGTH
895 + sizeof (UART_DEVICE_PATH
)
897 if (IsUartFlowControlDevicePathNode (FlowControl
) &&
898 ((BOOLEAN
) (ReadUnaligned32 (&FlowControl
->FlowControlMap
) == UART_FLOW_CONTROL_HARDWARE
) != SerialDevice
->HardwareFlowControl
)) {
900 // Flow Control setting is changed, need to reinstall device path protocol
902 WriteUnaligned32 (&FlowControl
->FlowControlMap
, SerialDevice
->HardwareFlowControl
? UART_FLOW_CONTROL_HARDWARE
: 0);
903 Status
= gBS
->ReinstallProtocolInterface (
904 SerialDevice
->Handle
,
905 &gEfiDevicePathProtocolGuid
,
906 SerialDevice
->DevicePath
,
907 SerialDevice
->DevicePath
912 gBS
->RestoreTPL (Tpl
);
920 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
921 @param Control Control signals of the serial device
923 @retval EFI_SUCCESS Get Control signals successfully
929 IN EFI_SERIAL_IO_PROTOCOL
*This
,
933 SERIAL_DEV
*SerialDevice
;
938 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
940 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
945 // Read the Modem Status Register
947 Msr
.Data
= READ_MSR (SerialDevice
);
949 if (Msr
.Bits
.Cts
== 1) {
950 *Control
|= EFI_SERIAL_CLEAR_TO_SEND
;
953 if (Msr
.Bits
.Dsr
== 1) {
954 *Control
|= EFI_SERIAL_DATA_SET_READY
;
957 if (Msr
.Bits
.Ri
== 1) {
958 *Control
|= EFI_SERIAL_RING_INDICATE
;
961 if (Msr
.Bits
.Dcd
== 1) {
962 *Control
|= EFI_SERIAL_CARRIER_DETECT
;
965 // Read the Modem Control Register
967 Mcr
.Data
= READ_MCR (SerialDevice
);
969 if (Mcr
.Bits
.DtrC
== 1) {
970 *Control
|= EFI_SERIAL_DATA_TERMINAL_READY
;
973 if (Mcr
.Bits
.Rts
== 1) {
974 *Control
|= EFI_SERIAL_REQUEST_TO_SEND
;
977 if (Mcr
.Bits
.Lme
== 1) {
978 *Control
|= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE
;
981 if (SerialDevice
->HardwareFlowControl
) {
982 *Control
|= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE
;
985 // Update FIFO status
987 SerialReceiveTransmit (SerialDevice
);
990 // See if the Transmit FIFO is empty
992 if (SerialFifoEmpty (&SerialDevice
->Transmit
)) {
993 *Control
|= EFI_SERIAL_OUTPUT_BUFFER_EMPTY
;
997 // See if the Receive FIFO is empty.
999 if (SerialFifoEmpty (&SerialDevice
->Receive
)) {
1000 *Control
|= EFI_SERIAL_INPUT_BUFFER_EMPTY
;
1003 if (SerialDevice
->SoftwareLoopbackEnable
) {
1004 *Control
|= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE
;
1007 gBS
->RestoreTPL (Tpl
);
1013 Write the specified number of bytes to serial device.
1015 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1016 @param BufferSize On input the size of Buffer, on output the amount of
1017 data actually written
1018 @param Buffer The buffer of data to write
1020 @retval EFI_SUCCESS The data were written successfully
1021 @retval EFI_DEVICE_ERROR The device reported an error
1022 @retval EFI_TIMEOUT The write operation was stopped due to timeout
1028 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1029 IN OUT UINTN
*BufferSize
,
1033 SERIAL_DEV
*SerialDevice
;
1040 UINTN BitsPerCharacter
;
1042 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1046 if (*BufferSize
== 0) {
1050 if (Buffer
== NULL
) {
1051 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1053 EFI_P_EC_OUTPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1054 SerialDevice
->DevicePath
1057 return EFI_DEVICE_ERROR
;
1060 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1062 CharBuffer
= (UINT8
*) Buffer
;
1065 // Compute the number of bits in a single character. This is a start bit,
1066 // followed by the number of data bits, followed by the number of stop bits.
1067 // The number of stop bits is specified by an enumeration that includes
1068 // support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits.
1072 This
->Mode
->DataBits
+
1073 ((This
->Mode
->StopBits
== TwoStopBits
) ? 2 : This
->Mode
->StopBits
);
1076 // Compute the timeout in microseconds to wait for a single byte to be
1077 // transmitted. The Mode structure contans a Timeout field that is the
1078 // maximum time to transmit or receive a character. However, many UARTs
1079 // have a FIFO for transmits, so the time required to add one new character
1080 // to the transmit FIFO may be the time required to flush a full FIFO. If
1081 // the Timeout in the Mode structure is smaller than the time required to
1082 // flush a full FIFO at the current baud rate, then use a timeout value that
1083 // is required to flush a full transmit FIFO.
1086 This
->Mode
->Timeout
,
1087 (UINTN
)DivU64x64Remainder (
1088 BitsPerCharacter
* (SerialDevice
->TransmitFifoDepth
+ 1) * 1000000,
1089 This
->Mode
->BaudRate
,
1094 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1095 SerialFifoAdd (&SerialDevice
->Transmit
, CharBuffer
[Index
]);
1097 while (SerialReceiveTransmit (SerialDevice
) != EFI_SUCCESS
|| !SerialFifoEmpty (&SerialDevice
->Transmit
)) {
1099 // Unsuccessful write so check if timeout has expired, if not,
1100 // stall for a bit, increment time elapsed, and try again
1102 if (Elapsed
>= Timeout
) {
1103 *BufferSize
= ActualWrite
;
1104 gBS
->RestoreTPL (Tpl
);
1108 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1110 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1115 // Successful write so reset timeout
1120 gBS
->RestoreTPL (Tpl
);
1126 Read the specified number of bytes from serial device.
1128 @param This Pointer to EFI_SERIAL_IO_PROTOCOL
1129 @param BufferSize On input the size of Buffer, on output the amount of
1130 data returned in buffer
1131 @param Buffer The buffer to return the data into
1133 @retval EFI_SUCCESS The data were read successfully
1134 @retval EFI_DEVICE_ERROR The device reported an error
1135 @retval EFI_TIMEOUT The read operation was stopped due to timeout
1141 IN EFI_SERIAL_IO_PROTOCOL
*This
,
1142 IN OUT UINTN
*BufferSize
,
1146 SERIAL_DEV
*SerialDevice
;
1153 SerialDevice
= SERIAL_DEV_FROM_THIS (This
);
1156 if (*BufferSize
== 0) {
1160 if (Buffer
== NULL
) {
1161 return EFI_DEVICE_ERROR
;
1164 Tpl
= gBS
->RaiseTPL (TPL_NOTIFY
);
1166 Status
= SerialReceiveTransmit (SerialDevice
);
1168 if (EFI_ERROR (Status
)) {
1171 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1173 EFI_P_EC_INPUT_ERROR
| EFI_PERIPHERAL_SERIAL_PORT
,
1174 SerialDevice
->DevicePath
1177 gBS
->RestoreTPL (Tpl
);
1179 return EFI_DEVICE_ERROR
;
1182 CharBuffer
= (UINT8
*) Buffer
;
1183 for (Index
= 0; Index
< *BufferSize
; Index
++) {
1184 while (SerialFifoRemove (&SerialDevice
->Receive
, &(CharBuffer
[Index
])) != EFI_SUCCESS
) {
1186 // Unsuccessful read so check if timeout has expired, if not,
1187 // stall for a bit, increment time elapsed, and try again
1188 // Need this time out to get conspliter to work.
1190 if (Elapsed
>= This
->Mode
->Timeout
) {
1191 *BufferSize
= Index
;
1192 gBS
->RestoreTPL (Tpl
);
1196 gBS
->Stall (TIMEOUT_STALL_INTERVAL
);
1197 Elapsed
+= TIMEOUT_STALL_INTERVAL
;
1199 Status
= SerialReceiveTransmit (SerialDevice
);
1200 if (Status
== EFI_DEVICE_ERROR
) {
1201 *BufferSize
= Index
;
1202 gBS
->RestoreTPL (Tpl
);
1203 return EFI_DEVICE_ERROR
;
1207 // Successful read so reset timeout
1212 SerialReceiveTransmit (SerialDevice
);
1214 gBS
->RestoreTPL (Tpl
);
1220 Use scratchpad register to test if this serial port is present.
1222 @param SerialDevice Pointer to serial device structure
1224 @return if this serial port is present
1228 IN SERIAL_DEV
*SerialDevice
1240 Temp
= READ_SCR (SerialDevice
);
1241 WRITE_SCR (SerialDevice
, 0xAA);
1243 if (READ_SCR (SerialDevice
) != 0xAA) {
1247 WRITE_SCR (SerialDevice
, 0x55);
1249 if (READ_SCR (SerialDevice
) != 0x55) {
1255 WRITE_SCR (SerialDevice
, Temp
);
1262 @param SerialDev Pointer to serial device
1263 @param Offset Offset in register group
1265 @return Data read from serial port
1269 SerialReadRegister (
1270 IN SERIAL_DEV
*SerialDev
,
1277 if (SerialDev
->PciDeviceInfo
== NULL
) {
1278 return IoRead8 ((UINTN
) SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
);
1280 if (SerialDev
->MmioAccess
) {
1281 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Read (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1282 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1284 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Read (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1285 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1287 ASSERT_EFI_ERROR (Status
);
1295 @param SerialDev Pointer to serial device
1296 @param Offset Offset in register group
1297 @param Data data which is to be written to some serial port register
1300 SerialWriteRegister (
1301 IN SERIAL_DEV
*SerialDev
,
1308 if (SerialDev
->PciDeviceInfo
== NULL
) {
1309 IoWrite8 ((UINTN
) SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, Data
);
1311 if (SerialDev
->MmioAccess
) {
1312 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Mem
.Write (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1313 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1315 Status
= SerialDev
->PciDeviceInfo
->PciIo
->Io
.Write (SerialDev
->PciDeviceInfo
->PciIo
, EfiPciIoWidthUint8
, EFI_PCI_IO_PASS_THROUGH_BAR
,
1316 SerialDev
->BaseAddress
+ Offset
* SerialDev
->RegisterStride
, 1, &Data
);
1318 ASSERT_EFI_ERROR (Status
);