mirror of https://github.com/acidanthera/audk.git
1379 lines
39 KiB
C
1379 lines
39 KiB
C
/** @file
|
|
SerialIo implementation for PCI or SIO UARTs.
|
|
|
|
Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "Serial.h"
|
|
|
|
/**
|
|
Skip the optional Controller device path node and return the
|
|
pointer to the next device path node.
|
|
|
|
@param DevicePath Pointer to the device path.
|
|
@param ContainsControllerNode Returns TRUE if the Controller device path exists.
|
|
@param ControllerNumber Returns the Controller Number if Controller device path exists.
|
|
|
|
@return Pointer to the next device path node.
|
|
**/
|
|
UART_DEVICE_PATH *
|
|
SkipControllerDevicePathNode (
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
BOOLEAN *ContainsControllerNode,
|
|
UINT32 *ControllerNumber
|
|
)
|
|
{
|
|
if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) &&
|
|
(DevicePathSubType (DevicePath) == HW_CONTROLLER_DP)
|
|
) {
|
|
if (ContainsControllerNode != NULL) {
|
|
*ContainsControllerNode = TRUE;
|
|
}
|
|
if (ControllerNumber != NULL) {
|
|
*ControllerNumber = ((CONTROLLER_DEVICE_PATH *) DevicePath)->ControllerNumber;
|
|
}
|
|
DevicePath = NextDevicePathNode (DevicePath);
|
|
} else {
|
|
if (ContainsControllerNode != NULL) {
|
|
*ContainsControllerNode = FALSE;
|
|
}
|
|
}
|
|
return (UART_DEVICE_PATH *) DevicePath;
|
|
}
|
|
|
|
/**
|
|
Checks whether the UART parameters are valid and computes the Divisor.
|
|
|
|
@param ClockRate The clock rate of the serial device used to verify
|
|
the BaudRate. Do not verify the BaudRate if it's 0.
|
|
@param BaudRate The requested baudrate of the serial device.
|
|
@param DataBits Number of databits used in serial device.
|
|
@param Parity The type of parity used in serial device.
|
|
@param StopBits Number of stopbits used in serial device.
|
|
@param Divisor Return the divisor if ClockRate is not 0.
|
|
@param ActualBaudRate Return the actual supported baudrate without
|
|
exceeding BaudRate. NULL means baudrate degradation
|
|
is not allowed.
|
|
If the requested BaudRate is not supported, the routine
|
|
returns TRUE and the Actual Baud Rate when ActualBaudRate
|
|
is not NULL, returns FALSE when ActualBaudRate is NULL.
|
|
|
|
@retval TRUE The UART parameters are valid.
|
|
@retval FALSE The UART parameters are not valid.
|
|
**/
|
|
BOOLEAN
|
|
VerifyUartParameters (
|
|
IN UINT32 ClockRate,
|
|
IN UINT64 BaudRate,
|
|
IN UINT8 DataBits,
|
|
IN EFI_PARITY_TYPE Parity,
|
|
IN EFI_STOP_BITS_TYPE StopBits,
|
|
OUT UINT64 *Divisor,
|
|
OUT UINT64 *ActualBaudRate
|
|
)
|
|
{
|
|
UINT64 Remainder;
|
|
UINT32 ComputedBaudRate;
|
|
UINT64 ComputedDivisor;
|
|
UINT64 Percent;
|
|
|
|
if ((DataBits < 5) || (DataBits > 8) ||
|
|
(Parity < NoParity) || (Parity > SpaceParity) ||
|
|
(StopBits < OneStopBit) || (StopBits > TwoStopBits) ||
|
|
((DataBits == 5) && (StopBits == TwoStopBits)) ||
|
|
((DataBits >= 6) && (DataBits <= 8) && (StopBits == OneFiveStopBits))
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Do not verify the baud rate if clock rate is unknown (0).
|
|
//
|
|
if (ClockRate == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Compute divisor use to program the baud rate using a round determination
|
|
// Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate)
|
|
// = ClockRate / (BaudRate << 4)
|
|
//
|
|
ComputedDivisor = DivU64x64Remainder (ClockRate, LShiftU64 (BaudRate, 4), &Remainder);
|
|
//
|
|
// Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate)
|
|
// BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3)
|
|
//
|
|
if (Remainder >= LShiftU64 (BaudRate, 3)) {
|
|
ComputedDivisor++;
|
|
}
|
|
//
|
|
// If the computed divisor is larger than the maximum value that can be programmed
|
|
// into the UART, then the requested baud rate can not be supported.
|
|
//
|
|
if (ComputedDivisor > MAX_UINT16) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the computed divisor is 0, then use a computed divisor of 1, which will select
|
|
// the maximum supported baud rate.
|
|
//
|
|
if (ComputedDivisor == 0) {
|
|
ComputedDivisor = 1;
|
|
}
|
|
|
|
//
|
|
// Actual baud rate that the serial port will be programmed for
|
|
// should be with in 4% of requested one.
|
|
//
|
|
ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4);
|
|
if (ComputedBaudRate == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
Percent = DivU64x32 (MultU64x32 (BaudRate, 100), ComputedBaudRate);
|
|
DEBUG ((EFI_D_INFO, "ClockRate = %d\n", ClockRate));
|
|
DEBUG ((EFI_D_INFO, "Divisor = %ld\n", ComputedDivisor));
|
|
DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));
|
|
|
|
//
|
|
// If the requested BaudRate is not supported:
|
|
// Returns TRUE and the Actual Baud Rate when ActualBaudRate is not NULL;
|
|
// Returns FALSE when ActualBaudRate is NULL.
|
|
//
|
|
if ((Percent >= 96) && (Percent <= 104)) {
|
|
if (ActualBaudRate != NULL) {
|
|
*ActualBaudRate = BaudRate;
|
|
}
|
|
if (Divisor != NULL) {
|
|
*Divisor = ComputedDivisor;
|
|
}
|
|
return TRUE;
|
|
}
|
|
if (ComputedBaudRate < BaudRate) {
|
|
if (ActualBaudRate != NULL) {
|
|
*ActualBaudRate = ComputedBaudRate;
|
|
}
|
|
if (Divisor != NULL) {
|
|
*Divisor = ComputedDivisor;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// ActualBaudRate is higher than requested baud rate and more than 4%
|
|
// higher than the requested value. Increment Divisor if it is less
|
|
// than MAX_UINT16 and computed baud rate with new divisor.
|
|
//
|
|
if (ComputedDivisor == MAX_UINT16) {
|
|
return FALSE;
|
|
}
|
|
ComputedDivisor++;
|
|
ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4);
|
|
if (ComputedBaudRate == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
DEBUG ((EFI_D_INFO, "ClockRate = %d\n", ClockRate));
|
|
DEBUG ((EFI_D_INFO, "Divisor = %ld\n", ComputedDivisor));
|
|
DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));
|
|
|
|
if (ActualBaudRate != NULL) {
|
|
*ActualBaudRate = ComputedBaudRate;
|
|
}
|
|
if (Divisor != NULL) {
|
|
*Divisor = ComputedDivisor;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Detect whether specific FIFO is full or not.
|
|
|
|
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
|
|
|
|
@return whether specific FIFO is full or not
|
|
**/
|
|
BOOLEAN
|
|
SerialFifoFull (
|
|
IN SERIAL_DEV_FIFO *Fifo
|
|
)
|
|
{
|
|
return (BOOLEAN) (((Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE) == Fifo->Head);
|
|
}
|
|
|
|
/**
|
|
Detect whether specific FIFO is empty or not.
|
|
|
|
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
|
|
|
|
@return whether specific FIFO is empty or not
|
|
**/
|
|
BOOLEAN
|
|
SerialFifoEmpty (
|
|
IN SERIAL_DEV_FIFO *Fifo
|
|
)
|
|
|
|
{
|
|
return (BOOLEAN) (Fifo->Head == Fifo->Tail);
|
|
}
|
|
|
|
/**
|
|
Add data to specific FIFO.
|
|
|
|
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
|
|
@param Data the data added to FIFO
|
|
|
|
@retval EFI_SUCCESS Add data to specific FIFO successfully
|
|
@retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full
|
|
**/
|
|
EFI_STATUS
|
|
SerialFifoAdd (
|
|
IN OUT SERIAL_DEV_FIFO *Fifo,
|
|
IN UINT8 Data
|
|
)
|
|
{
|
|
//
|
|
// if FIFO full can not add data
|
|
//
|
|
if (SerialFifoFull (Fifo)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// FIFO is not full can add data
|
|
//
|
|
Fifo->Data[Fifo->Tail] = Data;
|
|
Fifo->Tail = (Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Remove data from specific FIFO.
|
|
|
|
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
|
|
@param Data the data removed from FIFO
|
|
|
|
@retval EFI_SUCCESS Remove data from specific FIFO successfully
|
|
@retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SerialFifoRemove (
|
|
IN OUT SERIAL_DEV_FIFO *Fifo,
|
|
OUT UINT8 *Data
|
|
)
|
|
{
|
|
//
|
|
// if FIFO is empty, no data can remove
|
|
//
|
|
if (SerialFifoEmpty (Fifo)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// FIFO is not empty, can remove data
|
|
//
|
|
*Data = Fifo->Data[Fifo->Head];
|
|
Fifo->Head = (Fifo->Head + 1) % SERIAL_MAX_FIFO_SIZE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Reads and writes all available data.
|
|
|
|
@param SerialDevice The device to transmit.
|
|
|
|
@retval EFI_SUCCESS Data was read/written successfully.
|
|
@retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when
|
|
this happens, pending writes are not done.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SerialReceiveTransmit (
|
|
IN SERIAL_DEV *SerialDevice
|
|
)
|
|
|
|
{
|
|
SERIAL_PORT_LSR Lsr;
|
|
UINT8 Data;
|
|
BOOLEAN ReceiveFifoFull;
|
|
SERIAL_PORT_MSR Msr;
|
|
SERIAL_PORT_MCR Mcr;
|
|
UINTN TimeOut;
|
|
|
|
Data = 0;
|
|
|
|
//
|
|
// Begin the read or write
|
|
//
|
|
if (SerialDevice->SoftwareLoopbackEnable) {
|
|
do {
|
|
ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);
|
|
if (!SerialFifoEmpty (&SerialDevice->Transmit)) {
|
|
SerialFifoRemove (&SerialDevice->Transmit, &Data);
|
|
if (ReceiveFifoFull) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
SerialFifoAdd (&SerialDevice->Receive, Data);
|
|
}
|
|
} while (!SerialFifoEmpty (&SerialDevice->Transmit));
|
|
} else {
|
|
ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);
|
|
//
|
|
// For full handshake flow control, tell the peer to send data
|
|
// if receive buffer is available.
|
|
//
|
|
if (SerialDevice->HardwareFlowControl &&
|
|
!FeaturePcdGet(PcdSerialUseHalfHandshake)&&
|
|
!ReceiveFifoFull
|
|
) {
|
|
Mcr.Data = READ_MCR (SerialDevice);
|
|
Mcr.Bits.Rts = 1;
|
|
WRITE_MCR (SerialDevice, Mcr.Data);
|
|
}
|
|
do {
|
|
Lsr.Data = READ_LSR (SerialDevice);
|
|
|
|
//
|
|
// Flush incomming data to prevent a an overrun during a long write
|
|
//
|
|
if ((Lsr.Bits.Dr == 1) && !ReceiveFifoFull) {
|
|
ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);
|
|
if (!ReceiveFifoFull) {
|
|
if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Oe == 1 || Lsr.Bits.Pe == 1 || Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_ERROR_CODE,
|
|
EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
|
|
SerialDevice->DevicePath
|
|
);
|
|
if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Pe == 1|| Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {
|
|
Data = READ_RBR (SerialDevice);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Data = READ_RBR (SerialDevice);
|
|
|
|
SerialFifoAdd (&SerialDevice->Receive, Data);
|
|
|
|
//
|
|
// For full handshake flow control, if receive buffer full
|
|
// tell the peer to stop sending data.
|
|
//
|
|
if (SerialDevice->HardwareFlowControl &&
|
|
!FeaturePcdGet(PcdSerialUseHalfHandshake) &&
|
|
SerialFifoFull (&SerialDevice->Receive)
|
|
) {
|
|
Mcr.Data = READ_MCR (SerialDevice);
|
|
Mcr.Bits.Rts = 0;
|
|
WRITE_MCR (SerialDevice, Mcr.Data);
|
|
}
|
|
|
|
|
|
continue;
|
|
} else {
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_PROGRESS_CODE,
|
|
EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER | EFI_PERIPHERAL_SERIAL_PORT,
|
|
SerialDevice->DevicePath
|
|
);
|
|
}
|
|
}
|
|
//
|
|
// Do the write
|
|
//
|
|
if (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit)) {
|
|
//
|
|
// Make sure the transmit data will not be missed
|
|
//
|
|
if (SerialDevice->HardwareFlowControl) {
|
|
//
|
|
// For half handshake flow control assert RTS before sending.
|
|
//
|
|
if (FeaturePcdGet(PcdSerialUseHalfHandshake)) {
|
|
Mcr.Data = READ_MCR (SerialDevice);
|
|
Mcr.Bits.Rts= 0;
|
|
WRITE_MCR (SerialDevice, Mcr.Data);
|
|
}
|
|
//
|
|
// Wait for CTS
|
|
//
|
|
TimeOut = 0;
|
|
Msr.Data = READ_MSR (SerialDevice);
|
|
while ((Msr.Bits.Dcd == 1) && ((Msr.Bits.Cts == 0) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) {
|
|
gBS->Stall (TIMEOUT_STALL_INTERVAL);
|
|
TimeOut++;
|
|
if (TimeOut > 5) {
|
|
break;
|
|
}
|
|
|
|
Msr.Data = READ_MSR (SerialDevice);
|
|
}
|
|
|
|
if ((Msr.Bits.Dcd == 0) || ((Msr.Bits.Cts == 1) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) {
|
|
SerialFifoRemove (&SerialDevice->Transmit, &Data);
|
|
WRITE_THR (SerialDevice, Data);
|
|
}
|
|
|
|
//
|
|
// For half handshake flow control, tell DCE we are done.
|
|
//
|
|
if (FeaturePcdGet(PcdSerialUseHalfHandshake)) {
|
|
Mcr.Data = READ_MCR (SerialDevice);
|
|
Mcr.Bits.Rts = 1;
|
|
WRITE_MCR (SerialDevice, Mcr.Data);
|
|
}
|
|
} else {
|
|
SerialFifoRemove (&SerialDevice->Transmit, &Data);
|
|
WRITE_THR (SerialDevice, Data);
|
|
}
|
|
}
|
|
} while (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Flush the serial hardware transmit FIFO, holding register, and shift register.
|
|
|
|
@param SerialDevice The device to flush.
|
|
|
|
@retval EFI_SUCCESS The transmit FIFO is completely flushed.
|
|
@retval EFI_TIMEOUT A timeout occured waiting for the transmit FIFO to flush.
|
|
**/
|
|
EFI_STATUS
|
|
SerialFlushTransmitFifo (
|
|
SERIAL_DEV *SerialDevice
|
|
)
|
|
{
|
|
SERIAL_PORT_LSR Lsr;
|
|
UINTN Timeout;
|
|
UINTN Elapsed;
|
|
|
|
//
|
|
// If this is the first time the UART is being configured, then the current
|
|
// UART settings are not known, so compute a timeout to wait for the Tx FIFO
|
|
// assuming the worst case current settings.
|
|
//
|
|
// Timeout = (Max Bits per Char) * (Max Pending Chars) / (Slowest Baud Rate)
|
|
// Max Bits per Char = Start bit + 8 data bits + parity + 2 stop bits = 12
|
|
// Max Pending Chars = Largest Tx FIFO + hold + shift = 64 + 1 + 1 = 66
|
|
// Slowest Reasonable Baud Rate = 300 baud
|
|
// Timeout = 12 * 66 / 300 = 2.64 seconds = 2,640,000 uS
|
|
//
|
|
Timeout = 2640000;
|
|
|
|
//
|
|
// Use the largest of the computed timeout, the default timeout, and the
|
|
// currently set timeout.
|
|
//
|
|
Timeout = MAX (Timeout, SERIAL_PORT_DEFAULT_TIMEOUT);
|
|
Timeout = MAX (Timeout, SerialDevice->SerialMode.Timeout);
|
|
|
|
//
|
|
// Wait for the shortest time possible for the serial port to be ready making
|
|
// sure the transmit FIFO, holding register, and shift register are all
|
|
// empty. The actual wait time is expected to be very small because the
|
|
// number characters currently in the FIFO should be small when a
|
|
// configuration change is requested.
|
|
//
|
|
// NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
|
|
// in the rest of this function that may send additional characters to this
|
|
// UART device invalidating the flush operation.
|
|
//
|
|
Elapsed = 0;
|
|
Lsr.Data = READ_LSR (SerialDevice);
|
|
while (Lsr.Bits.Temt == 0 || Lsr.Bits.Thre == 0) {
|
|
if (Elapsed >= Timeout) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
gBS->Stall (TIMEOUT_STALL_INTERVAL);
|
|
Elapsed += TIMEOUT_STALL_INTERVAL;
|
|
Lsr.Data = READ_LSR (SerialDevice);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Interface Functions
|
|
//
|
|
/**
|
|
Reset serial device.
|
|
|
|
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
|
|
|
|
@retval EFI_SUCCESS Reset successfully
|
|
@retval EFI_DEVICE_ERROR Failed to reset
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SerialReset (
|
|
IN EFI_SERIAL_IO_PROTOCOL *This
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SERIAL_DEV *SerialDevice;
|
|
SERIAL_PORT_LCR Lcr;
|
|
SERIAL_PORT_IER Ier;
|
|
SERIAL_PORT_MCR Mcr;
|
|
SERIAL_PORT_FCR Fcr;
|
|
EFI_TPL Tpl;
|
|
UINT32 Control;
|
|
|
|
SerialDevice = SERIAL_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Report the status code reset the serial
|
|
//
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_PROGRESS_CODE,
|
|
EFI_P_PC_RESET | EFI_PERIPHERAL_SERIAL_PORT,
|
|
SerialDevice->DevicePath
|
|
);
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
//
|
|
// Wait for all data to be transmitted before changing the UART configuration.
|
|
//
|
|
// NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
|
|
// that may send additional characters to this UART device until the UART
|
|
// configuration change is complete.
|
|
//
|
|
SerialFlushTransmitFifo (SerialDevice);
|
|
|
|
//
|
|
// Make sure DLAB is 0.
|
|
//
|
|
Lcr.Data = READ_LCR (SerialDevice);
|
|
Lcr.Bits.DLab = 0;
|
|
WRITE_LCR (SerialDevice, Lcr.Data);
|
|
|
|
//
|
|
// Turn off all interrupts
|
|
//
|
|
Ier.Data = READ_IER (SerialDevice);
|
|
Ier.Bits.Ravie = 0;
|
|
Ier.Bits.Theie = 0;
|
|
Ier.Bits.Rie = 0;
|
|
Ier.Bits.Mie = 0;
|
|
WRITE_IER (SerialDevice, Ier.Data);
|
|
|
|
//
|
|
// Reset the FIFO
|
|
//
|
|
Fcr.Data = 0;
|
|
Fcr.Bits.TrFIFOE = 0;
|
|
WRITE_FCR (SerialDevice, Fcr.Data);
|
|
|
|
//
|
|
// Turn off loopback and disable device interrupt.
|
|
//
|
|
Mcr.Data = READ_MCR (SerialDevice);
|
|
Mcr.Bits.Out1 = 0;
|
|
Mcr.Bits.Out2 = 0;
|
|
Mcr.Bits.Lme = 0;
|
|
WRITE_MCR (SerialDevice, Mcr.Data);
|
|
|
|
//
|
|
// Clear the scratch pad register
|
|
//
|
|
WRITE_SCR (SerialDevice, 0);
|
|
|
|
//
|
|
// Enable FIFO
|
|
//
|
|
Fcr.Bits.TrFIFOE = 1;
|
|
if (SerialDevice->ReceiveFifoDepth > 16) {
|
|
Fcr.Bits.TrFIFO64 = 1;
|
|
}
|
|
Fcr.Bits.ResetRF = 1;
|
|
Fcr.Bits.ResetTF = 1;
|
|
WRITE_FCR (SerialDevice, Fcr.Data);
|
|
|
|
//
|
|
// Go set the current attributes
|
|
//
|
|
Status = This->SetAttributes (
|
|
This,
|
|
This->Mode->BaudRate,
|
|
This->Mode->ReceiveFifoDepth,
|
|
This->Mode->Timeout,
|
|
(EFI_PARITY_TYPE) This->Mode->Parity,
|
|
(UINT8) This->Mode->DataBits,
|
|
(EFI_STOP_BITS_TYPE) This->Mode->StopBits
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->RestoreTPL (Tpl);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
//
|
|
// Go set the current control bits
|
|
//
|
|
Control = 0;
|
|
if (SerialDevice->HardwareFlowControl) {
|
|
Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
|
|
}
|
|
if (SerialDevice->SoftwareLoopbackEnable) {
|
|
Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
|
|
}
|
|
Status = This->SetControl (
|
|
This,
|
|
Control
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->RestoreTPL (Tpl);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Reset the software FIFO
|
|
//
|
|
SerialDevice->Receive.Head = SerialDevice->Receive.Tail = 0;
|
|
SerialDevice->Transmit.Head = SerialDevice->Transmit.Tail = 0;
|
|
gBS->RestoreTPL (Tpl);
|
|
|
|
//
|
|
// Device reset is complete
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Set new attributes to a serial device.
|
|
|
|
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
|
|
@param BaudRate The baudrate of the serial device
|
|
@param ReceiveFifoDepth The depth of receive FIFO buffer
|
|
@param Timeout The request timeout for a single char
|
|
@param Parity The type of parity used in serial device
|
|
@param DataBits Number of databits used in serial device
|
|
@param StopBits Number of stopbits used in serial device
|
|
|
|
@retval EFI_SUCCESS The new attributes were set
|
|
@retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value
|
|
@retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6
|
|
@retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return)
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SerialSetAttributes (
|
|
IN EFI_SERIAL_IO_PROTOCOL *This,
|
|
IN UINT64 BaudRate,
|
|
IN UINT32 ReceiveFifoDepth,
|
|
IN UINT32 Timeout,
|
|
IN EFI_PARITY_TYPE Parity,
|
|
IN UINT8 DataBits,
|
|
IN EFI_STOP_BITS_TYPE StopBits
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SERIAL_DEV *SerialDevice;
|
|
UINT64 Divisor;
|
|
SERIAL_PORT_LCR Lcr;
|
|
UART_DEVICE_PATH *Uart;
|
|
EFI_TPL Tpl;
|
|
|
|
SerialDevice = SERIAL_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Check for default settings and fill in actual values.
|
|
//
|
|
if (BaudRate == 0) {
|
|
BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
|
|
}
|
|
|
|
if (ReceiveFifoDepth == 0) {
|
|
ReceiveFifoDepth = SerialDevice->ReceiveFifoDepth;
|
|
}
|
|
|
|
if (Timeout == 0) {
|
|
Timeout = SERIAL_PORT_DEFAULT_TIMEOUT;
|
|
}
|
|
|
|
if (Parity == DefaultParity) {
|
|
Parity = (EFI_PARITY_TYPE) PcdGet8 (PcdUartDefaultParity);
|
|
}
|
|
|
|
if (DataBits == 0) {
|
|
DataBits = PcdGet8 (PcdUartDefaultDataBits);
|
|
}
|
|
|
|
if (StopBits == DefaultStopBits) {
|
|
StopBits = (EFI_STOP_BITS_TYPE) PcdGet8 (PcdUartDefaultStopBits);
|
|
}
|
|
|
|
if (!VerifyUartParameters (SerialDevice->ClockRate, BaudRate, DataBits, Parity, StopBits, &Divisor, &BaudRate)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((ReceiveFifoDepth == 0) || (ReceiveFifoDepth > SerialDevice->ReceiveFifoDepth)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Timeout < SERIAL_PORT_MIN_TIMEOUT) || (Timeout > SERIAL_PORT_MAX_TIMEOUT)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
//
|
|
// Wait for all data to be transmitted before changing the UART configuration.
|
|
//
|
|
// NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
|
|
// that may send additional characters to this UART device until the UART
|
|
// configuration change is complete.
|
|
//
|
|
SerialFlushTransmitFifo (SerialDevice);
|
|
|
|
//
|
|
// Put serial port on Divisor Latch Mode
|
|
//
|
|
Lcr.Data = READ_LCR (SerialDevice);
|
|
Lcr.Bits.DLab = 1;
|
|
WRITE_LCR (SerialDevice, Lcr.Data);
|
|
|
|
//
|
|
// Write the divisor to the serial port
|
|
//
|
|
WRITE_DLL (SerialDevice, (UINT8) Divisor);
|
|
WRITE_DLM (SerialDevice, (UINT8) ((UINT16) Divisor >> 8));
|
|
|
|
//
|
|
// Put serial port back in normal mode and set remaining attributes.
|
|
//
|
|
Lcr.Bits.DLab = 0;
|
|
|
|
switch (Parity) {
|
|
case NoParity:
|
|
Lcr.Bits.ParEn = 0;
|
|
Lcr.Bits.EvenPar = 0;
|
|
Lcr.Bits.SticPar = 0;
|
|
break;
|
|
|
|
case EvenParity:
|
|
Lcr.Bits.ParEn = 1;
|
|
Lcr.Bits.EvenPar = 1;
|
|
Lcr.Bits.SticPar = 0;
|
|
break;
|
|
|
|
case OddParity:
|
|
Lcr.Bits.ParEn = 1;
|
|
Lcr.Bits.EvenPar = 0;
|
|
Lcr.Bits.SticPar = 0;
|
|
break;
|
|
|
|
case SpaceParity:
|
|
Lcr.Bits.ParEn = 1;
|
|
Lcr.Bits.EvenPar = 1;
|
|
Lcr.Bits.SticPar = 1;
|
|
break;
|
|
|
|
case MarkParity:
|
|
Lcr.Bits.ParEn = 1;
|
|
Lcr.Bits.EvenPar = 0;
|
|
Lcr.Bits.SticPar = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (StopBits) {
|
|
case OneStopBit:
|
|
Lcr.Bits.StopB = 0;
|
|
break;
|
|
|
|
case OneFiveStopBits:
|
|
case TwoStopBits:
|
|
Lcr.Bits.StopB = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
//
|
|
// DataBits
|
|
//
|
|
Lcr.Bits.SerialDB = (UINT8) ((DataBits - 5) & 0x03);
|
|
WRITE_LCR (SerialDevice, Lcr.Data);
|
|
|
|
//
|
|
// Set the Serial I/O mode
|
|
//
|
|
This->Mode->BaudRate = BaudRate;
|
|
This->Mode->ReceiveFifoDepth = ReceiveFifoDepth;
|
|
This->Mode->Timeout = Timeout;
|
|
This->Mode->Parity = Parity;
|
|
This->Mode->DataBits = DataBits;
|
|
This->Mode->StopBits = StopBits;
|
|
|
|
//
|
|
// See if Device Path Node has actually changed
|
|
//
|
|
if (SerialDevice->UartDevicePath.BaudRate == BaudRate &&
|
|
SerialDevice->UartDevicePath.DataBits == DataBits &&
|
|
SerialDevice->UartDevicePath.Parity == Parity &&
|
|
SerialDevice->UartDevicePath.StopBits == StopBits
|
|
) {
|
|
gBS->RestoreTPL (Tpl);
|
|
return EFI_SUCCESS;
|
|
}
|
|
//
|
|
// Update the device path
|
|
//
|
|
SerialDevice->UartDevicePath.BaudRate = BaudRate;
|
|
SerialDevice->UartDevicePath.DataBits = DataBits;
|
|
SerialDevice->UartDevicePath.Parity = (UINT8) Parity;
|
|
SerialDevice->UartDevicePath.StopBits = (UINT8) StopBits;
|
|
|
|
Status = EFI_SUCCESS;
|
|
if (SerialDevice->Handle != NULL) {
|
|
|
|
//
|
|
// Skip the optional Controller device path node
|
|
//
|
|
Uart = SkipControllerDevicePathNode (
|
|
(EFI_DEVICE_PATH_PROTOCOL *) (
|
|
(UINT8 *) SerialDevice->DevicePath + GetDevicePathSize (SerialDevice->ParentDevicePath) - END_DEVICE_PATH_LENGTH
|
|
),
|
|
NULL,
|
|
NULL
|
|
);
|
|
CopyMem (Uart, &SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH));
|
|
Status = gBS->ReinstallProtocolInterface (
|
|
SerialDevice->Handle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
SerialDevice->DevicePath,
|
|
SerialDevice->DevicePath
|
|
);
|
|
}
|
|
|
|
gBS->RestoreTPL (Tpl);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Set Control Bits.
|
|
|
|
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
|
|
@param Control Control bits that can be settable
|
|
|
|
@retval EFI_SUCCESS New Control bits were set successfully
|
|
@retval EFI_UNSUPPORTED The Control bits wanted to set are not supported
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SerialSetControl (
|
|
IN EFI_SERIAL_IO_PROTOCOL *This,
|
|
IN UINT32 Control
|
|
)
|
|
{
|
|
SERIAL_DEV *SerialDevice;
|
|
SERIAL_PORT_MCR Mcr;
|
|
EFI_TPL Tpl;
|
|
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// The control bits that can be set are :
|
|
// EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO
|
|
// EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO
|
|
// EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW
|
|
// EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW
|
|
// EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW
|
|
//
|
|
SerialDevice = SERIAL_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// first determine the parameter is invalid
|
|
//
|
|
if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |
|
|
EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
|
|
EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
//
|
|
// Wait for all data to be transmitted before changing the UART configuration.
|
|
//
|
|
// NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls
|
|
// that may send additional characters to this UART device until the UART
|
|
// configuration change is complete.
|
|
//
|
|
SerialFlushTransmitFifo (SerialDevice);
|
|
|
|
Mcr.Data = READ_MCR (SerialDevice);
|
|
Mcr.Bits.DtrC = 0;
|
|
Mcr.Bits.Rts = 0;
|
|
Mcr.Bits.Lme = 0;
|
|
SerialDevice->SoftwareLoopbackEnable = FALSE;
|
|
SerialDevice->HardwareFlowControl = FALSE;
|
|
|
|
if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
|
|
Mcr.Bits.DtrC = 1;
|
|
}
|
|
|
|
if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
|
|
Mcr.Bits.Rts = 1;
|
|
}
|
|
|
|
if ((Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) {
|
|
Mcr.Bits.Lme = 1;
|
|
}
|
|
|
|
if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {
|
|
SerialDevice->HardwareFlowControl = TRUE;
|
|
}
|
|
|
|
WRITE_MCR (SerialDevice, Mcr.Data);
|
|
|
|
if ((Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) {
|
|
SerialDevice->SoftwareLoopbackEnable = TRUE;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
if (SerialDevice->Handle != NULL) {
|
|
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) (
|
|
(UINTN) SerialDevice->DevicePath
|
|
+ GetDevicePathSize (SerialDevice->ParentDevicePath)
|
|
- END_DEVICE_PATH_LENGTH
|
|
+ sizeof (UART_DEVICE_PATH)
|
|
);
|
|
if (IsUartFlowControlDevicePathNode (FlowControl) &&
|
|
((BOOLEAN) (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) != SerialDevice->HardwareFlowControl)) {
|
|
//
|
|
// Flow Control setting is changed, need to reinstall device path protocol
|
|
//
|
|
WriteUnaligned32 (&FlowControl->FlowControlMap, SerialDevice->HardwareFlowControl ? UART_FLOW_CONTROL_HARDWARE : 0);
|
|
Status = gBS->ReinstallProtocolInterface (
|
|
SerialDevice->Handle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
SerialDevice->DevicePath,
|
|
SerialDevice->DevicePath
|
|
);
|
|
}
|
|
}
|
|
|
|
gBS->RestoreTPL (Tpl);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get ControlBits.
|
|
|
|
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
|
|
@param Control Control signals of the serial device
|
|
|
|
@retval EFI_SUCCESS Get Control signals successfully
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SerialGetControl (
|
|
IN EFI_SERIAL_IO_PROTOCOL *This,
|
|
OUT UINT32 *Control
|
|
)
|
|
{
|
|
SERIAL_DEV *SerialDevice;
|
|
SERIAL_PORT_MSR Msr;
|
|
SERIAL_PORT_MCR Mcr;
|
|
EFI_TPL Tpl;
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
SerialDevice = SERIAL_DEV_FROM_THIS (This);
|
|
|
|
*Control = 0;
|
|
|
|
//
|
|
// Read the Modem Status Register
|
|
//
|
|
Msr.Data = READ_MSR (SerialDevice);
|
|
|
|
if (Msr.Bits.Cts == 1) {
|
|
*Control |= EFI_SERIAL_CLEAR_TO_SEND;
|
|
}
|
|
|
|
if (Msr.Bits.Dsr == 1) {
|
|
*Control |= EFI_SERIAL_DATA_SET_READY;
|
|
}
|
|
|
|
if (Msr.Bits.Ri == 1) {
|
|
*Control |= EFI_SERIAL_RING_INDICATE;
|
|
}
|
|
|
|
if (Msr.Bits.Dcd == 1) {
|
|
*Control |= EFI_SERIAL_CARRIER_DETECT;
|
|
}
|
|
//
|
|
// Read the Modem Control Register
|
|
//
|
|
Mcr.Data = READ_MCR (SerialDevice);
|
|
|
|
if (Mcr.Bits.DtrC == 1) {
|
|
*Control |= EFI_SERIAL_DATA_TERMINAL_READY;
|
|
}
|
|
|
|
if (Mcr.Bits.Rts == 1) {
|
|
*Control |= EFI_SERIAL_REQUEST_TO_SEND;
|
|
}
|
|
|
|
if (Mcr.Bits.Lme == 1) {
|
|
*Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;
|
|
}
|
|
|
|
if (SerialDevice->HardwareFlowControl) {
|
|
*Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
|
|
}
|
|
//
|
|
// Update FIFO status
|
|
//
|
|
SerialReceiveTransmit (SerialDevice);
|
|
|
|
//
|
|
// See if the Transmit FIFO is empty
|
|
//
|
|
if (SerialFifoEmpty (&SerialDevice->Transmit)) {
|
|
*Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
|
|
}
|
|
|
|
//
|
|
// See if the Receive FIFO is empty.
|
|
//
|
|
if (SerialFifoEmpty (&SerialDevice->Receive)) {
|
|
*Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
|
|
}
|
|
|
|
if (SerialDevice->SoftwareLoopbackEnable) {
|
|
*Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
|
|
}
|
|
|
|
gBS->RestoreTPL (Tpl);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Write the specified number of bytes to serial device.
|
|
|
|
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
|
|
@param BufferSize On input the size of Buffer, on output the amount of
|
|
data actually written
|
|
@param Buffer The buffer of data to write
|
|
|
|
@retval EFI_SUCCESS The data were written successfully
|
|
@retval EFI_DEVICE_ERROR The device reported an error
|
|
@retval EFI_TIMEOUT The write operation was stopped due to timeout
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SerialWrite (
|
|
IN EFI_SERIAL_IO_PROTOCOL *This,
|
|
IN OUT UINTN *BufferSize,
|
|
IN VOID *Buffer
|
|
)
|
|
{
|
|
SERIAL_DEV *SerialDevice;
|
|
UINT8 *CharBuffer;
|
|
UINT32 Index;
|
|
UINTN Elapsed;
|
|
UINTN ActualWrite;
|
|
EFI_TPL Tpl;
|
|
UINTN Timeout;
|
|
UINTN BitsPerCharacter;
|
|
|
|
SerialDevice = SERIAL_DEV_FROM_THIS (This);
|
|
Elapsed = 0;
|
|
ActualWrite = 0;
|
|
|
|
if (*BufferSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (Buffer == NULL) {
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_ERROR_CODE,
|
|
EFI_P_EC_OUTPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
|
|
SerialDevice->DevicePath
|
|
);
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
CharBuffer = (UINT8 *) Buffer;
|
|
|
|
//
|
|
// Compute the number of bits in a single character. This is a start bit,
|
|
// followed by the number of data bits, followed by the number of stop bits.
|
|
// The number of stop bits is specified by an enumeration that includes
|
|
// support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits.
|
|
//
|
|
BitsPerCharacter =
|
|
1 +
|
|
This->Mode->DataBits +
|
|
((This->Mode->StopBits == TwoStopBits) ? 2 : This->Mode->StopBits);
|
|
|
|
//
|
|
// Compute the timeout in microseconds to wait for a single byte to be
|
|
// transmitted. The Mode structure contans a Timeout field that is the
|
|
// maximum time to transmit or receive a character. However, many UARTs
|
|
// have a FIFO for transmits, so the time required to add one new character
|
|
// to the transmit FIFO may be the time required to flush a full FIFO. If
|
|
// the Timeout in the Mode structure is smaller than the time required to
|
|
// flush a full FIFO at the current baud rate, then use a timeout value that
|
|
// is required to flush a full transmit FIFO.
|
|
//
|
|
Timeout = MAX (
|
|
This->Mode->Timeout,
|
|
(UINTN)DivU64x64Remainder (
|
|
BitsPerCharacter * (SerialDevice->TransmitFifoDepth + 1) * 1000000,
|
|
This->Mode->BaudRate,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
for (Index = 0; Index < *BufferSize; Index++) {
|
|
SerialFifoAdd (&SerialDevice->Transmit, CharBuffer[Index]);
|
|
|
|
while (SerialReceiveTransmit (SerialDevice) != EFI_SUCCESS || !SerialFifoEmpty (&SerialDevice->Transmit)) {
|
|
//
|
|
// Unsuccessful write so check if timeout has expired, if not,
|
|
// stall for a bit, increment time elapsed, and try again
|
|
//
|
|
if (Elapsed >= Timeout) {
|
|
*BufferSize = ActualWrite;
|
|
gBS->RestoreTPL (Tpl);
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
gBS->Stall (TIMEOUT_STALL_INTERVAL);
|
|
|
|
Elapsed += TIMEOUT_STALL_INTERVAL;
|
|
}
|
|
|
|
ActualWrite++;
|
|
//
|
|
// Successful write so reset timeout
|
|
//
|
|
Elapsed = 0;
|
|
}
|
|
|
|
gBS->RestoreTPL (Tpl);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Read the specified number of bytes from serial device.
|
|
|
|
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
|
|
@param BufferSize On input the size of Buffer, on output the amount of
|
|
data returned in buffer
|
|
@param Buffer The buffer to return the data into
|
|
|
|
@retval EFI_SUCCESS The data were read successfully
|
|
@retval EFI_DEVICE_ERROR The device reported an error
|
|
@retval EFI_TIMEOUT The read operation was stopped due to timeout
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SerialRead (
|
|
IN EFI_SERIAL_IO_PROTOCOL *This,
|
|
IN OUT UINTN *BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
SERIAL_DEV *SerialDevice;
|
|
UINT32 Index;
|
|
UINT8 *CharBuffer;
|
|
UINTN Elapsed;
|
|
EFI_STATUS Status;
|
|
EFI_TPL Tpl;
|
|
|
|
SerialDevice = SERIAL_DEV_FROM_THIS (This);
|
|
Elapsed = 0;
|
|
|
|
if (*BufferSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (Buffer == NULL) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
Status = SerialReceiveTransmit (SerialDevice);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
*BufferSize = 0;
|
|
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_ERROR_CODE,
|
|
EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
|
|
SerialDevice->DevicePath
|
|
);
|
|
|
|
gBS->RestoreTPL (Tpl);
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
CharBuffer = (UINT8 *) Buffer;
|
|
for (Index = 0; Index < *BufferSize; Index++) {
|
|
while (SerialFifoRemove (&SerialDevice->Receive, &(CharBuffer[Index])) != EFI_SUCCESS) {
|
|
//
|
|
// Unsuccessful read so check if timeout has expired, if not,
|
|
// stall for a bit, increment time elapsed, and try again
|
|
// Need this time out to get conspliter to work.
|
|
//
|
|
if (Elapsed >= This->Mode->Timeout) {
|
|
*BufferSize = Index;
|
|
gBS->RestoreTPL (Tpl);
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
gBS->Stall (TIMEOUT_STALL_INTERVAL);
|
|
Elapsed += TIMEOUT_STALL_INTERVAL;
|
|
|
|
Status = SerialReceiveTransmit (SerialDevice);
|
|
if (Status == EFI_DEVICE_ERROR) {
|
|
*BufferSize = Index;
|
|
gBS->RestoreTPL (Tpl);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
//
|
|
// Successful read so reset timeout
|
|
//
|
|
Elapsed = 0;
|
|
}
|
|
|
|
SerialReceiveTransmit (SerialDevice);
|
|
|
|
gBS->RestoreTPL (Tpl);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Use scratchpad register to test if this serial port is present.
|
|
|
|
@param SerialDevice Pointer to serial device structure
|
|
|
|
@return if this serial port is present
|
|
**/
|
|
BOOLEAN
|
|
SerialPresent (
|
|
IN SERIAL_DEV *SerialDevice
|
|
)
|
|
|
|
{
|
|
UINT8 Temp;
|
|
BOOLEAN Status;
|
|
|
|
Status = TRUE;
|
|
|
|
//
|
|
// Save SCR reg
|
|
//
|
|
Temp = READ_SCR (SerialDevice);
|
|
WRITE_SCR (SerialDevice, 0xAA);
|
|
|
|
if (READ_SCR (SerialDevice) != 0xAA) {
|
|
Status = FALSE;
|
|
}
|
|
|
|
WRITE_SCR (SerialDevice, 0x55);
|
|
|
|
if (READ_SCR (SerialDevice) != 0x55) {
|
|
Status = FALSE;
|
|
}
|
|
//
|
|
// Restore SCR
|
|
//
|
|
WRITE_SCR (SerialDevice, Temp);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Read serial port.
|
|
|
|
@param SerialDev Pointer to serial device
|
|
@param Offset Offset in register group
|
|
|
|
@return Data read from serial port
|
|
|
|
**/
|
|
UINT8
|
|
SerialReadRegister (
|
|
IN SERIAL_DEV *SerialDev,
|
|
IN UINT32 Offset
|
|
)
|
|
{
|
|
UINT8 Data;
|
|
EFI_STATUS Status;
|
|
|
|
if (SerialDev->PciDeviceInfo == NULL) {
|
|
return IoRead8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride);
|
|
} else {
|
|
if (SerialDev->MmioAccess) {
|
|
Status = SerialDev->PciDeviceInfo->PciIo->Mem.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
|
|
SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
|
|
} else {
|
|
Status = SerialDev->PciDeviceInfo->PciIo->Io.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
|
|
SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
|
|
}
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Write serial port.
|
|
|
|
@param SerialDev Pointer to serial device
|
|
@param Offset Offset in register group
|
|
@param Data data which is to be written to some serial port register
|
|
**/
|
|
VOID
|
|
SerialWriteRegister (
|
|
IN SERIAL_DEV *SerialDev,
|
|
IN UINT32 Offset,
|
|
IN UINT8 Data
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
if (SerialDev->PciDeviceInfo == NULL) {
|
|
IoWrite8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, Data);
|
|
} else {
|
|
if (SerialDev->MmioAccess) {
|
|
Status = SerialDev->PciDeviceInfo->PciIo->Mem.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
|
|
SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
|
|
} else {
|
|
Status = SerialDev->PciDeviceInfo->PciIo->Io.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
|
|
SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
|
|
}
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
}
|