blob: 5e112fe733509f1869db18e43197a92756e5a784 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_UART_UART_H_
#define LIB_UART_UART_H_
#include <lib/arch/intrin.h>
#include <lib/devicetree/devicetree.h>
#include <lib/zbi-format/driver-config.h>
#include <lib/zbi-format/zbi.h>
#include <lib/zircon-internal/macros.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <cstdlib>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
#include <hwreg/mmio.h>
#include <hwreg/pio.h>
#include "chars-from.h"
// While this header is unused in this file, it provides the basic implementations
// for `Sync` types.
#include "sync.h"
namespace acpi_lite {
struct AcpiDebugPortDescriptor;
}
namespace uart {
//
// These types are used in configuring the line control settings (i.e., in the
// `SetLineControl()` method).
//
// Number of bits transmitted per character.
enum class DataBits {
k5,
k6,
k7,
k8,
};
// The bit pattern mechanism to help detect transmission errors.
enum class Parity {
kNone, // No bits dedicated to parity.
kEven, // Parity bit present; is 0 iff the number of 1s in the word is even.
kOdd, // Parity bit present; is 0 iff the number of 1s in the word is odd.
};
// The duration of the stop period in terms of the transmitted bit rate.
enum class StopBits {
k1,
k2,
};
// This template is specialized by payload configuration type (see below).
// It parses bits out of strings from the "kernel.serial" boot option.
template <typename Config>
inline std::optional<Config> ParseConfig(std::string_view) {
static_assert(std::is_void_v<Config>, "missing specialization");
return {};
}
// This template is specialized by payload configuration type (see below).
// It recreates a string for Parse.
template <typename Config>
inline void UnparseConfig(const Config& config, FILE* out) {
static_assert(std::is_void_v<Config>, "missing specialization");
}
enum class IoRegisterType {
// MMIO is performed without any scaling what so ever, this means that
// registers offsets are treated as byte offsets from the base address.
kMmio8,
// MMIO is performed with an scaling factor of 4, this means that
// register offsets are treated as 4-byte offsets from the base address.
kMmio32,
// PIO.
kPio,
};
template <IoRegisterType IoRegType>
using IoSlotType = std::conditional_t<IoRegType == IoRegisterType::kPio, uint16_t, size_t>;
// Constant indicating that the number of `io_slots()` is to be determined at
// runtime.
constexpr size_t kDynamicIoSlot = std::numeric_limits<size_t>::max();
// Specific hardware support is implemented in a class uart::xyz::Driver,
// referred to here as UartDriver. The uart::DriverBase template class
// provides a helper base class for UartDriver implementations.
//
// The UartDriver object represents the hardware itself. Many UartDriver
// classes hold no state other than the initial configuration data used in the
// constructor, but a UartDriver is not required to be stateless. However, a
// UartDriver is required to be copy-constructible, trivially destructible,
// and contain no pointers. This makes it safe to copy an object set up by
// physboot into a new object in the virtual-memory kernel to hand off the
// configuration and the state of the hardware.
//
// All access to the UartDriver object is serialized by its caller, so it does
// no synchronization of its own. This serves to serialize the actual access
// to the hardware.
//
// The UartDriver API fills four roles:
// 1. Match a ZBI item that configures this driver.
// 2. Generate a ZBI item for another kernel to match this configuration.
// 3. Configure the IoProvider (see below).
// 4. Drive the actual hardware.
//
// The first three are handled by DriverBase. The KdrvExtra and KdrvConfig
// template arguments give the ZBI_KERNEL_DRIVER_* value and the zbi_dcfg_*_t type for the ZBI
// item. The Pio template argument tells the IoProvider whether this driver
// uses MMIO or PIO (including PIO via MMIO): the number of consecutive PIO
// ports used, or 0 for simple MMIO.
//
// Item matching is done by the MaybeCreate static method. If the item
// matches KdrvExtra, then the UartDriver(KdrvConfig) constructor is called.
// DriverBase provides this constructor to fill the cfg_ field, which the
// UartDriver can then refer to. The UartDriver copy constructor copies it.
//
// The calls to drive the hardware are all template functions passed an
// IoProvider object (see below). The driver accesses device registers using
// hwreg ReadFrom and WriteTo calls on the pointers from the provider. The
// IoProvider constructed is passed uart.config() and uart.pio_size().
//
// `IoSlots` is an opaque parameter whose meaning is tied to the value of `IoRegType`.
// A very broad description would be the number of 'slots' to perform I/O operations.
// * `kMmio8` and `kMmio32` represents the number of bytes from the base address with the proper
// scaling factor applied, 1 and 4 respectively.
// * `kPio` represents the number the port count.
template <typename Driver, uint32_t KdrvExtra, typename KdrvConfig, IoRegisterType IoRegType,
IoSlotType<IoRegType> IoSlots = kDynamicIoSlot>
class DriverBase {
public:
using config_type = KdrvConfig;
// No devicetree bindings by default.
static constexpr std::array<std::string_view, 0> kDevicetreeBindings{};
// Register Io Type.
static constexpr IoRegisterType kIoType = IoRegType;
explicit DriverBase(const config_type& cfg) : cfg_(cfg) {}
constexpr bool operator==(const Driver& other) const {
return memcmp(&cfg_, &other.cfg_, sizeof(cfg_)) == 0;
}
constexpr bool operator!=(const Driver& other) const { return !(*this == other); }
// API to fill a ZBI item describing this UART.
constexpr uint32_t type() const { return ZBI_TYPE_KERNEL_DRIVER; }
constexpr uint32_t extra() const { return KdrvExtra; }
constexpr size_t size() const { return sizeof(cfg_); }
void FillItem(void* payload) const { memcpy(payload, &cfg_, sizeof(cfg_)); }
// API to match a ZBI item describing this UART.
static std::optional<Driver> MaybeCreate(const zbi_header_t& header, const void* payload) {
static_assert(alignof(config_type) <= ZBI_ALIGNMENT);
if (header.type == ZBI_TYPE_KERNEL_DRIVER && header.extra == KdrvExtra &&
header.length >= sizeof(config_type)) {
return Driver{*reinterpret_cast<const config_type*>(payload)};
}
return {};
}
// API to match a configuration string.
static std::optional<Driver> MaybeCreate(std::string_view string) {
const auto config_name = Driver::config_name();
if (string.substr(0, config_name.size()) == config_name) {
string.remove_prefix(config_name.size());
auto config = ParseConfig<KdrvConfig>(string);
if (config) {
return Driver{*config};
}
}
return {};
}
// API to match a devicetree bindings.
static bool MatchDevicetree(const devicetree::PropertyDecoder& decoder) {
if constexpr (Driver::kDevicetreeBindings.size() == 0) {
return false;
} else {
auto compatible = decoder.FindProperty("compatible");
if (!compatible) {
return false;
}
auto compatible_list = compatible->AsStringList();
if (!compatible_list) {
return false;
}
return std::find_first_of(compatible_list->begin(), compatible_list->end(),
Driver::kDevicetreeBindings.begin(),
Driver::kDevicetreeBindings.end()) != compatible_list->end();
}
}
// API to match DBG2 Table (ACPI). Currently only 16550 compatible uarts are supported.
static std::optional<Driver> MaybeCreate(const acpi_lite::AcpiDebugPortDescriptor& debug_port) {
return {};
}
// API to reproduce a configuration string.
void Unparse(FILE* out) const {
fprintf(out, "%.*s", static_cast<int>(Driver::config_name().size()),
Driver::config_name().data());
UnparseConfig<KdrvConfig>(cfg_, out);
}
// TODO(https://fxbug.dev/42053694): Remove once all drivers define this method.
template <class IoProvider>
void SetLineControl(IoProvider& io, std::optional<DataBits> data_bits,
std::optional<Parity> parity, std::optional<StopBits> stop_bits) {
static_assert(!std::is_same_v<IoProvider, IoProvider>,
"TODO(https://fxbug.dev/42053694): implment me");
}
// API for use in IoProvider setup.
const config_type& config() const { return cfg_; }
// Number of 'slots' to perform I/O operations.
template <IoSlotType<IoRegType> _IoSlots = IoSlots,
std::enable_if_t<_IoSlots != kDynamicIoSlot, bool> = true>
constexpr IoSlotType<IoRegType> io_slots() const {
return IoSlots;
}
template <IoSlotType<IoRegType> _IoSlots = IoSlots,
std::enable_if_t<_IoSlots == kDynamicIoSlot, bool> = true>
constexpr IoSlotType<IoRegType> io_slots() const {
static_assert(
!std::is_same_v<void, void>,
"|IoSlots| must be different from |kDynamicIoSlot| or |io_slots| implementation must be provided in derived class.");
return IoSlots;
}
protected:
config_type cfg_;
private:
template <typename T>
static constexpr bool Uninstantiated = false;
// uart::KernelDriver API
//
// These are here just to cause errors if Driver is missing its methods.
// They also serve to document the API required by uart::KernelDriver.
// They should all be overridden by Driver methods.
//
// Each method is a template parameterized by an hwreg-compatible type for
// accessing the hardware registers via hwreg ReadFrom and WriteTo methods.
// This lets Driver types be used with hwreg::Mock in tests independent of
// actual hardware access.
template <typename IoProvider>
void Init(IoProvider& io) {
static_assert(Uninstantiated<IoProvider>, "derived class is missing Init");
}
// Return true if Write can make forward progress right now.
// The return value can be anything contextually convertible to bool.
// The value will be passed on to Write.
template <typename IoProvider>
bool TxReady(IoProvider& io) {
static_assert(Uninstantiated<IoProvider>, "derived class is missing TxReady");
return false;
}
// This is called only when TxReady() has just returned something that
// converts to true; that's passed here so it can convey more information
// such as a count. Advance the iterator at least one and as many as is
// convenient but not past end, outputting each character before advancing.
template <typename IoProvider, typename It1, typename It2>
auto Write(IoProvider& io, bool ready, It1 it, const It2& end) {
static_assert(Uninstantiated<IoProvider>, "derived class is missing Write");
return end;
}
// Poll for an incoming character and return one if there is one.
template <typename IoProvider>
std::optional<uint8_t> Read(IoProvider& io) {
static_assert(Uninstantiated<IoProvider>, "derived class is missing Read");
return {};
}
// Set the UART up to deliver interrupts. This is called after Init.
// After this, Interrupt (below) may be called from interrupt context.
template <typename IoProvider>
void InitInterrupt(IoProvider& io) {
static_assert(Uninstantiated<IoProvider>, "derived class is missing InitInterrupt");
}
// Enable transmit interrupts so Interrupt will be called when TxReady().
template <typename IoProvider>
void EnableTxInterrupt(IoProvider& io) {
static_assert(Uninstantiated<IoProvider>, "derived class is missing EnableTxInterrupt");
}
// Service an interrupt.
// Call tx(sync, disable_tx_irq) if transmission has become ready.
// If receiving has become ready, call rx(sync, read_char, full) one or more
// times, where read_char() -> uint8_t if there is receive buffer
// space and full() -> void if there is no space.
// |sync| provides access to the environment specific synchronization primitives(if any),
// and synchronization related data structures(if any).
template <typename IoProvider, typename Sync, typename Tx, typename Rx>
void Interrupt(IoProvider& io, Sync& sync, Tx&& tx, Rx&& rx) {
static_assert(Uninstantiated<IoProvider>, "derived class is missing Interrupt");
}
};
// The IoProvider is a template class parameterized by UartDriver::config_type,
// i.e. the zbi_dcfg_*_t type for the ZBI item's format. This class is responsible
// for supplying pointers to be passed to hwreg types' ReadFrom and WriteTo.
//
// The IoProvider(UartDriver::config_type, uint16_t pio_size) constructor
// initializes the object. Then IoProvider::io() is called to yield the
// pointer to pass to hwreg calls.
//
// uart::BasicIoProvider handles the simple case where physical MMIO and PIO
// base addresses are used directly. It also provides base classes that can be
// subclassed with an overridden constructor that does address mapping.
//
template <typename Config, IoRegisterType IoType>
class BasicIoProvider;
// This is the default "identity mapping" callback for BasicIoProvider::Init.
// A subclass can have an Init function that calls BasicIoProvider::Init with
// a different callback function.
//
// The `size` parameter represents the number of bytes from `phys`, such that
// the mmio region to be mapped is contained in p[`phys`, `phys` + `size`).
inline auto DirectMapMmio(uint64_t phys, size_t size) {
return reinterpret_cast<volatile void*>(phys);
}
// The specialization used most commonly handles simple MMIO devices.
template <IoRegisterType IoType>
class BasicIoProvider<zbi_dcfg_simple_t, IoType> {
public:
// Just install the MMIO base pointer. The third argument can be passed by
// a subclass constructor method to map the physical address to a virtual
// address.
template <typename T>
BasicIoProvider(const zbi_dcfg_simple_t& cfg, size_t io_slots, T&& map_mmio) {
if constexpr (IoType == IoRegisterType::kMmio8) {
io_.emplace<hwreg::RegisterMmio>(map_mmio(cfg.mmio_phys, io_slots));
} else if constexpr (IoType == IoRegisterType::kMmio32) {
io_.emplace<hwreg::RegisterMmioScaled<uint32_t>>(map_mmio(cfg.mmio_phys, io_slots * 4));
} else {
// Pio uses a different specialization, this should never be reached.
static_assert(!std::is_same_v<T, T>);
}
}
BasicIoProvider(const zbi_dcfg_simple_t& cfg, size_t io_slots)
: BasicIoProvider(cfg, io_slots, DirectMapMmio) {}
BasicIoProvider& operator=(BasicIoProvider&& other) {
io_.swap(other.io_);
return *this;
}
auto* io() { return &io_; }
private:
std::variant<hwreg::RegisterMmio, hwreg::RegisterMmioScaled<uint32_t>> io_{std::in_place_index<0>,
nullptr};
};
// The specialization for devices using actual PIO only occurs on x86.
#if defined(__x86_64__) || defined(__i386__)
template <IoRegisterType IoType>
class BasicIoProvider<zbi_dcfg_simple_pio_t, IoType> {
public:
explicit BasicIoProvider(const zbi_dcfg_simple_pio_t& cfg, uint16_t io_slots) : io_(cfg.base) {
static_assert(IoType == IoRegisterType::kPio);
ZX_DEBUG_ASSERT(io_slots > 0);
}
auto* io() { return &io_; }
private:
hwreg::RegisterDirectPio io_;
};
#endif
// Forward declaration.
namespace mock {
class Driver;
}
// The KernelDriver template class is parameterized by those three to implement
// actual driver logic for some environment.
//
// The KernelDriver constructor just passes its arguments through to the
// UartDriver constructor. So it can be created directly from a configuration
// struct or copied from another UartDriver object. In this way, the device is
// handed off from one KernelDriver instantiation to a different one using a
// different IoProvider (physboot vs kernel) and/or Sync (polling vs blocking).
//
template <class UartDriver, template <typename, IoRegisterType> class IoProvider, class SyncPolicy>
class KernelDriver {
using Waiter = typename SyncPolicy::Waiter;
template <typename LockPolicy>
using Guard = typename SyncPolicy::template Guard<LockPolicy>;
template <typename MemberOf>
using Lock = typename SyncPolicy::template Lock<MemberOf>;
using DefaultLockPolicy = typename SyncPolicy::DefaultLockPolicy;
public:
using uart_type = UartDriver;
static_assert(std::is_copy_constructible_v<uart_type>);
static_assert(std::is_trivially_destructible_v<uart_type> ||
std::is_same_v<uart_type, mock::Driver>);
// This sets up the object but not the device itself. The device might
// already have been set up by a previous instantiation's Init function,
// or might never actually be set up because this instantiation gets
// replaced with a different one before ever calling Init.
template <typename... Args>
explicit KernelDriver(Args&&... args)
: uart_(std::forward<Args>(args)...), io_(uart_.config(), uart_.io_slots()) {
if constexpr (std::is_same_v<mock::Driver, uart_type>) {
// Initialize the mock sync object with the mock driver.
lock_.Init(uart_);
waiter_.Init(uart_);
}
}
// Access underlying hardware driver object.
const auto& uart() const { return uart_; }
auto& uart() { return uart_; }
// Access IoProvider object.
auto& io() { return io_; }
// Set up the device for nonblocking output and polling input.
// If the device is handed off from a different instantiation,
// this won't be called in the new instantiation.
template <typename LockPolicy = DefaultLockPolicy>
void Init() {
Guard<LockPolicy> lock(&lock_, SOURCE_TAG);
uart_.Init(io_);
}
// Write out a string that Parse() can read back to recreate the driver
// state. This doesn't preserve the driver state, only the configuration.
template <typename LockPolicy = DefaultLockPolicy>
void Unparse(FILE* out) const {
Guard<LockPolicy> lock(&lock_, SOURCE_TAG);
uart_.Unparse(out);
}
// Configure the UART line control settings.
//
// An individual setting given by std::nullopt signifies that it should be
// left as previously configured.
//
// TODO(https://fxbug.dev/42053694): Separate out the setting of baud rate.
template <typename LockPolicy = DefaultLockPolicy>
void SetLineControl(std::optional<DataBits> data_bits = DataBits::k8,
std::optional<Parity> parity = Parity::kNone,
std::optional<StopBits> stop_bits = StopBits::k1) {
Guard<LockPolicy> lock(&lock_, SOURCE_TAG);
uart_.SetLineControl(io_, data_bits, parity, stop_bits);
}
// TODO(https://fxbug.dev/42079801): Asses the need of |enable_interrupt_callback|.
template <typename LockPolicy = DefaultLockPolicy, typename EnableInterruptCallback>
void InitInterrupt(EnableInterruptCallback&& enable_interrupt_callback) {
Guard<LockPolicy> lock(&lock_, SOURCE_TAG);
uart_.InitInterrupt(io_, std::forward<EnableInterruptCallback>(enable_interrupt_callback));
}
template <typename Tx, typename Rx>
void Interrupt(Tx&& tx, Rx&& rx) TA_NO_THREAD_SAFETY_ANALYSIS {
// Interrupt is responsible for properly acquiring and releasing sync
// where needed.
uart_.Interrupt(io_, lock_, waiter_, std::forward<Tx>(tx), std::forward<Rx>(rx));
}
// This is the FILE-compatible API: `FILE::stdout_ = FILE{&driver};`.
template <typename LockPolicy = DefaultLockPolicy>
int Write(std::string_view str) {
uart::CharsFrom chars(str); // Massage into uint8_t with \n -> CRLF.
auto it = chars.begin();
Guard<LockPolicy> lock(&lock_, SOURCE_TAG);
while (it != chars.end()) {
// Wait until the UART is ready for Write.
auto ready = uart_.TxReady(io_);
while (!ready) {
// Block or just unlock and spin or whatever "wait" means to Sync.
// If that means blocking for interrupt wakeup, enable tx interrupts.
waiter_.Wait(lock, [this]() {
SyncPolicy::AssertHeld(lock_);
uart_.EnableTxInterrupt(io_);
});
ready = uart_.TxReady(io_);
}
// Advance the iterator by writing some.
it = uart_.Write(io_, std::move(ready), it, chars.end());
}
return static_cast<int>(str.size());
}
// This is a direct polling read, not used in interrupt-based operation.
template <typename LockPolicy = DefaultLockPolicy>
auto Read() {
Guard<LockPolicy> lock(&lock_, SOURCE_TAG);
return uart_.Read(io_);
}
template <typename LockPolicy = DefaultLockPolicy>
void EnableRxInterrupt() {
Guard<LockPolicy> lock(&lock_, SOURCE_TAG);
uart_.EnableRxInterrupt(io_);
}
private:
Lock<KernelDriver> lock_;
Waiter waiter_ TA_GUARDED(lock_);
uart_type uart_ TA_GUARDED(lock_);
IoProvider<typename uart_type::config_type, uart_type::kIoType> io_;
};
// These specializations are defined in the library to cover all the ZBI item
// payload types used by the various drivers.
template <>
std::optional<zbi_dcfg_simple_t> ParseConfig<zbi_dcfg_simple_t>(std::string_view string);
template <>
void UnparseConfig(const zbi_dcfg_simple_t& config, FILE* out);
template <>
std::optional<zbi_dcfg_simple_pio_t> ParseConfig<zbi_dcfg_simple_pio_t>(std::string_view string);
template <>
void UnparseConfig(const zbi_dcfg_simple_pio_t& config, FILE* out);
} // namespace uart
#endif // LIB_UART_UART_H_