// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "sio.h" #include "controller.h" #include "util/state_wrapper.h" #include "common/bitfield.h" #include "common/bitutils.h" #include "common/log.h" #include #include Log_SetChannel(SIO); namespace SIO { namespace { union SIO_CTRL { u16 bits; BitField TXEN; BitField DTROUTPUT; BitField RXEN; BitField TXOUTPUT; BitField ACK; BitField RTSOUTPUT; BitField RESET; BitField RXIMODE; BitField TXINTEN; BitField RXINTEN; BitField ACKINTEN; }; union SIO_STAT { u32 bits; BitField TXRDY; BitField RXFIFONEMPTY; BitField TXDONE; BitField RXPARITY; BitField RXFIFOOVERRUN; BitField RXBADSTOPBIT; BitField RXINPUTLEVEL; BitField DSRINPUTLEVEL; BitField CTSINPUTLEVEL; BitField INTR; BitField TMR; }; union SIO_MODE { u16 bits; BitField reload_factor; BitField character_length; BitField parity_enable; BitField parity_type; BitField stop_bit_length; }; } // namespace static void SoftReset(); static SIO_CTRL s_SIO_CTRL = {}; static SIO_STAT s_SIO_STAT = {}; static SIO_MODE s_SIO_MODE = {}; static u16 s_SIO_BAUD = 0; } // namespace SIO void SIO::Initialize() { Reset(); } void SIO::Shutdown() { } void SIO::Reset() { SoftReset(); } bool SIO::DoState(StateWrapper& sw) { sw.Do(&s_SIO_CTRL.bits); sw.Do(&s_SIO_STAT.bits); sw.Do(&s_SIO_MODE.bits); sw.Do(&s_SIO_BAUD); return !sw.HasError(); } u32 SIO::ReadRegister(u32 offset) { switch (offset) { case 0x00: // SIO_DATA { ERROR_LOG("Read SIO_DATA"); const u8 value = 0xFF; return (ZeroExtend32(value) | (ZeroExtend32(value) << 8) | (ZeroExtend32(value) << 16) | (ZeroExtend32(value) << 24)); } case 0x04: // SIO_STAT { const u32 bits = s_SIO_STAT.bits; return bits; } case 0x08: // SIO_MODE return ZeroExtend32(s_SIO_MODE.bits); case 0x0A: // SIO_CTRL return ZeroExtend32(s_SIO_CTRL.bits); case 0x0E: // SIO_BAUD return ZeroExtend32(s_SIO_BAUD); [[unlikely]] default: ERROR_LOG("Unknown register read: 0x{:X}", offset); return UINT32_C(0xFFFFFFFF); } } void SIO::WriteRegister(u32 offset, u32 value) { switch (offset) { case 0x00: // SIO_DATA { WARNING_LOG("SIO_DATA (W) <- 0x{:02X}", value); return; } case 0x0A: // SIO_CTRL { DEBUG_LOG("SIO_CTRL <- 0x{:04X}", value); s_SIO_CTRL.bits = Truncate16(value); if (s_SIO_CTRL.RESET) SoftReset(); return; } case 0x08: // SIO_MODE { DEBUG_LOG("SIO_MODE <- 0x{:08X}", value); s_SIO_MODE.bits = Truncate16(value); return; } case 0x0E: { DEBUG_LOG("SIO_BAUD <- 0x{:08X}", value); s_SIO_BAUD = Truncate16(value); return; } default: ERROR_LOG("Unknown register write: 0x{:X} <- 0x{:08X}", offset, value); return; } } void SIO::SoftReset() { s_SIO_CTRL.bits = 0; s_SIO_STAT.bits = 0; s_SIO_STAT.DSRINPUTLEVEL = true; s_SIO_STAT.CTSINPUTLEVEL = true; s_SIO_STAT.TXDONE = true; s_SIO_STAT.TXRDY = true; s_SIO_MODE.bits = 0; s_SIO_BAUD = 0xDC; }