mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-18 16:32:05 +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