thermosphere: pl011 driver rewrite

This commit is contained in:
TuxSH 2020-03-06 01:42:29 +00:00
parent 2986967f2a
commit a8f28ab96d
2 changed files with 280 additions and 0 deletions

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_drivers_arm_pl011.hpp"
namespace ams::hvisor::drivers::arm {
void PL011::Initialize(u32 baudRate, u32 clkRate) const
{
/* The TRM (DDI0183) reads:
Program the control registers as follows:
1. Disable the UART.
2. Wait for the end of transmission or reception of the current character.
3. Flush the transmit FIFO by disabling bit 4 (FEN) in the line control register
(UARTCLR_H).
4. Reprogram the control register.
5. Enable the UART.
*/
// First, disable the UART. Flush the receive FIFO, wait for tx to complete, and disable both FIFOs.
m_regs->cr &= ~UARTCR_UARTEN;
while (!(m_regs->fr & UARTFR_RXFE)) {
m_regs->dr;
}
while (m_regs->fr & UARTFR_BUSY);
// This flushes the transmit FIFO:
m_regs->lcr_h &= ~UARTLCR_H_FEN;
// Set baudrate, Divisor = (Uart clock * 4) / baudrate; stored in IBRD|FBRD
u32 divisor = (4 * clkRate) / baudRate;
m_regs->ibrd = divisor >> 6;
m_regs->fbrd = divisor & 0x3F;
// Select FIFO fill levels for interrupts
m_regs->ifls = IFLS_RX4_8 | IFLS_TX4_8;
// FIFO Enabled / No Parity / 8 Data bit / One Stop Bit
m_regs->lcr_h = UARTLCR_H_FEN | UARTLCR_H_WLEN_8;
// Select the interrupts we want to have
// RX timeout and TX/RX fill interrupts
m_regs->imsc = RTI | RXI | RXI;
// Clear any pending errors
m_regs->ecr = 0;
// Clear all interrupts
m_regs->icr = ALL_INTERRUPTS;
// Enable tx, rx, and uart overall
m_regs->cr = UARTCR_RXE | UARTCR_TXE | UARTCR_UARTEN;
}
void PL011::WriteData(const void *buffer, size_t size) const
{
const u8 *buf8 = reinterpret_cast<const u8 *>(buffer);
for (size_t i = 0; i < size; i++) {
while (m_regs->fr & UARTFR_TXFF); // while TX FIFO full
m_regs->dr = buf8[i];
}
}
void PL011::ReadData(void *buffer, size_t size) const
{
u8 *buf8 = reinterpret_cast<u8 *>(buffer);
size_t i;
for (i = 0; i < size; i++) {
while (m_regs->fr & UARTFR_RXFE);
buf8[i] = m_regs->dr;
}
}
size_t PL011::ReadDataMax(void *buffer, size_t maxSize) const
{
u8 *buf8 = reinterpret_cast<u8 *>(buffer);
size_t count = 0;
for (size_t i = 0; i < maxSize && !(m_regs->fr & UARTFR_RXFE); i++) {
buf8[i] = m_regs->dr;
++count;
}
return count;
}
size_t PL011::ReadDataUntil(char *buffer, size_t maxSize, char delimiter) const
{
size_t count = 0;
for (size_t i = 0; i < maxSize; i++) {
while (m_regs->fr & UARTFR_RXFE);
buffer[i] = m_regs->dr;
++count;
if (buffer[i] == delimiter) {
break;
}
}
return count;
}
void PL011::SetRxInterruptEnabled(bool enabled) const
{
constexpr u32 mask = RTI | RXI;
if (enabled) {
m_regs->imsc |= mask;
} else {
m_regs->imsc &= ~mask;
}
}
}

