📄 pl011api.c
字号:
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Release Status:OS005-SW-70002-r0p0-00REL0
// $Copyright:
// ----------------------------------------------------------------
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
// (C) COPYRIGHT 2004 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
// by a licensing agreement from ARM Limited.
// ----------------------------------------------------------------
// File: PL011api.c,v
// Revision: 1.2
// ----------------------------------------------------------------
// $
//
#include <windows.h> // Main include file for Windows development
#include "PL011.h" // PL011 UART register and bit definitions
#include "PL011api.h" // PL011 API
// Types
// PL011 register
//
typedef ULONG volatile REG32; // PL011 register type (volatile, 32 bit registers)
// PL011 context
//
typedef struct tagPL011_CONTEXT
{
// Pointers to each register
//
REG32 *pDR; // Data transmit/receive [r:12,w:8]
REG32 const *pRSR; // Receive status [r:4]
REG32 *pECR; // Error clear [w:4]
REG32 const *pFR; // Flags [r:9]
REG32 *pILPR; // IrDA low-power counter [rw:8]
REG32 *pIBRD; // Integer baud rate divisor [rw:16]
REG32 *pFBRD; // Fractional baud rate divisor [rw:6]
REG32 *pLCR_H; // Line control (high byte) [rw:8]
REG32 *pCR; // Control [rw:16]
REG32 *pIFLS; // Interrupt FIFO level select [rw:6]
REG32 *pIMSC; // Interrupt mask set/clear [rw:11]
REG32 const *pRIS; // Raw interrupt status [r:11]
REG32 const *pMIS; // Masked interrupt status [r:11]
REG32 *pICR; // Interrupt clear [w:11]
REG32 *pDMACR; // DMA control [rw:3]
// Shadow registers (for power on/off)
//
ULONG ILPR; // IrDA low-power counter
ULONG IBRD; // Integer baud rate divisor
ULONG FBRD; // Fractional baud rate divisor
ULONG LCR_H; // Line control
ULONG CR; // Control
ULONG IFLS; // Interrupt FIFO level select
ULONG IMSC; // Interrupt mask set/clear
ULONG DMACR; // DMA control
// Critical sections for serialising access to:
CRITICAL_SECTION csXmit; // data register (for transmission)
CRITICAL_SECTION csRegs; // general registers (for non-atomic usage)
CRITICAL_SECTION csFlow; // control register (for free flow control)
// NOTE: To prevent deadlock, critical sections should be entered in the order 'csXmit, csRegs,
// csFlow' and left in the reverse order 'csFlow, csRegs, csXmit'.
} PL011_CONTEXT;
// Inline I/O helpers
//
static __inline ULONG ReadReg32(REG32 const *pReg32)
{
return *pReg32;
}
static __inline VOID WriteReg32(REG32 *pReg32, ULONG ulValue)
{
*pReg32 = ulValue;
}
// Inline helpers
//
static __inline BOOL PL011IsBusy(PL011_CONTEXT *pPL011)
{
return ((ReadReg32(pPL011->pFR) & PL011_FR_BUSY) != 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PL011 device API
//
PL011_CONTEXT* PL011Create(ULONG ulBaseAddress)
{
PL011_CONTEXT *pPL011;
// Must have a valid base address pointer for the UART, with write access to its register space
//
if ((ulBaseAddress == 0) || (IsBadWritePtr((VOID*)ulBaseAddress, 0x1000) != 0))
return NULL;
// Allocate space for the PL011 context (no need to use LPTR or LMEM_ZEROINIT to initialise the
// memory to zero because we will be explicitly initialising each member of the context next).
//
pPL011 = (PL011_CONTEXT*)LocalAlloc(LMEM_FIXED , sizeof(PL011_CONTEXT));
if (pPL011 == NULL)
return NULL;
// Pointers to each UART register
//
pPL011->pDR = (REG32*)(ulBaseAddress + PL011_DR); // Data transmit/receive
pPL011->pRSR = (REG32*)(ulBaseAddress + PL011_RSR); // Receive status (r)
pPL011->pECR = (REG32*)(ulBaseAddress + PL011_ECR); // Error clear (w)
pPL011->pFR = (REG32*)(ulBaseAddress + PL011_FR); // Flags (r)
pPL011->pILPR = (REG32*)(ulBaseAddress + PL011_ILPR); // IrDA low-power counter
pPL011->pIBRD = (REG32*)(ulBaseAddress + PL011_IBRD); // Integer baud rate divisor
pPL011->pFBRD = (REG32*)(ulBaseAddress + PL011_FBRD); // Fractional baud rate divisor
pPL011->pLCR_H = (REG32*)(ulBaseAddress + PL011_LCR_H); // Line control (high byte)
pPL011->pCR = (REG32*)(ulBaseAddress + PL011_CR); // Control
pPL011->pIFLS = (REG32*)(ulBaseAddress + PL011_IFLS); // Interrupt FIFO level select
pPL011->pIMSC = (REG32*)(ulBaseAddress + PL011_IMSC); // Interrupt mask set/clear
pPL011->pRIS = (REG32*)(ulBaseAddress + PL011_RIS); // Raw interrupt status (r)
pPL011->pMIS = (REG32*)(ulBaseAddress + PL011_MIS); // Masked interrupt status (r)
pPL011->pICR = (REG32*)(ulBaseAddress + PL011_ICR); // Interrupt clear (w)
pPL011->pDMACR = (REG32*)(ulBaseAddress + PL011_DMACR); // DMA control (rw)
// Shadow UART registers (for power on/off)
//
pPL011->IMSC = 0; // Interrupt mask set/clear
pPL011->ILPR = 0; // IrDA low-power counter
pPL011->IBRD = 0; // Integer baud rate divisor
pPL011->FBRD = 0; // Fractional baud rate divisor
pPL011->LCR_H = 0; // Line control
pPL011->CR = 0; // Control
pPL011->IFLS = 0; // Interrupt FIFO level select
pPL011->IMSC = 0; // Interrupt mask set/clear
pPL011->DMACR = 0; // DMA control
// Critical sections for serialising access to:
InitializeCriticalSection(&pPL011->csXmit); // data register (for transmission)
InitializeCriticalSection(&pPL011->csRegs); // general registers (for non-atomic usage)
InitializeCriticalSection(&pPL011->csFlow); // control register (for responsive flow control)
// The PL011 only has one type of transmit interrupt, which is generated as the FIFO trigger
// level is crossed. The serial device driver MDD implementation requires a physical interrupt
// to signal the end of transmission; this would never occur if insufficient data was written
// from an application (less than the FIFO trigger level). All this means that we need to be
// a bit creative and use the transmit interrupt mask to generate an interrupt when we need
// one, which means that we must ensure the actual transmit interrupt flag is always set. The
// following block of code should set the transmit interrupt flag and enable us to cause a
// physical interrupt on demand by unmasking that interrupt. We acheive all this transparently
// and very quickly by disabling the FIFO and sending just one character with all bits set, no
// parity, only 5 data bits and at the fastest posssible baud rate using IR mode to avoid
// activity on the RS232 transmit line. The only udesirable artefact of this scheme is a
// harmles, sub microsecond pulse of IR, if equiped, during cold-start.
//
WriteReg32(pPL011->pIMSC, 0); // Disable all interrupts
WriteReg32(pPL011->pIBRD, PL011_IBRD_MIN); // Fastest possible baud rate (write LCR_H next)
WriteReg32(pPL011->pLCR_H, 0); // 5 data bits, no parity, FIFO disabled
WriteReg32(pPL011->pCR, PL011_CR_TXE | // Enable transmitter
PL011_CR_SIREN | // IR mode (to avoid activity on RS232 line)
PL011_CR_UARTEN); // Enable transmitter, enable UART
WriteReg32(pPL011->pDR, 0xFF); // Transmit a character (all bits set)
// Wait up to 10ms for the transmitter to empty, then check that the TX interrupt flag is set
//
VERIFY(PL011WaitTxEmpty(pPL011, 10));
DEBUGCHK((ReadReg32(pPL011->pRIS) & PL011_RIS_TXRIS) != 0);
// Now the TX interrupt flag is set, de-activate the UART by restoring default settings
//
WriteReg32(pPL011->pIBRD, 0); // Reset integer baud rate divisor
WriteReg32(pPL011->pCR, 0); // Disable UART
return pPL011;
}
PL011_CONTEXT* PL011Delete(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return NULL;
DeleteCriticalSection(&pPL011->csFlow);
DeleteCriticalSection(&pPL011->csRegs);
DeleteCriticalSection(&pPL011->csXmit);
#ifdef DEBUG
memset(pPL011, 0, sizeof(PL011_CONTEXT));
#endif
return LocalFree(pPL011);
}
BOOL PL011WaitTxEmpty(PL011_CONTEXT *pPL011, ULONG ulmsTimeout)
{
ULONG ulmsStart;
if (pPL011 == NULL)
return FALSE;
ulmsStart = GetTickCount();
while (((GetTickCount() - ulmsStart) < ulmsTimeout) && PL011IsBusy(pPL011))
{
Sleep(1);
}
return !PL011IsBusy(pPL011);
}
BOOL PL011ClearDTR(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csFlow);
try
{
WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) & ~PL011_CR_DTR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
LeaveCriticalSection(&pPL011->csFlow);
return FALSE;
}
LeaveCriticalSection(&pPL011->csFlow);
return TRUE;
}
BOOL PL011SetDTR(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csFlow);
try
{
WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) | PL011_CR_DTR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csFlow);
return FALSE;
}
LeaveCriticalSection(&pPL011->csFlow);
return TRUE;
}
BOOL PL011ClearRTS(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csFlow);
try
{
WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) & ~PL011_CR_RTS);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csFlow);
return FALSE;
}
LeaveCriticalSection(&pPL011->csFlow);
return TRUE;
}
BOOL PL011SetRTS(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csFlow);
try
{
WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) | PL011_CR_RTS);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csFlow);
return FALSE;
}
LeaveCriticalSection(&pPL011->csFlow);
return TRUE;
}
BOOL PL011ClearBreak(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csRegs);
try
{
WriteReg32(pPL011->pLCR_H, ReadReg32(pPL011->pLCR_H) & ~PL011_LCR_H_BRK);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csRegs);
return FALSE;
}
LeaveCriticalSection(&pPL011->csRegs);
return TRUE;
}
BOOL PL011SetBreak(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csRegs);
try
{
WriteReg32(pPL011->pLCR_H, ReadReg32(pPL011->pLCR_H) | PL011_LCR_H_BRK);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csRegs);
return FALSE;
}
LeaveCriticalSection(&pPL011->csRegs);
return TRUE;
}
BOOL PL011ClearPendingInterrupts(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csRegs);
try
{
WriteReg32(pPL011->pICR, PL011_ICR_ALLINTERRUPTS & ~PL011_ICR_TXIC);
WriteReg32(pPL011->pECR, 0x1); // Need to clear any error status separately, can be any value
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csRegs);
return FALSE;
}
LeaveCriticalSection(&pPL011->csRegs);
return TRUE;
}
BOOL PL011DisableAllInterrupts(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csRegs);
try
{
WriteReg32(pPL011->pIMSC, 0);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csRegs);
return FALSE;
}
LeaveCriticalSection(&pPL011->csRegs);
return TRUE;
}
BOOL PL011EnableAllInterrupts(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csRegs);
try
{
WriteReg32(pPL011->pIMSC, PL011_IMSC_ALLINTERRUPTS & ~PL011_IMSC_TXIM);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csRegs);
return FALSE;
}
LeaveCriticalSection(&pPL011->csRegs);
return TRUE;
}
BOOL PL011DisableTxInterrupts(PL011_CONTEXT *pPL011)
{
if (pPL011 == NULL)
return FALSE;
EnterCriticalSection(&pPL011->csRegs);
try
{
WriteReg32(pPL011->pIMSC, ReadReg32(pPL011->pIMSC) & ~PL011_IMSC_TXIM);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Just get out of here.
LeaveCriticalSection(&pPL011->csRegs);
return FALSE;
}
LeaveCriticalSection(&pPL011->csRegs);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -