l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
PKGDIR = ..
L4DIR ?= $(PKGDIR)/../..
TARGET := include src
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,6 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../..
PKGNAME = drivers
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2008-2021 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* Alexander Warg <alexander.warg@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_16550 : public Uart
{
protected:
enum Registers
{
TRB = 0x00, // Transmit/Receive Buffer (read/write)
BRD_LOW = 0x00, // Baud Rate Divisor LSB if bit 7 of LCR is set (read/write)
IER = 0x01, // Interrupt Enable Register (read/write)
BRD_HIGH = 0x01, // Baud Rate Divisor MSB if bit 7 of LCR is set (read/write)
IIR = 0x02, // Interrupt Identification Register (read only)
FCR = 0x02, // 16550 FIFO Control Register (write only)
LCR = 0x03, // Line Control Register (read/write)
MCR = 0x04, // Modem Control Register (read/write)
LSR = 0x05, // Line Status Register (read only)
MSR = 0x06, // Modem Status Register (read only)
SPR = 0x07, // Scratch Pad Register (read/write)
};
enum Register_value_iir
{
IIR_BUSY = 7,
};
enum Register_value_lsr
{
LSR_DR = 0x01, // Receiver data ready
LSR_THRE = 0x20, // Transmit hold register empty
LSR_TSRE = 0x40, // Transmitter empty
};
enum Init_values
{
#ifdef UART_16550_INIT_MCR
Init_mcr = UART_16550_INIT_MCR,
#else
Init_mcr = 0,
#endif
#ifdef UART_16550_INIT_IER
Init_ier = UART_16550_INIT_IER,
#else
Init_ier = 0,
#endif
#ifdef UART_16550_INIT_FCR
Init_fcr = UART_16550_INIT_FCR,
#else
Init_fcr = 0,
#endif
};
public:
enum
{
PAR_NONE = 0x00,
PAR_EVEN = 0x18,
PAR_ODD = 0x08,
DAT_5 = 0x00,
DAT_6 = 0x01,
DAT_7 = 0x02,
DAT_8 = 0x03,
STOP_1 = 0x00,
STOP_2 = 0x04,
MODE_8N1 = PAR_NONE | DAT_8 | STOP_1,
MODE_7E1 = PAR_EVEN | DAT_7 | STOP_1,
// these two values are to leave either mode
// or baud rate unchanged on a call to change_mode
MODE_NC = 0x1000000,
BAUD_NC = 0x1000000,
Base_rate_x86 = 115200,
Base_rate_pxa = 921600,
};
explicit Uart_16550(unsigned long base_rate, unsigned char init_flags = 0,
unsigned char ier_bits = Init_ier,
unsigned char mcr_bits = Init_mcr,
unsigned char fcr_bits = Init_fcr)
: _base_rate(base_rate), _init_flags(init_flags), _mcr_bits(mcr_bits),
_ier_bits(ier_bits), _fcr_bits(fcr_bits)
{}
bool startup(Io_register_block const *regs) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
unsigned long _base_rate;
unsigned char _init_flags;
unsigned char _mcr_bits;
unsigned char _ier_bits;
unsigned char _fcr_bits;
};
} // namespace L4

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2015 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_16550.h"
namespace L4 {
class Uart_16550_dw : public Uart_16550
{
public:
explicit Uart_16550_dw(unsigned long base_rate)
: Uart_16550(base_rate)
{}
void irq_ack() override;
};
} // namespace L4

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2017, 2019, 2023-2024 Kernkonzept GmbH.
* Author(s): Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
/**
* Driver for the Advanced Peripheral Bus (APB) UART from the Cortex-M System
* Design Kit (CMSDK).
*/
class Uart_apb : public Uart
{
public:
/** freq == 0 means unknown and don't change baud rate */
Uart_apb(unsigned freq) : _freq(freq) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
void set_rate(Baud_rate r);
unsigned _freq;
};
} // namespace L4

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2009-2012 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <stddef.h>
#include <l4/drivers/io_regblock.h>
#include "poll_timeout_counter.h"
namespace L4 {
/**
* Uart driver abstraction.
*/
class Uart
{
protected:
unsigned _mode;
unsigned _rate;
Io_register_block const *_regs;
public:
void *operator new (size_t, void* a)
{ return a; }
public:
typedef unsigned Transfer_mode;
typedef unsigned Baud_rate;
Uart()
: _mode(~0U), _rate(~0U)
{}
/**
* Start the UART driver.
*
* \param regs IO register block of the UART.
* \retval true Startup succeeded.
* \retval false Startup failed.
*/
virtual bool startup(Io_register_block const *regs) = 0;
virtual ~Uart() {}
/**
* Terminate the UART driver. This includes disabling of interrupts.
*/
virtual void shutdown() = 0;
/**
* Set certain parameters of the UART.
*
* \param m UART mode. Depends on the hardware.
* \param r Baud rate.
* \retval true Mode setting succeeded (or was not performed at all).
* \retval false Mode setting failed for some reason.
*
* \note Some drivers don't perform any mode setting at all and just return
* true.
*/
virtual bool change_mode(Transfer_mode m, Baud_rate r) = 0;
/**
* Transmit a number of characters.
*
* \param s Buffer containing the characters.
* \param count Number of characters to transmit.
* \param blocking If true, wait until there is space in the transmit
* buffer and also wait until every character was
* successful transmitted. Otherwise do not wait.
* \return The number of successfully written characters.
*/
virtual int write(char const *s, unsigned long count,
bool blocking = true) const = 0;
/**
* Return the transfer mode.
*
* \return The transfer mode.
*/
Transfer_mode mode() const { return _mode; }
/**
* Return the baud rate.
*
* \return The baud rate.
*/
Baud_rate rate() const { return _rate; }
/**
* Enable the receive IRQ.
*
* \retval true The RX IRQ was successfully enabled / disabled.
* \retval false The RX IRQ couldn't be enabled / disabled. The
* driver does not support this operation.
*/
virtual bool enable_rx_irq(bool = true) { return false; }
/**
* Acknowledge a received interrupt.
*/
virtual void irq_ack() {}
/**
* Check if there is at least one character available for reading from the
* UART.
*
* \return 0 if there is no character available for reading, !=0 otherwise.
*/
virtual int char_avail() const = 0;
/**
* Read a character from the UART.
*
* \param blocking If true, wait until a character is available for
* reading. Otherwise do not wait and just return -1 if
* no character is available.
* \return The actual character read from the UART.
*/
virtual int get_char(bool blocking = true) const = 0;
protected:
/**
* Internal function transmitting each character one-after-another and
* finally waiting that the transmission did actually finish.
*
* \param s Buffer containing the characters.
* \param count The number of characters to transmit.
* \param blocking If true, wait until there is space in the transmit
* buffer and also wait until every character was
* successful transmitted. Otherwise do not wait.
* \return The number of successful written characters.
*/
template <typename Uart_driver, bool Timeout_guard = true>
int generic_write(char const *s, unsigned long count,
bool blocking = true) const
{
auto *self = static_cast<Uart_driver const*>(this);
unsigned long c;
for (c = 0; c < count; ++c)
{
if (!blocking && !self->tx_avail())
break;
if constexpr (Timeout_guard)
{
Poll_timeout_counter i(3000000);
while (i.test(!self->tx_avail()))
;
}
else
{
while (!self->tx_avail())
;
}
self->out_char(*s++);
}
if (blocking)
self->wait_tx_done();
return c;
}
};
} // namespace L4

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2013 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_cadence : public Uart
{
public:
explicit Uart_cadence(unsigned base_rate) : _base_rate(base_rate) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const {}
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool) override;
void irq_ack() override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
unsigned _base_rate;
};
} // namespace L4

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_dcc_v6 : public Uart
{
public:
explicit Uart_dcc_v6() {}
explicit Uart_dcc_v6(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
unsigned get_status() const;
};
} // namespace l4

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2021-2022 Stephan Gerhold <stephan@gerhold.net>
* Copyright (C) 2022-2024 Kernkonzept GmbH.
* Author(s): Stephan Gerhold <stephan@gerhold.net>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_dm : public Uart
{
public:
explicit Uart_dm(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
};
} // namespace L4

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_dummy : public Uart
{
public:
explicit Uart_dummy() {}
explicit Uart_dummy(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override { return true; }
void shutdown() override {}
bool change_mode(Transfer_mode, Baud_rate) override { return true; }
inline void out_char(char /*ch*/) const {}
int write(char const * /*str*/, unsigned long /*count*/,
bool /*blocking*/ = true) const override
{ return 0; }
int char_avail() const override { return false; }
int get_char(bool /*blocking*/ = true) const override { return 0; }
};
} // namespace L4

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022-2024 Kernkonzept GmbH.
* Author(s): Stephan Gerhold <stephan.gerhold@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_geni : public Uart
{
public:
explicit Uart_geni(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
void irq_ack() override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
};
} // namespace L4

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2008-2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_imx : public Uart
{
public:
enum platform_type
{
Type_imx21,
Type_imx35,
Type_imx51,
Type_imx6,
Type_imx7,
Type_imx8,
};
explicit Uart_imx(enum platform_type type) : _type(type) {}
explicit Uart_imx(enum platform_type type, unsigned /*base_rate*/)
: _type(type) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
enum platform_type _type;
};
class Uart_imx21 : public Uart_imx
{
public:
Uart_imx21() : Uart_imx(Type_imx21) {}
explicit Uart_imx21(unsigned base_rate) : Uart_imx(Type_imx21, base_rate) {}
};
class Uart_imx35 : public Uart_imx
{
public:
Uart_imx35() : Uart_imx(Type_imx35) {}
explicit Uart_imx35(unsigned base_rate) : Uart_imx(Type_imx35, base_rate) {}
};
class Uart_imx51 : public Uart_imx
{
public:
Uart_imx51() : Uart_imx(Type_imx51) {}
explicit Uart_imx51(unsigned base_rate) : Uart_imx(Type_imx51, base_rate) {}
};
class Uart_imx6 : public Uart_imx
{
public:
Uart_imx6() : Uart_imx(Type_imx6) {}
explicit Uart_imx6(unsigned base_rate) : Uart_imx(Type_imx6, base_rate) {}
void irq_ack() override;
};
class Uart_imx7 : public Uart_imx
{
public:
Uart_imx7() : Uart_imx(Type_imx7) {}
explicit Uart_imx7(unsigned base_rate) : Uart_imx(Type_imx7, base_rate) {}
};
class Uart_imx8 : public Uart_imx
{
public:
Uart_imx8() : Uart_imx(Type_imx8) {}
explicit Uart_imx8(unsigned base_rate) : Uart_imx(Type_imx8, base_rate) {}
};
} // namespace L4

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2011 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* Björn Döbel <doebel@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_leon3 : public Uart
{
public:
explicit Uart_leon3() {}
explicit Uart_leon3(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool = true) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
};
} // namespace L4

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2018 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_linflex : public Uart
{
public:
explicit Uart_linflex(unsigned) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
void set_uartcr(bool fifo);
bool is_tx_fifo_enabled() const;
bool is_rx_fifo_enabled() const;
};
} // namespace L4

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2019, 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_lpuart : public Uart
{
public:
/** freq == 0 means unknown and don't change baud rate */
Uart_lpuart(unsigned freq = 0) : _freq(freq) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const {}
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
unsigned _freq;
};
} // namespace L4

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2017, 2023-2024 Kernkonzept GmbH.
* Author(s) Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_mvebu : public Uart
{
public:
explicit Uart_mvebu(unsigned baserate) : _baserate(baserate) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const {}
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
unsigned _baserate;
};
} // namespace L4

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
#include <stdarg.h>
#include <string.h>
#include <l4/drivers/of.h>
namespace L4 {
class Uart_of : public Uart, public L4_drivers::Of
{
private:
ihandle_t _serial;
public:
Uart_of() : Of(), _serial(0) {}
explicit Uart_of(unsigned /*base_rate*/) : Of(), _serial(0) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
};
} // namespace L4

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s) Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_omap35x : public Uart
{
public:
explicit Uart_omap35x() {}
explicit Uart_omap35x(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
};
} // namespace L4

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_pl011 : public Uart
{
public:
/** freq == 0 means unknown and don't change baud rate */
Uart_pl011(unsigned freq) : _freq(freq) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
void set_rate(Baud_rate r);
unsigned _freq;
};
} // namespace L4

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2009-2012 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_s3c : public Uart
{
protected:
enum Uart_type
{
Type_24xx, Type_64xx, Type_s5pv210,
};
Uart_type type() const { return _type; }
public:
explicit Uart_s3c(Uart_type type) : _type(type) {}
explicit Uart_s3c(Uart_type type, unsigned /*base_rate*/) : _type(type) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
void fifo_reset();
int char_avail() const override;
int get_char(bool blocking = true) const override;
protected:
virtual void ack_rx_irq() const = 0;
virtual void wait_for_empty_tx_fifo() const = 0;
virtual unsigned is_rx_fifo_non_empty() const = 0;
virtual unsigned is_tx_fifo_not_full() const = 0;
private:
Uart_type _type;
};
class Uart_s3c2410 : public Uart_s3c
{
public:
Uart_s3c2410() : Uart_s3c(Type_24xx) {}
explicit Uart_s3c2410(unsigned base_rate) : Uart_s3c(Type_24xx, base_rate) {}
protected:
void ack_rx_irq() const override {}
void wait_for_empty_tx_fifo() const override;
unsigned is_rx_fifo_non_empty() const override;
unsigned is_tx_fifo_not_full() const override;
void auto_flow_control(bool on);
};
class Uart_s3c64xx : public Uart_s3c
{
public:
Uart_s3c64xx() : Uart_s3c(Type_64xx) {}
explicit Uart_s3c64xx(unsigned base_rate) : Uart_s3c(Type_64xx, base_rate) {}
protected:
void ack_rx_irq() const override;
void wait_for_empty_tx_fifo() const override;
unsigned is_rx_fifo_non_empty() const override;
unsigned is_tx_fifo_not_full() const override;
};
class Uart_s5pv210 : public Uart_s3c
{
public:
Uart_s5pv210() : Uart_s3c(Type_s5pv210) {}
explicit Uart_s5pv210(unsigned base_rate) : Uart_s3c(Type_s5pv210, base_rate) {}
protected:
void ack_rx_irq() const override;
void wait_for_empty_tx_fifo() const override;
unsigned is_rx_fifo_non_empty() const override;
unsigned is_tx_fifo_not_full() const override;
};
} // namespace L4

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2008-2012 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* Alexander Warg <alexander.warg@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_sa1000 : public Uart
{
public:
explicit Uart_sa1000() {}
explicit Uart_sa1000(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
};
} // namespace L4

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_sbi : public Uart
{
public:
Uart_sbi();
explicit Uart_sbi(unsigned /*base_rate*/) : Uart_sbi() {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const { return true; }
void wait_tx_done() const {}
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
mutable int _bufchar;
};
} // namespace L4

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2016 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_sh : public Uart
{
public:
explicit Uart_sh() {}
explicit Uart_sh(unsigned /*base_rate*/) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const {}
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable = true) override;
void irq_ack() override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
};
} // namespace L4

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2021-2024 Kernkonzept GmbH.
* Author(s): Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_sifive : public Uart
{
public:
Uart_sifive(unsigned freq) : _freq(freq), _bufchar(-1) {}
bool startup(Io_register_block const *) override;
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const;
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
bool enable_rx_irq(bool enable) override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
unsigned _freq;
mutable int _bufchar;
};
} // namespace L4

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "uart_base.h"
namespace L4 {
class Uart_tegra_tcu : public Uart
{
public:
Uart_tegra_tcu() {}
Uart_tegra_tcu(unsigned long) {}
bool startup(Io_register_block const *tx_mbox) override;
void set_rx_mbox(Io_register_block const *rx_mbox);
void shutdown() override;
bool change_mode(Transfer_mode m, Baud_rate r) override;
int tx_avail() const;
void wait_tx_done() const {}
inline void out_char(char c) const;
int write(char const *s, unsigned long count,
bool blocking = true) const override;
int char_avail() const override;
int get_char(bool blocking = true) const override;
private:
mutable unsigned _current_rx_val = 0u;
Io_register_block const *_rx_mbox = nullptr;
};
} // namespace L4

