blob: 5b754f9c0749b4bb8e89dad83c99ee2bf36c2708 [file]
// Copyright 2022 Google LLC
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/testing/test_rom/puppeteer_utils/uart.h"
#include "hw/top_matcha/sw/autogen/top_matcha.h" // Generated.
#include "pinmux_regs.h" // NOLINT(build/include_subdir) Generated.
#include "sw/device/lib/testing/test_rom/puppeteer_utils/opentitan_uart.h"
#include "uart_regs.h" // NOLINT(build/include_subdir) Generated.
// Opentitan toolchain doesn't invoke -lgcc, so the clk divider needs to be
// computed in preprocessor instead of in runtime to bypass the uint64 division.
#ifndef TINY_IO_PERIPHERAL_CLOCK
#define TINY_IO_PERIPHERAL_CLOCK 2500000ull
#endif
#ifndef TINY_IO_BUAD_RATE
#define TINY_IO_BUAD_RATE 115200ull
#endif
// The UART's clock scaler is a 16-bit accumulator that needs to overflow 16
// times per bit period to account for oversampling. We do this bit of math
// using 64-bit ints so we don't overflow, even though the resulting divider
// setting is only 16 bits.
#define TINY_IO_CLK_DIV \
(16ull * 65536ull * TINY_IO_BUAD_RATE) / TINY_IO_PERIPHERAL_CLOCK
// Simple helper method to initialize a TinyIO interface to point to Ibex's
// UART0 peripheral.
TinyIO init_uart0() {
// To use UART0, we need to set up the pinmux to connect uart 0 to some MIO
// pads and set the pin drive modes.
volatile uint32_t* insel_base = reinterpret_cast<uint32_t*>(
TOP_MATCHA_PINMUX_AON_BASE_ADDR + PINMUX_MIO_PERIPH_INSEL_0_REG_OFFSET);
volatile uint32_t* outsel_base = reinterpret_cast<uint32_t*>(
TOP_MATCHA_PINMUX_AON_BASE_ADDR + PINMUX_MIO_OUTSEL_0_REG_OFFSET);
// Uart0Rx input is tied to MIO pad 25 (GPIO C3).
insel_base[kTopMatchaPinmuxPeripheralInUart0Rx] = kTopMatchaPinmuxInselIoc3;
// MIO pad 25 output (GPIO C3) is set to High-Z.
outsel_base[kTopMatchaPinmuxMioOutIoc3] = kTopMatchaPinmuxOutselConstantHighZ;
// MIO pad 26 output (GPIO C4) is set to Uart0Tx.
outsel_base[kTopMatchaPinmuxMioOutIoc4] = kTopMatchaPinmuxOutselUart0Tx;
// Turn on the UART peripheral.
uint64_t clk_div = TINY_IO_CLK_DIV;
volatile OpenTitanUart* uart =
reinterpret_cast<OpenTitanUart*>(TOP_MATCHA_UART0_BASE_ADDR);
uart->CTRL = 0;
uart->FIFO_CTRL =
(1 << UART_FIFO_CTRL_RXRST_BIT) | (1 << UART_FIFO_CTRL_TXRST_BIT);
uart->OVRD = 0;
uart->VAL = 0;
uart->INTR_ENABLE = 0;
uart->INTR_STATE = ~0;
uart->CTRL =
(clk_div << 16) | (1 << UART_CTRL_TX_BIT) | (1 << UART_CTRL_RX_BIT);
// The "get byte" and "put byte" methods for the bootrom just busy-wait while
// the queue is empty/full.
auto getter = [](void* user) -> uint8_t {
volatile OpenTitanUart* uart = reinterpret_cast<OpenTitanUart*>(user);
while (uart->STATUS & (1 << UART_STATUS_RXEMPTY_BIT)) {
}
return uart->RDATA;
};
auto setter = [](void* user, uint8_t b) {
volatile OpenTitanUart* uart = reinterpret_cast<OpenTitanUart*>(user);
while (uart->STATUS & (1 << UART_STATUS_TXFULL_BIT)) {
}
uart->WDATA = b;
};
return {getter, setter, reinterpret_cast<void*>(TOP_MATCHA_UART0_BASE_ADDR)};
}
void init_smc_uart() {
volatile uint32_t* insel_base = reinterpret_cast<uint32_t*>(
TOP_MATCHA_PINMUX_AON_BASE_ADDR + PINMUX_MIO_PERIPH_INSEL_0_REG_OFFSET);
volatile uint32_t* outsel_base = reinterpret_cast<uint32_t*>(
TOP_MATCHA_PINMUX_AON_BASE_ADDR + PINMUX_MIO_OUTSEL_0_REG_OFFSET);
// SmcUartRx input is tied to MIO pad 32 (GPIO C10).
insel_base[kTopMatchaPinmuxPeripheralInSmcUartRx] =
kTopMatchaPinmuxInselIoc10;
// MIO pad 32 output (GPIO C10) is set to High-Z.
outsel_base[kTopMatchaPinmuxMioOutIoc10] =
kTopMatchaPinmuxOutselConstantHighZ;
// MIO pad 33 output (GPIO C11) is set to SmcTx.
outsel_base[kTopMatchaPinmuxMioOutIoc11] = kTopMatchaPinmuxOutselSmcUartTx;
}