RASCSI/cpp/hal/systimer_allwinner.cpp

156 lines
5.2 KiB
C++

//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
// [ High resolution timer for the Allwinner series of SoC's]
//
//---------------------------------------------------------------------------
#include "hal/systimer_allwinner.h"
#include <sys/mman.h>
#include "hal/gpiobus.h"
#include "shared/log.h"
const std::string SysTimer_AllWinner::dev_mem_filename = "/dev/mem";
//---------------------------------------------------------------------------
//
// Initialize the system timer
//
//---------------------------------------------------------------------------
void SysTimer_AllWinner::Init()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
int fd;
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
LOGERROR("I can't open /dev/mem. Are you running as root?")
exit(-1);
}
hsitimer_regs =
(sun8i_hsitimer_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, hs_timer_base_address);
if (hsitimer_regs == MAP_FAILED) {
LOGERROR("Unable to map high speed timer registers. Are you running as root?")
}
sysbus_regs = (struct sun8i_sysbus_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
system_bus_base_address);
if (sysbus_regs == MAP_FAILED) {
LOGERROR("Unable to map system bus registers. Are you running as root?")
}
enable_hs_timer();
}
void SysTimer_AllWinner::enable_hs_timer()
{
// By default, the HSTimer clock gating is masked. When it is necessary to use
// the HSTimer, its clock gating should be opened in BUS Clock Gating Register 0
// and then de-assert the software reset in BUS Software Reset Register 0 on the
// CCU module. If it is not needed to use the HSTimer, both the gating bit and
// the software reset bit should be set 0.
LOGTRACE("%s [Before Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 | (1 << BUS_CLK_GATING_REG0_HSTMR);
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 | (1 << BUS_SOFT_RST_REG0_HSTMR);
LOGTRACE("%s [After Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
// Set interval value to the maximum value. (its a 52 bit register)
hsitimer_regs->hs_tmr_intv_hi_reg = (1 << 20) - 1; //(0xFFFFF)
hsitimer_regs->hs_tmr_intv_lo_reg = UINT32_MAX;
// Select prescale value of 1, continuouse mode
hsitimer_regs->hs_tmr_ctrl_reg = HS_TMR_CLK_PRE_SCALE_1;
// Set reload bit
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_RELOAD;
// Enable HSTimer
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_EN;
}
// TODO: According to the data sheet, we should turn off the HS timer when we're done with it. But, its just going to
// eat up a little extra power if we leave it running.
void SysTimer_AllWinner::disable_hs_timer()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
LOGINFO("[Before Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 & ~(1 << BUS_CLK_GATING_REG0_HSTMR);
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 & ~(1 << BUS_SOFT_RST_REG0_HSTMR);
LOGINFO("[After Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
}
uint32_t SysTimer_AllWinner::GetTimerLow()
{
// PiSCSI expects the timer to count UP, but the Allwinner HS timer counts
// down. So, we subtract the current timer value from UINT32_MAX
return UINT32_MAX - (hsitimer_regs->hs_tmr_curnt_lo_reg / 200);
}
uint32_t SysTimer_AllWinner::GetTimerHigh()
{
return (uint32_t)0;
}
//---------------------------------------------------------------------------
//
// Sleep in nanoseconds
//
//---------------------------------------------------------------------------
void SysTimer_AllWinner::SleepNsec(uint32_t nsec)
{
// If time is less than one HS timer clock tick, don't do anything
if (nsec < 20) {
return;
}
// The HS timer receives a 200MHz clock input, which equates to
// one clock tick every 5 ns.
auto clockticks = (uint32_t)std::ceil(nsec / 5);
uint32_t enter_time = hsitimer_regs->hs_tmr_curnt_lo_reg;
// TODO: NEED TO HANDLE COUNTER OVERFLOW
LOGTRACE("%s entertime: %08X ns: %d clockticks: %d", __PRETTY_FUNCTION__, enter_time, nsec, clockticks)
while ((enter_time - hsitimer_regs->hs_tmr_curnt_lo_reg) < clockticks)
;
return;
}
//---------------------------------------------------------------------------
//
// Sleep in microseconds
//
//---------------------------------------------------------------------------
void SysTimer_AllWinner::SleepUsec(uint32_t usec)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
// If time is 0, don't do anything
if (usec == 0) {
return;
}
uint32_t enter_time = GetTimerLow();
while ((GetTimerLow() - enter_time) < usec)
;
}