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,3 @@
requires: libc
provides: drivers-frst drivers_uart drivers_of libdrivers-nand libdrivers_hpet
Maintainer: adam@os.inf.tu-dresden.de

View File

@@ -0,0 +1,23 @@
## This file states the license of this package and possibly its subpackages
## in machine and human readable format. The PackageName refers to the package
## whose license is defined by PackageLicenseConcluded.
## For more information about this file format visit the SPDX website at
## https://spdx.org
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentNamespace: spdx:kernkonzept/drivers-frst-276cbe63-8437-4cbb-a638-49e3bdcc9e59
DocumentName: drivers-frst
Creator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
Created: 2018-05-18T00:00:00Z
## Package Information
PackageName: drivers-frst
SPDXID: SPDXRef-drivers-frst
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only WITH GCC-exception-2.0
PackageLicenseConcluded: GPL-2.0-only WITH GCC-exception-2.0
FilesAnalyzed: false
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION

View File

@@ -0,0 +1,8 @@
PKGDIR = .
L4DIR ?= $(PKGDIR)/../..
TARGET = $(wildcard [a-z]*)
include $(L4DIR)/mk/subdir.mk
uart: of include

View File

@@ -0,0 +1,20 @@
# L4Re Boot Drivers
This package contains low-level drivers for the initial booting of the L4Re
operating system.
# Documentation
This package is part of the L4Re operating system. For documentation and
build instructions see the
[L4Re wiki](https://kernkonzept.com/L4Re/guides/l4re).
# Contributions
We welcome contributions. Please see our contributors guide on
[how to contribute](https://kernkonzept.com/L4Re/contributing/l4re).
# License
Detailed licensing and copyright information can be found in the
[LICENSE](LICENSE.spdx) file.

View File

@@ -0,0 +1,29 @@
# Security Policy
This document outlines security procedures for the open-source projects of the
L4Re Operating System Framework as found on https://github.com/kernkonzept.
# Reporting a vulnerability
Security is very important to us and we take all security vulnerabilities
seriously. Thank you for improving the security of our open source software. If
you have discovered a security issue, we appreciate your efforts and your
responsible disclosure.
Please report a security vulnerability by sending an encrypted email to our
security team using our [public
key](https://www.kernkonzept.com/dl/security-at-kernkonzept.pub)
to **security@kernkonzept.com**. The fingerprint of our public key is
````
C4DC 2909 A22E D080 C012 5373 4055 CBA2 A4FD 855B
````
Please include the following in your report:
* A description of the vulnerability
* Steps to reproduce the vulnerability
A member of Kernkonzept's security team will confirm the vulnerability,
determine its impact, and develop a fix. The fix will be applied to the master
branch, tested, and released.

View File

@@ -0,0 +1 @@
INPUT += %PKGDIR%/include %PKGDIR%/uart/include

View File

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

View File

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

View File

@@ -0,0 +1,154 @@
// vim: set ft=cpp:
/*
* Copyright (C) 2010 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)
*/
#pragma once
#include <l4/sys/compiler.h>
#include <l4/sys/types.h>
namespace L4drivers {
class Hpet
{
public:
// General Capabilities and ID Register
unsigned rev_id() const { l4_mb(); return _cap_and_id & 0xff; }
unsigned num_tim_cap() const { l4_mb(); return ((_cap_and_id >> 8) & 0x1f) + 1; }
unsigned count_size_cap() const { l4_mb(); return _cap_and_id & (1 << 13); }
unsigned leg_rt_cap() const { l4_mb(); return _cap_and_id & (1 << 13); }
unsigned vendor_id() const { l4_mb(); return (_cap_and_id >> 16) & 0xffff; };
l4_uint32_t counter_clk_period() const { l4_mb(); return _cap_and_id >> 32; }
// General Configuration Register
unsigned enabled() const { return _conf & 1; }
void enable() { _conf |= 1; l4_wmb(); }
void disable() { _conf &= ~1; l4_wmb(); }
unsigned leg_rt_cnf() const { return _conf & 2; }
void legacy_route_enable() { _conf |= 2; l4_wmb(); }
void legacy_route_disable() { _conf &= ~2; l4_wmb(); }
// General Interrupt Status Register
unsigned irq_active(int irqnum) const { l4_mb(); return (1 << irqnum) & _int_status; }
void irq_clear_active(int irqnum)
{
_int_status = 1 << irqnum; l4_wmb();
__typeof(_int_status) dummy = *(volatile __typeof(_int_status) *)&_int_status;
(void)dummy;
}
// Main Counter Register
l4_uint64_t main_counter_val() const { l4_mb(); return _main_counter; }
void main_counter_val(l4_uint64_t v) { _main_counter = v; l4_wmb(); }
class Timer
{
public:
// Timer N Configuration and Capability Register
void set_int_type_level() { _conf_and_cap |= 2; l4_wmb(); }
void set_int_type_edge() { _conf_and_cap &= ~2; l4_wmb(); }
unsigned is_int_type_level() const { return _conf_and_cap & 2; }
unsigned is_int_type_edge() const { return !is_int_type_level(); }
void enable_int() { _conf_and_cap |= 4; l4_wmb(); }
void disable_int() { _conf_and_cap &= ~4; l4_wmb(); }
unsigned is_int_enabled() const { return _conf_and_cap & 4; }
void set_periodic() { _conf_and_cap |= 8; l4_wmb(); }
void set_nonperiodic() { _conf_and_cap &= ~8; l4_wmb(); }
unsigned is_periodic() const { return _conf_and_cap & 8; }
unsigned is_nonperiodic() const { return !is_periodic(); }
unsigned periodic_int_capable() const { return _conf_and_cap & (1 << 4); }
unsigned can_64bit() const { return _conf_and_cap & (1 << 5); }
void val_set_cnf() { _conf_and_cap |= 1 << 6; l4_wmb(); }
void force_32bit() { _conf_and_cap |= 1 << 8; l4_wmb(); }
unsigned forced_32bit() const { return _conf_and_cap & (1 << 8); }
unsigned int_route_cnf() const { return (_conf_and_cap >> 9) & 0x1f; }
void set_int_route(unsigned irqnum)
{ _conf_and_cap = (_conf_and_cap & ~(31 << 9)) | (irqnum << 9); }
void enable_fsb() { _conf_and_cap |= 1 << 14; l4_wmb(); }
void disable_fsb() { _conf_and_cap &= ~(1 << 14); l4_wmb(); }
unsigned is_fsb() const { return _conf_and_cap & (1 << 14); }
unsigned can_fsb() const { return _conf_and_cap & (1 << 15); }
l4_uint32_t int_route_cap() const { return _conf_and_cap >> 32; }
unsigned int_avail(int int_nr) const { return int_route_cap() & (1 << int_nr); }
unsigned ints_avail() const { return int_route_cap(); }
int get_first_int(int i = 0)
{
l4_uint32_t cap = int_route_cap();
for (; i < 32; ++i)
if (cap & (1 << i))
return i;
return ~0U;
}
l4_uint64_t comparator() const { return _comp; }
void set_comparator(l4_uint64_t v) { _comp = v; l4_wmb(); }
void fsb_int_addr(l4_uint32_t addr) { _int_route_addr = addr; }
void fsb_int_val(l4_uint32_t val) { _int_route_val = val; }
void print_state() const;
l4_uint64_t conf_and_cap() const { return _conf_and_cap; }
l4_uint64_t comp() const { return _comp; }
private:
l4_uint64_t _conf_and_cap;
l4_uint64_t _comp;
l4_uint32_t _int_route_addr; // right order?
l4_uint32_t _int_route_val;
} __attribute__((packed));
Timer *timer(int nr) const
{
return reinterpret_cast<Timer *>((char *)this + 0x100 + 0x20 * nr);
}
unsigned ioapic_irq(int nr) const
{
return timer(nr)->int_route_cnf();
}
void print_state() const;
l4_uint64_t clk2ns(l4_uint64_t v) const
{ return v * counter_clk_period() / 1000000ULL; }
l4_uint64_t clk2us(l4_uint64_t v) const
{ return v * counter_clk_period() / 1000000000ULL; }
l4_uint64_t us2clk(unsigned us) const
{ return us * 1000000000ULL / counter_clk_period(); }
l4_uint64_t ns2clk(unsigned us) const
{ return us * 1000000ULL / counter_clk_period(); }
l4_uint64_t cap_and_id() const { return _cap_and_id; }
l4_uint64_t conf() const { return _conf; }
private:
l4_uint64_t _cap_and_id; // 0x0
l4_uint64_t _pad1;
l4_uint64_t _conf; // 0x10
l4_uint64_t _pad2;
l4_uint64_t _int_status; // 0x20
l4_uint64_t _pad3[(0xf0 - 0x28) / sizeof(l4_uint64_t)];
l4_uint64_t _main_counter; // 0xf0
l4_uint64_t _pad4;
};
static_assert(sizeof(Hpet) == 0x100, "Check size of MMIO registers");
}

View File

@@ -0,0 +1,10 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
SRC_CC += hpet.cc
TARGET = libdrivers_hpet.a
PC_FILENAME = libdrivers_hpet
PRIVATE_INCDIR += $(SRC_DIR)/../include
include $(L4DIR)/mk/lib.mk

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2010 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 <l4/drivers/hpet>
#include <stdio.h>
void
L4drivers::Hpet::print_state() const
{
printf("HPET Information:\n");
printf("Rev-id: 0x%x\n", rev_id());
printf("Num timers: %d\n", num_tim_cap());
printf("Count size: %s\n", count_size_cap() ? "64bit" : "32bit");
printf("Legacy capable: %s\n", leg_rt_cap() ? "Yes" : "No");
printf("Legacy enabled: %s\n", leg_rt_cnf() ? "Yes" : "No");
printf("Vedor ID: 0x%x\n", vendor_id());
printf("Counter clock period: 0x%x / %u\n", counter_clk_period(),
counter_clk_period());
printf("Frequency (Hz): %lld\n", 1000000000000000ULL / counter_clk_period());
printf("Enabled: %s\n", enabled() ? "Yes" : "No");
printf("Main counter value: %lld\n", main_counter_val());
printf("Raw values: %16llx %16llx\n", cap_and_id(), conf());
for (unsigned i = 0; i < num_tim_cap(); ++i)
{
printf("HPET Timer-%d Information:\n", i);
timer(i)->print_state();
}
}
void
L4drivers::Hpet::Timer::print_state() const
{
printf(" Int type: %s\n", is_int_type_level() ? "Level" : "Edge");
printf(" IRQ enabled: %s\n", is_int_enabled() ? "Yes" : "No");
printf(" Mode: %s\n", is_periodic() ? "Periodic" : "Non-periodic");
printf(" Periodic int capable: %s\n", periodic_int_capable() ? "Yes" : "No");
printf(" Can 64bit: %s\n", can_64bit() ? "Yes" : "No");
printf(" Forced to 32bit: %s\n", forced_32bit() ? "Yes" : "No");
printf(" Can FSB/MSI: %s\n", can_fsb() ? "Yes" : "No");
printf(" Does FSB/MSI: %s\n", is_fsb() ? "Yes" : "No");
printf(" IRQs available: %x\n", ints_avail());
printf(" IRQ set: %d\n", int_route_cnf());
printf(" Comparator value: %lld\n", comparator());
printf(" Raw values: %16llx %16llx\n", conf_and_cap(), comp());
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
#include <x86/l4/drivers/asm_access.h>
namespace Asm_access {
inline
l4_uint64_t
read(l4_uint64_t const *mem)
{
l4_uint64_t val;
asm volatile ("movq %[mem], %[val]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint64_t val, l4_uint64_t *mem)
{
asm volatile ("movq %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("ldrb %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("ldrh %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("ldr %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("strb %[val], %[mem]" :[mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("strh %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("str %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("ldrb %w[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("ldrh %w[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("ldr %w[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint64_t
read(l4_uint64_t const *mem)
{
l4_uint64_t val;
asm volatile ("ldr %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("strb %w[val], %[mem]" :[mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("strh %w[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("str %w[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint64_t val, l4_uint64_t *mem)
{
asm volatile ("str %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/asm_access_gen.h>

View File

@@ -0,0 +1,10 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/asm_access_gen.h>

View File

@@ -0,0 +1,90 @@
/*
* 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 <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("lb %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("lh %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("lw %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("sb %[val], %[mem]" :[mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("sh %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("sw %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
#if __riscv_xlen == 64
inline
l4_uint64_t
read(l4_uint64_t const *mem)
{
l4_uint64_t val;
asm volatile ("ld %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint64_t val, l4_uint64_t *mem)
{
asm volatile ("sd %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
#endif
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/asm_access_gen.h>

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("movb %[mem], %[val]" : [val] "=q" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("movw %[mem], %[val]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("movl %[mem], %[val]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("movb %[val], %[mem]" :[mem] "=m" (*mem) : [val] "qi" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("movw %[val], %[mem]" : [mem] "=m" (*mem) : [val] "ri" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("movl %[val], %[mem]" : [mem] "=m" (*mem) : [val] "ri" (val));
}
}

View File

@@ -0,0 +1,8 @@
PKGDIR = ..
L4DIR ?= $(PKGDIR)/../..
PKGNAME = drivers
PC_FILENAME = drivers-frst
EXTRA_TARGET += hw_mmio_register_block hw_register_block
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
#include <l4/cxx/type_traits>
namespace Asm_access {
template <typename T>
struct is_supported_type
{
static const bool value = cxx::is_same<T, l4_uint8_t>::value
|| cxx::is_same<T, l4_uint16_t>::value
|| cxx::is_same<T, l4_uint32_t>::value
|| cxx::is_same<T, l4_uint64_t>::value;
};
template <typename T>
inline
typename cxx::enable_if<is_supported_type<T>::value, T>::type
read(T const *mem)
{
return *reinterpret_cast<volatile T const *>(mem);
}
template <typename T>
inline
typename cxx::enable_if<is_supported_type<T>::value, void>::type
write(T val, T *mem)
{
*reinterpret_cast<volatile T *>(mem) = val;
}
}

View File

@@ -0,0 +1,51 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2014-2021, 2023-2024 Kernkonzept GmbH.
* Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
* Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/hw_register_block>
#include <l4/drivers/asm_access.h>
namespace L4drivers {
class Mmio_register_block_base
{
protected:
l4_addr_t _base;
l4_addr_t _shift;
public:
explicit Mmio_register_block_base(l4_addr_t base = 0, l4_addr_t shift = 0)
: _base(base), _shift(shift) {}
template< typename T >
T read(l4_addr_t reg) const
{ return Asm_access::read(reinterpret_cast<T const *>(_base + (reg << _shift))); }
template< typename T >
void write(T value, l4_addr_t reg) const
{ Asm_access::write(value, reinterpret_cast<T *>(_base + (reg << _shift))); }
void set_base(l4_addr_t base) { _base = base; }
void set_shift(l4_addr_t shift) { _shift = shift; }
};
/**
* An MMIO block with up to 64-bit register access (32-bit default) and little
* endian byte order.
*/
template< unsigned MAX_BITS = 32 >
struct Mmio_register_block
: Register_block_impl<Mmio_register_block<MAX_BITS>, MAX_BITS>,
Mmio_register_block_base
{
explicit Mmio_register_block(l4_addr_t base = 0, l4_addr_t shift = 0)
: Mmio_register_block_base(base, shift) {}
};
}

View File

@@ -0,0 +1,497 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2014-2021, 2023-2024 Kernkonzept GmbH.
* Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/types.h>
#include <l4/cxx/type_traits>
namespace L4drivers {
/**
* Register block.
* \class Register_block
* \details Example usage:
*
* \code{.cpp}
* void test()
* {
* // create a register block reference for max. 16bit accesses, using a
* // MMIO register block implementation (at address 0x1000).
* Hw::Register_block<16> regs = new Hw::Mmio_register_block<16>(0x1000);
*
* // Alternatively it is allowed to use an implementation that allows
* // wider access than actually needed.
* Hw::Register_block<16> regs = new Hw::Mmio_register_block<32>(0x1000);
*
* // read a 16bit register at offset 8byte
* unsigned short x = regs.r<16>(8);
* unsigned short x1 = regs[8]; // alternative
*
* // read an 8bit register at offset 0byte
* unsigned v = regs.r<8>(0);
*
* // do a 16bit write to register at offset 2byte (four variants)
* regs[2] = 22;
* regs.r<16>(2) = 22;
* regs[2].write(22);
* regs.r<16>().write(22);
*
* // do an 8bit write (two variants)
* regs.r<8>(0) = 9;
* regs.r<8>(0).write(9);
*
* // do 16bit read-modify-write (two variants)
* regs[4].modify(0xf, 3); // clear 4 lowest bits and set them to 3
* regs.r<16>(4).modify(0xf, 3);
*
* // do 8bit read-modify-write
* regs.r<8>(0).modify(0xf, 3);
*
* // fails to compile, because of too wide access
* // (32 bit access but regs is Hw::Register_block<16>)
* unsigned long v = regs.r<32>(4)
* }
* \endcode
*/
/**
* \brief Abstract register block interface
* \tparam MAX_BITS The maximum access width for the registers.
*
* This interfaces is based on virtual do_read_<xx> and do_write_<xx>
* methods that have to be implemented up to the maximum access width.
*/
template< unsigned MAX_BITS = 32 >
struct Register_block_base;
template<>
struct Register_block_base<8>
{
virtual l4_uint8_t do_read_8(l4_addr_t reg) const = 0;
virtual void do_write_8(l4_uint8_t value, l4_addr_t reg) = 0;
virtual ~Register_block_base() = 0;
};
inline Register_block_base<8>::~Register_block_base() {}
template<>
struct Register_block_base<16> : Register_block_base<8>
{
virtual l4_uint16_t do_read_16(l4_addr_t reg) const = 0;
virtual void do_write_16(l4_uint16_t value, l4_addr_t reg) = 0;
};
template<>
struct Register_block_base<32> : Register_block_base<16>
{
virtual l4_uint32_t do_read_32(l4_addr_t reg) const = 0;
virtual void do_write_32(l4_uint32_t value, l4_addr_t reg) = 0;
};
template<>
struct Register_block_base<64> : Register_block_base<32>
{
virtual l4_uint64_t do_read_64(l4_addr_t reg) const = 0;
virtual void do_write_64(l4_uint64_t value, l4_addr_t reg) = 0;
};
#undef REGBLK_READ_TEMPLATE
#undef REGBLK_WRITE_TEMPLATE
template<typename CHILD>
struct Register_block_modify_mixin
{
template< typename T >
T modify(T clear_bits, T set_bits, l4_addr_t reg) const
{
CHILD const *c = static_cast<CHILD const *>(this);
T r = (c->template read<T>(reg) & ~clear_bits) | set_bits;
c->template write<T>(r, reg);
return r;
}
template< typename T >
T set(T set_bits, l4_addr_t reg) const
{ return this->template modify<T>(T(0), set_bits, reg); }
template< typename T >
T clear(T clear_bits, l4_addr_t reg) const
{ return this->template modify<T>(clear_bits, T(0), reg); }
};
#define REGBLK_READ_TEMPLATE(sz) \
template< typename T > \
typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type read(l4_addr_t reg) const \
{ \
union X { T t; l4_uint##sz##_t v; } m; \
m.v = _b->do_read_##sz (reg); \
return m.t; \
}
#define REGBLK_WRITE_TEMPLATE(sz) \
template< typename T > \
void write(T value, l4_addr_t reg, typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type = T()) const \
{ \
union X { T t; l4_uint##sz##_t v; } m; \
m.t = value; \
_b->do_write_##sz(m.v, reg); \
}
/**
* \brief Helper template that translates to the Register_block_base
* interface.
* \tparam BLOCK The type of the Register_block_base interface to use.
*
* This helper translates read<T>(), write<T>(), set<T>(), clear<T>(),
* and modify<T>() calls to BLOCK::do_read_<xx> and BLOCK::do_write_<xx>.
*/
template< typename BLOCK >
class Register_block_tmpl
: public Register_block_modify_mixin<Register_block_tmpl<BLOCK> >
{
private:
BLOCK *_b;
public:
Register_block_tmpl(BLOCK *blk) : _b(blk) {}
Register_block_tmpl() = default;
operator BLOCK * () const { return _b; }
REGBLK_READ_TEMPLATE(8)
REGBLK_WRITE_TEMPLATE(8)
REGBLK_READ_TEMPLATE(16)
REGBLK_WRITE_TEMPLATE(16)
REGBLK_READ_TEMPLATE(32)
REGBLK_WRITE_TEMPLATE(32)
REGBLK_READ_TEMPLATE(64)
REGBLK_WRITE_TEMPLATE(64)
};
#undef REGBLK_READ_TEMPLATE
#undef REGBLK_WRITE_TEMPLATE
namespace __Type_helper {
template<unsigned> struct Unsigned;
template<> struct Unsigned<8> { typedef l4_uint8_t type; };
template<> struct Unsigned<16> { typedef l4_uint16_t type; };
template<> struct Unsigned<32> { typedef l4_uint32_t type; };
template<> struct Unsigned<64> { typedef l4_uint64_t type; };
};
/**
* \brief Single read only register inside a Register_block_base interface.
* \tparam BITS The access with of the register in bits.
* \tparam BLOCK The type for the Register_block_base interface.
* \note Objects of this type must be used only in temporary contexts
* not in global, class, or object scope.
*
* Allows simple read only access to a hardware register.
*/
template< unsigned BITS, typename BLOCK >
class Ro_register_tmpl
{
protected:
BLOCK _b;
unsigned _o;
public:
typedef typename __Type_helper::Unsigned<BITS>::type value_type;
Ro_register_tmpl(BLOCK const &blk, unsigned offset) : _b(blk), _o(offset) {}
Ro_register_tmpl() = default;
/**
* \brief read the value from the hardware register.
* \return value read from the hardware register.
*/
operator value_type () const
{ return _b.template read<value_type>(_o); }
/**
* \brief read the value from the hardware register.
* \return value from the hardware register.
*/
value_type read() const
{ return _b.template read<value_type>(_o); }
};
/**
* \brief Single hardware register inside a Register_block_base interface.
* \tparam BITS The access width for the register in bits.
* \tparam BLOCK the type of the Register_block_base interface.
* \note Objects of this type must be used only in temporary contexts
* not in global, class, or object scope.
*/
template< unsigned BITS, typename BLOCK >
class Register_tmpl : public Ro_register_tmpl<BITS, BLOCK>
{
public:
typedef typename Ro_register_tmpl<BITS, BLOCK>::value_type value_type;
Register_tmpl(BLOCK const &blk, unsigned offset)
: Ro_register_tmpl<BITS, BLOCK>(blk, offset)
{}
Register_tmpl() = default;
/**
* \brief write \a val into the hardware register.
* \param val the value to write into the hardware register.
*/
Register_tmpl &operator = (value_type val)
{ this->_b.template write<value_type>(val, this->_o); return *this; }
/**
* \brief write \a val into the hardware register.
* \param val the value to write into the hardware register.
*/
void write(value_type val)
{ this->_b.template write<value_type>(val, this->_o); }
/**
* \brief set bits in \a set_bits in the hardware register.
* \param set_bits bits to be set within the hardware register.
*
* This is a read-modify-write function that does a logical or
* of the old value from the register with \a set_bits.
*
* \code
* unsigned old_value = read();
* write(old_value | set_bits);
* \endcode
*/
value_type set(value_type set_bits)
{ return this->_b.template set<value_type>(set_bits, this->_o); }
/**
* \brief clears bits in \a clear_bits in the hardware register.
* \param clear_bits bits to be cleared within the hardware register.
*
* This is a read-modify-write function that does a logical and
* of the old value from the register with the negated value of
* \a clear_bits.
*
* \code
* unsigned old_value = read();
* write(old_value & ~clear_bits);
* \endcode
*/
value_type clear(value_type clear_bits)
{ return this->_b.template clear<value_type>(clear_bits, this->_o); }
/**
* \brief clears bits in \a clear_bits and sets bits in \a set_bits
* in the hardware register.
* \param clear_bits bits to be cleared within the hardware register.
* \param set_bits bits to set in the hardware register.
*
* This is a read-modify-write function that first does a logical and
* of the old value from the register with the negated value of
* \a clear_bits and then does a logical or with \a set_bits.
*
* \code{.c}
* unsigned old_value = read();
* write((old_value & ~clear_bits) | set_bits);
* \endcode
*/
value_type modify(value_type clear_bits, value_type set_bits)
{ return this->_b.template modify<value_type>(clear_bits, set_bits, this->_o); }
};
/**
* \brief Handles a reference to a register block of the given
* maximum access width.
* \tparam MAX_BITS Maximum access width for the registers in this
* block.
* \tparam BLOCK Type implementing the register accesses (`read<>()`,
* `write<>()`, `modify<>()`, `set<>()`, and `clear<>()`).
*
* Provides access to registers in this block via r<WIDTH>() and
* operator[]().
*/
template<
unsigned MAX_BITS,
typename BLOCK = Register_block_tmpl<
Register_block_base<MAX_BITS>
>
>
class Register_block
{
private:
template< unsigned B, typename BLK > friend class Register_block;
template< unsigned B, typename BLK > friend class Ro_register_block;
typedef BLOCK Block;
Block _b;
public:
Register_block() = default;
Register_block(Block const &blk) : _b(blk) {}
Register_block &operator = (Block const &blk)
{ _b = blk; return *this; }
template< unsigned BITS >
Register_block(Register_block<BITS> blk) : _b(blk._b) {}
typedef Register_tmpl<MAX_BITS, Block> Register;
typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
/**
* \brief Read only access to register at offset \a offset.
* \tparam BITS the access width in bits for the register.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a BITS.
*/
template< unsigned BITS >
Ro_register_tmpl<BITS, Block> r(unsigned offset) const
{ return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
/**
* \brief Read only access to register at offset \a offset.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a MAX_BITS.
*/
Ro_register operator [] (unsigned offset) const
{ return this->r<MAX_BITS>(offset); }
/**
* \brief Read/write access to register at offset \a offset.
* \tparam BITS the access width in bits for the register.
* \param offset The offset of the register within the register file.
* \return register object allowing read and write access with width \a BITS.
*/
template< unsigned BITS >
Register_tmpl<BITS, Block> r(unsigned offset)
{ return Register_tmpl<BITS, Block>(this->_b, offset); }
/**
* \brief Read/write access to register at offset \a offset.
* \param offset The offset of the register within the register file.
* \return register object allowing read and write access with
* width \a MAX_BITS.
*/
Register operator [] (unsigned offset)
{ return this->r<MAX_BITS>(offset); }
};
/**
* \brief Handles a reference to a read only register block of the given
* maximum access width.
* \tparam MAX_BITS Maximum access width for the registers in this block.
* \tparam BLOCK Type implementing the register accesses (read<>()),
*
* Provides read only access to registers in this block via r<WIDTH>()
* and operator[]().
*/
template<
unsigned MAX_BITS,
typename BLOCK = Register_block_tmpl<
Register_block_base<MAX_BITS> const
>
>
class Ro_register_block
{
private:
template< unsigned B, typename BLK > friend class Ro_register_block;
typedef BLOCK Block;
Block _b;
public:
Ro_register_block() = default;
Ro_register_block(BLOCK const &blk) : _b(blk) {}
template< unsigned BITS >
Ro_register_block(Register_block<BITS> const &blk) : _b(blk._b) {}
typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
typedef Ro_register Register;
/**
* \brief Read only access to register at offset \a offset.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a MAX_BITS.
*/
Ro_register operator [] (unsigned offset) const
{ return Ro_register(this->_b, offset); }
/**
* \brief Read only access to register at offset \a offset.
* \tparam BITS the access width in bits for the register.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a BITS.
*/
template< unsigned BITS >
Ro_register_tmpl<BITS, Block> r(unsigned offset) const
{ return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
};
/**
* \brief Implementation helper for register blocks.
* \param BASE The class implementing read<> and write<> template functions
* for accessing the registers. This class must inherit from
* Register_block_impl.
* \param MAX_BITS The maximum access width for the register file.
* Supported values are 8, 16, 32, or 64.
*
*
* This template allows easy implementation of register files by providing
* read<> and write<> template functions, see Mmio_register_block
* as an example.
*/
template< typename BASE, unsigned MAX_BITS = 32 >
struct Register_block_impl;
#define REGBLK_IMPL_RW_TEMPLATE(sz, ...) \
l4_uint##sz##_t do_read_##sz(l4_addr_t reg) const override \
{ return static_cast<BASE const *>(this)->template read<l4_uint##sz##_t>(reg); } \
\
void do_write_##sz(l4_uint##sz##_t value, l4_addr_t reg) override \
{ static_cast<BASE*>(this)->template write<l4_uint##sz##_t>(value, reg); }
template< typename BASE >
struct Register_block_impl<BASE, 8> : public Register_block_base<8>
{
REGBLK_IMPL_RW_TEMPLATE(8);
};
template< typename BASE >
struct Register_block_impl<BASE, 16> : public Register_block_base<16>
{
REGBLK_IMPL_RW_TEMPLATE(8);
REGBLK_IMPL_RW_TEMPLATE(16);
};
template< typename BASE >
struct Register_block_impl<BASE, 32> : public Register_block_base<32>
{
REGBLK_IMPL_RW_TEMPLATE(8);
REGBLK_IMPL_RW_TEMPLATE(16);
REGBLK_IMPL_RW_TEMPLATE(32);
};
template< typename BASE >
struct Register_block_impl<BASE, 64> : public Register_block_base<64>
{
REGBLK_IMPL_RW_TEMPLATE(8);
REGBLK_IMPL_RW_TEMPLATE(16);
REGBLK_IMPL_RW_TEMPLATE(32);
REGBLK_IMPL_RW_TEMPLATE(64);
};
#undef REGBLK_IMPL_RW_TEMPLATE
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright (C) 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)
*/
#pragma once
namespace L4
{
class Io_register_block
{
public:
/**
* \brief Read register with a byte access.
*/
virtual unsigned char read8(unsigned long reg) const = 0;
/**
* \brief Read register with a 2 byte access.
*/
virtual unsigned short read16(unsigned long reg) const = 0;
/**
* \brief Read register with a 4 byte access.
*/
virtual unsigned int read32(unsigned long reg) const = 0;
/*
* \brief Read register with an 8 byte access.
*/
//virtual unsigned long long read64(unsigned long reg) const = 0;
/**
* \brief Write register with a byte access.
*/
virtual void write8(unsigned long reg, unsigned char value) const = 0;
/**
* \brief Write register with a 2 byte access.
*/
virtual void write16(unsigned long reg, unsigned short value) const = 0;
/**
* \brief Write register with a 4 byte access.
*/
virtual void write32(unsigned long reg, unsigned int value) const = 0;
/*
* \brief Write register with an 8 byte access.
*/
//virtual void write64(unsigned long reg, unsigned long long value) const = 0;
/**
* \brief Get address of a register.
*/
virtual unsigned long addr(unsigned long reg) const = 0;
/**
* \brief A delay.
*
* Note, most likely this function does nothing.
*/
virtual void delay() const = 0;
virtual ~Io_register_block() = 0;
/**
* \brief Read register.
* \param reg Register to use.
* \return Value in the register
*
* The access width is defined by the return type.
*/
template< typename R >
R read(unsigned long reg) const
{
static_assert(sizeof(R) == 1 || sizeof(R) == 2 || sizeof(R) == 4,
"Invalid size");
switch (sizeof(R))
{
case 1: return read8(reg);
case 2: return read16(reg);
case 4: return read32(reg);
};
}
/**
* \brief Write register.
* \param reg Register to use.
* \param value Date to write to the register.
*
* The access width is defined by the type of the value
*/
template< typename R >
void write(unsigned long reg, R value) const
{
static_assert(sizeof(R) == 1 || sizeof(R) == 2 || sizeof(R) == 4,
"Invalid size");
switch (sizeof(R))
{
case 1: write8(reg, value); return;
case 2: write16(reg, value); return;
case 4: write32(reg, value); return;
};
}
/**
* \brief Modify register.
* \param reg Register to use.
* \param clear_bits Bits to clear in the register.
* \param set_bits Bits to set in the register.
* \return The written value.
*
* This function is a short form of
* write(reg, (read(reg) & ~clear_bits) | add_bits)
*/
template< typename R >
R modify(unsigned long reg, R clear_bits, R set_bits) const
{
R r = (read<R>(reg) & ~clear_bits) | set_bits;
write(reg, r);
return r;
}
/**
* \brief Set bits in register.
* \param reg Register to use.
* \param set_bits Bits to set in the registers.
* \return The written value.
*/
template< typename R >
R set(unsigned long reg, R set_bits) const
{
return modify<R>(reg, 0, set_bits);
}
/**
* \brief Clear bits in register.
* \param reg Register to use.
* \param clear_bits Bits to clear in the register.
* \return The written value.
*/
template< typename R >
R clear(unsigned long reg, R clear_bits) const
{
return modify<R>(reg, clear_bits, 0);
}
};
inline Io_register_block::~Io_register_block() {}
class Io_register_block_mmio : public Io_register_block
{
private:
template< typename R >
R _read(unsigned long reg) const
{ return *reinterpret_cast<volatile R *>(_base + (reg << _shift)); }
template< typename R >
void _write(unsigned long reg, R val) const
{ *reinterpret_cast<volatile R *>(_base + (reg << _shift)) = val; }
public:
Io_register_block_mmio(unsigned long base, unsigned char shift = 0)
: _base(base), _shift(shift)
{}
unsigned long addr(unsigned long reg) const override
{ return _base + (reg << _shift); }
unsigned char read8(unsigned long reg) const override
{ return _read<unsigned char>(reg); }
unsigned short read16(unsigned long reg) const override
{ return _read<unsigned short>(reg); }
unsigned int read32(unsigned long reg) const override
{ return _read<unsigned int>(reg); }
void write8(unsigned long reg, unsigned char val) const override
{ _write(reg, val); }
void write16(unsigned long reg, unsigned short val) const override
{ _write(reg, val); }
void write32(unsigned long reg, unsigned int val) const override
{ _write(reg, val); }
void delay() const override
{}
private:
unsigned long _base;
unsigned char _shift;
};
template<typename ACCESS_TYPE>
class Io_register_block_mmio_fixed_width : public Io_register_block
{
private:
template< typename R >
R _read(unsigned long reg) const
{ return *reinterpret_cast<volatile ACCESS_TYPE *>(_base + (reg << _shift)); }
template< typename R >
void _write(unsigned long reg, R val) const
{ *reinterpret_cast<volatile ACCESS_TYPE *>(_base + (reg << _shift)) = val; }
public:
Io_register_block_mmio_fixed_width(unsigned long base, unsigned char shift = 0)
: _base(base), _shift(shift)
{}
unsigned long addr(unsigned long reg) const
{ return _base + (reg << _shift); }
unsigned char read8(unsigned long reg) const override
{ return _read<unsigned char>(reg); }
unsigned short read16(unsigned long reg) const override
{ return _read<unsigned short>(reg); }
unsigned int read32(unsigned long reg) const override
{ return _read<unsigned int>(reg); }
void write8(unsigned long reg, unsigned char val) const override
{ _write(reg, val); }
void write16(unsigned long reg, unsigned short val) const override
{ _write(reg, val); }
void write32(unsigned long reg, unsigned int val) const override
{ _write(reg, val); }
void delay() const override
{}
private:
unsigned long _base;
unsigned char _shift;
};
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 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)
*/
#pragma once
#include "io_regblock.h"
namespace L4
{
class Io_register_block_port : public Io_register_block
{
public:
Io_register_block_port(unsigned long base)
: _base(base)
{}
unsigned long addr(unsigned long reg) const override { return _base + reg; }
unsigned char read8(unsigned long reg) const override
{
unsigned char val;
asm volatile("inb %w1, %b0" : "=a" (val) : "Nd" (_base + reg));
return val;
}
unsigned short read16(unsigned long reg) const override
{
unsigned short val;
asm volatile("inw %w1, %w0" : "=a" (val) : "Nd" (_base + reg));
return val;
}
unsigned int read32(unsigned long reg) const override
{
unsigned int val;
asm volatile("in %w1, %0" : "=a" (val) : "Nd" (_base + reg));
return val;
}
void write8(unsigned long reg, unsigned char val) const override
{ asm volatile("outb %b0, %w1" : : "a" (val), "Nd" (_base + reg)); }
void write16(unsigned long reg, unsigned short val) const override
{ asm volatile("outw %w0, %w1" : : "a" (val), "Nd" (_base + reg)); }
void write32(unsigned long reg, unsigned int val) const override
{ asm volatile("out %0, %w1" : : "a" (val), "Nd" (_base + reg)); }
void delay() const override
{ asm volatile ("outb %al,$0x80"); }
private:
unsigned long _base;
};
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 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)
*/
#pragma once
namespace L4 {
/**
* Evaluate an expression for a maximum number of times.
*
* A typical use case is testing for a bit change in a hardware register for a
* maximum number of times (polling). For example:
*
* \code{c++}
* Mmio_register_block regs;
* Poll_timeout_counter i(3000000);
* while (i.test(!(regs.read<l4_uint32_t>(0x04) & 1)))
* ;
* \endcode
*
* The following usage is \b wrong:
* \code{c++}
* ...
* Poll_timeout_counter i(3000000);
* while (!i.test((regs.read<l4_uint32_t>(0x04) & 1)))
* ;
* \endcode
*
* This loop would never terminate if the hardware register doesn't change!
*/
class Poll_timeout_counter
{
public:
/**
* Constructor.
*
* \param counter_val Maximum number of times to repeat the test.
*/
Poll_timeout_counter(unsigned counter_val)
{
set(counter_val);
}
/**
* Set the counter to a certain value.
*
* \param counter_val New counter value for maximum number of times to
* repeat the test.
*/
void set(unsigned counter_val)
{
_c = counter_val;
}
/**
* Evaluate the expression for a maximum number of times.
*/
bool test(bool expression = true)
{
if (!expression)
return false;
if (_c)
{
--_c;
return true;
}
return false;
}
/**
* Indicator if the maximum number of tests was required.
*
* \retval true, if the maximum number of tests was required or if the
* counter was initialized to zero.
*/
bool timed_out() const { return _c == 0; }
private:
unsigned _c;
};
}

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
#pragma once
#include <l4/sys/types.h>
#include "transfer.h"
class Nand_ctrl;
class Nand
{
public:
Nand(Nand_ctrl *nand_ctrl);
int read_page(l4_addr_t page, Transfer &transfer);
int write_page(l4_addr_t page, Transfer &transfer);
int erase(l4_addr_t block);
int handle_irq();
long unsigned page_size;
long unsigned spare_size;
long unsigned block_size;
long unsigned num_blocks;
private:
Nand_ctrl *_nand_ctrl;
};
class Nand_drv
{
public:
virtual int probe(const char *configstr) = 0;
virtual Nand_ctrl *create(l4_umword_t base) = 0;
};
class Nand *arm_nand_probe(const char *configstr, l4_addr_t base);
void arm_nand_register_driver(Nand_drv *nand_drv);

View File

@@ -0,0 +1,67 @@
#pragma once
struct Buffer
{
Buffer()
: addr(0), size(0)
{}
Buffer(unsigned char *a, unsigned s)
: addr(a), size(s)
{}
unsigned char *addr;
unsigned size;
};
struct Transfer
{
enum
{ Max = 4, };
#if 0
struct Elem
{
unsigned char *addr;
unsigned size;
};
#endif
Transfer()
: num(0), len(0)
{}
unsigned num;
unsigned len;
void clear()
{
num = 0;
len = 0;
}
void add(unsigned char *addr, unsigned size)
{
if (num == Max)
return;
_elems[num].addr = addr;
_elems[num].size = size;
len += size;
num++;
}
void add(Buffer buffer)
{
add(buffer.addr, buffer.size);
}
Buffer &operator [] (int index)
{
//assert(index < Max);
return _elems[index];
}
private:
Buffer _elems[Max];
};

View File

@@ -0,0 +1,12 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = libarm_nand.o.a
PC_FILENAME := libdrivers-nand
SRC_CC := lib_nand.cc nand.cc nand_ids.cc gpmc.cc mpc5121.cc
PRIVATE_INCDIR := $(SRCDIR)
#CFLAGS += -D__KERNEL__ -DHAVE_NET_DEVICE_OPS
include $(L4DIR)/mk/lib.mk

View File

@@ -0,0 +1,15 @@
#pragma once
#if 0
#include "l4/util/util.h"
inline void udelay(unsigned us)
{
l4_usleep(us);
}
#else
// environment using this library needs to supply a udelay function
void udelay(unsigned us);
#endif

View File

@@ -0,0 +1,124 @@
#include <stdio.h>
#include "l4/drivers-frst/lib_nand.h"
#include "nand.h"
#include "gpmc.h"
Gpmc::Gpmc(addr base_addr)
: _reg((Reg *)base_addr)
{
for (int i = 0; i < Num_cs; ++i)
{
/* Check if NAND type is set */
if (((_reg->cs[i].config1) & 0xc00) == 0x800)
{
//printf("found NAND chip\n");
/* Found it!! */
break;
}
}
// XXX just scan for one chip
scan(1);
// disable write protection
u32 config = _reg->config;
config |= 0x10;
_reg->config = config;
// enable interrupts
_reg->irqstatus = 256;
_reg->irqenable = 256;
}
void Gpmc::add(Nand_chip *chip)
{
chip->add_options(Opt_no_padding | Opt_cacheprg | Opt_no_autoincr);
if ((_reg->cs[0].config1 & 0x3000) == 0x1000)
chip->add_options(Opt_buswidth_16);
//chip->delay = 100;
_chips[0] = chip;
}
Nand_chip *Gpmc::select(loff_t /*addr*/)
{
// XXX hack:we currently have only one chip
return _chips[0];
}
void Gpmc::wr_cmd(u8 c)
{ *((volatile u8 *)&_reg->cs[0].nand_cmd) = c; }
void Gpmc::wr_adr(u8 a)
{ *((volatile u8 *)&_reg->cs[0].nand_adr) = a; }
void Gpmc::wr_dat(u8 d)
{ *((volatile u8 *)&_reg->cs[0].nand_dat) = d; }
u8 Gpmc::rd_dat()
{ return (u8)(*((volatile u16 *)&_reg->cs[0].nand_dat)); }
void Gpmc::rd_dat(u8 *buf, unsigned len)
{
u16 *p = (u16 *)buf;
len >>= 1;
for (unsigned i = 0; i < len; i++)
p[i] = *((volatile u16 *)&_reg->cs[0].nand_dat);
#if 0
for (int i = 0; i < len; i+=4)
{
p[i] = *((volatile u16 *)&_reg->cs[0].nand_dat);
p[i+1] = *((volatile u16 *)&_reg->cs[0].nand_dat);
p[i+2] = *((volatile u16 *)&_reg->cs[0].nand_dat);
p[i+3] = *((volatile u16 *)&_reg->cs[0].nand_dat);
}
#endif
}
void Gpmc::wr_dat(const u8 *buf, unsigned len)
{
u16 *p = (u16 *)buf;
len >>= 1;
for (unsigned i = 0; i < len; i++)
*((volatile u16 *)&_reg->cs[0].nand_dat) = p[i];
}
int Gpmc::handle_irq()
{
int ret = 0;
u32 v = _reg->irqstatus;
u32 v2 = _reg->irqstatus;
if (v & 256)
{
ret = _chips[0]->handle_irq();
_reg->irqstatus = 256;
}
else
{
printf("spurious interrupt\n");
printf("irqstatus:%x %x\n", v, v2);
return -1;
}
return ret;
}
class Gpmc_drv : public Nand_drv
{
public:
Gpmc_drv()
{ arm_nand_register_driver(this); }
int probe(const char *configstr)
{ return (strcmp(configstr, "GPMC")) ? 0 : 1; }
Nand_ctrl *create(addr base)
{ return new Gpmc(base); }
};
static Gpmc_drv gpmc_drv;

View File

@@ -0,0 +1,60 @@
#pragma once
#include "nand.h"
class Gpmc : public Nand_ctrl
{
enum
{ Num_cs = 8, };
struct Reg_cs
{
u32 config1;
u32 config2;
u32 config3;
u32 config4;
u32 config5;
u32 config6;
u32 config7;
volatile u32 nand_cmd;
volatile u32 nand_adr;
volatile u32 nand_dat;
};
struct Reg
{
u8 res1[0x10];
u32 sysconfig;
u8 res2[0x04];
u32 irqstatus;
u32 irqenable;
u8 res3[0x20];
u8 timeout_control;
u8 res4[0x0c];
u32 config;
u32 status;
u8 res5[0x08];
Reg_cs cs[Num_cs];
};
public:
Gpmc(addr base_addr);
protected:
void add(Nand_chip *chip);
Nand_chip *select(loff_t addr);
void wr_cmd(u8 c);
void wr_adr(u8 a);
void wr_dat(u8 d);
u8 rd_dat();
void rd_dat(u8 *buf, unsigned len);
void wr_dat(const u8 *buf, unsigned len);
int handle_irq();
private:
volatile Reg *_reg;
Nand_chip *_chips[Num_cs];
};

View File

@@ -0,0 +1,75 @@
#include <l4/drivers-frst/lib_nand.h>
#include "nand.h"
enum { Max_drivers = 10 };
static Nand_drv *nand_drv[Max_drivers];
static int nr_drivers;
Nand *arm_nand_probe(const char *configstr, l4_umword_t base)
{
for (int i = 0; i < nr_drivers; i++)
if (nand_drv[i] && nand_drv[i]->probe(configstr))
return new Nand(nand_drv[i]->create(base));
return 0;
}
void arm_nand_register_driver(Nand_drv *drv)
{
if (nr_drivers < Max_drivers)
nand_drv[nr_drivers++] = drv;
}
Nand::Nand(Nand_ctrl *nand_ctrl)
: _nand_ctrl(nand_ctrl)
{
page_size = nand_ctrl->sz_write;
spare_size = nand_ctrl->sz_spare;
block_size = nand_ctrl->sz_erase;
num_blocks = nand_ctrl->size / nand_ctrl->sz_erase;
}
int Nand::read_page(l4_addr_t page, Transfer &transfer)
{
if (!_nand_ctrl)
return -1;
static Read_op op;
op.addr = page * _nand_ctrl->sz_write;
op.transfer = &transfer;
return _nand_ctrl->read(&op);
}
int Nand::write_page(l4_addr_t page, Transfer &transfer)
{
if (!_nand_ctrl)
return -1;
static Write_op op;
op.addr = page * _nand_ctrl->sz_write;
op.transfer = &transfer;
return _nand_ctrl->write(&op);
}
int Nand::erase(l4_addr_t block)
{
if (!_nand_ctrl)
return -1;
static Erase_op op;
op.addr = block * _nand_ctrl->sz_erase;
op.len = _nand_ctrl->sz_erase;
_nand_ctrl->erase(&op);
return 0;
}
int Nand::handle_irq()
{
if (!_nand_ctrl)
return -1;
return _nand_ctrl->handle_irq();
}

View File

@@ -0,0 +1,326 @@
#include <stdio.h>
#include <stdlib.h>
#include "l4/drivers-frst/lib_nand.h"
#include "common.h"
#include "nand.h"
#include "mpc5121.h"
Mpc5121::Mpc5121(addr base_addr)
: _reg((Reg *)(base_addr + 0x1e00)),
_buffer_main((u32 *)base_addr), _buffer_spare((u32 *)(base_addr + 0x1000))
{
// reset controller
_reg->nand_flash_config1 |= 0x40;
unsigned num_us_reset = 0;
while (_reg->nand_flash_config1 & 0x40)
{
if (num_us_reset++ >= Max_us_reset)
{
printf("[Mpc5121] Timeout while resetting controller!\n");
return;
}
udelay(1);
}
// unlock buffer memory
_reg->nfc_configuration = 0x2;
// disable write protection
_reg->unlock_start_blk_add0 = 0x0000;
_reg->unlock_end_blk_add0 = 0xffff;
_reg->nf_wr_prot = 0x4;
// mask and configure interrupts only after full page transfer
_reg->nand_flash_config1 = 0x810;
// detect NAND chips
if (scan(1))
{
printf(": NAND Flash not found !\n");
return;
}
// set spare area size
_reg->spas = sz_spare >> 1;
// set erase block size
switch (sz_erase / sz_write)
{
case 32:
_reg->nand_flash_config1 |= 0x000;
break;
case 64:
_reg->nand_flash_config1 |= 0x200;
break;
case 128:
_reg->nand_flash_config1 |= 0x400;
break;
case 256:
_reg->nand_flash_config1 |= 0x600;
break;
default:
printf("[Mpc5121] Unsupported NAND flash chip!\n");
}
return;
}
void Mpc5121::add(Nand_chip *chip)
{
chip->add_options(Opt_no_autoincr);
_chips[0] = chip;
}
Nand_chip *Mpc5121::select(loff_t /*addr*/)
{
return _chips[0];
}
void Mpc5121::wr_cmd(u8 c)
{
switch (c)
{
case Cmd_pageprog:
_start_write();
_enable_irq();
break;
case Cmd_erase2:
_enable_irq();
break;
default:
break;
}
_reg->nand_flash_cmd = c;
_reg->nand_flash_config2 = 0x1;
// wait for completion
if ((c != Cmd_pageprog) && (c != Cmd_erase2))
// XXX udelay
while (_reg->nand_flash_config2 & 0x1) ;
switch (c)
{
case Cmd_status:
_get_status();
break;
case Cmd_readstart:
_start_read();
break;
case Cmd_seqin:
_buffer_ptr = 0;
break;
default:
break;
}
}
void Mpc5121::wr_adr(u8 a)
{
_reg->nand_flash_addr = a;
_reg->nand_flash_config2 = 0x2;
// XXX udelay
while (_reg->nand_flash_config2 & 0x2) ;
}
void Mpc5121::wr_dat(u8 d)
{ _buffer_main[0] = d; }
u8 Mpc5121::rd_dat()
{ return _buffer_main[0]; }
void Mpc5121::rd_dat(u8 *buf, unsigned len)
{
while (len && (_buffer_ptr < (sz_write + sz_spare)))
{
if (_buffer_ptr < sz_write)
{
int l = len < sz_write ? len : sz_write;
_copy_from_data(buf, l);
_buffer_ptr += l;
len -= l;
}
else if (_buffer_ptr < (sz_write + sz_spare))
{
int l = len < sz_spare ? len : sz_spare;
_copy_from_spare(buf, l);
_buffer_ptr += l;
len -= l;
}
}
}
void Mpc5121::wr_dat(const u8 *buf, unsigned len)
{
while (len && (_buffer_ptr < (sz_write + sz_spare)))
{
if (_buffer_ptr < sz_write)
{
int l = len < sz_write ? len : sz_write;
_copy_to_data(buf, l);
_buffer_ptr += l;
len -= l;
}
else if (_buffer_ptr < (sz_write + sz_spare))
{
int l = len < sz_spare ? len : sz_spare;
_copy_to_spare(buf, l);
_buffer_ptr += l;
len -= l;
}
}
unsigned sum = 0;
for (int i = 0; i < 4096/4; i++)
sum += _buffer_main[i];
printf("buffer sum:%d\n", sum);
}
static void spare_copy(u32 *dst, const u32 *src, int len)
{
for (int i = 0; i < (len/4); ++i)
dst[i] = src[i];
switch (len % 4)
{
case 1:
dst[len/4] = (dst[len/4] & 0xffffff00) | (src[len/4] & 0xff);
break;
case 2:
dst[len/4] = (dst[len/4] & 0xffff0000) | (src[len/4] & 0xffff);
break;
case 3:
dst[len/4] = (dst[len/4] & 0xff000000) | (src[len/4] & 0xffffff);
break;
default:
break;
}
}
void Mpc5121::_copy_from_data(u8 *buf, int len)
{
u32 *p = (u32 *)buf;
len >>= 2;
for (int i = 0; i < len; i++)
p[i] = _buffer_main[i];
}
void Mpc5121::_copy_to_data(const u8 *buf, int len)
{
u32 *p = (u32 *)buf;
len >>= 2;
for (int i = 0; i < len; i++)
_buffer_main[i] = p[i];
}
void Mpc5121::_copy_from_spare(u8 *buf, int len)
{
int section_len = (sz_spare / (sz_write >> 9) >> 1) << 1;
unsigned i = 0;
for (i = 0; i < (sz_write >> 9) - 1; ++i)
spare_copy((u32 *)&buf[i * Spare_section_len], &_buffer_spare[(i * Spare_section_len)/4], section_len);
spare_copy((u32 *)&buf[i * Spare_section_len], &_buffer_spare[(i * Spare_section_len)/4], len - i * section_len);
}
void Mpc5121::_copy_to_spare(const u8 *buf, int len)
{
int section_len = (sz_spare / (sz_write >> 9) >> 1) << 1;
unsigned i = 0;
for (i = 0; i < (sz_write >> 9) - 1; ++i)
spare_copy(&_buffer_spare[i * Spare_section_len/4], (u32 *)&buf[i * Spare_section_len], section_len);
spare_copy(&_buffer_spare[i * Spare_section_len/4], (u32 *)&buf[i * Spare_section_len], len - i * section_len);
}
void Mpc5121::_get_status()
{
_reg->nand_flash_config2 = 0x20;
while (_reg->nand_flash_config2 & 0x20) ;
}
int Mpc5121::get_id(char id[4])
{
wr_cmd(Cmd_readid);
wr_adr(0x00);
// transfer data
_reg->nand_flash_config2 = 0x10;
while (_reg->nand_flash_config2 & 0x10) ;
id[0] = _buffer_main[0];
id[1] = _buffer_main[0] >> 8;
id[2] = _buffer_main[1];
id[3] = _buffer_main[1] >> 8;
return 0;
}
void Mpc5121::_start_read()
{
// clear interrupt status
_reg->nand_flash_config2 = 0x0;
// unmask interrupt
_reg->nand_flash_config1 &= ~0x10;
// start data transfer
_reg->nand_flash_config2 = 0x8;
// reset buffer ptr
_buffer_ptr = 0;
}
void Mpc5121::_start_write()
{
// transfer data to chip
_reg->nand_flash_config2 = 0x4;
while (_reg->nand_flash_config2 & 0x4) ;
}
void Mpc5121::_enable_irq()
{
// clear interrupt status
_reg->nand_flash_config2 = 0x0;
// unmask interrupt
_reg->nand_flash_config1 &= ~0x10;
}
int Mpc5121::handle_irq()
{
// mask interrupt
_reg->nand_flash_config1 |= 0x10;
_chips[0]->handle_irq();
return 0;
}
class Mpc5121_drv : public Nand_drv
{
public:
Mpc5121_drv()
{ arm_nand_register_driver(this); }
int probe(const char *configstr)
{ return (strcmp(configstr, "MPC5121")) ? 0 : 1; }
Nand_ctrl *create(addr base)
{ return new Mpc5121(base); }
};
static Mpc5121_drv mpc5121_drv;

View File

@@ -0,0 +1,76 @@
#pragma once
#include "nand.h"
class Mpc5121 : public Nand_ctrl
{
enum
{ Max_cs = 4, };
enum
{ Max_us_reset = 100, };
enum
{ Spare_section_len = 64, };
struct Reg
{
u16 _res1[2];
u16 ram_buffer_addr;
u16 nand_flash_addr;
u16 nand_flash_cmd;
u16 nfc_configuration;
u16 ecc_status_result1;
u16 ecc_status_result2;
u16 spas;
u16 nf_wr_prot;
u16 _res2[2];
u16 nand_flash_wr_pr_st;
u16 nand_flash_config1;
u16 nand_flash_config2;
u16 _res3;
u16 unlock_start_blk_add0;
u16 unlock_end_blk_add0;
u16 unlock_start_blk_add1;
u16 unlock_end_blk_add1;
u16 unlock_start_blk_add2;
u16 unlock_end_blk_add2;
u16 unlock_start_blk_add3;
u16 unlock_end_blk_add3;
};
public:
Mpc5121(addr base_addr);
protected:
void add(Nand_chip *chip);
Nand_chip *select(loff_t addr);
void wr_cmd(u8 c);
void wr_adr(u8 a);
void wr_dat(u8 d);
u8 rd_dat();
void rd_dat(u8 *buf, unsigned len);
void wr_dat(const u8 *buf, unsigned len);
int handle_irq();
int get_id(char id[4]);
private:
void _get_status();
void _start_read();
void _start_write();
void _enable_irq();
void _copy_from_data(u8 *buf, int len);
void _copy_to_data(const u8 *buf, int len);
void _copy_from_spare(u8 *buf, int len);
void _copy_to_spare(const u8 *buf, int len);
volatile Reg *_reg;
Nand_chip *_chips[Max_cs];
volatile u32 *_buffer_main;
u32 *_buffer_spare;
u32 _buffer_ptr = 0;
};

View File

@@ -0,0 +1,315 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "common.h"
#include "nand.h"
Nand_chip::Nand_chip(Nand_ctrl *ctrl, Dev_desc *dev, Mfr_desc *mfr, int ext_id)
: _state(Ready), _ongoing_op(0), _ctrl(ctrl), _dev(dev), _mfr(mfr)
{
/* New devices have all the information in additional id bytes */
if (!dev->sz_page)
{
/* The 3rd id byte holds MLC / multichip data */
_sz_write = 1024 << (ext_id & 0x3);
ext_id >>= 2;
_sz_spare = (8 << (ext_id & 0x01)) * (_sz_write >> 9);
ext_id >>= 2;
_sz_erase = (64 * 1024) << (ext_id & 0x03);
ext_id >>= 2;
_bus_width = (ext_id & 0x01) ? Opt_buswidth_16 : 0;
}
/* Old devices have chip data hardcoded in the device id table */
else
{
_sz_write = dev->sz_page;
_sz_spare = dev->sz_spare ? dev->sz_spare : _sz_write / 32;
_sz_erase = dev->sz_erase;
_bus_width = dev->options & Opt_buswidth_16;
}
_sz_chip = (long long unsigned)dev->sz_chip << 20;
if (_bus_width != (_options & Opt_buswidth_16))
{
printf("NAND bus width %d instead %d bit\n",
(_options & Opt_buswidth_16) ? 16 : 8, _bus_width ? 16 : 8);
}
_options |= dev->options;
_options |= Opt_no_autoincr;
printf("NAND chip: Mfr ID: 0x%02x(%s), Device ID: 0x%02x(%s)\n",
mfr->id, mfr->name, dev->id, dev->name);
}
int Nand_chip::handle_irq()
{
switch (_state)
{
case Reading:
return _ctrl->done_read(static_cast<Read_op *>(_ongoing_op));
break;
case Writing:
return _ctrl->done_write(static_cast<Write_op *>(_ongoing_op));
break;
case Erasing:
return _ctrl->done_erase(static_cast<Erase_op *>(_ongoing_op));
break;
default:
_state = Ready;
break;
}
return 0;
}
bool Nand_ctrl::is_wp()
{
wr_cmd(Cmd_status);
return (rd_dat() & Status_wp) ? false : true;
}
int Nand_ctrl::get_status()
{
wr_cmd(Cmd_status);
return rd_dat();
}
int Nand_ctrl::read(Read_op *op)
{
Nand_chip *chip = select(op->addr);
if (!op->transfer->len)
return -EINVAL;
u16 col = op->addr & (chip->sz_write() - 1);
u32 row = (op->addr >> chip->page_shift()) & chip->page_mask();
assert(!col);
assert(op->transfer->len + col <= (chip->sz_write() + chip->sz_spare()));
wr_cmd(Cmd_read0);
wr_adr(col);
wr_adr(col >> 8);
wr_adr(row);
wr_adr(row >> 8);
wr_adr(row >> 16);
wr_cmd(Cmd_readstart);
chip->set_state(Nand_chip::Reading, op);
return 0;
}
int Nand_ctrl::done_read(Read_op *op)
{
Nand_chip *chip = select(op->addr);
assert(chip->state() == Nand_chip::Reading);
for (unsigned i = 0; i < op->transfer->num; ++i)
rd_dat((*op->transfer)[i].addr, (*op->transfer)[i].size);
chip->set_state(Nand_chip::Ready);
return 0;
}
int Nand_ctrl::write(Write_op *op)
{
Nand_chip *chip = select(op->addr);
if (!op->transfer->len)
return -EINVAL;
// check end of device
if ((op->addr + op->transfer->len) > size)
return -EINVAL;
// check page aligning
if (!aligned(op->addr))
{
printf("NAND: Attempt to write not page aligned data\n");
return -EINVAL;
}
if (is_wp())
{
printf("NAND: Device is write protected\n");
return -EIO;
}
u32 page = (op->addr >> chip->page_shift()) & chip->page_mask();
wr_cmd(Cmd_seqin);
wr_adr(0x00);
wr_adr(0x00);
wr_adr(page);
wr_adr(page >> 8);
wr_adr(page >> 16);
for (unsigned i = 0; i < op->transfer->num; ++i)
wr_dat((u8 *)(*op->transfer)[i].addr, (*op->transfer)[i].size);
wr_cmd(Cmd_pageprog);
chip->set_state(Nand_chip::Writing, op);
return 0;
}
int Nand_ctrl::done_write(Write_op *op)
{
Nand_chip *chip = select(op->addr);
assert(chip->state() == Nand_chip::Writing);
chip->set_state(Nand_chip::Ready);
return (get_status() & Status_fail) ? -EIO : 0;
}
int Nand_ctrl::erase(Erase_op *op)
{
Nand_chip *chip = select(op->addr);
#if 0
printf("nand: erase: start = 0x%08x, len = %u\n",
(unsigned int) op->addr, op->len);
#endif
/* address must align on block boundary */
if (op->addr & chip->erase_mask())
{
printf("nand: erase: Unaligned address\n");
return -EINVAL;
}
/* length must align on block boundary */
if (op->len & chip->erase_mask())
{
printf("nand: erase: Length not block aligned\n");
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((op->len + op->addr) > size)
{
printf("nand: erase: Erase past end of device\n");
return -EINVAL;
}
/* Check, if it is write protected */
if (is_wp())
{
printf("nand_erase: Device is write protected!!!\n");
return -EIO;
}
int page = op->addr >> chip->page_shift();
wr_cmd(Cmd_erase1);
wr_adr(page);
wr_adr(page >> 8);
wr_adr(page >> 16);
wr_cmd(Cmd_erase2);
chip->set_state(Nand_chip::Erasing, op);
return 0;
}
int Nand_ctrl::done_erase(Erase_op *op)
{
Nand_chip *chip = select(op->addr);
assert(chip->state() == Nand_chip::Erasing);
chip->set_state(Nand_chip::Ready);
return (get_status() & Status_fail) ? -EIO : 0;
}
int Nand_ctrl::get_id(char id[4])
{
wr_cmd(Cmd_readid);
wr_adr(0x00);
wr_dat(0xff);
id[0] = rd_dat();
id[1] = rd_dat();
id[2] = rd_dat();
id[3] = rd_dat();
return 0;
}
int Nand_ctrl::scan(int maxchips)
{
for (int i = 0; i < maxchips; ++i)
{
wr_cmd(Cmd_reset);
udelay(100);
wr_cmd(Cmd_status);
while (!(rd_dat() & Status_ready));
char id1[4], id2[4];
get_id(id1);
get_id(id2);
if (id1[0] != id2[0] || id1[1] != id2[1])
{
printf("manufacturer or device id corrupt:\n");
printf("mfr-id1:%02x mfr-id2:%02x\n", id1[0], id2[0]);
printf("dev-id1:%02x dev-id2:%02x\n", id1[1], id2[1]);
return -1;
}
/* identify manufacturer */
Mfr_desc *mfr = 0;
for (int j = 0; _mfr_ids[j].id != 0; j++)
{
if (id1[0] == _mfr_ids[j].id)
{
mfr = &_mfr_ids[j];
break;
}
}
/* identify device */
Dev_desc *dev = 0;
for (int j = 0; _dev_ids[j].name != 0; j++)
{
if (id1[1] == _dev_ids[j].id)
{
dev = &_dev_ids[j];
break;
}
}
if (!dev)
{
printf("no device device found\n");
continue;
}
if (!mfr)
{
printf("no manufacturer found\n");
continue;
}
Nand_chip *chip = new Nand_chip(this, dev, mfr, id1[3]);
add(chip);
// XXX all chips should have the same features
sz_write = chip->sz_write();
sz_spare = chip->sz_spare();
sz_erase = chip->sz_erase();
size += chip->sz_chip();
numchips++;
}
//printf("%d NAND chips detected\n", numchips);
return 0;
}
Nand_ctrl::Nand_ctrl()
{}

View File

@@ -0,0 +1,204 @@
#pragma once
#include <string.h>
#include "types.h"
#include <l4/drivers-frst/transfer.h>
struct Op
{};
struct Read_op : Op
{
u32 addr;
Transfer *transfer;
};
struct Write_op : Op
{
u32 addr;
Transfer *transfer;
};
struct Erase_op : Op
{
u32 addr;
u32 len;
u_char state;
};
struct Dev_desc
{
int id;
const char *name;
unsigned long sz_page; // bytes
unsigned long sz_spare; // bytes
unsigned long sz_chip; // MiB
unsigned long sz_erase; // bytes
unsigned long options;
};
struct Mfr_desc
{
int id;
const char *name;
};
extern Dev_desc _dev_ids[];
extern Mfr_desc _mfr_ids[];
enum
{
Opt_no_autoincr = 0x001,
Opt_buswidth_16 = 0x002,
Opt_no_padding = 0x004,
Opt_cacheprg = 0x008,
Opt_copyback = 0x010,
Opt_is_and = 0x020,
Opt_4page_array = 0x040,
/* Chip does not require ready check on read. True
* for all large page devices, as they do not support autoincrement.*/
Opt_no_readrdy = 0x100,
Opt_no_subpage_write = 0x200,
};
class Nand_ctrl;
class Nand_chip
{
public:
enum State
{
Ready,
Reading,
Writing,
Erasing,
Cachedprg,
};
Nand_chip(Nand_ctrl *ctrl, Dev_desc *dev, Mfr_desc *mfr, int ext_id = 0);
State state() {return _state; }
void set_state(State state) { _state = state; }
void set_state(State state, Op *op)
{
_state = state;
_ongoing_op = op;
}
u32 sz_write() const
{ return _sz_write; }
u32 sz_spare() const
{ return _sz_spare; }
u32 sz_erase() const
{ return _sz_erase; }
u64 sz_chip() const
{ return _sz_chip; }
void add_options(int options)
{ _options |= options; }
int page_shift() { return ffs(_sz_write) - 1; }
int page_mask() { return (sz_chip() >> page_shift()) - 1; }
int erase_shift() { return ffs(_sz_erase) - 1; }
int erase_mask() { return (sz_chip() >> erase_shift()) - 1; }
int handle_irq();
protected:
u32 _sz_write;
u32 _sz_spare;
u32 _sz_erase;
u64 _sz_chip;
int _bus_width;
int _options = 0;
private:
State _state;
Op *_ongoing_op;
Nand_ctrl *_ctrl;
Dev_desc *_dev;
Mfr_desc *_mfr;
};
class Nand_ctrl
{
protected:
// commands
enum
{
// standard device commands
Cmd_read0 = 0x00,
Cmd_read1 = 0x01,
Cmd_rndout = 0x05,
Cmd_pageprog = 0x10,
Cmd_readoob = 0x50,
Cmd_erase1 = 0x60,
Cmd_status = 0x70,
Cmd_seqin = 0x80,
Cmd_rndin = 0x85,
Cmd_readid = 0x90,
Cmd_erase2 = 0xd0,
Cmd_reset = 0xff,
// large page device commands
Cmd_readstart = 0x30,
Cmd_rndoutstart = 0xe0,
Cmd_cachedprog = 0x15,
};
/* status bits */
enum
{
Status_fail = 0x01,
Status_fail_n1 = 0x02,
Status_true_ready = 0x20,
Status_ready = 0x40,
Status_wp = 0x80,
};
public:
Nand_ctrl();
int read(Read_op *op);
int write(Write_op *op);
int erase(Erase_op *op);
int done_read(Read_op *op);
int done_write(Write_op *op);
int done_erase(Erase_op *op);
virtual int handle_irq() = 0;
protected:
int scan(int maxchips);
virtual void add(Nand_chip *chip) = 0;
virtual Nand_chip *select(loff_t addr) = 0;
virtual void wr_cmd(u8 c) = 0;
virtual void wr_adr(u8 a) = 0;
virtual void wr_dat(u8 d) = 0;
bool aligned(u32 addr) const
{ return !(addr & (sz_write - 1)); }
virtual u8 rd_dat() = 0;
virtual void rd_dat(u8 *buf, unsigned len) = 0;
virtual void wr_dat(const u8 *buf, unsigned len) = 0;
bool is_wp();
int get_status();
virtual int get_id(char id[4]);
public:
const char *name = 0;
unsigned numchips = 0;
u64 size = 0;
u32 sz_erase = 0;
u32 sz_write = 0;
u32 sz_spare = 0;
};

View File

@@ -0,0 +1,115 @@
#include "common.h"
#include "nand.h"
/*
* Chip ID list
*
* Id, name, page size, spare size, chip size in MegaByte, erase block size, options
*/
struct Dev_desc _dev_ids[] =
{
{0x6e, "NAND 1MiB 5V 8-bit", 256, 0, 1, 0x1000, 0},
{0x64, "NAND 2MiB 5V 8-bit", 256, 0, 2, 0x1000, 0},
{0x6b, "NAND 4MiB 5V 8-bit", 512, 0, 4, 0x2000, 0},
{0xe8, "NAND 1MiB 3,3V 8-bit", 256, 0, 1, 0x1000, 0},
{0xec, "NAND 1MiB 3,3V 8-bit", 256, 0, 1, 0x1000, 0},
{0xea, "NAND 2MiB 3,3V 8-bit", 256, 0, 2, 0x1000, 0},
{0xd5, "NAND 4MiB 3,3V 8-bit", 512, 0, 4, 0x2000, 0},
{0xe3, "NAND 4MiB 3,3V 8-bit", 512, 0, 4, 0x2000, 0},
{0xe5, "NAND 4MiB 3,3V 8-bit", 512, 0, 4, 0x2000, 0},
{0xd6, "NAND 8MiB 3,3V 8-bit", 512, 0, 8, 0x2000, 0},
{0x39, "NAND 8MiB 1,8V 8-bit", 512, 0, 8, 0x2000, 0},
{0xe6, "NAND 8MiB 3,3V 8-bit", 512, 0, 8, 0x2000, 0},
{0x49, "NAND 8MiB 1,8V 16-bit", 512, 0, 8, 0x2000, Opt_buswidth_16},
{0x59, "NAND 8MiB 3,3V 16-bit", 512, 0, 8, 0x2000, Opt_buswidth_16},
{0x33, "NAND 16MiB 1,8V 8-bit", 512, 0, 16, 0x4000, 0},
{0x73, "NAND 16MiB 3,3V 8-bit", 512, 0, 16, 0x4000, 0},
{0x43, "NAND 16MiB 1,8V 16-bit", 512, 0, 16, 0x4000, Opt_buswidth_16},
{0x53, "NAND 16MiB 3,3V 16-bit", 512, 0, 16, 0x4000, Opt_buswidth_16},
{0x35, "NAND 32MiB 1,8V 8-bit", 512, 0, 32, 0x4000, 0},
{0x75, "NAND 32MiB 3,3V 8-bit", 512, 0, 32, 0x4000, 0},
{0x45, "NAND 32MiB 1,8V 16-bit", 512, 0, 32, 0x4000, Opt_buswidth_16},
{0x55, "NAND 32MiB 3,3V 16-bit", 512, 0, 32, 0x4000, Opt_buswidth_16},
{0x36, "NAND 64MiB 1,8V 8-bit", 512, 0, 64, 0x4000, 0},
{0x76, "NAND 64MiB 3,3V 8-bit", 512, 0, 64, 0x4000, 0},
{0x46, "NAND 64MiB 1,8V 16-bit", 512, 0, 64, 0x4000, Opt_buswidth_16},
{0x56, "NAND 64MiB 3,3V 16-bit", 512, 0, 64, 0x4000, Opt_buswidth_16},
{0x78, "NAND 128MiB 1,8V 8-bit", 512, 0, 128, 0x4000, 0},
{0x39, "NAND 128MiB 1,8V 8-bit", 512, 0, 128, 0x4000, 0},
{0x79, "NAND 128MiB 3,3V 8-bit", 512, 0, 128, 0x4000, 0},
{0x72, "NAND 128MiB 1,8V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x49, "NAND 128MiB 1,8V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x74, "NAND 128MiB 3,3V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x59, "NAND 128MiB 3,3V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x71, "NAND 256MiB 3,3V 8-bit", 512, 0, 256, 0x4000, 0},
/*
* These are the new chips with large page size. The pagesize and the
* erasesize is determined from the extended id bytes
*/
#define LP_OPTIONS (Opt_no_readrdy | Opt_no_autoincr)
#define LP_OPTIONS16 (LP_OPTIONS | Opt_buswidth_16)
/*512 Megabit */
{0xA2, "NAND 64MiB 1,8V 8-bit", 0, 0, 64, 0, LP_OPTIONS},
{0xF2, "NAND 64MiB 3,3V 8-bit", 0, 0, 64, 0, LP_OPTIONS},
{0xB2, "NAND 64MiB 1,8V 16-bit", 0, 0, 64, 0, LP_OPTIONS16},
{0xC2, "NAND 64MiB 3,3V 16-bit", 0, 0, 64, 0, LP_OPTIONS16},
/* 1 Gigabit */
{0xA1, "NAND 128MiB 1,8V 8-bit", 0, 0, 128, 0, LP_OPTIONS},
{0xF1, "NAND 128MiB 3,3V 8-bit", 0, 0, 128, 0, LP_OPTIONS},
{0xB1, "NAND 128MiB 1,8V 16-bit", 0, 0, 128, 0, LP_OPTIONS16},
{0xC1, "NAND 128MiB 3,3V 16-bit", 0, 0, 128, 0, LP_OPTIONS16},
/* 2 Gigabit */
{0xAA, "NAND 256MiB 1,8V 8-bit", 0, 0, 256, 0, LP_OPTIONS},
{0xDA, "NAND 256MiB 3,3V 8-bit", 0, 0, 256, 0, LP_OPTIONS},
{0xBA, "NAND 256MiB 1,8V 16-bit", 0, 0, 256, 0, LP_OPTIONS16},
{0xCA, "NAND 256MiB 3,3V 16-bit", 0, 0, 256, 0, LP_OPTIONS16},
/* 4 Gigabit */
{0xAC, "NAND 512MiB 1,8V 8-bit", 0, 0, 512, 0, LP_OPTIONS},
{0xDC, "NAND 512MiB 3,3V 8-bit", 0, 0, 512, 0, LP_OPTIONS},
{0xBC, "NAND 512MiB 1,8V 16-bit", 0, 0, 512, 0, LP_OPTIONS16},
{0xCC, "NAND 512MiB 3,3V 16-bit", 0, 0, 512, 0, LP_OPTIONS16},
/* 8 Gigabit */
{0xA3, "NAND 1GiB 1,8V 8-bit", 0, 0, 1024, 0, LP_OPTIONS},
{0xD3, "NAND 1GiB 3,3V 8-bit", 0, 0, 1024, 0, LP_OPTIONS},
{0xB3, "NAND 1GiB 1,8V 16-bit", 0, 0, 1024, 0, LP_OPTIONS16},
{0xC3, "NAND 1GiB 3,3V 16-bit", 0, 0, 1024, 0, LP_OPTIONS16},
/* 16 Gigabit */
{0xA5, "NAND 2GiB 1,8V 8-bit", 0, 0, 2048, 0, LP_OPTIONS},
{0xD5, "NAND 2GiB 3,3V 8-bit", 0, 0, 2048, 0, LP_OPTIONS},
{0xB5, "NAND 2GiB 1,8V 16-bit", 0, 0, 2048, 0, LP_OPTIONS16},
{0xC5, "NAND 2GiB 3,3V 16-bit", 0, 0, 2048, 0, LP_OPTIONS16},
{0xD7, "NAND 4GiB ?V 16-bit", 4096, 218, 4096, 0x80000, LP_OPTIONS16},
{0, 0, 0, 0, 0, 0, 0}
};
/*
* Manufacturer ID list
*/
struct Mfr_desc _mfr_ids[] =
{
{0x01, "AMD"},
{0x04, "Fujitsu"},
{0x07, "Renesas"},
{0x20, "ST Micro"},
{0x2c, "Micron"},
{0x84, "National"},
{0x98, "Toshiba"},
{0xad, "Hynix"},
{0xec, "Samsung"},
{0x00, "Unknown"},
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include "sys/types.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
typedef long unsigned addr;

View File

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

View File

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

View File

@@ -0,0 +1,117 @@
#ifndef L4_PPC32_OF_H__
#define L4_PPC32_OF_H__
#include <stdarg.h>
#include <string.h>
namespace L4_drivers
{
class Of
{
private:
/* declarations */
struct prom_args
{
const char *service;
int nargs;
int nret;
void *args[10];
prom_args(const char *s, int na, int nr) : service(s), nargs(na), nret(nr) {}
};
typedef int (*prom_entry)(struct prom_args *);
//int prom_call(const char *service, int nargs, int nret, ...) const;
protected:
enum {
PROM_ERROR = -1u
};
typedef void *ihandle_t;
typedef void *phandle_t;
typedef struct
{
unsigned long len;
char data[];
} of_item_t;
/* methods */
unsigned long prom_call(const char *service, int nargs, int nret, ...) const
{
struct prom_args args = prom_args(service, nargs, nret);
va_list list;
va_start(list, nret);
for(int i = 0; i < nargs; i++)
args.args[i] = va_arg(list, void*);
va_end(list);
for(int i = 0; i < nret; i++)
args.args[nargs + i] = 0;
if(_prom()(&args) < 0)
return PROM_ERROR;
return (nret > 0) ? (unsigned long)args.args[nargs] : 0;
}
int prom_getprop(phandle_t node, const char *pname, void *value,
size_t size)
{
return prom_call("getprop", 4, 1, node, pname, (unsigned long)value,
(unsigned long)size);
}
int prom_next_node(phandle_t *nodep)
{
phandle_t node;
if ((node = *nodep) != 0
&& (*nodep = (phandle_t)prom_call("child", 1, 1, node)) != 0)
return 1;
if ((*nodep = (phandle_t)prom_call("peer", 1, 1, node)) != 0)
return 1;
for (;;) {
if ((node = (phandle_t)prom_call("parent", 1, 1, node)) == 0)
return 0;
if ((*nodep = (phandle_t)prom_call("peer", 1, 1, node)) != 0)
return 1;
}
}
template<typename T>
static inline bool handle_valid(T p)
{
return ((unsigned long)p != 0 && (unsigned long)p != PROM_ERROR);
}
static prom_entry _prom(unsigned long prom = 0)
{
static prom_entry local_prom;
if(prom)
local_prom = reinterpret_cast<prom_entry>(prom);
return local_prom;
}
public:
Of() {};
static void set_prom(unsigned long prom)
{
_prom(prom);
}
static unsigned long get_prom()
{
return reinterpret_cast<unsigned long>(_prom());
}
};
}
#endif /* L4_PPC32_OF_H__*/

View File

@@ -0,0 +1,24 @@
#ifndef _L4_PPC32_OF_DEV_H__
#define _L4_PPC32_OF_DEV_H__
enum
{
MAX_OF_DEVICES = 32
};
typedef struct of_device_t
{
char type[32]; //type name
char name[32]; //device name
unsigned long reg; //address
union {
struct {
unsigned long cpu_freq;
unsigned long bus_freq;
unsigned long time_freq;
} freq;
unsigned long interrupts[3]; //pin, int nr, sense
};
} of_device_t;
#endif

View File

@@ -0,0 +1,40 @@
#include <l4/drivers/of.h>
#include <stdio.h>
namespace L4_drivers
{
class Of_if : public Of
{
private:
phandle_t _chosen;
ihandle_t _root;
phandle_t get_device(const char*, const char *prop = "device_type");
unsigned long cpu_detect(const char *prop);
public:
Of_if() : Of(), _chosen(0), _root(0) {}
unsigned long detect_ramsize();
unsigned long detect_cpu_freq();
unsigned long detect_bus_freq();
unsigned long detect_time_freq();
bool detect_devices(unsigned long *start_addr, unsigned long *length);
void boot_finish();
phandle_t get_chosen()
{
if(handle_valid(_chosen)) return _chosen;
_chosen = (phandle_t)prom_call("finddevice", 1, 1, "/chosen");
return _chosen;
}
ihandle_t get_root()
{
if(handle_valid(_root)) return _root;
_root = (ihandle_t)prom_call("finddevice", 1, 1, "/");
return _root;
}
void vesa_set_mode(int mode);
};
}

View File

@@ -0,0 +1,13 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
SRC_CC := of.cc
SYSTEMS = $(SYSTEMS_PLAIN)
TARGET = libdrivers_of.a
PC_FILENAME = drivers_of
PRIVATE_INCDIR += $(SRC_DIR)/../include
include $(L4DIR)/mk/lib.mk
CXXFLAGS += -DL4_NO_RTTI -fno-exceptions -fno-rtti

View File

@@ -0,0 +1,152 @@
#include "of_if.h"
#include <l4/drivers/of_dev.h>
namespace L4_drivers
{
unsigned long Of_if::detect_ramsize()
{
unsigned long addr_cells, size_cells, ram_size;
unsigned long reg[100];
phandle_t mem;
prom_getprop(get_root(), "#address-cells", &addr_cells, sizeof(&addr_cells));
prom_getprop(get_root(), "#size-cells", &size_cells, sizeof(&size_cells));
ram_size = 0;
mem = get_device("memory");
unsigned int len = prom_getprop(mem, "reg", &reg, sizeof(reg));
len /= 4; /* always 32 bit values */
if(len > sizeof(reg))
len = sizeof(reg);
unsigned long base, size;
for(unsigned i = 0; i < len; i += (addr_cells + size_cells)) {
base = reg[i + addr_cells - 1];
size = reg[i + addr_cells + size_cells - 1];
if((base + size) > ram_size)
ram_size = base + size;
printf(" Memory base: [%08lx; %08lx] size: %lu KB\n",
base, base + size, size / 1024);
}
return ram_size;
}
unsigned long Of_if::cpu_detect(const char* prop)
{
phandle_t cpu;
unsigned long val;
cpu = get_device("cpu");
prom_getprop(cpu, prop, &val, sizeof(&val));
return val;
}
unsigned long Of_if::detect_bus_freq()
{
return cpu_detect("bus-frequency");
}
unsigned long Of_if::detect_cpu_freq()
{
return cpu_detect("clock-frequency");
}
unsigned long Of_if::detect_time_freq()
{
return cpu_detect("timebase-frequency");
}
L4_drivers::Of_if::phandle_t Of_if::get_device(const char *device, const char *prop)
{
phandle_t dev;
char type[32];
for(dev = 0; prom_next_node(&dev); )
{
prom_getprop(dev, prop, type, sizeof(type));
if (strcmp(type, device))
continue;
return dev;
}
return 0;
}
bool Of_if::detect_devices(unsigned long *start_addr, unsigned long *length)
{
static of_device_t dev[MAX_OF_DEVICES];
char descr[64];
printf(" Detecting hardware ...\n");
int i = -1;
for(phandle_t node = 0; prom_next_node(&node) && i < MAX_OF_DEVICES-1;)
{
if (prom_getprop(node, "device_type", &(dev[i+1].type),
sizeof(dev[i].type)) < 0)
continue;
i++;
prom_getprop(node, "name", &dev[i].name, sizeof(dev[i].name));
if (prom_getprop(node, "reg", &(dev[i].reg), sizeof(dev[i].reg)) < 0)
dev[i].reg = 0;
if (!strcmp(dev[i].type, "cpu"))
{
dev[i].freq.cpu_freq = detect_cpu_freq();
dev[i].freq.bus_freq = detect_bus_freq();
dev[i].freq.time_freq = detect_time_freq();
}
else if (prom_getprop(node, "interrupts", (dev[i].interrupts),
sizeof(dev[i].interrupts)) < 0)
dev[i].interrupts[0] = dev[i].interrupts[1] = dev[i].interrupts[2] = ~0UL;
//just for information
prom_getprop(node, ".description", descr, sizeof(descr));
printf("\33[1;36m"
" [%s]\33[0;36m (%s)\33[0m\n"
" reg: %08lx interrupts: %08lx, %08lx, %08lx\n",
dev[i].name, descr, dev[i].reg, dev[i].interrupts[0],
dev[i].interrupts[1], dev[i].interrupts[2]);
}
if (i < 0)
return false;
*start_addr = (unsigned long)dev;
*length = (i + 1) * sizeof(of_device_t);
return true;
}
void Of_if::boot_finish()
{
ihandle_t _std;
//close stdin
prom_getprop(get_chosen(), "stdin", &_std, sizeof(_std));
prom_call("close", 1, 0, _std);
//close stdout
prom_getprop(get_chosen(), "stdout", &_std, sizeof(_std));
prom_call("close", 1, 0, _std);
//finish pending dma requests
prom_call("quiesce", 0, 0);
}
void Of_if::vesa_set_mode(int mode)
{
(void)mode;
unsigned long test_mode = 0x117;
phandle_t disp = get_device("display", "name");
int ret = prom_call("vesa-set-mode", 2, 1, disp, test_mode);// &test_mode);
unsigned long adr = prom_call("vesa-frame-buffer-adr", 1, 1);
printf("VESA: returned %d fb %08lx\n", ret, adr);
}
}

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