View File

@@ -0,0 +1,24 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
SRC_CC-riscv := uart_sbi.cc uart_sifive.cc
SRC_CC += uart_pl011.cc uart_sa1000.cc \
uart_16550.cc uart_omap35x.cc uart_imx.cc \
uart_s3c2410.cc uart_of.cc uart_leon3.cc \
uart_cadence.cc uart_dcc-v6.cc uart_16550_dw.cc \
uart_sh.cc uart_mvebu.cc uart_linflex.cc uart_lpuart.cc \
uart_dm.cc uart_geni.cc uart_apb.cc uart_tegra-tcu.cc \
$(SRC_CC-$(ARCH))
SYSTEMS = $(SYSTEMS_PLAIN)
TARGET = libdrivers_uart.a libdrivers_uart.p.a
PC_FILENAME = drivers_uart
PRIVATE_INCDIR += $(SRC_DIR)/../include $(SRC_DIR)/../../include
INCLUDE_MAKE_RULES = $(SRC_DIR)/*/Make.rules
include $(L4DIR)/mk/lib.mk
CXXFLAGS += -DL4_NO_RTTI -fno-exceptions -fno-rtti
CXXFLAGS_arm64 += -mstrict-align

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2008-2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* Alexander Warg <alexander.warg@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/*!
* \file uart_16550.cc
* \brief 16550 UART implementation
*
* \date 2008-01-04
*
*/
#include "uart_16550.h"
#include "poll_timeout_counter.h"
namespace L4 {
bool Uart_16550::startup(Io_register_block const *regs)
{
_regs = regs;
// Only do the detection on x86 where we want to avoid
// to write to I/O ports unnecessarily.
// On other platforms there are UARTs where this check
// fails.
#if defined(__x86_64__) || defined(__i686__) || defined(__i386__)
char scratch, scratch2, scratch3;
scratch = _regs->read<unsigned char>(IER);
_regs->write<unsigned char>(IER, 0);
_regs->delay();
scratch2 = _regs->read<unsigned char>(IER);
_regs->write<unsigned char>(IER, 0xf);
_regs->delay();
scratch3 = _regs->read<unsigned char>(IER);
_regs->write<unsigned char>(IER, scratch);
if (!(scratch2 == 0x00 && scratch3 == 0x0f))
return false; // this is not the uart
#endif
/* disable all rs-232 interrupts */
_regs->write<unsigned char>(IER, _ier_bits);
/* rts, and dtr enabled */
_regs->write<unsigned char>(MCR, _mcr_bits | 3);
/* enable fifo */
_regs->write<unsigned char>(FCR, _fcr_bits | 1);
/* clear line control register: set to (8N1) */
_regs->write<unsigned char>(LCR, 3);
/* clearall interrupts */
_regs->read<unsigned char>(MSR); /* IRQID 0*/
_regs->read<unsigned char>(IIR); /* IRQID 1*/
_regs->read<unsigned char>(TRB); /* IRQID 2*/
_regs->read<unsigned char>(LSR); /* IRQID 3*/
Poll_timeout_counter i(5000000);
while (i.test(_regs->read<unsigned char>(LSR) & LSR_DR))
_regs->read<unsigned char>(TRB);
return true;
}
void Uart_16550::shutdown()
{
_regs->write<unsigned char>(MCR, 6);
_regs->write<unsigned char>(FCR, 0);
_regs->write<unsigned char>(LCR, 0);
_regs->write<unsigned char>(IER, 0);
}
bool Uart_16550::change_mode(Transfer_mode m, Baud_rate r)
{
unsigned long old_lcr = _regs->read<unsigned char>(LCR);
if(r != BAUD_NC && _base_rate) {
unsigned short divisor = _base_rate / r;
_regs->write<unsigned char>(LCR, old_lcr | 0x80/*DLAB*/);
_regs->write<unsigned char>(TRB, divisor & 0x0ff); /* BRD_LOW */
_regs->write<unsigned char>(IER, (divisor >> 8) & 0x0ff); /* BRD_HIGH */
_regs->write<unsigned char>(LCR, old_lcr);
}
if (m != MODE_NC)
_regs->write<unsigned char>(LCR, m & 0x07f);
return true;
}
int Uart_16550::tx_avail() const
{
return _regs->read<unsigned char>(LSR) & LSR_THRE;
}
void Uart_16550::wait_tx_done() const
{
Poll_timeout_counter i(5000000);
while (i.test(!(_regs->read<unsigned char>(LSR) & LSR_TSRE)))
;
}
void Uart_16550::out_char(char c) const
{
_regs->write<unsigned char>(TRB, c);
}
int Uart_16550::write(char const *s, unsigned long count, bool blocking) const
{
/* disable UART IRQs */
unsigned char old_ier = _regs->read<unsigned char>(IER);
_regs->write<unsigned char>(IER, old_ier & ~0x0f);
int c = generic_write<Uart_16550>(s, count, blocking);
_regs->write<unsigned char>(IER, old_ier);
return c;
}
bool Uart_16550::enable_rx_irq(bool enable)
{
unsigned char ier = _regs->read<unsigned char>(IER);
bool res = ier & 1;
if (enable)
ier |= 1;
else
ier &= ~1;
_regs->write<unsigned char>(IER, ier);
return res;
}
int Uart_16550::char_avail() const
{
return _regs->read<unsigned char>(LSR) & LSR_DR;
}
int Uart_16550::get_char(bool blocking) const
{
char old_ier, ch;
if (!blocking && !char_avail())
return -1;
old_ier = _regs->read<unsigned char>(IER);
_regs->write<unsigned char>(IER, old_ier & ~0xf);
while (!char_avail())
;
ch = _regs->read<unsigned char>(TRB);
_regs->write<unsigned char>(IER, old_ier);
return ch;
}
} // namespace L4

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2015 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/*!
* \file uart_16550_dw.cc
* \brief Implementation of DW-based 16550 UART
*/
#include "uart_16550_dw.h"
namespace L4 {
void Uart_16550_dw::irq_ack()
{
enum Registers_dw
{
DW_USR = 0x1f,
};
typedef unsigned char U8;
U8 iir = _regs->read<U8>(IIR);
if ((iir & IIR_BUSY) == IIR_BUSY)
{
U8 lcr = _regs->read<U8>(LCR);
U8 usr = _regs->read<U8>(DW_USR);
asm volatile("" : : "r" (usr) : "memory");
_regs->write<U8>(lcr, LCR);
}
}
} // namespace L4

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2024 Kernkonzept GmbH.
* Author(s): Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_apb.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum Registers
{
DATA = 0x00,
STATE = 0x04,
CTRL = 0x08,
INT = 0x0c,
BAUDDIV = 0x10,
STATE_TX_FULL = 1 << 0, // TX buffer full, read-only.
STATE_RX_FULL = 1 << 1, // RX buffer full, read-only.
STATE_TX_OVERUN = 1 << 2, // TX buffer overrun, write 1 to clear.
STATE_RX_OVERUN = 1 << 3, // RX buffer overrun, write 1 to clear.
CTRL_TX_EN = 1 << 0, // TX enable.
CTRL_RX_EN = 1 << 1, // RX enable.
CTRL_TX_INT_EN = 1 << 2, // TX interrupt enable.
CTRL_RX_INT_EN = 1 << 3, // RX interrupt enable.
CTRL_TX_OVERUN_INT_EN = 1 << 4, // TX overrun interrupt enable.
CTRL_RX_OVERUN_INT_EN = 1 << 5, // RX overrun interrupt enable.
CTRL_TX_HIGH_SPEED = 1 << 6, // High-speed test mode for TX only.
INT_TX = 1 << 0, // TX interrupt. Write 1 to clear.
INT_RX = 1 << 1, // RX interrupt. Write 1 to clear.
INT_TX_OVERRUN = 1 << 2, // TX overrun interrupt. Write 1 to clear.
INT_RX_OVERRUN = 1 << 3, // RX overrun interrupt. Write 1 to clear.
// TODO: How to handle chars lost due to overrun?
Default_baud = 115200,
};
void Uart_apb::set_rate(Baud_rate r)
{
_regs->write<unsigned int>(BAUDDIV, _freq / r);
}
bool Uart_apb::startup(Io_register_block const *regs)
{
_regs = regs;
if (_freq)
set_rate(Default_baud);
_regs->write<unsigned char>(CTRL, CTRL_TX_EN | CTRL_RX_EN | CTRL_TX_HIGH_SPEED);
wait_tx_done();
return true;
}
void Uart_apb::shutdown()
{
_regs->write<unsigned char>(CTRL, 0);
_regs->write<unsigned char>(STATE, STATE_TX_OVERUN | STATE_RX_OVERUN);
_regs->write<unsigned char>(INT, INT_TX | INT_RX | INT_TX_OVERRUN | INT_RX_OVERRUN);
}
bool Uart_apb::change_mode(Transfer_mode, Baud_rate r)
{
if (_freq)
set_rate(r);
return true;
}
int Uart_apb::tx_avail() const
{
return !(_regs->read<unsigned char>(STATE) & STATE_TX_FULL);
}
void Uart_apb::wait_tx_done() const
{
Poll_timeout_counter i(3000000);
while (i.test(!tx_avail()))
;
}
void Uart_apb::out_char(char c) const
{
_regs->write<unsigned char>(DATA, c);
}
int Uart_apb::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_apb>(s, count, blocking);
}
bool Uart_apb::enable_rx_irq(bool enable)
{
if (enable)
_regs->set<unsigned char>(CTRL, CTRL_RX_INT_EN);
else
_regs->clear<unsigned char>(CTRL, CTRL_RX_INT_EN);
return true;
}
int Uart_apb::char_avail() const
{
return _regs->read<unsigned char>(STATE) & STATE_RX_FULL;
}
int Uart_apb::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
return _regs->read<unsigned char>(DATA);
}
} // namespace L4

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2013 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_cadence.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
CR = 0x00,
MR = 0x04,
IER = 0x08,
IDR = 0x0C,
IMR = 0x10,
ISR = 0x14,
BAUDGEN = 0x18,
RXTOUT = 0x1C,
RXWM = 0x20,
MODEMCR = 0x24,
MODEMSR = 0x28,
SR = 0x2C,
FIFO = 0x30,
Baud_rate_divider_reg0 = 0x34,
Flow_delay_reg0 = 0x38,
Tx_FIFO_trigger_level0 = 0x44,
};
namespace Ctrl {
enum
{
Rxres = 1 << 0,
Txres = 1 << 1,
Rxen = 1 << 2,
Rxdis = 1 << 3,
Txen = 1 << 4,
Txdis = 1 << 5,
Rstto = 1 << 6,
};
} // namespace Ctrl
enum
{
IXR_TXFULL = 1 << 4,
IXR_TXEMPTY = 1 << 3,
IXR_RXFULL = 1 << 2,
IXR_RXEMPTY = 1 << 1,
IXR_RXOVR = 1 << 0,
};
bool Uart_cadence::startup(Io_register_block const *regs)
{
_regs = regs;
_regs->write<unsigned>(CR, Ctrl::Txres | Ctrl::Rxres);
change_mode(0, 115200);
_regs->write<unsigned>(CR, Ctrl::Rxen | Ctrl::Txen);
return true;
}
void Uart_cadence::shutdown()
{
_regs->write<unsigned>(CR, Ctrl::Rxdis | Ctrl::Txdis);
}
bool Uart_cadence::change_mode(Transfer_mode, Baud_rate r)
{
if (_base_rate)
{
unsigned div = 4;
_regs->write<unsigned>(Baud_rate_divider_reg0, div);
_regs->write<unsigned>(BAUDGEN, _base_rate / r / (div + 1));
}
_regs->write<unsigned>(MR, 0x20); // 8N1
return true;
}
int Uart_cadence::tx_avail() const
{
return !(_regs->read<unsigned>(SR) & IXR_TXFULL);
}
void Uart_cadence::out_char(char c) const
{
_regs->write<unsigned>(FIFO, c);
}
int Uart_cadence::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_cadence>(s, count, blocking);
}
bool Uart_cadence::enable_rx_irq(bool enable)
{
_regs->write<unsigned>(RXWM, 1);
_regs->write<unsigned>(ISR, ~0U);
_regs->write<unsigned>(IER, enable ? IXR_RXOVR : 0);
return true;
}
void Uart_cadence::irq_ack()
{
_regs->write<unsigned>(ISR, IXR_RXOVR);
}
int Uart_cadence::char_avail() const
{
return !(_regs->read<unsigned>(SR) & IXR_RXEMPTY);
}
int Uart_cadence::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
_regs->write<unsigned>(ISR, IXR_RXOVR);
return _regs->read<unsigned>(FIFO);
}
} // namespace L4

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_dcc-v6.h"
namespace {
enum
{
DCC_STATUS_TX = 0x20000000, // DTRTX full
DCC_STATUS_RX = 0x40000000, // DTRRX full
};
}
namespace L4 {
bool Uart_dcc_v6::startup(Io_register_block const *)
{ return true; }
void Uart_dcc_v6::shutdown()
{}
bool Uart_dcc_v6::change_mode(Transfer_mode, Baud_rate)
{ return true; }
unsigned Uart_dcc_v6::get_status() const
{
#ifdef __arm__
unsigned c;
asm volatile("mrc p14, 0, %0, c0, c1, 0": "=r" (c));
return c;
#else
return 0;
#endif
}
int Uart_dcc_v6::tx_avail() const
{
#ifdef __arm__
return !(get_status() & DCC_STATUS_TX);
#else
return true;
#endif
}
void Uart_dcc_v6::wait_tx_done() const
{
#ifdef __arm__
// The DCC interface allows only to check if the TX queue is full.
while (get_status() & DCC_STATUS_TX)
;
#endif
}
void Uart_dcc_v6::out_char(char c) const
{
#ifdef __arm__
asm volatile("mcr p14, 0, %0, c0, c5, 0": : "r" (c & 0xff));
#else
(void)c;
#endif
}
int Uart_dcc_v6::write(char const *s, unsigned long count,
bool blocking) const
{
#ifdef __arm__
return generic_write<Uart_dcc_v6, false>(s, count, blocking);
#else
(void)s;
(void)count;
(void)blocking;
return count;
#endif
}
int Uart_dcc_v6::char_avail() const
{
#ifdef __arm__
return get_status() & DCC_STATUS_RX;
#else
return false;
#endif
}
int Uart_dcc_v6::get_char(bool blocking) const
{
#ifdef __arm__
while (!char_avail())
if (!blocking)
return -1;
int c;
asm volatile("mrc p14, 0, %0, c0, c5, 0": "=r" (c));
return c & 0xff;
#else
static_cast<void>(blocking);
return -1;
#endif
}
} // namespace L4

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2021-2022 Stephan Gerhold <stephan@gerhold.net>
* Copyright (C) 2022-2024 Kernkonzept GmbH.
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* \file
* Driver for Qualcomm "UART with Data Mover" (or sometimes serial_msm),
* used in many older Qualcomm platforms such as the Snapdragon 210 and 410.
*
* At the moment the driver implements the minimum necessary to get serial
* output/input and interrupts. Further optimizations would be possible,
* e.g. by writing/reading 4 chars at a time when possible.
*/
#include "uart_dm.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
DM_TFWR = 0x01c, // transmit FIFO watermark register
DM_RFWR = 0x020, // receive FIFO watermark register
DM_DMEN = 0x03c, // DMA / data packing
DM_SR = 0x0a4, // status register
DM_CR = 0x0a8, // command register
DM_IMR = 0x0b0, // interrupt mask register
DM_ISR = 0x0b4, // interrupt status register
DM_TF = 0x100, // transmit FIFO register
DM_RF = 0x140, // receive FIFO register
DM_DMEN_TX_SC = 1 << 4, // TX single character mode
DM_DMEN_RX_SC = 1 << 5, // RX single character mode
DM_SR_RXRDY = 1 << 0, // receive FIFO has data
DM_SR_RXFULL = 1 << 1, // receive FIFO is full
DM_SR_TXRDY = 1 << 2, // transmit FIFO has space
DM_SR_TXEMT = 1 << 3, // transmit FIFO empty
DM_CR_RST_RX = 0x01 << 4, // reset receiver
DM_CR_RST_TX = 0x02 << 4, // reset transmitter
DM_CR_DIS_TX = 1 << 3, // disable transmitter
DM_CR_ENA_TX = 1 << 2, // enable transmitter
DM_CR_DIS_RX = 1 << 1, // disable receiver
DM_CR_ENA_RX = 1 << 0, // enable receiver
DM_IMR_RXLEV = 1 << 4, // RX FIFO above watermark level
DM_RF_CHAR = 0xff, // (higher bits contain error information)
};
bool Uart_dm::startup(Io_register_block const *regs)
{
_regs = regs;
enable_rx_irq(false);
_regs->write32(DM_CR, DM_CR_RST_RX);
_regs->write32(DM_CR, DM_CR_RST_TX);
// Use single character mode to read/write one char at a time
_regs->write32(DM_DMEN, DM_DMEN_RX_SC | DM_DMEN_TX_SC);
_regs->write32(DM_RFWR, 0); // Generate interrupt for every character
_regs->write32(DM_CR, DM_CR_ENA_RX);
_regs->write32(DM_CR, DM_CR_ENA_TX);
return true;
}
void Uart_dm::shutdown()
{
_regs->write32(DM_CR, DM_CR_DIS_RX);
_regs->write32(DM_CR, DM_CR_DIS_TX);
}
bool Uart_dm::change_mode(Transfer_mode, Baud_rate)
{
return true;
}
int Uart_dm::tx_avail() const
{
return _regs->read32(DM_SR) & DM_SR_TXRDY;
}
void Uart_dm::out_char(char c) const
{
_regs->write32(DM_TF, c);
}
void Uart_dm::wait_tx_done() const
{
Poll_timeout_counter i(3000000);
while (i.test(!(_regs->read32(DM_SR) & DM_SR_TXEMT)))
;
}
int Uart_dm::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_dm>(s, count, blocking);
}
bool Uart_dm::enable_rx_irq(bool enable)
{
if (enable)
_regs->write32(DM_IMR, DM_IMR_RXLEV);
else
_regs->write32(DM_IMR, 0);
return true;
}
int Uart_dm::char_avail() const
{
return _regs->read32(DM_SR) & DM_SR_RXRDY;
}
int Uart_dm::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
return _regs->read32(DM_RF) & DM_RF_CHAR;
}
} // namespace L4

View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) 2022-2024 Kernkonzept GmbH.
* Author(s): Stephan Gerhold <stephan.gerhold@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* \file
* Driver for Qualcomm "Generic Interface (GENI) Serial Engine (SE)",
* used in most Qualcomm platforms starting from the Snapdragon 845.
*
* At the moment the driver implements the minimum necessary to get serial
* output/input and interrupts. Further optimizations would be possible,
* e.g. by writing/reading 4 chars at a time when possible. The packing
* format for the TX/RX FIFO can be adjusted arbitrarily through the
* TX/RX_PACKING_* registers.
*/
#include "uart_geni.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
FORCE_DEFAULT_REG = 0x020,
STATUS = 0x040,
BYTE_GRAN = 0x254,
TX_PACKING_CFG0 = 0x260,
TX_PACKING_CFG1 = 0x264,
UART_TX_TRANS_LEN = 0x270,
RX_PACKING_CFG0 = 0x284,
RX_PACKING_CFG1 = 0x288,
M_CMD0 = 0x600,
M_CMD_CTRL = 0x604,
M_IRQ_STATUS = 0x610,
M_IRQ_EN = 0x614,
M_IRQ_CLEAR = 0x618,
S_CMD0 = 0x630,
S_CMD_CTRL = 0x634,
S_IRQ_STATUS = 0x640,
S_IRQ_EN = 0x644,
S_IRQ_CLEAR = 0x648,
TX_FIFOn = 0x700,
RX_FIFOn = 0x780,
TX_FIFO_STATUS = 0x800,
RX_FIFO_STATUS = 0x804,
IRQ_EN = 0xe1c,
STATUS_M_GENI_CMD_ACTIVE = 1 << 0,
STATUS_S_GENI_CMD_ACTIVE = 1 << 12,
M_CMD_CTRL_ABORT = 1 << 1,
M_CMD0_OP_UART_START_TX = 0x1 << 27,
S_CMD_CTRL_ABORT = 1 << 1,
S_CMD0_OP_UART_START_RX = 0x1 << 27,
S_IRQ_RX_FIFO_LAST = 1 << 27,
TX_FIFO_STATUS_WC = 0xfffffff,
RX_FIFO_STATUS_WC = 0x1ffffff,
IRQ_EN_M = 1 << 2,
IRQ_EN_S = 1 << 3,
// 4x8 packing (TX_FIFOn accepts up to 4 chars in a single 32-bit write)
// NOTE: For simplicity all bytes are written separately at the moment
TX_PACKING_CFG0_4BYTE = 0x4380e,
TX_PACKING_CFG1_4BYTE = 0xc3e0e,
// 1x8 packing (a 32-bit read of RX_FIFOn always returns a single char)
RX_PACKING_CFG0_1BYTE = 0x0000f,
RX_PACKING_CFG1_1BYTE = 0x00000,
};
bool Uart_geni::startup(Io_register_block const *regs)
{
_regs = regs;
shutdown();
// Configure FIFO packing
_regs->write32(TX_PACKING_CFG0, TX_PACKING_CFG0_4BYTE);
_regs->write32(TX_PACKING_CFG1, TX_PACKING_CFG1_4BYTE);
_regs->write32(RX_PACKING_CFG0, RX_PACKING_CFG0_1BYTE);
_regs->write32(RX_PACKING_CFG1, RX_PACKING_CFG1_1BYTE);
enable_rx_irq(false);
_regs->write32(S_CMD0, S_CMD0_OP_UART_START_RX);
return true;
}
void Uart_geni::shutdown()
{
wait_tx_done();
// Abort RX and TX commands
auto status = _regs->read32(STATUS);
if (status & STATUS_M_GENI_CMD_ACTIVE)
_regs->write32(M_CMD_CTRL, M_CMD_CTRL_ABORT);
if (status & STATUS_S_GENI_CMD_ACTIVE)
_regs->write32(S_CMD_CTRL, S_CMD_CTRL_ABORT);
// Wait until commands are aborted
Poll_timeout_counter i(3000000);
while (_regs->read32(STATUS) &
(STATUS_M_GENI_CMD_ACTIVE | STATUS_S_GENI_CMD_ACTIVE))
;
// Force defaults on output pads
_regs->write32(FORCE_DEFAULT_REG, 1);
}
bool Uart_geni::change_mode(Transfer_mode, Baud_rate)
{ return true; }
int Uart_geni::tx_avail() const
{
return !(_regs->read32(STATUS) & STATUS_M_GENI_CMD_ACTIVE);
}
void Uart_geni::wait_tx_done() const
{
Poll_timeout_counter i(3000000);
while (i.test(_regs->read32(STATUS) & STATUS_M_GENI_CMD_ACTIVE))
;
}
void Uart_geni::out_char(char c) const
{
_regs->write32(UART_TX_TRANS_LEN, 1);
_regs->write32(M_CMD0, M_CMD0_OP_UART_START_TX);
_regs->write32(TX_FIFOn, c);
}
int Uart_geni::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_geni>(s, count, blocking);
}
bool Uart_geni::enable_rx_irq(bool enable)
{
if (!enable)
{
_regs->write32(IRQ_EN, 0);
return true;
}
// Set up interrupts for secondary sequencer (used for RX)
_regs->write32(S_IRQ_CLEAR, ~0U);
_regs->write32(S_IRQ_EN, S_IRQ_RX_FIFO_LAST);
_regs->write32(IRQ_EN, IRQ_EN_S);
return true;
}
void Uart_geni::irq_ack()
{
// FIXME: Is RX_FIFO_LAST really the correct interrupt type here?
_regs->write32(S_IRQ_CLEAR, S_IRQ_RX_FIFO_LAST);
}
int Uart_geni::char_avail() const
{
return _regs->read32(RX_FIFO_STATUS) & RX_FIFO_STATUS_WC;
}
int Uart_geni::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
return _regs->read32(RX_FIFOn) & 0xff;
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (C) 2008-2011 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_imx.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
URXD = 0x00, // Receiver
UTXD = 0x40, // Transmitter
UCR1 = 0x80, // Control 1
UCR2 = 0x84, // Control 2
UCR3 = 0x88, // Control 3
UCR4 = 0x8c, // Control 4
UFCR = 0x90, // FIFO Control
USR1 = 0x94, // Status 1
USR2 = 0x98, // Status 2
UESC = 0x9c, // Escape Charater
UTIM = 0xa0, // Escape Timer
UBIR = 0xa4, // BRM Incremental Registers
UBMR = 0xa8, // BRM Modulator Registers
UBRC = 0xac, // Baud Rate Count
ONEMS = 0xb0, // One millisecond
UTS = 0xb4, // Test
UCR1_EN = 1 << 0, // Enable UART
UCR1_RTSDEN = 1 << 5, // RTS Delta Interrupt Enable
UCR1_RRDYEN = 1 << 9, // Receiver Ready Interrupt Enable
UCR2_SRST = 1 << 0, // Software Reset
UCR2_RXEN = 1 << 1, // Receiver Enable
UCR2_TXEN = 1 << 2, // Transmitter Enable
UCR2_WS = 1 << 5, // 8-bit character length
UCR2_STPB = 1 << 6, // 0 = 1 Stop bit, 1 = 2 stop bits
UCR2_PROE = 1 << 7, // 0 = even parity, 1 = odd parity
UCR2_PREN = 1 << 8, // Parity enable
UCR2_RTEC_MASK = 3 << 9, // Request to Send Edge Control mask
UCR2_RTEC_RISI = 0 << 9, // Trigger IRQ on rising edge
UCR2_RTEC_FALL = 1 << 9, // Trigger IRQ on falling edge
UCR2_RTEC_ANY = 2 << 9, // Trigger IRQ on any edge
UCR2_ESCEN = 1 << 11, // Escape enable
UCR2_CTS = 1 << 12, // Clear to Send: 0 = pin is high (inactive), 1 = pin is low (active)
UCR2_CTSC = 1 << 13, // CTS Pin Control: 0 = pin controlled by CTS bit, 1 = pin controlled by the receiver
UCR2_IRTS = 1 << 14, // Ignore RTS pin
UCR2_ESCI = 1 << 15, // Escape Sequence Interrupt Enable
UCR3_ACIEN = 1 << 0, // Autobaud Counter Interrupt enable
UCR3_INVT = 1 << 1, // Inverted Infrared Transmission
UCR3_RXDMUXSEL = 1 << 2, // RXD Muxed Input Selected: 0 = serial ist IPP_UART_RXD, IR is IPP_UART_RXD_IR, 1 = IPP_UART_RXD_MUX for both
UCR3_AWAKEN = 1 << 3, // Asynchronous WAKE Interrupt Enable
UCR4_DREN = 1 << 0, // Receive Data Ready Interrupt Enable
UCR4_OREN = 1 << 1, // Receiver Overrun Interrupt enable
UCR4_BKEN = 1 << 2, // BREAK Condition Dected Interrupt enable
UCR4_TCEN = 1 << 3, // Transmit Complete Interrupt Enable
UCR4_LPBYP = 1 << 4, // Low Power Bypass
UCR4_IRSC = 1 << 5, // IR Special Case
// Bit 6 is reserve, should be written as 0
UCR4_WKEN = 1 << 7, // WAKE Interrupt Enable
UCR4_ENIRI = 1 << 8, // Serial Infrared Interrupt Enable
UCR4_INVR = 1 << 9, // Inverted Infrared Reception
UCR4_CTSTL_32 = 32 << 10, // CTS Trigger Level
UFCR_RXTL_MASK = 63 << 0, // Receiver Trigger Level Mask
UFCR_RXTL_1 = 1 << 0, // Receiver Trigger Level: 1 char
UFCR_RFDIV_2 = 4 << 7, // Reference Frequency Divier: by 2
UFCR_TXTL_MASK = 63 << 10, // Trasmitter Trigger Level: 8 chars
UFCR_TXTL_8 = 8 << 10, // Trasmitter Trigger Level: 8 chars
UFCR_TXTL_32 = 32 << 10, // Trasmitter Trigger Level: 32 chars
USR1_AWAKE = 1 << 4, // Asynchronous WAKE Interrupt Flag
USR1_TRDY = 1 << 13, // Transmitter Ready
USR2_RDR = 1 << 0, // Receive Data Ready
USR2_ORE = 1 << 1, // Overrun Error
USR2_BRCD = 1 << 2, // Break Condition Detected
USR2_TXDC = 1 << 3, // Transmitter Complete
USR2_TXFE = 1 << 14, // Transmit Buffer FIFO Empty
};
bool Uart_imx::startup(Io_register_block const *regs)
{
_regs = regs;
// 115200Baud, 8n1
switch (_type)
{
case Type_imx21:
_regs->write<unsigned int>(UBIR, 0x0344);
_regs->write<unsigned int>(UBMR, 0x270f);
break;
case Type_imx35:
_regs->write<unsigned int>(UBIR, 0xf);
_regs->write<unsigned int>(UBMR, 0x1b2);
break;
case Type_imx51:
_regs->write<unsigned int>(UBIR, 0xf);
_regs->write<unsigned int>(UBMR, 0x120);
break;
case Type_imx6:
_regs->write<unsigned int>(UBIR, 0xf);
_regs->write<unsigned int>(UBMR, 0x15b);
break;
case Type_imx7:
_regs->write<unsigned int>(UBIR, 0xf);
_regs->write<unsigned int>(UBMR, 0x68);
break;
case Type_imx8:
_regs->write<unsigned int>(UBIR, 0xf);
_regs->write<unsigned int>(UBMR, 0x6c);
break;
}
_regs->write<unsigned int>(UCR1, UCR1_EN);
_regs->write<unsigned int>(UCR2, UCR2_SRST | UCR2_RXEN | UCR2_TXEN | UCR2_WS | UCR2_IRTS); // note: no IRQs enabled
_regs->write<unsigned int>(UCR3, UCR3_RXDMUXSEL);
_regs->write<unsigned int>(UCR4, UCR4_CTSTL_32);
_regs->write<unsigned int>(UFCR, UFCR_TXTL_8 | UFCR_RFDIV_2 | UFCR_RXTL_1);
return true;
}
void Uart_imx::shutdown()
{
_regs->write<unsigned int>(UCR1, 0); // Disable UART
}
bool Uart_imx::change_mode(Transfer_mode, Baud_rate)
{ return true; }
int Uart_imx::tx_avail() const
{
return _regs->read<unsigned int>(USR1) & USR1_TRDY;
}
void Uart_imx::wait_tx_done() const
{
Poll_timeout_counter i(3000000);
while (i.test(!(_regs->read<unsigned int>(USR2) & USR2_TXDC)))
;
}
void Uart_imx::out_char(char c) const
{
_regs->write<unsigned int>(UTXD, c);
}
int Uart_imx::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_imx>(s, count, blocking);
}
bool Uart_imx::enable_rx_irq(bool enable)
{
if (enable)
{
_regs->write<unsigned int>(UCR2, _regs->read<unsigned int>(UCR2) | UCR2_RTEC_ANY);
_regs->write<unsigned int>(UCR4, _regs->read<unsigned int>(UCR4) | UCR4_DREN);
}
else
_regs->write<unsigned int>(UCR4, _regs->read<unsigned int>(UCR4) & ~UCR4_DREN);
return true;
}
void Uart_imx6::irq_ack()
{
unsigned int usr1 = _regs->read<unsigned int>(USR1);
if (usr1 & USR1_AWAKE)
_regs->write<unsigned int>(USR1, USR1_AWAKE);
}
int Uart_imx::char_avail() const
{
return _regs->read<unsigned int>(USR2) & USR2_RDR;
}
int Uart_imx::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking) return -1;
return _regs->read<unsigned int>(URXD) & 0xff;
}
} // namespace L4

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2011 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* Björn Döbel <doebel@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_leon3.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
DATA_REG = 0x00,
STATUS_REG = 0x04,
CTRL_REG = 0x08,
SCALER_REG = 0x0C,
DATA_MASK = 0x0F,
STATUS_DR = 0x001, // data ready
STATUS_TS = 0x002, // transmit shift empty
STATUS_TE = 0x004, // transmit fifo empty
STATUS_BR = 0x008, // BREAK received
STATUS_OV = 0x010, // overrun
STATUS_PE = 0x020, // parity error
STATUS_FE = 0x040, // framing error
STATUS_TH = 0x080, // transmit fifo half-full
STATUS_RH = 0x100, // recv fifo half-full
STATUS_TF = 0x200, // transmit fifo full
STATUS_RF = 0x400, // recv fifo full
STATUS_TCNT_MASK = 0x3F,
STATUS_TCNT_SHIFT = 20,
STATUS_RCNT_MASK = 0x3F,
STATUS_RCNT_SHIFT = 26,
CTRL_RE = 0x001,
CTRL_TE = 0x002,
CTRL_RI = 0x004,
CTRL_TI = 0x008,
CTRL_PS = 0x010,
CTRL_PE = 0x020,
CTRL_FL = 0x040,
CTRL_LB = 0x080,
CTRL_EC = 0x100,
CTRL_TF = 0x200,
CTRL_RF = 0x400,
SCALER_MASK = 0xFFF,
};
bool Uart_leon3::startup(Io_register_block const *regs)
{
_regs = regs;
_regs->write<unsigned int>(CTRL_REG, CTRL_TE | CTRL_RE);
return true;
}
void Uart_leon3::shutdown()
{ }
bool Uart_leon3::change_mode(Transfer_mode, Baud_rate r)
{
if (r != 115200)
return false;
return true;
}
int Uart_leon3::tx_avail() const
{
return !(_regs->read<unsigned int>(STATUS_REG) & STATUS_TF);
}
void Uart_leon3::wait_tx_done() const
{
enum { Tx_empty = STATUS_TS | STATUS_TE }; /* be conservative */
Poll_timeout_counter i(3000000);
while (i.test((_regs->read<unsigned int>(STATUS_REG) & Tx_empty) != Tx_empty))
;
}
void Uart_leon3::out_char(char c) const
{
_regs->write<unsigned int>(DATA_REG, c);
}
int Uart_leon3::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_leon3>(s, count, blocking);
}
bool Uart_leon3::enable_rx_irq(bool enable)
{
unsigned r = _regs->read<unsigned int>(CTRL_REG);
if (enable)
r |= CTRL_RI;
else
r &= ~CTRL_RI;
_regs->write<unsigned int>(CTRL_REG, r);
return 0;
}
int Uart_leon3::char_avail() const
{
return _regs->read<unsigned int>(STATUS_REG) & STATUS_DR;
}
int Uart_leon3::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
return _regs->read<unsigned int>(DATA_REG) & DATA_MASK;
}
} // namespace L4

