mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
thermosphere: pl011 driver rewrite
This commit is contained in:
parent
2986967f2a
commit
a8f28ab96d
2 changed files with 280 additions and 0 deletions
131
thermosphere/src/drivers/arm/hvisor_drivers_arm_pl011.cpp
Normal file
131
thermosphere/src/drivers/arm/hvisor_drivers_arm_pl011.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
149
thermosphere/src/drivers/arm/hvisor_drivers_arm_pl011.hpp
Normal file
149
thermosphere/src/drivers/arm/hvisor_drivers_arm_pl011.hpp
Normal 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;
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue