123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 |
- /*++
- Copyright (c) 2015 Minoca Corp. All Rights Reserved
- Module Name:
- ns16550.c
- Abstract:
- This module implements the firmware serial port interface on a 16550
- standard UART.
- Author:
- Chris Stevens 10-Jul-2015
- Environment:
- Firmware
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "uefifw.h"
- #include "dev/ns16550.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // Macros to read from and write to 16550 registers.
- //
- #define NS16550_READ8(_Device, _Register) \
- ((PNS16550_READ8)(_Device)->Read8)((_Device), (_Register))
- #define NS16550_WRITE8(_Device, _Register, _Value) \
- ((PNS16550_WRITE8)(_Device)->Write8)((_Device), (_Register), (_Value))
- //
- // This macro returns the offset of a given register from its base.
- //
- #define NS16550_REGISTER_OFFSET(_Device, _Register) \
- ((_Device)->RegisterOffset + ((_Register) << (_Device)->RegisterShift))
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the bits for the PC UART Line Status register.
- //
- #define NS16550_LINE_STATUS_DATA_READY 0x01
- #define NS16550_LINE_STATUS_TRANSMIT_EMPTY 0x20
- #define NS16550_LINE_STATUS_ERRORS 0x8E
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef enum _NS16550_REGISTER {
- Ns16550Data = 0,
- Ns16550DivisorLow = 0,
- Ns16550InterruptEnable = 1,
- Ns16550DivisorHigh = 1,
- Ns16550InterruptStatus = 2,
- Ns16550FifoControl = 2,
- Ns16550LineControl = 3,
- Ns16550ModemControl = 4,
- Ns16550LineStatus = 5,
- Ns16550ModemStatus = 6,
- Ns16550Scratch = 7
- } NS16550_REGISTER, *PNS16550_REGISTER;
- typedef
- UINT8
- (*PNS16550_READ8) (
- PNS16550_CONTEXT Context,
- NS16550_REGISTER Register
- );
- /*++
- Routine Description:
- This routine reads a 16550 register.
- Arguments:
- Context - Supplies a pointer to the device context.
- Register - Supplies the register to read.
- Return Value:
- Returns the value at the register.
- --*/
- typedef
- VOID
- (*PNS16550_WRITE8) (
- PNS16550_CONTEXT Context,
- NS16550_REGISTER Register,
- UINT8 Value
- );
- /*++
- Routine Description:
- This routine writes to a 16550 register.
- Arguments:
- Context - Supplies a pointer to the device context.
- Register - Supplies the register to write to.
- Value - Supplies the value to write.
- Return Value:
- None.
- --*/
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- UINT8
- EfipNs16550ReadIo8 (
- PNS16550_CONTEXT Device,
- NS16550_REGISTER Register
- );
- VOID
- EfipNs16550WriteIo8 (
- PNS16550_CONTEXT Device,
- NS16550_REGISTER Register,
- UINT8 Value
- );
- UINT8
- EfipNs16550ReadMemory8 (
- PNS16550_CONTEXT Device,
- NS16550_REGISTER Register
- );
- VOID
- EfipNs16550WriteMemory8 (
- PNS16550_CONTEXT Device,
- NS16550_REGISTER Register,
- UINT8 Value
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- //
- // --------------------------------------------------------- Internal Functions
- //
- EFI_STATUS
- EfipNs16550ComputeDivisor (
- UINT32 BaseBaud,
- UINT32 BaudRate,
- UINT16 *Divisor
- )
- /*++
- Routine Description:
- This routine computes the divisor rates for a NS 16550 UART at a given baud
- rate.
- Arguments:
- BaseBaud - Supplies the baud rate for a divisor of 1.
- BaudRate - Supplies the desired baud rate.
- Divisor - Supplies a pointer where the divisor will be returned on success.
- Return Value:
- EFI_SUCCESS on success.
- EFI_UNSUPPORTED if the given baud rate cannot be achieved.
- --*/
- {
- UINT32 CurrentBaud;
- UINT32 LocalDivisor;
- //
- // Compute the baud rate divisor.
- //
- if (BaudRate > BaseBaud) {
- return EFI_UNSUPPORTED;
- }
- LocalDivisor = 1;
- while (TRUE) {
- CurrentBaud = BaseBaud / LocalDivisor;
- if ((CurrentBaud <= BaudRate) || (CurrentBaud == 0)) {
- break;
- }
- LocalDivisor += 1;
- }
- if ((CurrentBaud == 0) || (LocalDivisor > MAX_UINT16)) {
- return EFI_UNSUPPORTED;
- }
- *Divisor = (UINT16)LocalDivisor;
- return EFI_SUCCESS;
- }
- EFI_STATUS
- EfipNs16550Initialize (
- PNS16550_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine initializes the NS 16550 serial port hardware. The caller
- should have initialized at least some of the context structure.
- Arguments:
- Context - Supplies the pointer to the port's initialized context.
- Return Value:
- EFI Status code.
- --*/
- {
- UINT8 Value;
- //
- // Determine the correct register access function.
- //
- if (Context->MemoryBase != NULL) {
- Context->Read8 = EfipNs16550ReadMemory8;
- Context->Write8 = EfipNs16550WriteMemory8;
- } else {
- Context->Read8 = EfipNs16550ReadIo8;
- Context->Write8 = EfipNs16550WriteIo8;
- }
- //
- // Begin programming the 16550 controller. The topmost bit in the line
- // control register turns the DLAB (Data Latch Address Byte) on. This
- // changes the meanings of the registers, allowing us to program the baud
- // rate divisor values.
- //
- Value = NS16550_READ8(Context, Ns16550LineControl);
- Value |= 0x80;
- NS16550_WRITE8(Context, Ns16550LineControl, Value);
- //
- // Set the divisor bytes. This programs the baud rate generator.
- //
- NS16550_WRITE8(Context,
- Ns16550DivisorLow,
- (UINT8)(Context->BaudRateDivisor & 0x00FF));
- NS16550_WRITE8(Context,
- Ns16550DivisorHigh,
- (UINT8)((Context->BaudRateDivisor >> 8) & 0x00FF));
- //
- // Now program the FIFO queue configuration. It is assumed that the FIFOs
- // are operational, which is not true on certain machines with very old
- // UARTs. Setting bit 0 enables the FIFO. Setting bits 1 and 2 clears both
- // FIFOs. Clearing bit 3 disables DMA mode. The top 4 bits vary depending
- // on the version. Setting bit 5 enables the 64 byte FIFO, which is only
- // available on 16750s. Bit 4 is reserved. Otherwise bits 4 and 5 are
- // either reserved or dictate the transmit FIFO's empty trigger. Bits 6 and
- // 7 set the receive FIFO's trigger, where setting both bits means that
- // "2 less than full", which for the default 16 byte FIFO means 14 bytes
- // are in the buffer.
- //
- Value = 0xC7;
- if ((Context->Flags & NS16550_FLAG_TRANSMIT_TRIGGER_2_CHARACTERS) != 0) {
- Value |= 0x10;
- } else if ((Context->Flags & NS16550_FLAG_64_BYTE_FIFO) != 0) {
- Value |= 0x20;
- }
- NS16550_WRITE8(Context, Ns16550FifoControl, Value);
- //
- // Now program the Line Control register again. Setting bits 0 and 1 sets
- // 8 data bits. Clearing bit 2 sets one stop bit. Clearing bit 3 sets no
- // parity. Additionally, clearing bit 7 turns the DLAB latch off, changing
- // the meaning of the registers back and allowing other control registers to
- // be accessed.
- //
- NS16550_WRITE8(Context, Ns16550LineControl, 0x03);
- //
- // Setting the Modem Control register to zero disables all hardware flow
- // control.
- //
- NS16550_WRITE8(Context, Ns16550ModemControl, 0x00);
- return EFI_SUCCESS;
- }
- EFI_STATUS
- EfipNs16550Transmit (
- PNS16550_CONTEXT Context,
- VOID *Data,
- UINT32 Size
- )
- /*++
- Routine Description:
- This routine writes data out the serial port. This routine should busily
- spin if the previously sent byte has not finished transmitting.
- Arguments:
- Context - Supplies the pointer to the port context.
- Data - Supplies a pointer to the data to write.
- Size - Supplies the size to write, in bytes.
- Return Value:
- EFI_SUCCESS on success.
- EFI_DEVICE_ERROR if a device error occurred.
- --*/
- {
- UINT32 ByteIndex;
- UINT8 *Bytes;
- UINT8 StatusRegister;
- Bytes = Data;
- for (ByteIndex = 0; ByteIndex < Size; ByteIndex += 1) {
- //
- // Spin waiting for the buffer to become ready to send. If an error is
- // detected, bail out and report to the caller.
- //
- do {
- StatusRegister = NS16550_READ8(Context, Ns16550LineStatus);
- if ((StatusRegister & NS16550_LINE_STATUS_ERRORS) != 0) {
- return EFI_DEVICE_ERROR;
- }
- } while ((StatusRegister & NS16550_LINE_STATUS_TRANSMIT_EMPTY) == 0);
- //
- // Send the byte and return.
- //
- NS16550_WRITE8(Context, Ns16550Data, Bytes[ByteIndex]);
- }
- return EFI_SUCCESS;
- }
- EFI_STATUS
- EfipNs16550Receive (
- PNS16550_CONTEXT Context,
- VOID *Data,
- UINT32 *Size
- )
- /*++
- Routine Description:
- This routine reads bytes from the serial port.
- Arguments:
- Context - Supplies the pointer to the port context.
- Data - Supplies a pointer where the read data will be returned on success.
- Size - Supplies a pointer that on input contains the size of the receive
- buffer. On output, returns the number of bytes read.
- Return Value:
- EFI_SUCCESS on success.
- EFI_NOT_READY if there was no data to be read at the current time.
- EFI_DEVICE_ERROR if a device error occurred.
- --*/
- {
- UINT32 ByteCount;
- UINT32 ByteIndex;
- UINT8 *Bytes;
- EFI_STATUS Status;
- UINT8 StatusRegister;
- ByteCount = *Size;
- Bytes = Data;
- Status = EFI_NOT_READY;
- for (ByteIndex = 0; ByteIndex < ByteCount; ByteIndex += 1) {
- StatusRegister = NS16550_READ8(Context, Ns16550LineStatus);
- if ((StatusRegister & NS16550_LINE_STATUS_ERRORS) != 0) {
- Status = EFI_DEVICE_ERROR;
- break;
- }
- if ((StatusRegister & NS16550_LINE_STATUS_DATA_READY) == 0) {
- break;
- }
- Bytes[ByteIndex] = NS16550_READ8(Context, Ns16550Data);
- Status = EFI_SUCCESS;
- }
- *Size = ByteIndex;
- return Status;
- }
- EFI_STATUS
- EfipNs16550GetStatus (
- PNS16550_CONTEXT Context,
- BOOLEAN *ReceiveDataAvailable
- )
- /*++
- Routine Description:
- This routine returns the current device status.
- Arguments:
- Context - Supplies a pointer to the serial port context.
- ReceiveDataAvailable - Supplies a pointer where a boolean will be returned
- indicating whether or not receive data is available.
- Return Value:
- EFI_SUCCESS on success.
- EFI_DEVICE_ERROR if a device error occurred.
- --*/
- {
- UINT8 StatusRegister;
- *ReceiveDataAvailable = FALSE;
- StatusRegister = NS16550_READ8(Context, Ns16550LineStatus);
- if ((StatusRegister & NS16550_LINE_STATUS_DATA_READY) != 0) {
- *ReceiveDataAvailable = TRUE;
- }
- return EFI_SUCCESS;
- }
- UINT8
- EfipNs16550ReadIo8 (
- PNS16550_CONTEXT Context,
- NS16550_REGISTER Register
- )
- /*++
- Routine Description:
- This routine reads a 16550 register from an I/O port.
- Arguments:
- Context - Supplies a pointer to the device context.
- Register - Supplies the register to read.
- Return Value:
- Returns the value at the register.
- --*/
- {
- UINT16 Port;
- Port = Context->IoBase + NS16550_REGISTER_OFFSET(Context, Register);
- return EfiIoPortIn8(Port);
- }
- VOID
- EfipNs16550WriteIo8 (
- PNS16550_CONTEXT Context,
- NS16550_REGISTER Register,
- UINT8 Value
- )
- /*++
- Routine Description:
- This routine writes to an I/O port based 16550 register.
- Arguments:
- Context - Supplies a pointer to the device context.
- Register - Supplies the register to write to.
- Value - Supplies the value to write.
- Return Value:
- None.
- --*/
- {
- UINT16 Port;
- Port = Context->IoBase + NS16550_REGISTER_OFFSET(Context, Register);
- EfiIoPortOut8(Port, Value);
- return;
- }
- UINT8
- EfipNs16550ReadMemory8 (
- PNS16550_CONTEXT Context,
- NS16550_REGISTER Register
- )
- /*++
- Routine Description:
- This routine reads a 16550 register from a memory mapped register.
- Arguments:
- Context - Supplies a pointer to the device context.
- Register - Supplies the register to read.
- Return Value:
- Returns the value at the register.
- --*/
- {
- VOID *Address;
- UINT8 Value;
- Address = Context->MemoryBase + NS16550_REGISTER_OFFSET(Context, Register);
- switch (Context->RegisterShift) {
- case NS16550_1_BYTE_REGISTER_SHIFT:
- Value = EfiReadRegister8(Address);
- break;
- case NS16550_2_BYTE_REGISTER_SHIFT:
- Value = EfiReadRegister16(Address);
- break;
- case NS16550_4_BYTE_REGISTER_SHIFT:
- default:
- Value = EfiReadRegister32(Address);
- break;
- }
- return Value;
- }
- VOID
- EfipNs16550WriteMemory8 (
- PNS16550_CONTEXT Context,
- NS16550_REGISTER Register,
- UINT8 Value
- )
- /*++
- Routine Description:
- This routine writes to a memory mapped 16550 register.
- Arguments:
- Context - Supplies a pointer to the device context.
- Register - Supplies the register to write to.
- Value - Supplies the value to write.
- Return Value:
- None.
- --*/
- {
- VOID *Address;
- Address = Context->MemoryBase + NS16550_REGISTER_OFFSET(Context, Register);
- switch (Context->RegisterShift) {
- case NS16550_1_BYTE_REGISTER_SHIFT:
- EfiWriteRegister8(Address, Value);
- break;
- case NS16550_2_BYTE_REGISTER_SHIFT:
- EfiWriteRegister16(Address, (UINT16)Value);
- break;
- case NS16550_4_BYTE_REGISTER_SHIFT:
- default:
- EfiWriteRegister32(Address, (UINT32)Value);
- break;
- }
- return;
- }
|