View File

@@ -0,0 +1,261 @@
/*
* Copyright (C) 2018, 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_linflex.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
LINCR1 = 0x00,
LINIER = 0x04,
LINSR = 0x08,
UARTCR = 0x10,
UARTSR = 0x14,
BDRL = 0x38,
BDRM = 0x3c,
LINCR1_INIT = 1 << 0,
LINCR1_MME = 1 << 4,
LINCR1_BF = 1 << 7,
LINSR_LINS = 0xf << 12,
LINSR_LINS_INITMODE = 1 << 12,
LINIER_DRIE = 1 << 2,
// RX FIFO mode does not work together with enabled receive IRQ: The IRQ is
// triggered when the UARTSR_DRF/RFE bit is set.
// - This works in buffered mode: UARTSR_DRF is set when number of bytes
// programmed in RDFL have been received.
// - This does NOT work in FIFO mode: UARTSR_RFE is set when the RX FIFO is
// empty.
// Therefore we enable FIFO mode only if the receive IRQ is disabled.
Fifo_rx_enabled = 1,
Fifo_tx_enabled = 0,
UARTCR_UART = 1 << 0, // 1=UART mode, 0=LIN mode
UARTCR_WL0 = 1 << 1, // word length in UART mode
UARTCR_PCE = 1 << 2, // parity transmit check enable
UARTCR_PC0 = 1 << 3, // parity control
UARTCR_TXEN = 1 << 4, // transmitter enable
UARTCR_RXEN = 1 << 5, // receiver enable
UARTCR_PC1 = 1 << 6, // parity control
UARTCR_WL1 = 1 << 7, // word length in UART mode
UARTCR_TFBM = 1 << 8, // TX FIFO enabled
UARTCR_RFBM = 1 << 9, // RX FIFO enabled
// RX buffered mode (UARTCR_RFBM=0)
UARTCR_RDFL_1_byte = 0 << 10, // RX buffer has 1 byte
UARTCR_RDFL_2_bytes = 1 << 10, // RX buffer has 2 bytes
UARTCR_RDFL_3_bytes = 2 << 10, // RX buffer has 3 bytes
UARTCR_RDFL_4_bytes = 3 << 10, // RX buffer has 4 bytes
// RX buffered mode (UARTCR_TFBM=0)
UARTCR_TDFL_1_byte = 0 << 13, // TX buffer has 1 byte
UARTCR_TDFL_2_bytes = 1 << 13, // TX buffer has 2 bytes
UARTCR_TDFL_3_bytes = 2 << 13, // TX buffer has 3 bytes
UARTCR_TDFL_4_bytes = 3 << 13, // TX buffer has 4 bytes
// RX FIFO mode (UARTCR_RFBM=1)
UARTCR_RFC_mask = 7 << 10, // number of bytes in RX FIFO (ro)
UARTCR_RFC_empty = 0 << 10, // RX FIFO is empty
UARTCR_RFC_1_byte = 1 << 10, // RX FIFO filled with 1 byte
UARTCR_RFC_2_bytes = 2 << 10, // RX FIFO filled with 2 bytes
UARTCR_RFC_3_bytes = 3 << 10, // RX FIFO filled with 3 bytes
UARTCR_RFC_4_bytes = 4 << 10, // RX FIFO filled with 4 bytes
// TX FIFO mode (UARTCR_TFBM=1)
UARTCR_TFC_mask = 7 << 13, // number of bytes in TX FIFO (ro)
UARTCR_TFC_empty = 0 << 13, // TX FIFO is empty
UARTCR_TFC_1_byte = 1 << 13, // TX FIFO filled with 1 byte
UARTCR_TFC_2_bytes = 2 << 13, // TX FIFO filled with 2 bytes
UARTCR_TFC_3_bytes = 3 << 13, // TX FIFO filled with 3 bytes
UARTCR_TFC_4_bytes = 3 << 13, // TX FIFO filled with 4 bytes
UARTCR_NEF = 0 << 20, // number of expected frames
UARTSR_DTF = 1 << 1, // buffer: data transmission completed flag
UARTSR_TFF = 1 << 1, // FIFO: TX FIFO full
UARTSR_DRF = 1 << 2, // buffer: data reception completed flag
UARTSR_RFE = 1 << 2, // FIFO: RX FIFO empty
UARTSR_TO = 1 << 3, // timeout occurred
UARTSR_RFNE = 1 << 4, // FIFO: RX FIFO not empty
UARTSR_BOF = 1 << 7, // FIFO/buffer overrun flag
UARTSR_RMB = 1 << 9, // buffer ready to read by software if set
};
bool Uart_linflex::startup(Io_register_block const *regs)
{
_regs = regs;
_regs->write<unsigned>(LINCR1, LINCR1_INIT | LINCR1_MME | LINCR1_BF);
while ((_regs->read<unsigned>(LINSR) & LINSR_LINS) != LINSR_LINS_INITMODE)
;
_regs->write<unsigned>(UARTCR, UARTCR_UART);
set_uartcr(true);
_regs->write<unsigned>(UARTSR, UARTSR_DRF | UARTSR_RMB);
_regs->clear<unsigned>(LINCR1, LINCR1_INIT);
return true;
}
bool Uart_linflex::is_tx_fifo_enabled() const
{
return _regs->read<unsigned>(UARTCR) & UARTCR_TFBM;
}
bool Uart_linflex::is_rx_fifo_enabled() const
{
return _regs->read<unsigned>(UARTCR) & UARTCR_RFBM;
}
void Uart_linflex::set_uartcr(bool fifo_rx_enable)
{
unsigned v = UARTCR_UART // UART mode (in contrast to LIN mode)
| UARTCR_WL0 // word length: 8 bits
| UARTCR_PC0 // parity handling
| UARTCR_PC1 // parity handling
| UARTCR_TXEN // transmit enabled
| UARTCR_RXEN // receive enabled
| UARTCR_NEF; // number of expected frames
if (fifo_rx_enable && Fifo_rx_enabled)
v |= UARTCR_RFBM; // RX FIFO enabled
else
v |= UARTCR_RDFL_1_byte; // 1 byte in RX buffer
if (Fifo_tx_enabled)
v |= UARTCR_TFBM; // TX FIFO enabled
else
v |= UARTCR_TDFL_1_byte; // 1 byte in TX buffer
_regs->write<unsigned>(UARTCR, v);
}
void Uart_linflex::shutdown()
{
}
bool Uart_linflex::change_mode(Transfer_mode, Baud_rate)
{
return true;
}
int Uart_linflex::tx_avail() const
{
if (is_tx_fifo_enabled())
// UARTSR_TFF=1: TX FIFO full.
return !(_regs->read<unsigned>(UARTSR) & UARTSR_TFF);
else
// UARTSR_DTF indicating that the character was transmitted was
// immediately cleared in out_char() after transmission.
return true;
}
void Uart_linflex::wait_tx_done() const
{
if (is_tx_fifo_enabled())
{
Poll_timeout_counter i(3000000);
// FIFO: This is not 100% reliable, it might still happen that not all
// characters are send when UARTCR_TFC signals the TX buffer is empty.
while (i.test((_regs->read<unsigned>(UARTCR) & UARTCR_TFC_mask)
!= UARTCR_TFC_empty))
;
}
else
{
// Already done in out_char() after writing a single character.
}
}
void Uart_linflex::out_char(char c) const
{
_regs->write<unsigned char>(BDRL, c);
if (is_tx_fifo_enabled())
{
// tx_avail() will return false as long as the TX FIFO is full.
}
else
{
Poll_timeout_counter i(3000000);
// Buffer mode: UARTSR_DTF=1: Data transmission completed.
while (i.test(!(_regs->read<unsigned>(UARTSR) & UARTSR_DTF)))
;
_regs->write<unsigned>(UARTSR, UARTSR_DTF);
}
}
int Uart_linflex::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_linflex>(s, count, blocking);
}
bool Uart_linflex::enable_rx_irq(bool enable)
{
_regs->set<unsigned>(LINCR1, LINCR1_INIT);
while ((_regs->read<unsigned>(LINSR) & LINSR_LINS) != LINSR_LINS_INITMODE)
;
if (enable)
{
if (Fifo_rx_enabled)
set_uartcr(false);
_regs->set<unsigned>(LINIER, LINIER_DRIE);
}
else
{
if (Fifo_rx_enabled)
set_uartcr(true);
_regs->clear<unsigned>(LINIER, LINIER_DRIE);
}
_regs->clear<unsigned>(LINCR1, LINCR1_INIT);
return true;
}
int Uart_linflex::char_avail() const
{
unsigned sr = _regs->read<unsigned>(UARTSR);
sr &= (UARTSR_TO | UARTSR_BOF);
if (sr)
_regs->write<unsigned>(UARTSR, sr);
if (is_rx_fifo_enabled())
// UARTSR_RFNE=1: RX FIFO not empty.
return (_regs->read<unsigned>(UARTSR) & UARTSR_RFNE);
else
// UARTSR_RMB=1: Buffer ready to read.
return _regs->read<unsigned>(UARTSR) & UARTSR_RMB;
}
int Uart_linflex::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
unsigned c;
if (is_rx_fifo_enabled())
{
unsigned sr = _regs->read<unsigned>(UARTSR);
_regs->write<unsigned>(UARTSR, sr);
c = _regs->read<unsigned char>(BDRM);
}
else
{
unsigned sr = _regs->read<unsigned>(UARTSR);
c = _regs->read<unsigned char>(BDRM);
_regs->write<unsigned>(UARTSR, sr);
}
return c;
}
} // namespace L4

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2019, 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_lpuart.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
VERID = 0x00,
PARAM = 0x04,
GLOBAL = 0x08,
PINCFG = 0x0c,
BAUD = 0x10,
STAT = 0x14,
CTRL = 0x18,
DATA = 0x1c,
MATCH = 0x20,
MODIR = 0x24,
FIFO = 0x28,
WATER = 0x2c,
CTRL_RE = 1 << 18,
CTRL_TE = 1 << 19,
CTRL_RIE = 1 << 21,
FIFO_RXEMPT = 1 << 22, // Receive buffer is empty?
FIFO_TXEMPT = 1 << 23, // Transmit FIFO empty?
BAUD_BOTHEDGE = 1 << 17,
};
bool Uart_lpuart::startup(Io_register_block const *regs)
{
_regs = regs;
_regs->write<unsigned>(CTRL, CTRL_RE | CTRL_TE);
return true;
}
void Uart_lpuart::shutdown()
{
_regs->write<unsigned>(CTRL, 0);
}
bool Uart_lpuart::change_mode(Transfer_mode, Baud_rate r)
{
if (_freq)
{
// The following needs to use _freq to derive values from
enum
{
BAUD_CLOCK = 80064000,
BAUD_OSR_VAL = 4,
BAUD_OSR = BAUD_OSR_VAL << 24,
};
_regs->clear<unsigned>(CTRL, CTRL_RE | CTRL_TE);
_regs->write<unsigned>(BAUD,
((BAUD_CLOCK / r / (BAUD_OSR_VAL + 1)) & 0x1fff)
| BAUD_OSR);
_regs->set<unsigned>(CTRL, CTRL_RE | CTRL_TE);
}
return true;
}
int Uart_lpuart::tx_avail() const
{
return _regs->read<unsigned>(FIFO) & FIFO_TXEMPT;
}
void Uart_lpuart::out_char(char c) const
{
_regs->write<unsigned char>(DATA, c);
}
int Uart_lpuart::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_lpuart>(s, count, blocking);
}
bool Uart_lpuart::enable_rx_irq(bool enable)
{
if (enable)
_regs->set<unsigned>(CTRL, CTRL_RIE);
else
_regs->clear<unsigned>(CTRL, CTRL_RIE);
return true;
}
int Uart_lpuart::char_avail() const
{
return !(_regs->read<unsigned>(FIFO) & FIFO_RXEMPT);
}
int Uart_lpuart::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
return _regs->read<unsigned char>(DATA);
}
} // namespace L4

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2017, 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_mvebu.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
// use names from manual
RBR = 0x00,
TSH = 0x04,
CTRL = 0x08,
STAT = 0x0c,
BRDV = 0x10,
STAT_RX_RDY = 0x0010,
STAT_TXFIFO_FULL = 0x0800,
CTRL_RX_RDY_INT_EN = 1 << 4,
CTRL_RXFIFO_RST = 1 << 14,
CTRL_TXFIFO_RST = 1 << 15,
};
bool Uart_mvebu::startup(Io_register_block const *regs)
{
_regs = regs;
regs->write<unsigned>(CTRL, CTRL_RXFIFO_RST | CTRL_TXFIFO_RST);
regs->write<unsigned>(CTRL, 0);
return true;
}
void Uart_mvebu::shutdown()
{
//_regs->write<unsigned>(CTRL, 0);
}
bool Uart_mvebu::change_mode(Transfer_mode, Baud_rate r)
{
_regs->write<unsigned>(BRDV, _baserate / r / 16);
return true;
}
int Uart_mvebu::tx_avail() const
{
return !(_regs->read<unsigned>(STAT) & STAT_TXFIFO_FULL);
}
void Uart_mvebu::out_char(char c) const
{
_regs->write<unsigned>(TSH, c);
//_regs->clear<unsigned short>(SCFSR, SR_TEND | SR_TDFE);
}
int Uart_mvebu::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_mvebu>(s, count, blocking);
}
bool Uart_mvebu::enable_rx_irq(bool enable)
{
if (enable)
_regs->set<unsigned>(CTRL, CTRL_RX_RDY_INT_EN);
else
_regs->clear<unsigned>(CTRL, CTRL_RX_RDY_INT_EN);
return true;
}
int Uart_mvebu::char_avail() const
{
return _regs->read<unsigned>(STAT) & STAT_RX_RDY;
}
int Uart_mvebu::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
return _regs->read<unsigned>(RBR) & 0xff;
}
} // namespace L4

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2009-2012 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_of.h"
#include <stdio.h>
namespace L4 {
bool Uart_of::startup(Io_register_block const *)
{
char path[64], type[16];
for (phandle_t node = 0; prom_next_node(&node); )
{
prom_getprop(node, "device_type", type, sizeof(type));
if (strcmp(type, "serial"))
continue;
if (prom_call("package-to-path", 3, 1, node,
path, sizeof(path)) == Of::PROM_ERROR)
return false;
/* open port */
if ((_serial = (ihandle_t)prom_call("open", 1, 1, path)) != 0)
return false;
break;
}
//prom_call("exit", 0, 0);
return _serial;
}
void Uart_of::shutdown()
{
prom_call("close", 1, 0, _serial);
}
int Uart_of::write(char const *s, unsigned long count, bool) const
{
/* non-blocked write ignored! */
return prom_call("write", 3, 1, _serial, s, count);
}
void Uart_of::out_char(char c) const
{
prom_call("write", 3, 0, _serial, c, 1);
}
/* UNIMPLEMENTED */
bool Uart_of::change_mode(Transfer_mode, Baud_rate){ return true; }
int Uart_of::tx_avail() const { return 1; }
int Uart_of::char_avail() const { return 1; }
int Uart_of::get_char(bool blocking) const
{
int c, len = 0;
while (len != 1 && blocking)
len = prom_call("read", 3, 1, _serial, &c, 1);
return len ? c :-1;
}
} // namespace L4

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2009-2012 Technische Universität Dresden.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_omap35x.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
DLL_REG = 0x00,
RHR_REG = 0x00,
THR_REG = 0x00,
IER_REG = 0x04,
DLH_REG = 0x04,
LCD_REG = 0x08,
LSR_REG = 0x14,
SSR_REG = 0x44,
SYSC_REG = 0x54,
SYSS_REG = 0x58,
LCD_REG_CHAR_LENGTH_5BIT = 0 << 0,
LCD_REG_CHAR_LENGTH_6BIT = 1 << 0,
LCD_REG_CHAR_LENGTH_7BIT = 2 << 0,
LCD_REG_CHAR_LENGTH_8BIT = 3 << 0,
LCD_REG_CHAR_NB_STOP_2 = 1 << 2,
LCD_REG_CHAR_PARITY_EN = 1 << 3,
LCD_REG_CHAR_PARITY_TYPE1_EVEN = 1 << 4,
LSR_REG_RX_FIFO_E = 1 << 0, // at least one character in FIFO
LSR_REG_TX_FIFO_E = 1 << 5, // transmit hold register empty
LSR_REG_TX_SR_E = 1 << 6, // transmit+shift registers empty
SSR_REG_TX_FIFO_FULL = 1 << 0,
SYSC_REG_SOFTRESET = 1 << 1,
SYSC_REG_RESETDONE = 1 << 0,
};
bool Uart_omap35x::startup(Io_register_block const *regs)
{
_regs = regs;
// Reset UART
//_regs->write<unsigned int>(SYSC_REG, _regs->read<unsigned int>(SYSC_REG) | SYSC_REG_SOFTRESET);
// Poll_timeout_counter i(3000000);
//while (i.test(!(_regs->read<unsigned int>(SYSS_REG) & SYSC_REG_RESETDONE)))
// ;
return true;
}
void Uart_omap35x::shutdown()
{
}
bool Uart_omap35x::change_mode(Transfer_mode, Baud_rate r)
{
if (r != 115200)
return false;
return true;
}
int Uart_omap35x::tx_avail() const
{
return _regs->read<unsigned int>(LSR_REG) & LSR_REG_TX_FIFO_E;
}
void Uart_omap35x::wait_tx_done() const
{
Poll_timeout_counter i(3000000);
while (i.test(!(_regs->read<unsigned char>(LSR_REG) & LSR_REG_TX_SR_E)))
;
}
void Uart_omap35x::out_char(char c) const
{
_regs->write<unsigned int>(THR_REG, c);
}
int Uart_omap35x::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_omap35x>(s, count, blocking);
}
bool Uart_omap35x::enable_rx_irq(bool enable)
{
_regs->write<unsigned int>(IER_REG, enable ? 1 : 0);
return true;
}
int Uart_omap35x::char_avail() const
{
return _regs->read<unsigned int>(LSR_REG) & LSR_REG_RX_FIFO_E;
}
int Uart_omap35x::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
return _regs->read<unsigned int>(RHR_REG);
}
} // namespace L4

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2009-2012 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_pl011.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
UART011_RXIM = 1 << 4,
UART011_TXIM = 1 << 5,
UART011_RTIM = 1 << 6,
UART011_FEIM = 1 << 7,
UART011_PEIM = 1 << 8,
UART011_BEIM = 1 << 9,
UART011_OEIM = 1 << 10,
UART011_RXIS = 1 << 4,
UART011_RTIS = 1 << 6,
UART011_RXIC = 1 << 4,
UART011_RTIC = 1 << 6,
UART01x_CR_UARTEN = 1, // UART enable
UART011_CR_LBE = 0x080, // loopback enable
UART011_CR_TXE = 0x100, // transmit enable
UART011_CR_RXE = 0x200, // receive enable
UART01x_FR_BUSY = 0x008,
UART01x_FR_RXFE = 0x010,
UART01x_FR_TXFF = 0x020,
UART01x_LCRH_PEN = 0x02, // parity enable
UART01x_LCRH_FEN = 0x10, // FIFO enable
UART01x_LCRH_WLEN_8 = 0x60,
UART01x_DR = 0x00,
UART011_ECR = 0x04,
UART01x_FR = 0x18,
UART011_IBRD = 0x24,
UART011_FBRD = 0x28,
UART011_LCRH = 0x2c,
UART011_CR = 0x30,
UART011_IMSC = 0x38,
UART011_MIS = 0x40,
UART011_ICR = 0x44,
Default_baud = 115200,
};
void Uart_pl011::set_rate(Baud_rate r)
{
unsigned fi_val = _freq * 4 / r;
_regs->write<unsigned int>(UART011_FBRD, fi_val & 0x3f);
_regs->write<unsigned int>(UART011_IBRD, fi_val >> 6);
}
bool Uart_pl011::startup(Io_register_block const *regs)
{
_regs = regs;
_regs->write<unsigned int>(UART011_CR, UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE);
if (_freq)
set_rate(Default_baud);
_regs->write<unsigned int>(UART011_LCRH, UART01x_LCRH_WLEN_8);
_regs->write<unsigned int>(UART011_IMSC, 0);
wait_tx_done();
return true;
}
void Uart_pl011::shutdown()
{
_regs->write<unsigned int>(UART011_IMSC, 0);
_regs->write<unsigned int>(UART011_ICR, 0xffff);
_regs->write<unsigned int>(UART011_CR, 0);
}
bool Uart_pl011::change_mode(Transfer_mode, Baud_rate r)
{
unsigned long old_cr = _regs->read<unsigned int>(UART011_CR);
_regs->write<unsigned int>(UART011_CR, 0);
if (_freq)
set_rate(r);
_regs->write<unsigned int>(UART011_LCRH, UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN);
_regs->write<unsigned int>(UART011_CR, old_cr);
return true;
}
int Uart_pl011::tx_avail() const
{
return !(_regs->read<unsigned int>(UART01x_FR) & UART01x_FR_TXFF);
}
void Uart_pl011::wait_tx_done() const
{
Poll_timeout_counter i(3000000);
while (i.test(_regs->read<unsigned int>(UART01x_FR) & UART01x_FR_BUSY))
;
}
void Uart_pl011::out_char(char c) const
{
_regs->write<unsigned int>(UART01x_DR, c);
}
int Uart_pl011::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_pl011>(s, count, blocking);
}
bool Uart_pl011::enable_rx_irq(bool enable)
{
unsigned long mask = UART011_RXIM | UART011_RTIM;
_regs->write<unsigned int>(UART011_ICR, 0xffff & ~mask);
_regs->write<unsigned int>(UART011_ECR, 0xff);
if (enable)
_regs->write<unsigned int>(UART011_IMSC,
_regs->read<unsigned int>(UART011_IMSC) | mask);
else
_regs->write<unsigned int>(UART011_IMSC,
_regs->read<unsigned int>(UART011_IMSC) & ~mask);
return true;
}
int Uart_pl011::char_avail() const
{
return !(_regs->read<unsigned int>(UART01x_FR) & UART01x_FR_RXFE);
}
int Uart_pl011::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
//_regs->write(UART011_ICR, UART011_RXIC | UART011_RTIC);
int c = _regs->read<unsigned int>(UART01x_DR);
_regs->write<unsigned int>(UART011_ECR, 0xff);
return c;
}
} // namespace L4