View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../../defines.hpp"
// AMBA PL011 driver
// Originally from
/*
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
namespace ams::hvisor::drivers::arm {
class PL011 final {
private:
struct Registers {
u32 dr;
union {
u32 sr;
u32 ecr;
};
u32 _0x08, _0x0C, _0x10, _0x14;
u32 fr;
u32 _0x1C;
u32 ilpr;
u32 ibrd;
u32 fbrd;
u32 lcr_h;
u32 cr;
u32 ifls;
u32 imsc;
u32 ris;
u32 mis;
u32 icr;
u32 dmacr;
};
enum Mask : u32 {
DATA_ERROR_MASK = 0x0F00, // Data status bits
PL011_STATUS_ERROR_MASK = 0x0F, // Status reg bits
};
enum Error : u32 {
OE = BIT(3), // Overrun error
BE = BIT(2), // Break error
PE = BIT(1), // Parity error
FE = BIT(0), // Framing error
};
enum Interrupt : u32 {
OEI = BIT(10), // Overrun error interrupt
BEI = BIT(9), // Break error interrupt
PEI = BIT(8), // Parity error interrupt
FEI = BIT(7), // Framing error interrupt
RTI = BIT(6), // Receive timeout interrupt
TXI = BIT(5), // Transmit interrupt
RXI = BIT(4), // Receive interrupt
DSRMI = BIT(3), // DSR modem interrupt
DCDMI = BIT(2), // DCD modem interrupt
CTSMI = BIT(1), // CTS modem interrupt
RIMI = BIT(0), // RI modem interrupt
ALL_INTERRUPTS = MASK(11),
};
// Flag reg bits
enum FrFlags : u32 {
UARTFR_RI = BIT(8), // Ring indicator
UARTFR_TXFE = BIT(7), // Transmit FIFO empty
UARTFR_RXFF = BIT(6), // Receive FIFO full
UARTFR_TXFF = BIT(5), // Transmit FIFO full
UARTFR_RXFE = BIT(4), // Receive FIFO empty
UARTFR_BUSY = BIT(3), // UART busy
UARTFR_DCD = BIT(2), // Data carrier detect
UARTFR_DSR = BIT(1), // Data set ready
UARTFR_CTS = BIT(0), // Clear to send
};
// Control reg bits
enum CrFlags : u32 {
UARTCR_CTSEN = BIT(15), // CTS hardware flow control enable
UARTCR_RTSEN = BIT(14), // RTS hardware flow control enable
UARTCR_RTS = BIT(11), // Request to send
UARTCR_DTR = BIT(10), // Data transmit ready.
UARTCR_RXE = BIT(9), // Receive enable
UARTCR_TXE = BIT(8), // Transmit enable
UARTCR_LBE = BIT(7), // Loopback enable
UARTCR_UARTEN = BIT(0), // UART Enable
};
// Line Control Register Bits
enum LcrFlags : u32 {
UARTLCR_H_SPS = BIT(7), // Stick parity select
UARTLCR_H_WLEN_8 = (3 << 5),
UARTLCR_H_WLEN_7 = (2 << 5),
UARTLCR_H_WLEN_6 = BIT(5),
UARTLCR_H_WLEN_5 = (0 << 5),
UARTLCR_H_FEN = BIT(4), // FIFOs Enable
UARTLCR_H_STP2 = BIT(3), // Two stop bits select
UARTLCR_H_EPS = BIT(2), // Even parity select
UARTLCR_H_PEN = BIT(1), // Parity Enable
UARTLCR_H_BRK = BIT(0), // Send break
};
// FIFO level select register
enum IflsLevels : u32 {
IFLS_RX1_8 = (0 << 3),
IFLS_RX2_8 = (1 << 3),
IFLS_RX4_8 = (2 << 3),
IFLS_RX6_8 = (3 << 3),
IFLS_RX7_8 = (4 << 3),
IFLS_TX1_8 = (0 << 0),
IFLS_TX2_8 = (1 << 0),
IFLS_TX4_8 = (2 << 0),
IFLS_TX6_8 = (3 << 0),
IFLS_TX7_8 = (4 << 0),
};
private:
volatile Registers *m_regs = nullptr;
void Initialize(u32 baudRate, u32 clkRate = 1) const;
// TODO friend
public:
void WriteData(const void *buffer, size_t size) const;
void ReadData(void *buffer, size_t size) const;
size_t ReadDataMax(void *buffer, size_t maxSize) const;
size_t ReadDataUntil(char *buffer, size_t maxSize, char delimiter) const;
void SetRxInterruptEnabled(bool enabled) const;
};
}