blob: f3a922f79b81d0f0f64ebbe1cbd4c851975f4a30 [file] [log] [blame]
// Copyright 2021 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_MOTMOT_H_
#define LIB_UART_MOTMOT_H_
#include <lib/zbi-format/driver-config.h>
#include <lib/zbi-format/zbi.h>
#include <array>
#include <hwreg/bitfields.h>
#include "uart.h"
namespace uart::motmot {
// line control register
struct ULCON : public hwreg::RegisterBase<ULCON, uint32_t> {
// 31:6 Reserved.
DEF_FIELD(5, 3, parity_mode);
DEF_BIT(2, num_stop_bits);
DEF_FIELD(1, 0, word_length);
static auto Get() { return hwreg::RegisterAddr<ULCON>(0); }
};
// general control register
struct UCON : public hwreg::RegisterBase<UCON, uint32_t> {
// 31:23 Reserved.
DEF_FIELD(22, 20, tx_dma_burst_size);
// 19 Reserved.
DEF_FIELD(18, 16, rx_dma_burst_size);
DEF_FIELD(15, 12, rx_timeout_interrupt_interval);
DEF_BIT(11, rx_timeout_with_empty_rx_fifo);
DEF_BIT(10, rx_timeout_dma_suspend_enable);
// 9:8 Reserved.
DEF_BIT(7, rx_timeout_enable);
DEF_BIT(6, rx_error_status_interrupt_enable);
DEF_BIT(5, loop_back_mode);
DEF_BIT(4, send_break_signal);
DEF_FIELD(3, 2, transmit_mode);
DEF_FIELD(1, 0, receive_mode);
static auto Get() { return hwreg::RegisterAddr<UCON>(0x4); }
};
// fifo control register
struct UFCON : public hwreg::RegisterBase<UFCON, uint32_t> {
// 31:11 Reserved.
DEF_FIELD(10, 8, tx_fifo_trigger_level);
// 7 Reserved.
DEF_FIELD(6, 4, rx_fifo_trigger_level);
// 3 Reserved.
DEF_BIT(2, tx_fifo_reset);
DEF_BIT(1, rx_fifo_reset);
DEF_BIT(0, fifo_enable);
static auto Get() { return hwreg::RegisterAddr<UFCON>(0x8); }
};
// fifo status register
struct UFSTAT : public hwreg::RegisterBase<UFSTAT, uint32_t> {
// 31:25 Reserved.
DEF_BIT(24, tx_fifo_full);
DEF_FIELD(23, 16, tx_fifo_count);
// 15:10 Reserved.
DEF_BIT(9, rx_fifo_error);
DEF_BIT(8, rx_fifo_full);
DEF_FIELD(7, 0, rx_fifo_count);
static auto Get() { return hwreg::RegisterAddr<UFSTAT>(0x18); }
};
// transmit register
struct UTXH : public hwreg::RegisterBase<UTXH, uint32_t> {
// 31:8 Reserved.
DEF_FIELD(7, 0, data);
static auto Get() { return hwreg::RegisterAddr<UTXH>(0x20); }
};
// receive register
struct URXH : public hwreg::RegisterBase<URXH, uint32_t> {
// 31:8 Reserved.
DEF_FIELD(7, 0, data);
static auto Get() { return hwreg::RegisterAddr<URXH>(0x24); }
};
// interrupt mask register
struct UINTM : public hwreg::RegisterBase<UINTM, uint32_t> {
// 31:4 Reserved.
DEF_BIT(3, mask_cts_irq);
DEF_BIT(2, mask_transmit_irq);
DEF_BIT(1, mask_error_irq);
DEF_BIT(0, mask_receive_irq);
static auto Get() { return hwreg::RegisterAddr<UINTM>(0x38); }
};
// Universal Serial Config register
struct USI_CONFIG : public hwreg::RegisterBase<USI_CONFIG, uint32_t> {
// 31:3 Reserved.
DEF_BIT(2, config_i2c);
DEF_BIT(1, config_spi);
DEF_BIT(0, config_uart);
static auto Get() { return hwreg::RegisterAddr<USI_CONFIG>(0xc0); }
};
// Universal Serial Control register
struct USI_CON : public hwreg::RegisterBase<USI_CON, uint32_t> {
// 31:1 Reserved.
DEF_BIT(0, reset);
static auto Get() { return hwreg::RegisterAddr<USI_CON>(0xc4); }
};
// Universal Serial FIFO Depth register
struct FIFO_DEPTH : public hwreg::RegisterBase<FIFO_DEPTH, uint32_t> {
// 31:25 Reserved.
DEF_FIELD(24, 16, tx_fifo_depth);
// 15:9 Reserved.
DEF_FIELD(8, 0, rx_fifo_depth);
static auto Get() { return hwreg::RegisterAddr<FIFO_DEPTH>(0xdc); }
};
// The number of `IoSlots` used by this driver, determined by the last accessed register, see
// `FIFO_DEPTH`. For unscaled MMIO, this corresponds to the size of the MMIO region
// from a provided base address.
static constexpr size_t kIoSlots = 0xdc + sizeof(uint32_t);
struct Driver : public DriverBase<Driver, ZBI_KERNEL_DRIVER_MOTMOT_UART, zbi_dcfg_simple_t,
IoRegisterType::kMmio8, kIoSlots> {
using Base = DriverBase<Driver, ZBI_KERNEL_DRIVER_MOTMOT_UART, zbi_dcfg_simple_t,
IoRegisterType::kMmio8, kIoSlots>;
static constexpr std::string_view config_name() { return "motmot"; }
template <typename... Args>
explicit Driver(Args&&... args) : Base(std::forward<Args>(args)...) {}
template <class IoProvider>
void Init(IoProvider& io) {
// Do a very basic setup to ensure the RX and TX path is enabled and interrupts
// are masked.
// Read the fifo depth
auto fifo_depth = FIFO_DEPTH::Get().ReadFrom(io.io());
rx_fifo_depth_ = fifo_depth.rx_fifo_depth();
tx_fifo_depth_ = fifo_depth.tx_fifo_depth();
// Mask all IRQs
UINTM::Get()
.FromValue(0)
.set_mask_cts_irq(1)
.set_mask_transmit_irq(1)
.set_mask_error_irq(1)
.set_mask_receive_irq(1)
.WriteTo(io.io());
// Disable fifo
UFCON::Get()
.FromValue(0)
.set_tx_fifo_trigger_level(0)
.set_rx_fifo_trigger_level(0)
.set_fifo_enable(0)
.WriteTo(io.io());
// Reset the fifo and wait for it to clear
UFCON::Get().ReadFrom(io.io()).set_tx_fifo_reset(1).set_rx_fifo_reset(1).WriteTo(io.io());
// wait for both the tx and rx fifo reset to clear
auto ufcon_reg = UFCON::Get().FromValue(0);
do {
ufcon_reg.ReadFrom(io.io());
} while (ufcon_reg.tx_fifo_reset() || ufcon_reg.rx_fifo_reset());
// enable the fifos
ufcon_reg.ReadFrom(io.io()).set_fifo_enable(1).WriteTo(io.io());
// enable rx/tx
UCON::Get()
.ReadFrom(io.io())
.set_transmit_mode(1) // interrupt/polling mode
.set_receive_mode(1) // interrupt/polling mode
.WriteTo(io.io());
}
template <class IoProvider>
bool TxReady(IoProvider& io) {
return !UFSTAT::Get().ReadFrom(io.io()).tx_fifo_full();
}
template <class IoProvider, typename It1, typename It2>
auto Write(IoProvider& io, bool, It1 it, const It2& end) {
UTXH::Get().FromValue(0).set_data(*it).WriteTo(io.io());
return ++it;
}
template <class IoProvider>
std::optional<uint8_t> Read(IoProvider& io) {
if (UFSTAT::Get().ReadFrom(io.io()).rx_fifo_count() == 0) {
return {};
}
// TODO(https://fxbug.dev/42167593) handle clearing a rx fifo error
return URXH::Get().ReadFrom(io.io()).data();
}
template <class IoProvider>
void EnableTxInterrupt(IoProvider& io, bool enable = true) {
// Stubbed out implementation that does nothing.
}
template <class IoProvider>
void EnableRxInterrupt(IoProvider& io, bool enable = true) {
// Stubbed out implementation that does nothing.
}
template <class IoProvider, typename EnableInterruptCallback>
void InitInterrupt(IoProvider& io, EnableInterruptCallback&& enable_interrupt_callback) {
// Stubbed out implementation.
// TODO(https://fxbug.dev/42066912): implement me
enable_interrupt_callback();
}
template <class IoProvider, class Lock, class Waiter, class Tx, class Rx>
void Interrupt(IoProvider& io, Lock& lock, Waiter& waiter, Tx&& tx, Rx&& rx) {
// Stubber out implementation.
// TODO(https://fxbug.dev/42066912): implement me
}
private:
uint32_t rx_fifo_depth_ = 0;
uint32_t tx_fifo_depth_ = 0;
};
} // namespace uart::motmot
#endif // LIB_UART_MOTMOT_H_