View File

@@ -0,0 +1,253 @@
/*
* Copyright (C) 2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_s3c2410.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
Type_24xx, Type_64xx, Type_s5pv210,
};
enum
{
ULCON = 0x0, // line control register
UCON = 0x4, // control register
UFCON = 0x8, // FIFO control register
UMCON = 0xc, // modem control register
UTRSTAT = 0x10, // Tx/Rx status register
UERSTAT = 0x14, // Rx error status register
UFSTAT = 0x18, // FIFO status register
UMSTAT = 0x1c, // modem status register
UTXH = 0x20, // transmit buffer register (little endian, 0x23 for BE)
URXH = 0x24, // receive buffer register (little endian, 0x27 for BE)
UBRDIV = 0x28, // baud rate divisor register
UFRACVAL= 0x2c,
// 64xx++
UINTP = 0x30, // interrupt pending register
UINTSP = 0x34, // interrupt source pending register
UINTM = 0x38, // interrupt mask register
ULCON_8N1_MODE = 0x3,
UCON_MODE_RECEIVE_IRQ_POLL = 1 << 0,
UCON_MODE_TRANSMIT_IRQ_POLL = 1 << 2,
UCON_SEND_BREAK_SIGNAL = 1 << 4,
UCON_LOOPBACK_MODE = 1 << 5,
UCON_RX_ERROR_STATUS_IRQ_EN = 1 << 6,
UCON_RX_TIME_OUT_EN = 1 << 7,
UCON_MODE = UCON_MODE_RECEIVE_IRQ_POLL
| UCON_MODE_TRANSMIT_IRQ_POLL
| UCON_RX_TIME_OUT_EN,
UFCON_ENABLE = 1 << 0, // enable fifo
UFCON_RX_FIFO_RESET = 1 << 1, // Rx Fifo reset
UFCON_TX_FIFO_RESET = 1 << 2, // Tx Fifo reset
UMCON_AFC = 1 << 4,
UTRSTAT_Rx_RDY = 1 << 0,
UTRSTAT_Tx_RDY = 1 << 1,
UINT_RXD = 1 << 0,
UINT_ERROR = 1 << 1,
UINT_TXD = 1 << 2,
UINT_MODEM = 1 << 3,
// 2410
UFSTAT_2410_Rx_COUNT_MASK = 0x00f,
UFSTAT_2410_Tx_COUNT_MASK = 0x0f0,
UFSTAT_2410_RxFULL = 0x100,
UFSTAT_2410_TxFULL = 0x200,
// 64xx
UFSTAT_64XX_Rx_COUNT_MASK = 0x003f,
UFSTAT_64XX_Tx_COUNT_MASK = 0x3f00,
UFSTAT_64XX_RxFULL = 0x0040,
UFSTAT_64XX_TxFULL = 0x4000,
// s5pv210
UFSTAT_S5PV210_Rx_COUNT_MASK = 0x000000ff,
UFSTAT_S5PV210_Tx_COUNT_MASK = 0x00ff0000,
UFSTAT_S5PV210_RxFULL = 0x00000100,
UFSTAT_S5PV210_TxFULL = 0x01000000,
};
void Uart_s3c::fifo_reset()
{
_regs->write<unsigned int>(UFCON, UFCON_RX_FIFO_RESET | UFCON_TX_FIFO_RESET);
Poll_timeout_counter i(3000000);
while (i.test(_regs->read<unsigned int>(UFCON) & (UFCON_RX_FIFO_RESET | UFCON_TX_FIFO_RESET)))
;
}
bool Uart_s3c::startup(Io_register_block const *regs)
{
_regs = regs;
fifo_reset();
#if 0
_regs->write<unsigned int>(UMCON, 0);
#endif
_regs->write<unsigned int>(ULCON, ULCON_8N1_MODE);
_regs->write<unsigned int>(UCON, UCON_MODE);
_regs->write<unsigned int>(UFCON, UFCON_ENABLE);
switch (type())
{
case Type_24xx:
break;
case Type_64xx:
case Type_s5pv210:
_regs->write<unsigned int>(UINTM, ~UINT_RXD); // mask all but receive irq
_regs->write<unsigned int>(UINTP, ~0); // clear all pending irqs
break;
}
#if 0
_regs->write<unsigned int>(UBRDIV, 0x23);
#endif
return true;
}
void Uart_s3c::shutdown()
{
// more
}
bool Uart_s3c::change_mode(Transfer_mode, Baud_rate r)
{
if (r != 115200)
return false;
#if 0
_regs->write<unsigned int>(ULCON, ULCON_8N1_MODE);
_regs->write<unsigned int>(UCON, UCON_MODE);
_regs->write<unsigned int>(UFCON, 1);
_regs->write<unsigned int>(UBRDIV, 0x23);
#endif
return true;
}
int Uart_s3c::tx_avail() const
{
return is_tx_fifo_not_full();
}
void Uart_s3c::wait_tx_done() const
{
wait_for_empty_tx_fifo();
}
void Uart_s3c::out_char(char c) const
{
_regs->write<unsigned int>(UTXH, c);
}
int Uart_s3c::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_s3c>(s, count, blocking);
}
int Uart_s3c::char_avail() const
{
return is_rx_fifo_non_empty();
}
int Uart_s3c::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
_regs->read<unsigned int>(UFCON);
int c = _regs->read<unsigned int>(URXH) & 0xff;
ack_rx_irq();
return c;
}
// -----------------------
void Uart_s3c2410::wait_for_empty_tx_fifo() const
{
Poll_timeout_counter i(3000000);
while (i.test(_regs->read<unsigned int>(UFSTAT) & (UFSTAT_2410_Tx_COUNT_MASK | UFSTAT_2410_TxFULL)))
;
}
unsigned Uart_s3c2410::is_rx_fifo_non_empty() const
{
return _regs->read<unsigned int>(UFSTAT) & (UFSTAT_2410_Rx_COUNT_MASK | UFSTAT_2410_RxFULL);
}
unsigned Uart_s3c2410::is_tx_fifo_not_full() const
{
return !(_regs->read<unsigned int>(UFSTAT) & UFSTAT_2410_TxFULL);
}
void Uart_s3c2410::auto_flow_control(bool on)
{
_regs->write<unsigned int>(UMCON, (_regs->read<unsigned int>(UMCON) & ~UMCON_AFC) | (on ? UMCON_AFC : 0));
}
// -----------------------
void Uart_s3c64xx::wait_for_empty_tx_fifo() const
{
Poll_timeout_counter i(3000000);
while (i.test(_regs->read<unsigned int>(UFSTAT) & (UFSTAT_64XX_Tx_COUNT_MASK | UFSTAT_64XX_TxFULL)))
;
}
unsigned Uart_s3c64xx::is_rx_fifo_non_empty() const
{
return _regs->read<unsigned int>(UFSTAT) & (UFSTAT_64XX_Rx_COUNT_MASK | UFSTAT_64XX_RxFULL);
}
unsigned Uart_s3c64xx::is_tx_fifo_not_full() const
{
return !(_regs->read<unsigned int>(UFSTAT) & UFSTAT_64XX_TxFULL);
}
void Uart_s3c64xx::ack_rx_irq() const
{
_regs->write<unsigned int>(UINTP, UINT_RXD);
}
// -----------------------
void Uart_s5pv210::wait_for_empty_tx_fifo() const
{
Poll_timeout_counter i(3000000);
while (i.test(_regs->read<unsigned int>(UFSTAT) & (UFSTAT_S5PV210_Tx_COUNT_MASK | UFSTAT_S5PV210_TxFULL)))
;
}
unsigned Uart_s5pv210::is_rx_fifo_non_empty() const
{
return _regs->read<unsigned int>(UFSTAT) & (UFSTAT_S5PV210_Rx_COUNT_MASK | UFSTAT_S5PV210_RxFULL);
}
unsigned Uart_s5pv210::is_tx_fifo_not_full() const
{
return !(_regs->read<unsigned int>(UFSTAT) & UFSTAT_S5PV210_TxFULL);
}
void Uart_s5pv210::ack_rx_irq() const
{
_regs->write<unsigned int>(UINTP, UINT_RXD);
}
} // namespace L4

View File

@@ -0,0 +1,189 @@
/*
* Copyright (C) 2008-2009 Technische Universität Dresden.
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* Alexander Warg <alexander.warg@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/*!
* \file uart_sa1000.cc
* \brief SA1000 Uart
*
* \date 2008-01-02
*/
#include "uart_sa1000.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
PAR_NONE = 0x00,
PAR_EVEN = 0x03,
PAR_ODD = 0x01,
DAT_5 = static_cast<unsigned>(-1),
DAT_6 = static_cast<unsigned>(-1),
DAT_7 = 0x00,
DAT_8 = 0x08,
STOP_1 = 0x00,
STOP_2 = 0x04,
MODE_8N1 = PAR_NONE | DAT_8 | STOP_1,
MODE_7E1 = PAR_EVEN | DAT_7 | STOP_1,
// these two values are to leave either mode
// or baud rate unchanged on a call to change_mode
MODE_NC = 0x1000000,
BAUD_NC = 0x1000000,
};
enum
{
UTCR0 = 0x00,
UTCR1 = 0x04,
UTCR2 = 0x08,
UTCR3 = 0x0c,
UTCR4 = 0x10,
UTDR = 0x14,
UTSR0 = 0x1c,
UTSR1 = 0x20,
UTCR0_PE = 0x01,
UTCR0_OES = 0x02,
UTCR0_SBS = 0x04,
UTCR0_DSS = 0x08,
UTCR0_SCE = 0x10,
UTCR0_RCE = 0x20,
UTCR0_TCE = 0x40,
UTCR3_RXE = 0x01,
UTCR3_TXE = 0x02,
UTCR3_BRK = 0x04,
UTCR3_RIE = 0x08,
UTCR3_TIE = 0x10,
UTCR3_LBM = 0x20,
UTSR0_TFS = 0x01,
UTSR0_RFS = 0x02,
UTSR0_RID = 0x04,
UTSR0_RBB = 0x08,
UTSR0_REB = 0x10,
UTSR0_EIF = 0x20,
UTSR1_TBY = 0x01,
UTSR1_RNE = 0x02,
UTSR1_TNF = 0x04,
UTSR1_PRE = 0x08,
UTSR1_FRE = 0x10,
UTSR1_ROR = 0x20,
UARTCLK = 3686400,
};
bool Uart_sa1000::startup(Io_register_block const *regs)
{
_regs = regs;
_regs->write<unsigned int>(UTSR0, ~0U); // clear pending status bits
_regs->write<unsigned int>(UTCR3, UTCR3_RXE | UTCR3_TXE); //enable transmitter and receiver
return true;
}
void Uart_sa1000::shutdown()
{
_regs->write<unsigned int>(UTCR3, 0);
}
bool Uart_sa1000::change_mode(Transfer_mode m, Baud_rate baud)
{
unsigned old_utcr3, quot;
//proc_status st;
if (baud == static_cast<Baud_rate>(-1))
return false;
if (baud != BAUD_NC && (baud > 115200 || baud < 96))
return false;
if (m == static_cast<Transfer_mode>(-1))
return false;
//st = proc_cli_save();
old_utcr3 = _regs->read<unsigned int>(UTCR3);
_regs->write<unsigned int>(UTCR3, (old_utcr3 & ~(UTCR3_RIE|UTCR3_TIE)));
//proc_sti_restore(st);
wait_tx_done();
/* disable all */
_regs->write<unsigned int>(UTCR3, 0);
/* set parity, data size, and stop bits */
if (m != MODE_NC)
_regs->write<unsigned int>(UTCR0, m & 0x0ff);
/* set baud rate */
if (baud!=BAUD_NC)
{
quot = (UARTCLK / (16 * baud)) -1;
_regs->write<unsigned int>(UTCR1, (quot & 0xf00) >> 8);
_regs->write<unsigned int>(UTCR2, quot & 0x0ff);
}
_regs->write<unsigned int>(UTSR0, static_cast<unsigned>(-1));
_regs->write<unsigned int>(UTCR3, old_utcr3);
return true;
}
int Uart_sa1000::tx_avail() const
{
return _regs->read<unsigned int>(UTSR1) & UTSR1_TNF;
}
void Uart_sa1000::wait_tx_done() const
{
Poll_timeout_counter cnt(3000000);
while (cnt.test(_regs->read<unsigned int>(UTSR1) & UTSR1_TBY))
;
}
void Uart_sa1000::out_char(char c) const
{
// do UTCR3 thing here as well?
_regs->write<unsigned int>(UTDR, c);
}
int Uart_sa1000::write(char const *s, unsigned long count, bool blocking) const
{
unsigned old_utcr3 = _regs->read<unsigned>(UTCR3);
_regs->write<unsigned>(UTCR3, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) | UTCR3_TXE );
int c = generic_write<Uart_sa1000>(s, count, blocking);
_regs->write<unsigned>(UTCR3, old_utcr3);
return c;
}
int Uart_sa1000::char_avail() const
{
return !!(_regs->read<unsigned int>(UTSR1) & UTSR1_RNE);
}
int Uart_sa1000::get_char(bool blocking) const
{
unsigned long old_utcr3 = _regs->read<unsigned int>(UTCR3);
_regs->write<unsigned int>(UTCR3, old_utcr3 & ~(UTCR3_RIE|UTCR3_TIE));
while (!char_avail())
if (!blocking)
return -1;
int ch = _regs->read<unsigned int>(UTDR);
_regs->write<unsigned int>(UTCR3, old_utcr3);
return ch;
}
} // namespace L4

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_sbi.h"
namespace {
enum
{
Sbi_console_putchar = 1,
Sbi_console_getchar = 2,
};
inline
unsigned long _sbi_call(unsigned long call_type, unsigned long arg0 = 0)
{
register unsigned long a0 asm("a0") = arg0;
register unsigned long a7 asm("a7") = call_type;
__asm__ __volatile__ ("ecall" : "+r"(a0) : "r"(a7) : "memory");
return a0;
}
inline
void sbi_console_putchar(int ch)
{
_sbi_call(Sbi_console_putchar, ch);
}
inline
int sbi_console_getchar(void)
{
return _sbi_call(Sbi_console_getchar);
}
} // namespace
namespace L4 {
Uart_sbi::Uart_sbi() : _bufchar(-1)
{}
bool Uart_sbi::startup(Io_register_block const *)
{ return true; }
void Uart_sbi::shutdown()
{}
bool Uart_sbi::change_mode(Transfer_mode, Baud_rate)
{ return true; }
void Uart_sbi::out_char(char c) const
{
sbi_console_putchar(c);
}
int Uart_sbi::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_sbi>(s, count, blocking);
}
int Uart_sbi::char_avail() const
{
if (_bufchar == -1)
_bufchar = sbi_console_getchar();
return _bufchar != -1;
}
int Uart_sbi::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
int c = _bufchar;
_bufchar = -1;
return c;
}
} // namespace L4

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2016, 2023-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_sh.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum
{
SCSMR = 0x00,
SCBRR = 0x04,
SCSCR = 0x08,
SCFTDR = 0x0C,
SCFSR = 0x10,
SCFRDR = 0x14,
SCFCR = 0x18,
SCFDR = 0x1C,
SCSPTR = 0x20,
SCLSR = 0x24,
DL = 0x28,
CKS = 0x2C,
};
enum
{
SR_DR = 1 << 0, // Data ready
SR_RDF = 1 << 1, // Receive FIFO full
SR_BRK = 1 << 4, // Break Detect
SR_TDFE = 1 << 5, // Transmit FIFO data empty
SR_TEND = 1 << 6, // Transmission end
SR_ER = 1 << 7, // Receive Error
SCR_RE = 1 << 4, // Receive enable
SCR_TE = 1 << 5, // Transmit enable
SCR_RIE = 1 << 6, // Receive IRQ enable
SCR_TIE = 1 << 7, // Transmit IRQ enable
LSR_ORER = 1 << 0, // Overrun error
};
bool Uart_sh::startup(Io_register_block const *regs)
{
_regs = regs;
_regs->write<unsigned short>(SCSCR, SCR_RE | SCR_TE);
return true;
}
void Uart_sh::shutdown()
{
_regs->write<unsigned short>(SCSCR, 0);
}
bool Uart_sh::change_mode(Transfer_mode, Baud_rate r)
{
static_cast<void>(r);
// Set 8N1 and clock rate in SCSMR
return true;
}
int Uart_sh::tx_avail() const
{
return _regs->read<unsigned short>(SCFSR) & SR_TDFE;
}
void Uart_sh::out_char(char c) const
{
_regs->write<unsigned char>(SCFTDR, c);
_regs->clear<unsigned short>(SCFSR, SR_TEND | SR_TDFE);
}
int Uart_sh::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_sh>(s, count, blocking);
}
bool Uart_sh::enable_rx_irq(bool enable)
{
if (enable)
_regs->set<unsigned short>(SCSCR, SCR_RIE);
else
_regs->clear<unsigned short>(SCSCR, SCR_RIE);
return true;
}
void Uart_sh::irq_ack()
{
unsigned short status = _regs->read<unsigned short>(SCLSR);
if (status & LSR_ORER) // Overrun error
_regs->clear<unsigned short>(SCLSR, LSR_ORER);
}
int Uart_sh::char_avail() const
{
return _regs->read<unsigned short>(SCFSR) & (SR_DR | SR_RDF | SR_BRK);
}
int Uart_sh::get_char(bool blocking) const
{
int sr;
while (!(sr = char_avail()))
if (!blocking)
return -1;
int ch;
if (sr & SR_BRK)
ch = -1;
else
ch = _regs->read<unsigned char>(SCFRDR);
_regs->clear<unsigned short>(SCFSR, SR_DR | SR_RDF | SR_ER | SR_BRK);
return ch;
}
} // namespace L4

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2021-2024 Kernkonzept GmbH.
* Author(s): Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_sifive.h"
#include "poll_timeout_counter.h"
namespace L4 {
enum : unsigned
{
UARTSFV_TXDATA = 0x00, // Transmit data register
UARTSFV_RXDATA = 0x04, // Receive data register
UARTSFV_TXCTRL = 0x08, // Transmit control register
UARTSFV_RXCTRL = 0x0C, // Receive control register
UARTSFV_IE = 0x10, // UART interrupt enable
UARTSFV_IP = 0x14, // UART interrupt pending
UARTSFV_DIV = 0x18, // Baud rate divisor
UARTSFV_TXDATA_FULL = 0x80000000,
UARTSFV_RXDATA_EMPTY = 0x80000000,
UARTSFV_RXDATA_DATA = 0xff,
UARTSFV_TXCTRL_TXEN = 1,
UARTSFV_TXCTRL_TXCNT_SHIFT = 16,
UARTSFV_RXCTRL_RXEN = 1,
UARTSFV_IE_RXWM = 2,
UARTSFV_IP_TXWM = 1,
UARTSFV_IP_RXWM = 2,
};
bool Uart_sifive::startup(Io_register_block const *regs)
{
_regs = regs;
// Disable TX and RX interrupts
_regs->write<unsigned>(UARTSFV_IE, 0);
// Enable TX and set transmit watermark level to 1, i.e. it triggers when
// the transmit FIFO is empty.
_regs->write<unsigned>(UARTSFV_TXCTRL, UARTSFV_TXCTRL_TXEN
| 1u << UARTSFV_TXCTRL_TXCNT_SHIFT);
// Enable RX
_regs->write<unsigned>(UARTSFV_RXCTRL, UARTSFV_RXCTRL_RXEN);
return true;
}
void Uart_sifive::shutdown()
{
_regs->write<unsigned>(UARTSFV_IE, 0);
_regs->write<unsigned>(UARTSFV_TXCTRL, 0);
_regs->write<unsigned>(UARTSFV_RXCTRL, 0);
}
bool Uart_sifive::change_mode(Transfer_mode, Baud_rate r)
{
unsigned div = _freq / r;
_regs->write<unsigned>(UARTSFV_DIV, div);
return true;
}
int Uart_sifive::tx_avail() const
{
return !(_regs->read<unsigned>(UARTSFV_TXDATA) & UARTSFV_TXDATA_FULL);
}
void Uart_sifive::wait_tx_done() const
{
Poll_timeout_counter i(5000000);
while (i.test(!(_regs->read<unsigned>(UARTSFV_IP) & UARTSFV_IP_TXWM)))
;
}
void Uart_sifive::out_char(char c) const
{
_regs->write<unsigned>(UARTSFV_TXDATA, c);
}
int Uart_sifive::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_sifive>(s, count, blocking);
}
bool Uart_sifive::enable_rx_irq(bool enable)
{
if (enable)
_regs->set<unsigned>(UARTSFV_IE, UARTSFV_IE_RXWM);
else
_regs->clear<unsigned>(UARTSFV_IE, UARTSFV_IE_RXWM);
return true;
}
int Uart_sifive::char_avail() const
{
if (_bufchar == -1)
{
unsigned data = _regs->read<unsigned>(UARTSFV_RXDATA);
if (!(data & UARTSFV_RXDATA_EMPTY))
_bufchar = data & UARTSFV_RXDATA_DATA;
}
return _bufchar != -1;
}
int Uart_sifive::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
int c = _bufchar;
_bufchar = -1;
return c;
}
} // namespace L4

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam@l4re.org>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "uart_tegra-tcu.h"
namespace L4 {
enum
{
Shift_num_bytes = 24,
Doorbell = 1 << 31,
Busy = 1 << 31,
};
bool Uart_tegra_tcu::startup(Io_register_block const *regs)
{
_regs = regs;
return true;
}
void Uart_tegra_tcu::set_rx_mbox(Io_register_block const *rx_mbox)
{
_rx_mbox = rx_mbox;
}
void Uart_tegra_tcu::shutdown()
{}
bool Uart_tegra_tcu::change_mode(Transfer_mode, Baud_rate)
{ return true; }
int Uart_tegra_tcu::tx_avail() const
{
return !(_regs->read<unsigned>(0) & Busy);
}
void Uart_tegra_tcu::out_char(char c) const
{
_regs->write<unsigned>(0, Doorbell | (1U << Shift_num_bytes) | c);
}
int Uart_tegra_tcu::write(char const *s, unsigned long count, bool blocking) const
{
return generic_write<Uart_tegra_tcu>(s, count, blocking);
}
int Uart_tegra_tcu::char_avail() const
{
if (_current_rx_val)
return true;
if (_rx_mbox)
{
unsigned v = _rx_mbox->read<unsigned>(0);
return v >> Shift_num_bytes; // Not sure all of v would be 0
}
return false;
}
int Uart_tegra_tcu::get_char(bool blocking) const
{
while (!char_avail())
if (!blocking)
return -1;
if (_current_rx_val == 0)
{
if (_rx_mbox)
{
unsigned v = _rx_mbox->read<unsigned>(0);
if (v >> Shift_num_bytes) // Not sure all of v would be 0
_current_rx_val = v;
}
}
if (_current_rx_val)
{
unsigned char ch = _current_rx_val & 0xff;
unsigned num = (_current_rx_val >> Shift_num_bytes) & 3;
// rewrite _current_rx_val according to mbox format
if (num > 1)
_current_rx_val = (_current_rx_val & Doorbell)
| ((num - 1) << Shift_num_bytes)
| ((_current_rx_val & 0xffffff) >> 8);
else
{
_current_rx_val = 0;
_rx_mbox->write<unsigned>(0, 0);
}
return ch;
}
return -1;
}
} // namespace L4