567 lines
15 KiB
C
567 lines
15 KiB
C
/** @file
|
|
PCI Serial driver for standard UARTS on an PCI bus.
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
|
|
#include <PciUartDxe.h>
|
|
|
|
#define PCI_DEVICE_NAME_SIZE 50
|
|
#define PCI_DEVICE_NAME L"PCI_UART"
|
|
#define PCI_UART_REGISTER_BYTE_WIDTH sizeof(UINT8)
|
|
#define PCI_UART_FIFO_DEPTH 16
|
|
#define PCI_UART_NON_FIFO_DEPTH 1
|
|
#define PCI_UART_SERIAL_CLOCK_FREQUENCY (1 * 115200 * 16)
|
|
#define PCI_UART_SIMPLE_RATE 16
|
|
|
|
#define PCI_CLASS_SCC 0x07
|
|
#define PCI_SUBCLASS_SERIAL 0x00
|
|
#define PCI_IF_16550 0x02
|
|
|
|
#define PCI_16550_DEV_SIGNATURE SIGNATURE_32('P','C','I','U')
|
|
#define PCI_16550_DEV_FROM_THIS(a) CR (a, PCI_UART_DEVICE_DATA, U16550Access, PCI_16550_DEV_SIGNATURE)
|
|
|
|
PCI_DEVICE_PFA mPciNonFifoList[] = {0x0, 0x0, 0x0};
|
|
|
|
//
|
|
// data type definitions
|
|
//
|
|
typedef struct {
|
|
UINT32 Signature;
|
|
EFI_PCI_IO_PROTOCOL *PCIIo;
|
|
UART_16550_DEVICE_INFO DeviceInfo;
|
|
H2O_UART_16550_ACCESS_PROTOCOL U16550Access;
|
|
} PCI_UART_DEVICE_DATA;
|
|
|
|
EFI_DRIVER_BINDING_PROTOCOL gPciUartControllerDriver = {
|
|
Pci16550ControllerDriverSupported,
|
|
Pci16550ControllerDriverStart,
|
|
Pci16550ControllerDriverStop,
|
|
0x10,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
extern EFI_COMPONENT_NAME_PROTOCOL gPciUartComponentName;
|
|
extern EFI_COMPONENT_NAME2_PROTOCOL gPciUartComponentName2;
|
|
|
|
|
|
/**
|
|
|
|
Providing PciIo read for serial register I/O macros
|
|
|
|
@param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance.
|
|
@param[in] BarIndex The BAR index of the standard PCI Configuration header to use as the
|
|
base address for the memory or I/O operation to perform.
|
|
@param[in] Offset The offset within the selected BAR to start the memory or I/O operation.
|
|
|
|
@retval VOID
|
|
|
|
**/
|
|
STATIC
|
|
UINT8
|
|
PciSerialReadPort (
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
|
IN UINT16 BarIndex,
|
|
IN UINT16 Offset
|
|
)
|
|
{
|
|
UINT8 Data;
|
|
|
|
//
|
|
// Use PciIo to access IO
|
|
//
|
|
PciIo->Io.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
(UINT8) BarIndex,
|
|
(UINT16) Offset,
|
|
(UINTN) 1,
|
|
&Data
|
|
);
|
|
return Data;
|
|
}
|
|
|
|
/**
|
|
|
|
Providing PciIo write for serial register I/O macros
|
|
|
|
@param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance.
|
|
@param[in] BarIndex The BAR index of the standard PCI Configuration header to use as the
|
|
base address for the memory or I/O operation to perform.
|
|
@param[in] Offset The offset within the selected BAR to start the memory or I/O operation.
|
|
@param[in] Buffer For read operations, the destination buffer to store the results. For write
|
|
operations, the source buffer to write data from.
|
|
|
|
@retval VOID
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
PciSerialWritePort (
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
|
IN UINT16 BarIndex,
|
|
IN UINT16 Offset,
|
|
IN UINT8 Data
|
|
)
|
|
{
|
|
//
|
|
// Use PciIo to access IO
|
|
//
|
|
PciIo->Io.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
(UINT8) BarIndex,
|
|
(UINT16) Offset,
|
|
(UINTN) 1,
|
|
&Data
|
|
);
|
|
}
|
|
|
|
/**
|
|
The module Entry Point for module PciSerial.
|
|
|
|
@param[in] ImageHandle The firmware allocated handle for the EFI image.
|
|
@param[in] SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The entry point is executed successfully.
|
|
@retval other Some error occurs when executing this entry point.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PciUartEntryPoint(
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Install driver model protocol(s).
|
|
//
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gPciUartControllerDriver,
|
|
ImageHandle,
|
|
&gPciUartComponentName,
|
|
&gPciUartComponentName2
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Test controller is a PciSerial Controller.
|
|
|
|
@param This Pointer of EFI_DRIVER_BINDING_PROTOCOL
|
|
@param Controller driver's controller
|
|
@param RemainingDevicePath children device path
|
|
|
|
@retval EFI_UNSUPPORTED This driver does not support this device
|
|
@retval EFI_SUCCESS This driver supports this device
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Pci16550ControllerDriverSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
union {
|
|
UINT8 ByteBuf[4];
|
|
UINT16 WordBuf[2];
|
|
} Buffer;
|
|
UINT16 Temp;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **)&PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Use the PCI I/O Protocol to see if Controller is standard ISA UART that
|
|
// can be managed by this driver.
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
|
|
//
|
|
// Looks for a PCI CLASS / SUBCLASS / INTERFACE of 0x07 / 0x00 / 0x02
|
|
// To allow supportting all PCI Devices that are 16550 compatible UARTS.
|
|
//
|
|
// Also if want general PCI Serial com devices to work as well
|
|
// can duplicate this driver one for Tekoa and AMT and the other
|
|
// for general serial devices.
|
|
//
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
(UINT32) PCI_REVISION_ID_OFFSET,
|
|
(UINTN) 1,
|
|
(VOID *) &Buffer
|
|
);
|
|
//
|
|
// Check Pci Serial Device Class code
|
|
//
|
|
if ((Buffer.ByteBuf[3] != PCI_CLASS_SCC) ||
|
|
(Buffer.ByteBuf[2] != PCI_SUBCLASS_SERIAL) ||
|
|
(Buffer.ByteBuf[1] != PCI_IF_16550)) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Make sure the PCI io space is enabled
|
|
//
|
|
Temp = 0x0003;
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0x04,
|
|
0x01,
|
|
&Temp
|
|
);
|
|
|
|
Exit:
|
|
//
|
|
// Close the I/O Abstraction(s) used to perform the supported test
|
|
//
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
UINT32
|
|
GetUartDeviceSupportFIFOSize (
|
|
UINTN Bus,
|
|
UINTN Dev,
|
|
UINTN Func
|
|
)
|
|
{
|
|
UINTN Count;
|
|
UINTN Index;
|
|
|
|
Count = sizeof (mPciNonFifoList) / sizeof (PCI_DEVICE_PFA);
|
|
|
|
for (Index = 0; Index < Count; Index++) {
|
|
if ( Bus == mPciNonFifoList[Index].Bus &&
|
|
Dev == mPciNonFifoList[Index].Dev &&
|
|
Func == mPciNonFifoList[Index].Func ) {
|
|
return PCI_UART_NON_FIFO_DEPTH;
|
|
}
|
|
}
|
|
return PCI_UART_FIFO_DEPTH;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InitPci16550DeviceData (
|
|
PCI_UART_DEVICE_DATA *Pci16550Device,
|
|
EFI_PCI_IO_PROTOCOL *PciIo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UART_16550_DEVICE_INFO *DevInfo;
|
|
UINTN Segment;
|
|
UINTN Bus;
|
|
UINTN Device;
|
|
UINTN Function;
|
|
UINTN NameSize;
|
|
UINTN Index;
|
|
UINT16 UID;
|
|
UINT16 BarIndex;
|
|
CHAR16 *Name;
|
|
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Resources;
|
|
|
|
BarIndex = 0;
|
|
Status = EFI_UNSUPPORTED;
|
|
|
|
for (Index = 0; Index < PCI_MAX_BAR; Index++) {
|
|
Status = PciIo->GetBarAttributes (
|
|
PciIo,
|
|
(UINT8) Index,
|
|
NULL,
|
|
(VOID**)&Resources
|
|
);
|
|
|
|
if (Resources->ResType == ACPI_ADDRESS_SPACE_TYPE_IO) {
|
|
BarIndex = (UINT16) Index;
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Convert PCI location to UID
|
|
//
|
|
Status = PciIo->GetLocation (
|
|
PciIo,
|
|
&Segment,
|
|
&Bus,
|
|
&Device,
|
|
&Function
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
UID = (UINT16)(Bus<<8 | Device<<3 | Function);
|
|
|
|
//
|
|
// Set Device Name
|
|
//
|
|
NameSize = PCI_DEVICE_NAME_SIZE;
|
|
Name = AllocateZeroPool (NameSize);
|
|
|
|
UnicodeSPrint (
|
|
Name,
|
|
NameSize,
|
|
L"%s (0x%x:0x%x:0x%x)",
|
|
PCI_DEVICE_NAME,
|
|
Bus,
|
|
Device,
|
|
Function
|
|
);
|
|
|
|
//
|
|
// Initialize PCI_HS_UART_DEVICE_DATA structure
|
|
//
|
|
Pci16550Device->Signature = PCI_16550_DEV_SIGNATURE;
|
|
Pci16550Device->PCIIo = PciIo;
|
|
|
|
DevInfo = &Pci16550Device->DeviceInfo;
|
|
DevInfo->DeviceName = Name;
|
|
DevInfo->DeviceType = PCI_SERIAL_DEVICE;
|
|
DevInfo->UID = UID;
|
|
DevInfo->RegisterByteWidth = PCI_UART_REGISTER_BYTE_WIDTH;
|
|
DevInfo->BaseAddressType = UBAT_BAR_INDEX;
|
|
DevInfo->BaseAddress = (UINTN)BarIndex;
|
|
DevInfo->FifoSize = GetUartDeviceSupportFIFOSize (Bus, Device, Function);
|
|
DevInfo->SerialClockFreq = PCI_UART_SERIAL_CLOCK_FREQUENCY;
|
|
DevInfo->SampleRate = PCI_UART_SIMPLE_RATE;
|
|
DevInfo->LegacySupport = TRUE;
|
|
|
|
Pci16550Device->U16550Access.RegRead = Pci16550DeviceRegRead;
|
|
Pci16550Device->U16550Access.RegWrite = Pci16550DeviceRegWrite;
|
|
Pci16550Device->U16550Access.DeviceInfo = DevInfo;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Start this driver on ControllerHandle by opening a PciIo protocol, creating
|
|
SERIAL_DEV device and install gEfiSerialIoProtocolGuid
|
|
finally.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param ControllerHandle Handle of device to bind driver to
|
|
@param RemainingDevicePath Optional parameter use to pick a specific child
|
|
device to start.
|
|
|
|
@retval EFI_SUCCESS This driver is added to ControllerHandle
|
|
@retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
|
|
@retval other This driver does not support this device
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Pci16550ControllerDriverStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
PCI_UART_DEVICE_DATA *Pci16550Device;
|
|
|
|
Pci16550Device = NULL;
|
|
|
|
//
|
|
// Grab the IO abstraction we need to get any work done
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **)&PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Initialize the serial device instance
|
|
//
|
|
Pci16550Device = AllocateZeroPool (sizeof (PCI_UART_DEVICE_DATA));
|
|
if (Pci16550Device == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Initialize the device Private data
|
|
//
|
|
Status = InitPci16550DeviceData (Pci16550Device, PciIo);
|
|
if (EFI_ERROR(Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Install Uart16550Access protocol on the contorller handle
|
|
//
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Controller,
|
|
&gH2OCrUart16550AccessProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&Pci16550Device->U16550Access
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
Error:
|
|
|
|
if (Pci16550Device != NULL) {
|
|
FreePool (Pci16550Device);
|
|
}
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Stop this driver on ControllerHandle. Support stoping any child handles
|
|
created by this driver.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param ControllerHandle Handle of device to stop driver on
|
|
@param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
|
|
children is zero stop the entire bus driver.
|
|
@param ChildHandleBuffer List of Child Handles to Stop.
|
|
|
|
@retval EFI_SUCCESS This driver is removed ControllerHandle
|
|
@retval other This driver was not removed from this device
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Pci16550ControllerDriverStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *Protocol;
|
|
PCI_UART_DEVICE_DATA *Pci16550Device;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gH2OCrUart16550AccessProtocolGuid,
|
|
(VOID**)&Protocol,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Pci16550Device = PCI_16550_DEV_FROM_THIS (This);
|
|
|
|
Status = gBS->UninstallProtocolInterface (
|
|
Controller,
|
|
&gH2OCrUart16550AccessProtocolGuid,
|
|
&Pci16550Device->U16550Access
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
gBS->FreePool (Pci16550Device);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
Pci16550DeviceRegRead (
|
|
H2O_UART_16550_ACCESS_PROTOCOL *This,
|
|
UINT16 Index,
|
|
UINT8 *Data
|
|
)
|
|
{
|
|
PCI_UART_DEVICE_DATA *Pci16550Device;
|
|
|
|
Pci16550Device = PCI_16550_DEV_FROM_THIS (This);
|
|
|
|
*Data = PciSerialReadPort (Pci16550Device->PCIIo, (UINT16)Pci16550Device->DeviceInfo.BaseAddress, (UINT16)Index);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
Pci16550DeviceRegWrite (
|
|
H2O_UART_16550_ACCESS_PROTOCOL *This,
|
|
UINT16 Index,
|
|
UINT8 Data
|
|
)
|
|
{
|
|
PCI_UART_DEVICE_DATA *Pci16550Device;
|
|
|
|
Pci16550Device = PCI_16550_DEV_FROM_THIS (This);
|
|
|
|
PciSerialWritePort (Pci16550Device->PCIIo, (UINT16)Pci16550Device->DeviceInfo.BaseAddress, (UINT16)Index, Data);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|