-
-
Notifications
You must be signed in to change notification settings - Fork 606
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The irq bits need to be cleared after reading the fifo register, so the fifo register needs to be read from interrupt context. Therefore, I introduced the variables _input_ready and _uart_fifo to store the RX character during the interrupt handler to be retrieved by the service thread later. The driver will be initialized from device tree in a follow-up patch. I modeled the structure of the driver roughly after the OSv pl011 driver. Signed-off-by: Stewart Hildebrand <[email protected]> Message-Id: <[email protected]>
- Loading branch information
Showing
3 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/* | ||
* Copyright (C) 2021 DornerWorks, Ltd | ||
* Author: Stewart Hildebrand | ||
* | ||
* This work is open source software, licensed under the terms of the | ||
* BSD license as described in the LICENSE file in the top-level directory. | ||
*/ | ||
|
||
#include "cadence-uart.hh" | ||
#include <stdint.h> | ||
|
||
namespace console { | ||
|
||
// Register reference: | ||
// https://www.xilinx.com/html_docs/registers/ug1087/mod___uart.html | ||
|
||
#define BIT(x) (1U << (x)) | ||
|
||
#define UART_CR_RXRES BIT(0) // RX reset | ||
#define UART_CR_TXRES BIT(1) // TX reset | ||
#define UART_CR_RXEN BIT(2) // RX enable | ||
#define UART_CR_TXEN BIT(4) // TX enable | ||
|
||
#define UART_MR_PARITY_NONE BIT(5) // No parity | ||
|
||
#define UART_SR_RTRIG BIT(0) // RX trigger | ||
#define UART_SR_REMPTY BIT(1) // RX empty | ||
#define UART_SR_TEMPTY BIT(3) // TX empty | ||
#define UART_SR_TFUL BIT(4) // TX full | ||
#define UART_SR_TACTIVE BIT(11) // TX active | ||
|
||
typedef struct __attribute__ ((aligned (4))) { | ||
volatile uint32_t cr; // 0x00 Control register | ||
volatile uint32_t mr; // 0x04 Mode register | ||
volatile uint32_t ier; // 0x08 Interrupt enable register | ||
volatile uint32_t idr; // 0x0C Interrupt disable register | ||
volatile uint32_t imr; // 0x10 Interrupt mask register | ||
volatile uint32_t cisr; // 0x14 Channel interrupt status register | ||
uint32_t pad1[2]; // 0x18 - 0x1C | ||
volatile uint32_t rtrig; // 0x20 RX trigger threshold | ||
uint32_t pad2[2]; // 0x24 - 0x28 | ||
volatile uint32_t sr; // 0x2C Status register | ||
volatile uint32_t fifo; // 0x30 RX/TX FIFO register | ||
} cadence_t; | ||
|
||
static_assert(sizeof(cadence_t) == 0x34, "Wrong size for cadence_t"); | ||
|
||
// Default base addr | ||
cadence_t *uart = (cadence_t *)0xff000000; | ||
|
||
bool Cadence_Console::active = false; | ||
|
||
void Cadence_Console::set_base_addr(u64 addr) | ||
{ | ||
// Intentional bitwise AND inside condition | ||
if (addr & 0x3U) { | ||
abort("UART base address is not 32-bit aligned"); | ||
} | ||
uart = (cadence_t *)addr; | ||
} | ||
|
||
void Cadence_Console::set_irqid(int irqid) { | ||
this->irqid = irqid; | ||
} | ||
|
||
u64 Cadence_Console::get_base_addr() { | ||
return (u64)uart; | ||
} | ||
|
||
void Cadence_Console::flush() | ||
{ | ||
uint32_t sr; | ||
do { | ||
sr = uart->sr; | ||
asm volatile("nop"); | ||
// Intentional bitwise AND inside condition | ||
} while (!(sr & UART_SR_TEMPTY) || (sr & UART_SR_TACTIVE)); | ||
} | ||
|
||
bool Cadence_Console::input_ready() { | ||
return _input_ready; | ||
} | ||
|
||
char Cadence_Console::readch() | ||
{ | ||
_input_ready = false; | ||
return _uart_fifo; | ||
} | ||
|
||
bool Cadence_Console::ack_irq() | ||
{ | ||
// Intentional bitwise AND inside condition | ||
if (uart->cisr & uart->imr) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
void Cadence_Console::irq_handler() | ||
{ | ||
uint32_t cisr = uart->cisr & uart->imr; | ||
|
||
// Intentional bitwise AND inside condition | ||
if ((cisr & UART_SR_RTRIG) && !(uart->sr & UART_SR_REMPTY)) { | ||
_uart_fifo = uart->fifo; | ||
_input_ready = true; | ||
} | ||
|
||
// IRQ must be cleared after character is read from FIFO | ||
uart->cisr = cisr; | ||
|
||
_thread->wake(); | ||
} | ||
|
||
void Cadence_Console::dev_start() { | ||
flush(); | ||
// Reset and enable the RX and TX paths | ||
uart->cr = UART_CR_RXRES | UART_CR_TXRES | UART_CR_RXEN | UART_CR_TXEN; | ||
|
||
uart->mr = UART_MR_PARITY_NONE; | ||
|
||
_irq.reset(new spi_interrupt(gic::irq_type::IRQ_TYPE_LEVEL, this->irqid, | ||
[this] { return this->ack_irq(); }, | ||
[this] { this->irq_handler(); })); | ||
|
||
uart->rtrig = 1U; | ||
uart->cisr = ~0U; | ||
uart->idr = ~0U; | ||
|
||
uart->ier = UART_SR_RTRIG; | ||
} | ||
|
||
void Cadence_Console::write(const char *str, size_t len) { | ||
while (len > 0) { | ||
// Intentional bitwise AND inside condition | ||
while (uart->sr & UART_SR_TFUL) { | ||
// spin | ||
asm volatile("nop"); | ||
} | ||
uart->fifo = *str++; | ||
len--; | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Copyright (C) 2021 DornerWorks, Ltd | ||
* Author: Stewart Hildebrand | ||
* | ||
* This work is open source software, licensed under the terms of the | ||
* BSD license as described in the LICENSE file in the top-level directory. | ||
*/ | ||
|
||
#ifndef CADENCE_UART_HH | ||
#define CADENCE_UART_HH | ||
|
||
#include "console-driver.hh" | ||
#include "exceptions.hh" | ||
#include <osv/interrupt.hh> | ||
#include <osv/types.h> | ||
|
||
namespace console { | ||
|
||
class Cadence_Console : public console_driver { | ||
public: | ||
virtual void write(const char *str, size_t len); | ||
virtual void flush(); | ||
virtual bool input_ready(); | ||
virtual char readch(); | ||
|
||
void set_base_addr(u64 addr); | ||
u64 get_base_addr(); | ||
void set_irqid(int irqid); | ||
|
||
static bool active; | ||
private: | ||
virtual void dev_start(); | ||
virtual const char *thread_name() { return "cadence-input"; } | ||
bool ack_irq(); | ||
void irq_handler(); | ||
// Default UART irq = SPI 21 = 32 + 21 | ||
unsigned int irqid = 53; | ||
std::unique_ptr<spi_interrupt> _irq; | ||
char _uart_fifo = 0; | ||
bool _input_ready = false; | ||
}; | ||
|
||
} | ||
|
||
#endif /* CADENCE_UART_HH */ |